[springboot-webserivce] Spring Data JPA 적용과 테스트
Updated:
[2020.12.28] 부터 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 통해 스프링 공부를 시작했다.
공부한 내용을 글로 정리하려고 한다.
그래야 나중에 다시 찾아보며 공부 할 수 있을테니까 !!
🍪 JPA, ORM
JPA (Java Persistence API) 의 줄임말로써 자바 ORM 기술에 대한 표준이라고 할 수 있다.
ORM (Object Relational Mapping) 은 무엇일까 ?
바로 객체가 테이블이 될 수 있도록 매핑 시켜주는 프레임워크이다.
결국 JPA 는 ORM 을 사용하기 위한 인터페이스를 모아 둔 것으로 이해하면 된다.
그렇다면 ORM 을 왜 사용하려고 하는 것 일까?
관계형 데이터베이스는 어떻게 데이터를 저장할지에 초점이 맞추어진 반면
자바와 같은 객체지향 언어는 기능과 속성을 한 곳에서 관리한다.
이렇게 각 기술에 대한 패러다임이 맞지 않아 생기는 문제를 패러다임 불일치 라고 하는데, 예를 들어보자
User user = findUser();
Group group = user.getGroup();
이 코드는 누가 봐도 user 를 통해 group 을 가져오기 때문에 group - user 간 부모 - 자식 관계를 알 수 있다.
하지만 이 코드에 데이터베이스가 추가 되면
User user = userDao.findUser();
Group group = groupDao.findGroup(user.getUserID());
user 와 group 을 따로 검색하는 문제가 발생한다.
JPA 는 이 문제를 해결하기 위해 사용한다.
개발자는 객체지향적으로 프로그래밍을 하면 JPA 가 알아서 SQL 문으로 바꾸어 실행을 해준다.
🍼 Spring Data JPA
JPA 는 인터페이스를 모아둔 것 이라고 했다.
때문에 Hibernate, EclipseLink 등 JPA 를 구현해둔 구현체를 사용한다.
하지만 스프링에서는 이 구현체들을 직접 다루지 않고 Spring Data JPA 라는 모듈을 통해 관리한다.
JPA ← Hibernate ← Spring Data JPA
이렇게 Spring Data JPA 를 사용하면 두 가지 장점이 있다.
- 구현체 교체의 용이성
- 저장소 교체의 용이성
Spring Data JPA 는 구현체를 자동으로 매핑 해주기 때문에 구현체를 Hibernate 에서 다른 것으로 바꾸어도 된다.
또 데이터베이스를 MySQL 에서 MongoDB 로 바꾸고 싶은 경우에도 손쉽게 바꿀 수 있다.
Spring Data JPA 를 Spring Data MongoDB 로 바꾸기만 하면 된다.
Spring Data 하위 프로젝트의 CRUD 구현 인터페이스는 모두 같기 때문이다.
⚙️ Spring Data JPA 적용
먼저 build.gradle 파일에 다음 두 가지 라이브러리를 등록한다.
compile(‘org.springframework.boot:spring-boot-starter-data-jpa’)
compile(‘com.h2database:h2’)
buildscript {
ext {
springBootVersion = '2.1.9.RELEASE'
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group 'com.boks'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.projectlombok:lombok')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
H2 데이터베이스는 인메모리 관계형 데이터베이스로 별도의 설치가 필요 없이 라이브러리로 관리할 수 있다.
메모리에서 돌아가기 때문에 프로그램을 재시작할 때마다 초기화가 되어 테스트 용도로 많이 사용된다.
다음으로 domain 패키지 안에 posts 패키지를 만들고 Posts 클래스를 만들자.
Posts 클래스는 Entity 클래스로 데이터베이스의 테이블과 매핑 될 클래스이다.
package com.boks.springboot.domain.posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter // 롬복의 Get 생성 어노테이션
@NoArgsConstructor // 롬복의 기본 생성자 자동 추가 어노테이션
@Entity // DB의 테이블과 매칭 될 클래스임을 나타냄
public class Posts {
@Id // 해당 테이블의 PK를 나타냄
@GeneratedValue (strategy = GenerationType.IDENTITY) // PK auto_increment
private Long id;
@Column (length = 500, nullable = false)
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;
}
}
@Entity 를 통해 클래스를 생성하면 클래스는 데이터베이스의 테이블이 된다.
@Id 을 통해 기본키를 설정하고 @Column 을 통해 테이블의 칼럼을 만든다.
@Column 을 안써도 필드는 모두 칼럼이 되지만 속성을 변경하고 싶은 경우에 사용한다.
@Builder 는 굉장히 중요한데 기본 생성자와 기능은 같지만
생성을 하는 경우에 어느 필드에 어떤 값을 넣어야 할지 정확히 알 수 있다.
이 클래스는 id,title, content, author 를 칼럼으로 가지는 테이블이 됐다.
이렇게 Entity 클래스를 생성 했으면 이 클래스로 데이터베이스를 접근하게 해 줄 PostsRepository 인터페이스를 생성한다.
package com.boks.springboot.domain.posts;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostsRepository extends JpaRepository<Posts, Long> {
}
인터페이스로 만든 뒤 JpaRepository<Entity Class, PK 타입> 을 상속받으면 기본적인 CRUD 메소드가 생성된다.
이 때 가장 중요한 부분은 Entity 클래스와 EntityRepository 인터페이스가 같은 위치에 있어야 한다.
🔨 Spring Data JPA 테스트
Entity 클래스도 만들고 Repository 도 만들었으니 정상 작동을 하는지 테스트 할 시간이다.
앞서 테스트 했던 것 처럼 test 폴더에 같은 경로로 패키지를 만들고 PostsRepositoryTest 클래스를 만들자.
package com.boks.springboot.domain.posts;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {
@Autowired
PostsRepository postsRepository;
@After // 테스트가 끝난 뒤 수행 할 메소드 지정
public void cleanUp() {
postsRepository.deleteAll();
}
@Test
public void findAllPosts() {
// given
String title = "TEST 제목";
String content = "TEST 본문";
postsRepository.save(Posts.builder()
.title(title)
.content(content)
.author("nkb7714@gmail.com")
.build());
// when
List<Posts> postsList = postsRepository.findAll();
// then
Posts posts = postsList.get(0);
assertThat(posts.getTitle()).isEqualTo(title);
assertThat(posts.getContent()).isEqualTo(content);
}
}
[Spring] 롬복을 이용한 DTO 생성과 테스트 의 테스트코드와 비슷하다.
save 메소드는 posts 테이블에 insert / update 쿼리를 실행 한다.
findAll 메소드는 테이블에 모든 데이터를 가져온다.
그 다음 assertThat 을 통해 잘 가져왔는지 확인한다.
결과는 통과!
📡 쿼리 로그 확인
아무런 설정없이 위 테스트 과정을 따라왔다면 테스트 로그에 쿼리문은 보이지 않는다.
테스트를 할 때 어떤 쿼리문이 동작하는지 알고 싶다면 다음과 같은 설정을 하면 된다.
main → resources 폴더에 application.properties 파일을 생성한다.
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
그리고 두 가지 옵션을 추가하면 된다.
첫 번째 옵션은 말 그대로 로그에 쿼리문을 보이게 해주는 옵션이고
첫 번째 옵션만 적용한 경우 H2DB 의 쿼리문을 보여준다.
두 번째 옵션은 H2DB 의 쿼리문을 MySQL 로 바꾸어 보여준다.
두 가지 옵션을 적용한 뒤 로그를 다시 보면
쿼리문도 로그로 확인을 할 수 있다.
📕 정리
ORM 은 객체가 테이블이 될 수 있도록 만들어 주는 것..
JPA 는 ORM 의 기능을 인터페이스로 모아 둔 것..
JPA 를 구현한 구현체는 Hibernate, Eclipse Link 등이 있으나 Spring Data JPA 를 사용..
테이블을 의미하는 객체를 Entity 클래스 라고 함..
이제 등록 / 수정 / 조회 API 만들 차례..
Leave a comment