스프링부트 공부기록(5) - 게시판 프로젝트 :: Dao 도메인 패키지, 글 등록, 수정, 조회 JPA 어노테이션, 테스트 코드 작성
my code archive
article thumbnail
반응형
  • domain 패키지 : 도메인을 담을 패키지, 여기에서 도메인이란? 게시글, 댓글, 회원, 결제 등 소프트웨어에 대한 요구사항 혹은 문제 영역에 해당됨. 기존 MyBatis에서 dao 패키지 역할과 비슷함. 단, 기존에는 xml에 쿼리를 작성하고 클래스에서 쿼리의 결과를 담았다면 이제 모든 것이 도메인 패키지에서 해결됨.
  • Web, Sercice, Repository, Dto, Domain 5가지 레이어 중 비지니스 처리를 담당할 곳이 바로 Domain.
  • 기존에는 모든 로직이 서비스 클래스 내부에서 처리됐기 때문에 서비스 계층이 무의미했으나
  • 도메인 모델에서 처리할 경우 각자 본인의 이벤츠 처리를 하며 서비스 메소드는 트랜잭션도메인 간의 순서만 보장해 준다.

  • 테스트 코드 작성 패키지 구조


🤍1. Posts 클래스 만들기

 

🔍JPA 어노테이션

  • @Entity : 테이블과 링크될 클래스임을 나타냄.
  • @Id : 해당 테이블의 PK 필드를 나타냄.
  • @GeneratedValue : PK의 생성 규칙을 나타냄.
  • @Column : 테이블의 컬럼을 나타냄. 굳이 선언하지 않아도 해당 클래스의 필드는 모두 컬럼이 되지만, 기본값 이외에 추가 변경이 필요할 시 사용함. ex) VARCHAR2(255) -> VARCHAR2(500) 변경이 필요할 때.

 

🔍롬복 어노테이션

  • @NoArgsConstructor : 기본 생성자 자동 추가 (=public Posts(){ })
  • @Getter : 롬복 어노테이션, 클래스 내 모든 필드의 Getter 메소드를 자동 생성
  • @Builder : 해당 클래스의 빌더 패턴 클래스 생성, 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함하겠다.

한 가지 특이점은 Setter 메소드가 없다는 것!!

 

  • 기존 자바빈 규약을 생각하며 getter/setter를 무작정 생성 시 해당 클래스의 인스턴스 값들이 언제 어디서 변해야 하는지 코드상으로 명확하게 구분할 수 없다.
  • Entity 클래스에서는 절대 Setter 메소드를 만들지 않으며 생성자를 통해 최종값을 채운 후 DB에 삽입한다.
  • 이 책에서는 생성자 대신 @Builder를 사용했고, 생성자나 빌더나 생성 시점에 값을 채워주는 역할은 똑같다고 한다.

 

🤍2. PostsRepository 인터페이스 만들기

  • Posts 클래스로 데이터베이스를 접근하게 하도록 JpaRepository 상속받음.
  • JpaRepository : Dao라고 불리는 DB Layer 접근자.
  • Entity 클래스 & 기본 Entity Repository는 함께 위치해야 한다. 밀접한 관계이고 기본 Repository 없이는 Entity 클래스가 제대로 역할을 할 수가 없다. => 모두 도메인 패키지에서 함께 관리한다.

 

🤍3. 테스트 코드 작성

  • 출력되는 쿼리 로그를 MySQL 버전으로 변경하도록 application.properties 파일 생성 후 다음 내용 추가.
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

 

테스트 통과

 

쿼리 로그 확인

 

 

🤍4. 등록/수정/조회 API 만들기

 

🔍Request 데이터를 받을 Dto - PostsUpdateRequestDto, PostsResponseDto, PostsSaveRequestDto

  • Entity 클래스와 유사한 형태이지만 Entity 클래스는 절대로 Request/Response 클래스로 사용해서는 안 된다.
  • Request/Response용 Dto는 View를 위한 클래스, 자주 변경이 필요하므로 View Layer와 DB Layer의 역할 분리는 매우매우 중요하다!!
  • 실제로 Controller에서 여러 테이블을 조인해야 할 경우가 빈번하므로 Entity 클래스만으로 표현이 어렵기 때문에 꼭 Entity 클래스와 Controller에서 쓸 Dto는 분리해서 사용해야 한다!
@Getter
@NoArgsConstructor
public class PostsUpdateRequestDto {

    private String title;
    private String content;

    @Builder
    public PostsUpdateRequestDto(String title, String content){
        this.title = title;
        this.content = content;
    }
}
import com.book.springboot.domain.posts.Posts;
import lombok.Getter;

@Getter
public class PostsResponseDto {

    private Long id;
    private String title;
    private String content;
    private String author;

                        //Entity의 필드 중 일부만 사용하므로 생성자로 Entity를 받아 필드에 값을 넣음.
    public PostsResponseDto(Posts entity){
        this.id = entity.getId();
        this.title = entity.getTitle();
        this.content = entity.getContent();
        this.author = entity.getAuthor();
    }
}
import com.book.springboot.domain.posts.Posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {

    private String title;
    private String content;
    private String author;

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

    public Posts toEntity() {
        return Posts.builder()
                        .title(title).content(content).author(author)
                        .build();
    }
}

 

🔍API 요청을 받을 Controller

 

🔍트랜잭션, 도메인 기능 간의 순서를 보장하는 Service

 

  • 기존 스프링에서는 @Autowired로 Bean을 주입받았다.
  • 주입 방식은 @Autowired, setter, 생성자 총 가지 방식이 있는데 이중 가장 권장하는 방식이 생성자 주입 방식이다.
  • @RequiredArgsContructor 어노테이션으로 final이 선언된 모든 필드를 인자값으로 하는 생성자를 자동 생성했다.
  • 생성자를 직접 안 쓰고 롬복 어노테이션을 사용=>클래스의 의존성 관계가 변경될 때마다 생성자 코드를 계속 수정해야 하는 번거로움 해결.

🤍5. 테스트, 톰캣 실행하여 확인

@Test
    public void Posts_등록된다() throws Exception{
        //given
        String title = "title";
        String content = "content";
        PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder().title(title).content(content).author("autor").build();

        String url = "http://localhost:"+port+"/api/v1/posts";

        //when
        ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);

        //then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);
        List<Posts> all = postsRepository.findAll();
        assertThat(all.get(0).getTitle()).isEqualTo(title);
        assertThat(all.get(0).getContent()).isEqualTo(content);
    }

    @Test
    public void Posts_수정된다() throws Exception{
        //given
        Posts savedPosts = postsRepository.save(Posts.builder().title("title").content("content").author("author").build());

        Long updateId = savedPosts.getId();
        String expectedTitle = "title2";
        String expectedContent = "content2";

        PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder().title(expectedTitle).content(expectedContent).build();

        String url = "http://localhost:"+port+"/api/v1/posts/"+updateId;
        HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);

        //when
        ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class);

        //then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);
        List<Posts> all = postsRepository.findAll();
        assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
        assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
    }

 

테스트 통과

 

 

Application.java 실행 후 톰캣 8080 포트로 활성화 시킴->http://localhost:8080/h2-console 접속하면 웹 콘솔 화면이 등장함.

 

Connect 버튼 누르면 현재 프로젝트의 H2를 관리할 수 있는 페이지로 이동함.

 

 

INSERT 쿼리문을 실행해본다.

 

 

브라우저로 API 조회

 

 

기본적인 등록/수정/조회 기능 만들고 테스트 완료!

반응형
profile

my code archive

@얼레벌레 개발자👩‍💻

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

반응형