[SpringBoot] (2) 게시판 구현하기(+ JPA, H2)

윤경·2022년 1월 24일
1

Spring Boot

목록 보기
70/79

모든 코드는 🔗깃허브를 참고


✔️ lombok

: 자바 개발시 자주 사용되는 Getter, Setter, 기본 생성자, toString 등 어노테이션 자동 생성 제공

롬복 적용하기

build.gradle에서 아래의 코드를 의존관계에 추가

    implementation('org.projectlombok:lombok')

그리고 plugin에서 아래 사진 설치하고,

아래와 같이 적용까지 해주기(설치는 해두면 계속 설치된 상태이지만 새로운 프로젝트를 할 때는 항상 이렇게 체크 박스를 체크해주어야 함)

기존 코드 리팩토링(롬복)

.../web/dto/HelloResponseDto.java 생성
➡️ dto 패키지는 앞으로 모든 응답 Dto가 담길 것

@Getter
: get 메소드 자동 생성

  • @RequiredArgsConstructor
    : 선언된 모든 final 필드에 생성자를 자동 생성
    final이 없는 필드는 생성자에 포함되지 않음

먼저 테스트 코드 활용하기

/test/.../web/dto/HelloResponseDtoTest.java 생성

  • assertThat
    : 검증 메소드로 Assertions.assertThat을 static으로 변환한 것
    Assertions를 import 할 때는 반드시 org.assertj.core.api.Assertions로 import 시켜줘야 함
  • isEqualTo
    : assertThat에 있는 값과 isEqualTo의 값을 비교해 일치할 때만 성공

assertThat을 import 할 때 Junit 대신 assertj를 선택한 이유는 다음 장점을 가졌기 때문이다.

  • CoreMatchers와 달리 추가적으로 라이브러리가 필요하지 않음
  • 자동 완성이 조금 더 확실하게 지원됨

📌 참고로 테스트 메소드 이름은 한글로 지어도 됨

HelloController 리팩토링

  • @RequestParam
    : 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션

추가된 코드 테스트하기

test/.../web/HelloControllerTest.java에 코드 추가하기

  • param
    : API 테스트할 때 사용될 요청 파라미터 설정
    (String 값만 허용)
  • jsonPath
    : JSON 응답값을 필드별로 검증할 수 있는 메소드
    ($를 기준으로 필드명을 명시)

⚠️ 잘 안된다 싶을 땐 import를 확인하기


✔️ JPA

: 자바 표준 ORM(Object Relational Mapping)

(MyBatis, iBatis는 ORM이 아님)
ORM은 객체를 매핑하는 것이고, SQL은 Mapper는 쿼리를 매핑한다.

개발자는 객체지향적으로 프로그래밍하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행한다.
➡️ 더이상 SQL에 종속적이지 않게 됨

SpringData JPA

JPA: 인터페이스
Hibernate, Eclipse, Link ..: 구현체

하지만 스프링에서 JPA를 다룰 때 이 구현체들을 직접 다루진 않는다.
구현체들을 조금 더 쉽게 다루기 위해 추상화 시킨 것 ➡️ SpringData JPA라는 모듈

이들의 관계: JPA ⬅️ Hibernate ⬅️ SpringData JPA

Hibernate와 SpringData JPA는 사실 별 차이가 없음에도 불구하고 SpringData JPA가 등장한 이유는 다음과 같다.

  • 구현체 교체의 용이성 (→ Hibernate 외에 다른 구현체로 쉽게 교체하기 위함)
  • 저장소 교체의 용이성 (→ 관계형 데이터베이스 외의 다른 저장소로 쉽게 교체하기 위함)

📌 참고하기

🔗JPA에 대하여
🔗JPA vs Hibernate vs SpringData JPA
🔗SpringData JPA vs MyBatis

JPA와 H2 의존성 등록하기

build.gradle에 다음 코드 추가하기

    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation('com.h2database:h2')

딱히 설치하지 않아도 사용할 수 있지만 🔗H2를 설치하기 위해서는 링크 참고

SpringData JPA 사용하기

web과 동일한 위치에 domain 패키지 생성

