티스토리 뷰
스프링 부트와 AWS로 혼자 구현하는 웹 서비스를 읽고 정리한 내용입니다.
프로젝트를 할 때 프로젝트의 패키지를 나누는 것은 중요합니다. 패키지 마다 구분된 역할을 하도록 설계하여 모듈마다 결합도는 낮추고 응집도를 높여 모듈은 각자의 역할만 해야되기 때문입니다. 스프링 웹 계층에는 Web, Service, Repository, DTOs, Domain Model 5가지 계층이 있습니다. 각 계층의 역할에 대해서 알아보겠습니다.
Web Layer
흔히 사용하는 Controller와 JSP등의 View 템플릿 영역입니다. 이외에도 Filter, Intercepter, ControllerAdvise등 외부 요청과 응답에 대한 전반적인 영역을 의미합니다. 웹 애플리케이션의 최상위에 존재한다고 생각하면 됩니다.
Service Layer
Web Layer의 바로 아래에 존재하느느 층으로 @Service에 사용되는 서비스 영역입니다. 일반적으로 Controller와 DAO의 중간 영역에서 사용됩니다. @Transaction이 사용되는 영역이기도 합니다. 여기서 많은 사람들이 오해하는 것이, 서비스 계층에서 비즈니스 로직을 처리한다는 것입니다. 하지만 전혀 그렇지 않습니다. 서비스 계층은 트랜잭션, 도메인 간 순서 보장의 역할만 합니다.
서비스 계층에서 비즈니스 로직을 처리하는 코드를 보겠습니다. 배송을 주문하고 취소하는 로직이 전부 서비스 클래스 내부에서 처리됩니다. '서비스' 계층이 무의미해지며 객체는 단순히 데이터 덩 어리의 역할밖에 하지 못하는 상황이 됩니다. 프로젝트의 규모가 커지면 하나의 서비스에서 엄청나게 많은 모델을 읽어 로직이 구성된다면 서비스의 복잡도가 매우 높아지고 갈수록 유지보수 하기가 어려워 질 것 입니다.
그러면 비즈니스 로직은 어떤 계층에서 처리하는것이 바람직할까요?
@Transactional
public Order cancelOrder(int orderId){
// 1) 데이터베이스로 부터 주문, 배송, 결제 정보 조회
OrdersDto order = ordersDao.selectOrders(orderId);
BillingDto billing = billingDao.selectBilling(orderId);
DeliveryDto delivery = deliveryDao.selectDelivery(orderId);
// 2) 배송 취소가 가능한지 확인
String deliveryStatus = delivery.getStatus();
// 3) 배송 상태 변경
if("IN_PROGRESS".equals(deliveryStatus){
delivery.setStatus("CANCEL");
deliveryDao.update(delivery);
}
// 4) 각 테이블에 취소 상태 업데이트
order.setStatus("CANCEL");
ordersDao.update(order);
billing.setStatus("CANCEL");
deliveryDao.update(billing);
return order;
}
Domain Model
도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화 시킨 것을 도메인 모델이라고 합니다. 즉, 비즈니스 로직을 처리하는 영역입니다. 예를 들어 택시 앱이라고 하면 배차, 탑승, 요금 등이 모두 도메인이 될 수 있습니다. 서비스 계층에서 모든 비즈니스 로직을 처리하던 코드를 분리해보겠습니다. Order, Billing, Delivery 객체가 각자 본인 취소 이벤트를 처리하며, 서비스 메소드는 트랜잭션과 도메인 간의 순서만 보장해줍니다. @Entity가 사용된 영역 역시 도메인 모델이라고 이해하면 됩니다.
public Order cancelOrder(int orderId){
// 1)
Order orders = ordersRepository.findById(orderId);
Billing billing = billingRepository.findByOrderId(orderId);
Delivery delivery= deliveryRepository.findByOrderId(orderId);
// 2 & 3)
delivery.cancel();
// 4)
order.cancel();
billing.cancel();
return order;
}
Repository Layer
데이터베이스와 직접 연결되는 계층으로, 데이터 저장소에 접근하는 영역을 말합니다. DAO 혹은 @Repository를 사용한 DAO 구현 클래스가 이 계층에 속합니다. 즉 데이터베이스에 원하는 데이터를 Create, Read, Update, Delete하는 곳 입니다.
DTOs
DTO는 단순이 데이터를 저장하는 컨테이너, 즉 Data Transfer Object로, 다른 계층 간 교환을 위한 객체(Java Beans)입니다. 뷰 템플릿 엔진에서 사용될 객체, Repository Layer에서 결과로 넘겨 준 객체 등이 DTO에 해당합니다. 도메인 객체를 직접 View에 전달할 수도 있지만, 민감한 도메인 비즈니스 기능이 노출될 수 있으며 모델과 뷰 사이에 의존성이 생기기 때문입니다.
// User.java
public class User{
public Long id;
public String name;
public String email;
public String password; // 외부에 노출되면 안되는 정보
public String detailInformation; // 외부에 노출되면 안되는 정보
}
// UserController.java
@GetMapping("/{id}") // 예시 주소입니다.
public ResponseEntity<User> showArticle(@PathVariable long id){
User user = userService.findById(id);
return ResponseEntity.ok().body(user);
}
이 처럼 컨트롤러가 클라이언트의 요청에 대한 응답으로 도메인 객체인 User를 넘겨주면 몇 가지 문제점이 발생합니다.
1. 도메인 객체의 모든 속성이 외부에 노출됩니다.
2. 사용하지 않는 객체의 속성까지 전달하게 됩니다.
3. Model과 View가 서로 결합되어 View의 수정사항이 생길 때 Model에 영향을 미칠 수 있습니다.
4. 전달받은 객체를 이용해 다른 함수를 호출할 수 있습니다.
// UserDto.java
public class UserDto{
public final long id;
public final String name;
public final String email;
public static UserDto from(User user){
return new UserDto(user.getId(), user.getName(), user.getEmail());
}
}
// UserController.java
@GetMapping
public ResponseEntity<UserDto> showArticle(@PathVariable long id){
User user = userService.findById(id);
return ResponseEntity.ok().body(UserDto.from(user));
}
반면 DTO를 사용하면 앞서 언급된 문제들을 해결할 수 있습니다. 도메인 객체를 캡슐화하고, 화면에서 사용하는 데이터만 선택적으로 보낼 수 있습니다. password, detailInformation 같은 정보는 전달하지 않는 것을 볼 수 있습니다.
즉, DTO는 클라이언트 요청에 포함된 데이터를 담아 서버에 전달하고, 서버로 부터 응답 데이터를 담아 클라이언트에 전달하는 계층간 전달자 역할을 하는 것입니다.
Reference
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱
https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
'Spring' 카테고리의 다른 글
[Spring] JPA 상속관계 매핑, @MappedSuperClass (0) | 2022.06.15 |
---|---|
[Spring] JPA 다양한 연관관계 매핑 (0) | 2022.05.29 |
[Spring] JPA 연관관계 매핑 (0) | 2022.05.09 |
[Spring] JPA 엔티티 매핑 (0) | 2022.05.09 |
[Spring] JPA Entity Manager 영속성 컨텍스트 (0) | 2022.04.26 |
- Total
- Today
- Yesterday
- 런칭 페스티벌
- 피움
- 네트워크
- java
- jpa
- CI/CD
- 스프링 프레임워크
- 8주차 회고
- 스프링MVC
- 팀프로젝트
- 피움 6주차 회고
- 프로젝트
- dm-zoned 코드분석
- 스프링 Logback
- 우테코 회고
- 3차 데모데이
- 백준
- 스프링 부트
- ZNS SSD
- 환경 별 로깅 전략 분리
- 파이썬
- dm-zoned
- 5주차 회고
- 알림개선기
- Spring
- 회고
- 알림기능개선기
- 우테코
- 2차 데모데이
- ZNS
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |