본문으로 바로가기
더보기

<스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱> 책을 활용해

공부한 내용을 정리할 예정입니다.


1. 요구사항 분석

앞으로 게시판을 구현할 예정입니다.

게시판의 요구사항은 아래와 같습니다.

  1. 게시판 기능
    • 게시글 조회
    • 게시글 등록
    • 게시글 수정
    • 게시글 삭제
  2. 회원 기능
    • 구글/네이버 로그인
    • 로그인한 사용자 글 작성 권한
    • 본인 작성 글에 대한 권한 관리

총 3개의 페이지가 만들어 질 것입니다.

게시글을 한번에 보여주고 로그인이 가능한 메인페이지, 게시글 등록 페이지, 게시글 수정 페이지

 

2. 프로젝트에 Spring Data Jpa 설정

1) 의존성 추가

먼저 build.gradle에 두가지 의존성들을 추가합니다.

compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('com.h2database:h2')
  • spring-boot-starter-data-jpa : 스프링 부트용 Spring Data Jpa 추상화 라이브러리
  • h2 : 인메모리 관계형 데이터베이스
          메모리에서 실행하기 때문에 애플리케이션 재시작마다 초기화 된다는 점을 이용해 테스트 용도로 많이 사용
          (JPA 테스트, 로컬 구동에 사용될 예정)

2) domain 패키지 생성

domain패키지를 생성합니다.

domain 패키지

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

 

( 기존에 MyBatis 같은 쿼리 매퍼를 사용했다면 dao 패키지를 떠올리겠지만, dao 패키지와 조금 결이 다릅니다.

  그간 xml에 쿼리를 담고, 클래스는 오로지 쿼리의 결과만 담던 일들이 모두 도메인 클래스에서 해결됩니다. )

 

3) Posts 클래스

domain 패키지에 posts패키지와 Posts클래스를 만듭니다.

 

Posts 클래스는 실제 DB의 테이블과 매칭될 클래스이며 보통 Entity클래스라고도 합니다.

JPA를 사용하면 DB 데이터에 작업할 경우 실제 쿼리를 날리기보다는,

Entity 클래스의 수정을 통해 작업합니다.

 

코드

package com.hyemcomi.springboot.domain.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
@NoArgsConstructor
@Entity
public class Posts{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    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 : 해당 테이블의 PK 필드를 나타냄
  • @GeneratedValue : PK의 생성 규칙을 나타냄
                              스프링 부트 2.0에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 됨
  • @Column : 테이블의 칼럼을 나타냄 굳이 선언 안해도 해당 클래스의 필드는 모두 칼럼이 됨
                    기본값 외에 추가로 변경이 필요한 옵션이 있으면 선언해서 사용함

이 외에 @NoArgsConstructor, @Getter, @Builder는 롬복 어노테이션들입니다.

서비스 초기에는 테이블 설계가 빈번하게 변경되는데 롬복 어노테이션들 사용은 코드 변경량을 최소화 시켜줍니다.

 

그리고 Entity클래스에서는 절대 Setter 메서드를 만들지 않습니다.

자바빈 규약을 생각하면 getter/setter를 무작정 생성하는 경우가 있는데

이렇게 되면 해당 클래스의 인스턴스 값들이 언제 어디서 변해야하는지 코드상으로 구분이 잘안가고

차후 기능 변경이 필요할 때 매우 복잡해집니다.

 

해당 필드 값 변경이 필요하면 명확히 목적과 의도를 나타낼 수 있는 메소드를 추가해 사용합니다.

또한 Setter 없이 값을 채워 DB에 삽입하기 위해 생성자 대신 @Builder를 통ㅇ해 제공되는 빌더 클래스를 사용

 

// 생성자 new Example(b,a)가 있다면, a와 b 위치를 변경해도 코드 실행 전까지 문제를 알 수 없음
public Example(String a, String b){
	this.a = a;
    	this.b = b;
}

// 빌더 패턴 사용, 명확하게 인지가능
Example.builder()
	.a(a)
    	.b(b)
    	.build();

4) PostsRepository 인터페이스

보통 ibatis나 MyBatis 등에서 Dao라고 불리는 DB Layer 접근자입니다.

JPA에서는 Repository라고 부르며 인터페이스로 생성합니다.

단순한 인터페이스 생성 후, JpaRepository<Entity 클래스, PK 타입>를 상속하면

기본적인 CRUD메소드가 자동 생성됩니다.

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

주의할 점은 Entity클래스와 기본 Entity Repository는 함께 위치해야 하는 점입니다.

package com.hyemcomi.springboot.domain.posts;

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

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

3. Spring Data JPA 테스트

1) 테스트 코드 작성

test 디렉토리에 domain.posts 패키지를 생성하고 테스트 클래스는 PostsRepositoryTest란 이름으로 생성합니다.

PostsRepositoryTest 디렉토리

코드

package com.hyemcomi.springboot.web.domain.posts;

import com.hyemcomi.springboot.domain.posts.Posts;
import com.hyemcomi.springboot.domain.posts.PostsRepository;
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 게시글저장_불러오기(){
        //given
        String title = "테스트 게시글";
        String content = "테스트 본문";

        postsRepository.save(Posts.builder()
                .title(title)
                .content(content)
                .author("hyemin0103@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);
    }
}

  • @After : Junit에서 단위 테스트가 끝날 때마다 수행되는 메소드를 지정
  • postsRepository.save : 테이블 posts에 insert/update 쿼리 실행 (id 값이 있다면 update, 없으면 insert)
  • postsRepository.findAll : 테이블 posts에 있는 모든 데이터를 조회

 

2) 쿼리 로그 보기 설정

이 상태로 테스트를 돌리게 되면 실제 실행 쿼리의 형태를 볼 수 없습니다.

실행 쿼리를 로그로 확인할 수 있도록 설정할 수 있습니다.

src/main/resources 디렉토리 아래에 application.properties파일을 생성합니다.

application.properties 위치

아래 코드를 추가해줍니다.

spring.jpa.show-sql=true

3) 테스트 결과 확인

테스트를 수행시켜 봅니다.

성공화면
쿼리 로그 확인

근데 쿼리문 중 create table에서 id bigint generated by dafault as identity라는 옵션으로 생성됩니다.

H2 쿼리 문법이 적용되어 있습니다. MySQL 쿼리로 수행해도 정상 작동하기 때문에

MySQL 버전으로 변경해봅니다.

아래 코드를 추가해줍니다.

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

MySQL 쿼리 로그 확인

다시 수행시키면 변경된 것을 확인할 수 있습니다.

 

이렇게 테스트까지 완료하면 JPA를 프로젝트에 정상적으로 적용시킨것입니다.

그 다음 포스트에서는 게시글 등록/수정/조회를 위한 API를 만들겠습니다.