이제 여기에 도메인을 담을 것
(도메인: 게시글, 댓글, 회원, ... SW에 대한 요구사항 혹은 문제 영역)

domain/posts/Posts.java 생성해 코드 작성 (깃허브 참고)

  • @Entity
    : 테이블과 링크될 클래스임을 알림
  • @Id
    : 해당 테이블의 PK 필드
  • @GeneratedValue
    : PK 생성 규칙
  • @Column
    : 테이블의 column을 나타냄
    굳이 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼이 되지만 굳이 사용하는 이유는 기본값 외 추가로 변경이 필요한 옵션이 있다면 추가하면 됨

JPA 어노테이션: @Entity
lombok 어노테이션(필수 어노테이션은 아님): @Getter, @NoArgsConstructor

  • @Builder
    : 해당 클래스의 빌더 패턴 클래스 생성
    생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

⚠️ Entity 클래스에 절대 Setter 금지

setter 대신 @Builder를 통해 제공되는 빌더 클래스를 사용
(생성자나 빌더나 생성 시점 값을 채워주는 역할은 동일)

.../posts/PostsRepository.java 인터페이스 생성
→ Posts 클래스로 DB에 접근하게 해줄 인터페이스

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

이렇게 Entity 클래스, PK 타입만 지정해 상속해주면 기본적인 CRUD 메소드가 자동 생성된다.

@Repository를 추가할 필요도 없다.
⚠️ 단, Entity 클래스와 기본 Entity Repository가 함께 위치해야 한다.
(Entity 클래스는 기본 Repository 없인 제대로된 역할을 수행할 수 없기 때문)

테스트 코드 작성하기

test/.../domain/posts/PostsRepositoryTest.java 생성

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

쿼리 확인하기

아래 코드 추가하기

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

➡️ 이를 통해 MySQL 버전으로 쿼리를 확인할 수 있게 되었다.


✔️ 등록/수정/조회 API

API를 만들기 위해 필요한 클래스

  • Request 데이터를 받을 Dto
  • API 요청을 받을 Controller
  • 트랜잭션, 도메인 기능 간 순서를 보장하는 Service

⚠️ 꼭 Service에서 비즈니스 로직을 처리할 필요는 없음

비즈니스 로직을 담당해야 하는 곳은 Domain

Domain model은 도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고, 공유할 수 있도록 단순화 시킨 것 이다.

⚠️ Entity 클래스를 Request/Response 클래스로 사용하면 안됨
(Entity 클래스는 DB와 맞닿은 핵심 클래스라 이를 기준으로 테이블이 생성되고 스키마가 변경된다.
그런데 화면 변경은 아주 사소한 변화가 일어나는 부분이다. 이를 위해 Entity 클래스가 변경된다면 너무 큰 변경이 수행되기 때문이다.)
➡️ Dto는 View를 위한 클래스이기 때문에 Dto를 사용하기

코드에 @Autowired가 없는 이유

스프링빈을 주입받는 방법은 세 가지가 존재한다.
1. @Autowired ➡️ 권장하지 않는 방법
2. setter
3. 생성자

이미 @RequiredArgsConstructor로 생성자를 생성했기 때문에 @Autowired 없이도 빈을 주입받을 수 있었다.

업데이트에서 쿼리를 날리지 않는 이유

더티 체킹 (dirty checking)때문이다.

🔗더티 체킹에 대해서 알아보기

⌨️ 구현해야 하는 코드

🔗깃허브

  • PostsApiController.java
  • PostsService.java
  • PostsSaveRequestDto.java
  • PostsResponseDto.java
  • PostsUpdateRequestDto.java
  • Posts.java
  • PostsService.java

파일구조

테스트하기

test/.../web/PostsApiControllerTest.java

📌 Posts_등록된다 테스트에서 자꾸 실패를 했다가 겨우 성공했다. 하루종일 찾았는데 결국 /가 빠져있었다.

오타를 주의하자


이 글은 이동욱님의 <스프링 부트와 AWS로 혼자 구현하는  서비스> 를 보고 작성한 글입니다.
profile
개발 바보 이사 중

0개의 댓글