API 생성을 위한 클래스
1. Dto : Request 데이터 받기
2. Controller : API 요청 받기
3. Service : 트랜잭션, 도메인 기능 간의 순서 보장
Service에서 비즈니스 로직을 처리해야 한다고 오해하기 쉬운데, 트랜잭션과 도메인 간 순서만 보장하면 된다.
Spring 웹 계층
Web Layer
- Controller와 View Template 영역
- 필터, 인터셉터, @ControllerAdvice 등 외부 요청과 응답에 대한 전반적인 영역
Service Layer
- @Service
- 보통 Controller와 Dao의 중간 영역
- @Transactional이 사용되어야 하는 영역
Repository Layer
- DB 같은 데이터 저장소에 접근하는 영역
- Dao 영역과 유사
Dtos
- Dto: 계층 간 데이터 교환을 위한 객체
Domain Model
: 도메인이라는 개발 대상을 모든 사람이 동일한 관점에서 이해, 공유할 수 있도록 단순화시킨 것
- db의 테이블과 관계 필수 X. (VO처럼 값 객체들도 이 영역에 해당)
- ex: 택시 앱-> 도메인: 배차, 탑승, 요금, ...
그렇다면 로직은 어느 영역에서 수행되어야 할까?
기존 서비스로 처리하던 방식 (트랜잭션 스크립트)
-> 모든 로직이 서비스 클래스 내부에서 처리
-> 서비스 계층이 무의미
@Transactional
public Order cancelOrer(int orderId) {
OrdersDto order = orderDao.selectOrders(orderId);
BillingDto billing = billingDao.selectBilling(orderId);
DeliveryDto delivery= deliveryDao.selectDelivery(orderId);
String deliveryStatus = delivery.getStatus();
if("IN_PROGRESS".equals(deliveryStatus)) {
delivery.setStatus("CANCEL");
deliveryDao.update(delivery);
}
order.setStatus("CANCEL");
ordersDao.update(order);
billing.setStauts("CANCEL");
deliveryDao.update(billing);
return order;
}
도메인 모델에서 처리할 경우
-> 트랜잭션과 도메인 간 순서만 보장
@Transactional
public Order cancelOrer(int orderId) {
Orders order = orderRepository.findById(orderId);
Billing billing = billingRepository.findByOrderId(orderId);
Delivery delivery= deliveryRepository.findByOrderId(orderId);
delivery.cancel();
order.cancel();
billing.cancel();
return order;
}
이제 등록 기능을 만들어보자.
/web/PostsApiController.java
@RequiredArgsConstructor
@RestController
public class PostApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto) {
return postsService.save(requestDto);
}
}
/service/posts/PostsService.java
- @Autowired가 아닌 생성자를 이용한 Bean 주입
@RequiredArgsConstructor // final로 선언된 모든 필드로 생성자 생성
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
}
/web/dto/PostsSaveRequestDto.java
@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();
}
}
이제 Test 코드를 짜보겠다.
/web/PostsApiControllerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private PostsRepository postsRepository;
@After
public void tearDown() throws Exception {
postsRepository.deleteAll();
}
@Test
public void Posts_등록() throws Exception {
//given
String title = "title";
String content = "content";
String url = "http://localhost:" + port + "/api/v1/posts";
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("author")
.build();
//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);
}
}
- 이전의 HelloController와 달리 @WebMvcTest를 사용하지 않았다.
-> @WebMvcTest에서는 JPA 기능이 작동하지 않기 때문
=> JPA 기능까지 한 번에 테스트할 경우엔 @SpringBootTest와 TestRestTemplate을 사용하면 된다.
수정, 조회 기능도 이와 비슷한 구조로 생성하면 된다.
자세한 코드는 책을 참고하길 바란다.
참고로 db 사용에 앞서 웹 콘솔을 이용하려면 application.properties를 수정해야 한다. (다음 코드 추가)
spring.h2.console.enabled=true
이후에 Application 클래스의 main 메소드 실행 후, http://localhost:8080/h2-console로 접속하면 다음 화면처럼 보인다.
JDBC URL을 jdbc:h2:mem:testdb로 수정하고 connect 버튼을 누른다.
위 과정을 잘 따라왔다면 왼쪽 상단에 POSTS 테이블이 존재해야 한다.
해당 게시글은 [ 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 / 이동욱 ] 책을 따라한 것을 정리하기 위한 게시글입니다. 요약, 생략한 부분이 많으니 보다 자세한 설명은 책 구매를 권장합니다.
'Clone Coding > 스프링 부트와 AWS' 카테고리의 다른 글
[Mustache] 화면 구성 (0) | 2021.01.18 |
---|---|
[JPA Auditing] 생성/수정시간 자동화 (0) | 2021.01.18 |
[Spring] JPA로 database 다루기 (0) | 2021.01.11 |
[Spring] 테스트 코드 작성하기 (0) | 2021.01.08 |
[Spring] Gradle Project를 Spring boot로 변환하기 (0) | 2021.01.08 |