notepad by Oxix
스프링 부트에서 JPA로 데이터베이스 다루기-2 본문
이전에 클래스 생성이 완료되었으면 Database를 접근하기 위한 JpaRepository를 생성한다. Dao라고 불리는 DBLayer 접근자를 JPA에서는 Repository라고 부르며 인터페이스로 생성한다. JpaRepository<Entity 클래스, PK타입>을 상속하면 기본적인 CRUD 메서드가 자동으로 생성된다.
@Repository를 추가할 필요도 없다. 다만 Entity 클래스와 기본 Entity Repository는 함께 위치 해야하는 점이며 기본 Repository 없이는 제대로 역할을 할 수가 없다. 나중에 프로젝트 규모가 커지면 Repository와 Entity클래스는 domain 패키지에서 함께 관리한다.
Spring Data JPA 테스트 코드 작성하기
save, findAll 기능을 테스트 하려고 한다.
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 PostRepositoryTest {
@Autowired
PostsRepository postsRepository;
@After
public void cleanup() {
postsRepository.deleteAll();
}
@Test
public void dataLoad() {
String title = "테스트 게시글";
String content = "테스트 본문";
postsRepository.save(Posts.builder()
.title(title)
.content(content)
.author("ieejo716@naver.com")
.build());
List postsList = postsRepository.findAll();
Posts posts = postsList.get(0);
assertThat(posts.getTitle()).isEqualTo(title);
assertThat(posts.getContent()).isEqualTo(content);
}
}
@After
- Junit에서 단위 테스트가 끝날 때마다 수행되는 메서드를 지정
- 보통은 배포 전 전체 테스트를 수행할 때 테스트간 데이터 침범을 막기 위해 사용한다.
- 여러 테스트가 동시에 수행되면 테스트용 데이터베이스인 H2에 데이터가 그대로 남아 있어 다음 테스트 실행시 테스트가 실패할 수 있다.
postsRepository.save
- 테이블 posts에 insert/update 쿼리를 실행한다.
- id 값이 있다면 update, 없다면 insert 쿼리가 실행된다.
postRepository.findAll
- 테이블 posts에 있는 모든 데이터를 조회하는 메서드
별다른 설정 없이 @SpringBootTest를 사용할 경우 H2 데이터베이스를 자동으로 실행해 준다.
등록/수정/조회 API 만들기
API를 만들기 위해서는 총 3개의 클래스가 필요하다.
- Request 데이터를 받을 Dao
- API 요청을 받을 Controller
- 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service
Web Layer
- 흔히 사용하는 컨트롤러와 JSP/Frameaker 등의 뷰 템플릿 영역이다.
- 이외에도 필터, 인터셉터, 컨트롤러 어드바이스 등 외부 요청과 응답에 대한 전반적인 영역을 의미한다.
Service Layer
- @Service에 사용되는 서비스 영역이다.
- 일반적으로 Controller와 Dao의 중간 영역에서 사용된다.
- @Transactional이 사용되어야 하는 영역이기도 하다.
Repository Layer
- Database와 같이 데이터 저장소에 접근하는 영역이다.
- Dao영역으로 이해하면 이해가 쉬울수도 있다.
Dtos
- Dto (Data Transfer Object) 는 계층 간에 데이터 교환을 위한 객체를 이야기하며 Dtos는 이들의 영역을 얘기한다.
- ex) 뷰 템플릿 ㅇ네진에서 사용될 객체, Repository Layer에서 넘겨준 객체 등
Domain Model
- 도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화 시킨 것을 의미한다.
- 택시 앱을 예로 들면 배차, 탑승, 요금 등이 모두 도메인이 될 수 있다.
- @Entity가 사용된 영역 역시 도메인 모델이라고 할 수 있다
Web, Service, Repository, Dto, Domain 5가지 레이어에서 비즈니스 처리를 담당해야 할 곳은 Domain이다. 기존에 서비스로 처리하던 방식을 트랜잭션 스크립트이다. 모든 로직이 서비스 클래스 내부에서 처리되어 서비스 계층이 무의미하며, 객체란 단순히 데이터 덩어리 역할만 하게된다. 반면 도메인 모델에서 처리할 경우는 다음과 같다.
public Order cancelOrder(int orderId){
Orders order = orderRepository.findByid(orderId);
Billing billing = billingRepository.finByOrderId(orderId);
Delivery delivery = deliveryRepository.findByOrderId(orderId);
delivery.cancel();
order.cancel();
billing.cancel();
return order;
}
각자 본인의 취소 이벤트 처리를 하며, 서비스 메서드는 트랜잭션과 도메인 간의 순서만 보장해준다.
PostsService
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
}
PostsApiController
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostsService service;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto) {
return service.save(requestDto);
}
}
PostsSaveRequestDto
@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();
}
}
Entity는 데이터베이스와 맞닿은 핵심 클래스이다. 그렇기 때문에 Request/Response 클래스로 사용해서는 안된다. 이유는 Entity클래스를 변경하는 것은 수많은 서비스나 비즈니스 로직이 Entity로 동작하는데 Entity 클래스가 변경되는 여러 클래스에 영향을 끼치기 때문이다. 그렇기 때문에 Request/Respons용 Dto -> view를 위한 클래스를 변경한다. 즉, 역할 분리를 철저하게 하는 것이 중요하다.
'Spring-boot' 카테고리의 다른 글
스프링 부트에서 JPA로 데이터베이스 다루기-4 (0) | 2022.07.08 |
---|---|
스프링 부트에서 JPA로 데이터베이스 다루기-3 (0) | 2022.07.05 |
스프링 부트에서 JPA로 데이터베이스 다루기-1 (0) | 2022.07.03 |
스프링부트에서 테스트 코드 작성하기-2 (0) | 2022.07.01 |
스프링부트에서 테스트 코드 작성하기-1 (0) | 2022.06.29 |