반응형
- 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 조회
기본적인 등록/수정/조회 기능 만들고 테스트 완료!
반응형
'💻 my code archive > 🏷️JAVA & Spring(Boot)' 카테고리의 다른 글
스프링부트 공부기록(7) - 머스테치(Mustache) 화면 구성하기 (0) | 2022.02.27 |
---|---|
스프링부트 공부기록(6) - JPA Auditing으로 생성 시간/수정시간 자동화하기 (0) | 2022.02.27 |
스프링부트 공부기록(4) - JPA로 데이터베이스 다루기 (0) | 2022.02.27 |
스프링부트 공부기록(3) - 롬복 사용하기 (0) | 2022.02.26 |
스프링부트 공부기록(2) - JUnit4 테스트 코드 작성, HelloController 테스트 (0) | 2022.02.25 |