이번 글에서는 스프링 부트와 AWS로 혼자 구현하는 웹 서비스라는 책 3장에서 스프링 부트에서 JPA로 데이터베이스를 다루는 내용을 정리한다.
1. JPA란?
JPA는 자바 표준 ORM(Object Relation Mapping)으로 어떻게하면 데이터베이스(어떻게 데이터를 저장할지에 초점)와 객체지향(메시지를 기반으로 기능과 속성을 한 곳에서 관리하는 기술)간의 패러다임 불일치를 해소할 수 있을까 라는 고민에서 나오게된 기술로 개발자는 객체지향적으로 프로그래밍을 하고 JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성한다.
2. Spring Data JPA
JPA는 인터페이스로 자바 표준 명세서로 이를 구현하기 위해서는 구현체가 필요한데 이 구현체는 대표적으로 Hibernate, Eclipse Link등이있다. 하지만 Spring 에서 JPA를 사용할 때 구현체를 직접 다루지는 않고 Spring Data JPA를 이용해 다루는데 이는 구현체를 다른 구현체로 바꾸기 용이하게 만들어준다. 아래는 JPA를 사용함에 따른 이점을 정리해 보았다.
- CRUD 쿼리를 직접 작성할 필요가 없어 생산성과 유지보수가 편해짐.
- 객체지향과 데이터베이스간의 패러다임 불일치 해소.
3. 프로젝트에 Spring Boot 적용하기.
build.gradle 파일에 아래와 같이 의존성을 등록한다.
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.projectlombok:lombok')
compile('org.springframework.boot:spring-boot-starter-data-jpa') // spring-jpa 추가
compile('com.h2database:h2') // h2라는 인메모리 테이터베이스를 사용할 수 있도록 해줌
testCompile('org.springframework.boot:spring-boot-starter-test')
}
4. Entity 클래스란?
JPA에서는 엔티티는 테이블에 대응하는 하나의 클래스로 DB 데이터에 작업할 경우 실제 쿼리를 날리기 보다는 이 Entity 클래스의 수정을 통해 작업한다. Entity 클래스는 아래와 같이 만든다.
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter//클래스 내 모든 필드의 Getter 메소드를 자동 생성해주는 lombok어노테이션
@NoArgsConstructor//기본 생성자를 자동 추가해주는 lombok어노테이션
@Entity//테이블과 링크될 클래스임을 나타내는 JPA어노테이션 / Entity 클래스에서는 절대로 Setter메소드를 만들지 않는다!!!
public class Posts extends BaseTimeEntity {
@Id//PK임을 나타내주는 JPA어노테이션
@GeneratedValue(strategy = GenerationType.IDENTITY)//PK의 생성 규칙을 나타내는 JPA어노테이션 스프링 부트 2.0에서는 GenerationType.IDENTITY를 선언해야 auto incremet됨
private Long id;
@Column(length = 500, nullable = false)//테이블 컬럼을 나타내주는 JPA어노테이션 선언안해도 자동으로 인식되지만, 기본값 외에 추가 옵션이 필요하면 설정. (문자열 길이등)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String author;
@Builder // 해당 클래스의 빌더 패턴 클래스를 생성. 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함.
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
}
여기서 빌더 패턴은 인스턴스를 생성할 때 생성자를 통해서 생성하지 않고 build() 메소를 만들어 인스턴스를 생성하는 디자인패턴이다.
코드로보면 아래와 같은 패턴이다.
생성
public class User {
private final String userName;
private final Long userAge;
public static class UserBuilder {
private String userName = "";
private String userAge = "";
public Builder userName(String userName){
userName = userName;
return this;
}
public Builder userAge(Long userAge){
userAge = userAge;
return this;
}
public User build(){
return new User(this);
}
}
public User(Builder builder) {
userName = builder.userName;
userAge = builder.userAge;
}
}
사용
User newUser = new UserBuilder()
.userName('hyungkang')
.userAge('29')
.build();
이렇게 Entity 클래스를 만들면 해당 Entity에 Repository인터페이스를 만들어 CRUD를 수행해야하는데 아래와 같이 생성한다. 여기서 JpaRepository<Entity 클래스,PK 타입>을 상속받으면 자동으로 CRUD가 생성된다.
import org.springframework.data.jpa.repository.JpaRepository;
/**
* JpaRepository<Entity Class,PK Type> 자동으로 CRUD메서드가 생성됨.
*/
public interface PostsRepository extends JpaRepository <Posts,Long> {
}
4. Spring 웹 계층
4.1. Web Layer
흔히 사용하는 컨트롤러(Controller)와 등의 뷰 템플릿 영역이다. 이외에도 필터(@Filter), 인터셉터, 컨트롤러 어드바이스(@ControllerAdvice) 등 외부 요청과 응답에 대한 전반적인 영역을 의미한다.
4.2. Service Layer
@Service에 사용되는 서비스 영역이다. 일반적으로 Controller와 Dao의 중간 영역에서 사용된다. @Transactional이 사용되어야 하는 영역이기도 하다.
4.3. Repository Layer
Database와 같이 데이터 저장소에 접근하는 영역이다. Dao(Data Access Object) 영역이라고 생각하면 된다.
4.4. Dtos
Dto(Data Transfer Object)는 계층 간에 데이터 교환을 위한 객체를 의미하며, Dtos는 이들의 영역을 의미한다. 예를 들어 뷰 템플릿 엔진에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체 등이 이들을 이야기한다.
4.5. Domain Model
도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화시킨 것을 도메인 모델이라고 한다. 비즈니스 로직을 처리하는 영역이다. 이를테면 택시 앱이라고 하면 배차, 탑승, 요금 등이 모두 도메인이 될 수 있다. @Entity가 사용된 영역 역시 도메인 모델이라고 이해하면 된다. 다만, 무조건 데이터베이스의 테이블과 관계가 있어야 하는 것은 아니다. VO처럼 값 객체들도 이 영역에 해당하기 때문이다.