elevne's Study Note

Spring Boot 복습 (1) 본문

Backend/Spring

Spring Boot 복습 (1)

elevne 2023. 4. 4. 19:00

Spring Boot 는 웹 프로그램을 쉽고 빠르게 만들 수 있도록 도와주는 웹 프레임워크이다. Spring Boot 는 장점이 많은 프레임워크라고 한다. 우선 보안 기능이 기본적으로 어느정도 되어있는 튼튼한 웹 프레임워크라고 한다. SQL 인젝션(악의적인 SQL 주입 공격), XSS(Cross-Site Scripting)(자바스크립트 삽입 공격), CSRF(Cross-Site Request Forgery)(위조된 요청 공격), Click Jacking(사용자의 의도하지 않은 클릭 유도) 과 같은 보안 공격을 기본적으로 막아준다. 또한 로그인, 페이징 등 많은 기본 기능들이 내장되어있다. 뿐만 아니라, Spring Boot 는 WAS 가 따로 필요없다. Spring Boot 대신 Spring 만 사용하여 웹 애플리케이션을 개발한다면 WAS 가 필요하다. 하지만 Spring Boot 에는 Tomcat 서버가 내장되어 있고 설정도 자동 적용되기에 WAS 에 대해 신경쓸 필요가 없다고 한다. (배포되는 jar 파일에도 Tomcat 이 내장되어 실행되므로 서로 다른 WAS 들로 인해 발생되는 문제들도 없다고 한다) 

 

 

 

Spring Initializr

 

 

 

Spring Boot

 

 

 

이러한 장점이 많은 Spring Boot 의 구조에 대해서도 알아보았다. src/main/java 디렉토리에는 자바 파일들을 작성한다. 컨트롤러, 폼과 DTO, 데이터베이스 처리를 위한 엔티티, 서비스 파일 등이 들어간다. 또한, 지정한 이름에 맞춰 시작을 담당하는 파일이 생성된다(위의 경우에는 StudyApplication.java). 해당 파일의 클래스에는 @SpringBootApplication 어노테이션이 붙게 되는데 이를 통해 Spring Boot 의 모든 설정이 관리된다고 한다. resources 디렉토리의 templates 디렉토리에는 HTML 파일 형태의 템플릿 파일들, static 디렉토리에는 css, js, 이미지 파일 등, application.properties 에는 프로젝트의 각종 환경설정이 들어간다. 그 다음으로 src/test/java 디렉토리는 JUnit Spring Boot 의 테스트 도구를 사용하여 서버를 실행하지 않은 상태에서 src/main/java 의 코드를 테스트 하는 코드를 작성한다. 마지막으로 build.gradle 파일은 Groovy 를 기반으로 한 빌드 도구로 Maven 과 같은 이전 세대 빌드 도구의 단점을 보완하고 장점을 취합하여 만든 빌드 도구라고 한다. 여기에는 프로젝트를 위해 필요한 플러그인과 라이브러리 등을 작성한다.

 

 

 

@Controller
public class MainController {

    @GetMapping("/controller")
    @ResponseBody
    public String controller(){
        return "CONTROLLER WORKING WELL";
    }

}

 

 

 

Spring Boot 에서는 URL 매핑을 위해 Controller 을 사용한다. 컨트롤러로 지정할 클래스에 @Controller 어노테이션을 적용하면 해당 클래스는 Spring Boot 의 컨트롤러가 된다. 그리고 @GetMapping (혹은 PostMapping,RequestMapping) 은 요청된 URL 과의 매핑을 담당한다. (@GetMapping http://localhost:8080 과 같은 도메인명과 포트는 서버 설정에 따라 변하기에 적지 않는다) 또한 위와 같이 @ResponseBody 어노테이션을 사용하면 URL 요청에 대한 응답으로 문자열을 return 하게된다.

 

 

 

또한 웹 서비스를 위해 DB 와 연동이 필요하다. DB 를 사용할 때는 SQL 쿼리를 작성하는 복잡한 과정이 필요한데, ORM 을 이용하여 자바 문법만으로도 이를 해결할 수 있다. ORM 은 DB 에 데이터를 저장하는 테이블을 자바 클래스로 만들어서 관리하는 기술로 이해해도 되는 것이다. 데이터를 관리하는데 사용되는 ORM 클래스를 Entity 라고 부르며, ORM 을 사용하면 내부에서 SQL 쿼리문을 자동으로 생성해주는 것이다. ORM 을 사용하면 DB 종류에 상관없이 일관된 코드를 유지할 수 있어서 프로그램에 대한 유지보수가 편리해지고, 내부에서 안전한 쿼리문을 생성하기에 오류 발생률을 줄일 수 있다.

 

 

 

ORM 을 사용하기 전 H2 데이터베이스를 아래와 같이 설치해준다.

 

 

 

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	runtimeOnly 'com.h2database:h2'
}

 

 

build.gradle 파일 dependencies 맨 밑에 추가해주었다. runtimeOnly 는 해당 라이브러리가 런타임 시에만 필요하다는 뜻이고, Compile 시에만 필요한 경우에는 complieOnly 를 사용한다. H2 를 사용하기 위해 application.properties 파일에 추가적인 설정들을 작성해준다.

 

 

 

# DATABASE
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

 

 

 

첫 번째 줄은 H2 콘솔의 접속을 허용할지의 여부이다. 둘 째는 콘솔 접속을 위한 URL 경로, 셋 째는 데이터베이스 접속을 위한 경로이다. 그 다음은 DB 접속에 사용하는 드라이버, 그 이후는 사용자명과 패스워드이다. 사용자명의 기본값은 sa 이고 패스워드는 로컬 개발 용도로만 사용하기에 따로 설정하지 않는다. 또한 위에서 datasource 의 경로를 ~/local 로 설정했기에 사용자의 홈디렉토리(~ 에 해당하는 경로) 밑에 local.mv.db 라는 파일을 생성해주면 된다 (윈도우의 경우에는 C:\Users\사용자명). 콘솔 화면에 접속해볼 수 있다.

 

 

 

result

 

 

 

H2 데이터베이스가 준비되었으니 JPA 를 사용할 준비를 진행한다. build.gradle 파일의 dependencies implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 를 추가해준다. build.gradle 의 implementation 은 해당 라이브러리 설치를 위해 일반적으로 사용하는 설정인데, 이는 해당 라이브러리가 변경되더라도 이 라이브러리와 연관된 모든 모듈을 컴파일하지 않고 직접 관련이 있는 모듈만 컴파일하기에 rebuild 속도가 빠르다고 한다. 또 application.properties 에도 아래 정보를 추가해준다.

 

 

 

# JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

 

 

 

위 둘은 각각 데이터베이스 엔진의 종류엔티티를 기준으로 테이블을 생성하는 규칙을 정의한 것이다. ddl-auto, 엔티티 기준 테이블 생성 규칙에는 다양한 설정 값이 올 수 있다. none 이 들어오면 엔티티가 변경되더라도 DB 를 변경하지 않는다. update 는 엔티티의 변경된 부분만 적용, validate 는 변경사항이 있는지 검사만 진행한다. create 는 스프링부트 서버가 시작될 때 모두 drop 하고 다시 생성하는 것이며, create-drop 은 create 와 동일하지만 종료시에도 모두 drop 한다. 개발환경에서는 보통 update 모드를 사용하고 운영환경에서는 none 이나 validate 모드를 사용하게된다고 한다.

 

 

 

그 다음으로는 사용할 Entity 들을 작성해주었다. Entity 는 DB 테이블과 매칭되는 자바 클래스들이다. 질의응답 게시판을 위한 엔티티들을 구성해보았다. 아래 표는 각각 질문과 답변 엔티티에 포함되어야 할 요소들이다.

 

 

 

Question

 

 

Answer

 

 

 

먼저 Question.java 클래스이다.

 

 

 

package com.springboot.study.entity;

import lombok.Data;

import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
public class Question {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(length = 200)
    private String subject;
    
    @Column(columnDefinition = "TEXT")
    private String content;
    
    private LocalDateTime createdDate;
}

 

 

 

Entity 로 만들기 위해 Question 클래스에 @Entity 어노테이션을 적용한다. 이를 적용해야 JPA 가 엔티티로 인식한다. 고유번호 id 속성에 적용한 @Id 어노테이션은 id 속성을 기본 키로 지정해준다. 기본 키로 지정하면 id 속성의 값은 DB 에 저장할 때 동일한 값으로 저장할 수 없다. @GeneratedValue 어노테이션을 적용하면 데이터를 저장할 때 해당 속성에 값을 따로 세팅하지 않아도 1 씩 자동으로 증가하여 저장된다. strategy 는 고유번호를 생성하는 옵션으로 IDENTITY해당 컬럼만의 독립적인 시퀀스를 생성하여 번호를 증가시킬 때 사용한다. strategy 옵션을 생략하면 @GenerativdValue 어노테이션이 지정된 컬럼들이 모두 동일한 시퀀스 번호를 생성하여 일정한 순서의 고유번호를 가질 수 없게된다. 또한, 엔티티의 속성은 테이블의 컬럼명과 일치하는데 컬럼의 세부 설정을 위해 @Column 어노테이션을 사용한다. length 는 컬럼의 길이를 설정, columnDefinition 은 컬럼의 속성을 정의할 때 사용한다. columnDefinition="TEXT"글자 수를 제한할 수 없는 경우에 사용한다. 엔티티의 속성은 @Column 어노테이션을 적용하지 않더라도 테이블 컬럼으로 인식되는데, 테이블 컬럼으로 인식되는 걸 원치 않는다면 @Transient 어노테이션을 적용해주면 된다. 그 다음으로는 동일하게 Answer 엔티티를 작성해준다.

 

 

 

