본문 바로가기

Spring/스프링 부트와 AWS로 혼자 구현하는 웹 서비스

(IntelliJ) Spring Data JPA 사용

728x90

1. 프로젝트에 Spring Data Jpa 적용하기

 

1) build.gradle 에 의존성 추가

- implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

: 스프링 부트용 Spring Data Jpa 추상화 라이브러리

: 스프링 부트 버전에 맞춰 자동으로 JPA 관련 라이브러리들의 버전을 관리

 

- runtimeOnly 'com.h2database:h2:1.4.197'

* h2

: 인 메모리 관계형 데이터 베이스

: 별도의 설치가 필요 없이 프로젝트 의존성만으로 관리할 수 있다.

: 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때마다 초기화된다는 점을 이용하여 테스트 용도로 많이 사용

: JPA의 테스트, 로컬 환경에서의 구동에서 사용할 예정

 

 

2. domain 패키지

- 도메인이란?

: 게시글, 댓글, 회원, 정산, 결제 등 소프트웨어에 대한 요구사항 혹은 문제 영역이라 생각하면 됩니다.

 

- Eclipse, STS 등에서는 dao 패키지와 비슷한 역할을 수행하지만 조금 결이 다르다고 생각하면 됩니다.

dao 패키지 : xml에 쿼리 담고, 클래스는 쿼리의 결과만을 담는다.

domain 패키지 : 모두 해결

 

1) posts 패키지와 Posts 클래스 생성

경로 : com/project/spring/springboot/domain/posts/Posts

 

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Getter // 6
@NoArgsConstructor // 5
@Entity // 1
public class Posts {

    @Id // 2
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 3
    private Long id;

    @Column(length = 500, nullable = false) // 4
    private String title;

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

    private String author;

    @Builder // 7
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

}

* 주요 어노테이션

@Entity = JPA 어노테이션

@Getter, @NoArgsConstructor =  롬복 어노테이션

>> 롬복 어노테이션은 필수 어노테이션인 아닙니다. 주요 어노테이션인 JPA 어노테이션을 클래스에 가까이 둡니다.

 

* Posts 클래스 : DB의 테이블과 매칭될 클래스(Entity 클래스라고 부름)

>> JPA 사용시 DB 데이터에 작업할 경우 실제 쿼리를 날리기보단, Entity 클래스의 수정을 통해 작업을 수행

 

1. @Entity

: 테이블과 링크될 클래스임을 나타냄

: 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍(_)으로 테이블 이름을 매칭

ex) StudentScore.java -> student_score table

 

2. @Id

: 해당 테이블의 PK 필드를 나타냄

: 웬만하면 PK는 Long 타입의 Auto_increment를 사용하길 추천

 

3. @GeneratedValue

: PK의 생성 규칙을 나타냄

: 스프링부트 2.0에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 됩니다.

 

4. @Column

: 테이블의 칼럼을 나타내며 굳이 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼이 된다.

: 사용하는 이유 >> 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용하기 위해서

: 문자열의 경우 VARCHAR(255)가 기본값 (사이즈를 500으로 늘리거나, 타입을 TEXT로 변경하고 싶은 경우 사용)

 

 5. @NoArgsConstructor

: 기본 생성자 기본 추가

: public Posts() {} 와 같은 효과

 

6. @Getter

: 클래스 내 모든 필드의 Getter 메소드를 자동생성

 

7. @Builder

: 해당 클래스의 빌더 패턴 클래스를 생성

: 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

 

 

* Setter 메소드가 없는 이유

>> 자바빈 규약을 생각하면서 getter/setter를 무작정 생성하는 경우가 있는데, 그럴 경우 해당 클래스의 인스턴스 값들이 언제 어디서 변해야하는지 코드상으로 명확하게 구분할 수가 없어, 차후 기능 변경 시 매우 복잡해지기 때문입니다.

 

- 해결 방안 >> 해당 필드의 값 변경이 필요할 경우 명확히 그 목적과 의도를 나타낼 수 있는 메소드를 추가해야 합니다.

 

- Setter 없이 DB에 값 삽입하는 방법

1) 기본 구조를 활용하는 방법

   1. 생성자를 통해 최종값을 채운 후 DB에 삽이하는 것

   2. 값 변경이 필요한 경우 해당 이벤트에 맞는 public 메소드를 호출하여 변경하는 것

 

2) @Builder를 통해 제공되는 빌더 클래스를 사용하는 방법

 

* 생성자와 빌더나 생성 시점에 값을 채워주는 역할은 똑같다, 다만 생성자의 경우 지금 채워야 할 필드가 무엇인지 명확히 지정할 수가 없다.

 

ex 생성자를 활용하는 방식) a 와 b의 위치를 변경해도 코드를 실행하기 전까지는 문제를 찾을 수 없다 

public Example(String a, String b) {

   this.a = a;

   this.b = b;

}

 

ex 빌더를 활용하는 방식) 어느 필드에 어떤 값을 채워야할지 명확하게 인지할 수 있음

Example.builder()

   .a(a)

   .b(b)

   .builder();

 

 

3. Posts 클래스로 Database를 접근하게 해줄 JpaRepository 생성

경로: com/project/spring/springboot/domain/posts/PostsRepository

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepository extends JpaRepository<Posts, Long> { }

* ibatis나 MyBatis 등에서 Dao라고 불리는 DB Layer 접근자 (JPA 에선 Repository라고 부르며 인터페이스로 생성)

 

- JpaRepository<Entity 클래스, PK타입>를 상속 >> 기본적인 CRUD 메소드가 자동으로 생성

 

- @Repository를 추가할 필요 없습니다.

 

- Entity 클래스와 기본 Entity Repository는 같은 패키지에 위치해야 합니다. (도메인 페키지)

 

 

 

4. PostsRepositoryTest 생성

경로: com/project/spring/springboot/domain/posts/PostsRepositoryTest

 

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  // 1
    public void cleanup() {
        postsRepository.deleteAll();
    }

    @Test
    public void 게시글저장_불러오기() {
        // given
        String title = "테스트 게시글";
        String content = "테스트 본문";

        postsRepository.save(Posts.builder() // 2
                .title(title)
                .content(content)
                .author("YSM")
                .build());

        // when
        List<Posts> postsList = postsRepository.findAll();  // 3

        // then
        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}

 

- 별다른 설정 없이 @SpringBootTest 를 사용할 경우 H2 데이터베이스를 자동으로 실행해 줍니다.

 

- 결과

정상 작동됐을 때 모습

 

* 하지만 해당 결과문에선 실제로 실행된 쿼리 형태를 볼 수 없습니다. 실행된 쿼리를 로그로 볼 수 있도록 설정을 할 수 있습니다.

경로: src/main/resource/application.properties

spring.jpa.show_sql=true

- 해당 옵션을 추가하게 되면 콘솔창에서 쿼리 로그를 확인할 수 있습니다.

 

- 옵션 추가후 결과

위와 같이 실행된 쿼리 로그를 확인할 수 있습니다.

- 하지만 여기서 create table 쿼리를 보면 id bigint generated by default by as dientity라는 옵션으로 생성됩니다.

이는 H2의 쿼리 문법이 적용되었기 때문입니다.

 

- MySQL의 쿼리를 수행해도 정상적으로 작동하기 때문에 이후 디버깅을 위해서 출력되는 쿼리 로그를 MySQL 버전으로 변경

경로: src/main/resource/application.properties

 

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

- 해당 옵션을 추가하게 되면 쿼리 로그를 MySQL 버전으로 볼 수 있습니다.

 

 

다음장에서 본격적으로 API를 만들어 보겠습니다.

728x90