package com.springboot.study.entity;

import lombok.Data;

import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
public class Answer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(columnDefinition = "TEXT")
    private String content;

    private LocalDateTime createDate;

    @ManyToOne
    private Question question;
}

 

 

 

Question 속성은 답변 엔티티에서 질문 엔티티를 참조하기 위해 추가되었다. 이렇게 다른 엔티티를 요소로 추가해줄 때는 이것이 연결된 속성이라는 것을 명시적으로 표시해주어야 한다고 한다. @ManyToOne 어노테이션이 사용되었다. 답변은 하나의 질문에 여러 개가 달릴 수 있는 구조이기에 ManyToOne, N:1 관계이다. 이 어노테이션을 붙여주면 Answer 엔티티의 Question 속성과 Question 엔티티가 연결된다고 한다. (실제 DB 에는 ForeignKey 관계가 생성된다) Question 이 부모, Answer 이 자식이 되는 구조인 것이다. 그런데 Question 에서 Answer 을 참조하는 반대 방향의 경우도 필요할 것이다. Question 에는 OneToMany 어노테이션을 사용해서 아래와 같이 수정해주면 되는 것이다.

 

 

 

package com.springboot.study.entity;

import lombok.Data;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;

@Data
@Entity
public class Question {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(length = 200)
    private String subject;

    @Column(columnDefinition = "TEXT")
    private String content;

    private LocalDateTime createdDate;

    @OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
    private List<Answer> answerList;
}

 

 

 

Answer 엔티티 객체로 구성된 answerList 를 속성으로 추가하고 @OneToMany 어노테이션을 사용하였다. 인자로 들어간 mappedBy 는 참조 엔티티의 속성명이 들어간 것이고, CascadeType.REMOVE 는 질문을 삭제하면 그에 달린 답변들도 전부 삭제되어야 하기에 이것으로 지정된 것이다. 

 

 

위에서 Cascading 이란 한국어로 영속성 전이라고 부른다. 이는 부모 엔티티가 영속화될 때 자식 엔티티도 같이 영속화되고, 부모 엔티티가 삭제될 때 자식 엔티티도 삭제되는 등 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 전이되는 것을 의미한다. REMOVE 외에도 ALL 로 지정하면 상위 엔티티에서 하위 엔티티로 모든 작업이 전파되고, PERSIST 를 사용하면 하위 엔티티까지 영속성을 전달하는 등 다양한 타입을 사용할 수 있다.

 

 

 

또, @ManyToOne 에는 fetchType 이라는 파라미터를 넣어줄 수 있다. fetchType = FetchType.EAGER 로 지정하면 연관된 엔티티를 즉시 조회, 하이버네이트는 가능하면 SQL 조인을 사용해서 한 번에 조회한다. EAGER 을 사용할 때 주의할 점으로 컬렉션을 둘 이상 즉시 로딩하면 좋지 않다는 것이다. 컬렉션과 조인한다는 것은 DB 테이블을 일대다 조인한다는 것이다. A 테이블을 N, M 두 테이블과 1대다 조인을 하면 실행결과가 N*M 이 되면서 너무 많은 데이터를 반환, 성능 저하를 유발할 수 있다고 한다. EAGER 외에 LAZY 를 사용하면 연관된 엔티티를 프록시로 조회한다. Entity 를 처음으로 로드할 때 해당 엔티티의 실제 데이터를 로드하는 것이 아니라 프록시 객체를 반환한다. 프록시 객체의 메서드를 호출할 때 실제 엔티티 데이터를 로드하는 것이고, 이 때 DB에서 쿼리를 실행하고 실제 데이터를 가져와서 메모리에 로드하는 것이다. 만약 A 엔티티와 연관된 엔티티 B 를 LAZY 로 설정하면, 엔티티 A 를 조회할 때 실제로는 A 와 B 데이터가 모두 로드되지 않고 A 에서 B 를 접근하려고 할 때 B 를 로드하는 것이다.

 

 

 

H2 콘솔에 접속해보면 테이블이 잘 생성된 것을 확인할 수 있다.

 

 

 

result

 

 

 

 

 

 

 

 

Reference:

https://wikidocs.net/161165

https://dar0m.tistory.com/263

 

'Backend > Spring' 카테고리의 다른 글

Spring Boot 복습 (5)  (0) 2023.04.09
Spring Boot 복습 (4)  (0) 2023.04.08
Spring Boot 복습 (3)  (0) 2023.04.07
Spring Boot 복습 (2)  (0) 2023.04.05
웹 개발 공부 (MyBatis)  (0) 2022.11.16