240516
DTO(데이터 전송용 객체)
Domain Object : 부족한 점
- 클라이언트에게 굳이 전달하지 않아도 되는 데이터를 포함한다. ex) 시스템 활용 데이터
- 필요한 데이터가 없을 수 있다. ex)
- 계층 간의 전송(이유) 시 데이터 변환 위험 - 주요 이유
- 화면 - Controller => Service => Repository => DB(무결성)
- 검증을 했어도 내부 로직을 거쳤기 때문에 무결성을 지킬 수 있는지
🔔 Entity(DTO 원본)
=> setter 가지면 안됩니다.
🔔 DTO(DTO 사본)
=> 계산된 데이터 가질 수 있어야함.
=> setter를 가질 수 있다.
DTO, Entity 반환 위치
Service는 Controller에서 받아온 DTO를 Entity로 변환해야하고, 결과를 받아와 다시 Controller로 Entity를 반환하면
Controller는 Service가 반환한 Entity를 다시 DTO로 변환해서 사용하는 식이다.
Controller 반환타입: DTO Service 반환타입: Entity
Controller -- DTO --> Service(초기에 Entity로 변환) -- > Entity ---> Controller(DTO로 변환)
DTO를 Entity로 변환하는 것.
DTO-> Entity
1. DTO 클래스 생성 - 필드(추가, 삭제 고려)
2. 변환하는 방법 고민 - 기본 생성자 + setter
3. 생성자 또는 setter
Order.java
package com.example.shoppingmall.Order;
import com.example.shoppingmall.Product.Product;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Order {
private int orderId;
// private String orderName;
Product product;
private int orderCount;
}
주문하기 기능을 구현할 때 Order 객체에서 주문할 상품명을 String으로 받아주었다가
Product 객체를 받아주면 상품에 대한 정보들을 전부 받아줄 수 있다.
아래 코드를
@PostMapping("/orders")
public ResponseEntity<Integer> orderProduct(@RequestBody Order order){
log.info(order.toString());
Integer orderId = orderService.orderProduct(order);
return new ResponseEntity<>(orderId, HttpStatus.CREATED);
}
이 코드로 바꿔줄 수 있다.
@PostMapping("/orders")
public ResponseEntity<Integer> orderProduct(@RequestBody Integer productId, Integer count){
log.info(order.toString());
Integer orderId = orderService.orderProduct(order);
return new ResponseEntity<>(orderId, HttpStatus.CREATED);
}
하지만 DTO를 등장할 타이밍,,,,!
productId 값과 count값을 DTO에 담아서 한번에 보낼 수 있을 것 같다.
⭐️⭐️ 방법 1. DTO(id,count) => order object(entity) ⭐️⭐️
방법 2. DTOa(id, dount) => DTOb(Product) => order(entity)
하지만 productId를 Product에서 꺼내와야한다,,,(Order 객체와 Product 객체가 다르기 때문에,,,)
주문할 때 주문 수량과 상품정보를 같이 Requset에 보내주어야 하는데
상품ID만 알면 사용가능하기 때문에 DTO를 만들어서 한번에 보내주기 위해,,,,,,DTO를 만들어야하는듯 하다.!
Product.java
package com.example.shoppingmall.Product;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Product {
private String name;
private int price;
private String description;
private int id;
private int categoryId;
// setter는 가능한 도메인 객체에는 만들지 말자!
}
두가지를 묶어주려면 아래와 같은 방법들이 존재하는데,,,,,,,,,,!
1. OrderController => ProductController => ProductService => ProductRepository
2. OrderController => ProductService => ProductRepository(상품을 등록할 때 괜찮, 검증과정 거치기때문)
3. OrderService => ProductService => ProductRepository
4. OrderService => ProductRepository
MSA vs 모놀리식
MA의 개념
어플리케이션은 하나의 아키텍처로 구성되어 있기에, 대부분의 기업용 어플리케이션은 하나의 아키텍처를 두는 모놀리식으로 개발되었다.
모놀리식 아키텍처는 개발과 관리가 용이하다는 장점이 있으나, 시스템이 복잡해지고 커질수록 코드를 이해하기가 어려워지고 그럴수록 유지보수하기 어려워진다.
또한 작은 버그를 수정하더라도 전체를 다시 빌드 & 배포해야된다는 점에서 서비스의 크기와 비례하여 시스템이 무거워진다.
비즈니스 로직들을 담은 시스템은 하나의 DB와 하나의 애플리케이션과 상호작용한다.
MA의 장점
1️⃣ 첫째, 모든 기능들이 하나의 시스템에서 동작하므로 서버 환경, DB 환경이 하나이기에 환경 세팅에 시간 소요가 상대적으로 적고, 환경을 고려한 개발이 쉽다.
2️⃣ 둘째, 모든 기능들이 하나의 시트템에서 동작하므로, 모든 기능들에 대한 테스트(End to End)를 하기 쉽다.
MA의 단점
하나의 시스템에서 모든 것을 관리하는 것은 단순하게 생각하면 간편하다고 생각할 수 있다. 하지만 앞서 말한 것처럼 기능이 무수히 추가되면서 시스템의 규모가 늘어나면 다양한 문제 발생 가능성이 높아진다.
1️⃣ 첫째, 기능이 많아진다는 것은 하나의 프로젝트에 코드들이 무수히 많아진다는 것을 의미한다. 코드가 많아지면 코드를 이해하고 해석하고 개발하는데 시간이 오래걸리는 것을 의미한다. 이는 유지보수가 어려워진다고 할 수 있다.
2️⃣ 둘째, 하나의 시스템에 모든 기능이 담겨있다면 기능 하나가 수정되면 전체를 다시 빌드하고 배포해야 한다. 만약 시스템의 크기가 크다면 빌드 및 배포 시간이 늘어난다는 것을 의미한다. 이는 버그나 수정사항의 즉시 적용이 어렵다는 점을 내포한다.
3️⃣ 셋째, 만약 특정 기능에 오류가 발생했다고 가정해보자. 해당 오류로 시스템이 죽으면 전체가 시스템이 먹통이 된다. 이는 작은 요소가 전체에 영향을 미치는 것을 내포하는데, 트래픽이 급증하게 되면 전체 시스템에 영향을 미치는 것까지 생각할 수 있다.
4️⃣ 넷째, 다양한 기능들은 그 종류에 따라 다양한 언어와 프레임워크를 선택하여 개발할 수 있다. 하지만 MA일 경우, 하나의 시스템으로 구성되기 때문에 기능별로 알맞은 언어 및 프레임워크를 선택하기 어렵다.
서비스가 커지면 커질수록 시스템이 무거워지는 모놀리식의 단점을 해결하기 위해, 여러 모듈(경량화되고 독립적인 서비스)을 조합하여 애플리케이션을 구현하는 방식으로 모듈마다 자체 DB를 가지고 있기 때문에, 개발부터 배포까지 효율적으로 수행할 수 있다. 하지만 독립적이고 확장성을 고려한 설계가 어려운 점과, 모듈이 나눠져 있어 트랜잭션 처리 등이 어렵다는 단점이 있다.
MSA의 개념
서비스가 커지면 커질수록 시스템이 무거워지는 모놀리식의 단점을 해결하기 위해, 여러 모듈(경량화되고 독립적인 서비스)을 조합하여 애플리케이션을 구현하는 방식으로 모듈마다 자체 DB를 가지고 있기 때문에, 개발부터 배포까지 효율적으로 수행할 수 있다.
하지만 독립적이고 확장성을 고려한 설계가 어려운 점과, 모듈이 나눠져 있어 트랜잭션 처리 등이 어렵다는 단점이 있다.
여러 비즈니스 로직은 기능(모듈)별 각자의 DB 환경을 공유하고, 모듈별로 애플리케이션과 독립적으로 통신하는 것을 확인할 수 있다.
MSA의 장점
1️⃣ 첫째, 서비스별 시스템이 독립적으로 구성되어 있어, 그만큼 하나의 기능을 개발할 때 봐야할 코드의 수도 적으며, 이해하기 쉽다. 즉 적고 작은 만큼 유지보수하기 쉽다.
2️⃣ 둘째, 서비스가 독립적으로 나눠져 있어, 하나의 서비스에 문제는 하나의 서비스만 수정하여 빌드 및 배포하면 되므로 상대적으로 빌드 및 배포가 빠르다.
3️⃣ 셋째, 각 기능에 맞는 언어와 프레임워크를 선택할 수 있다.
MSA의 단점
1️⃣ 첫째, 위에서 말한 것처럼 여러 모듈들이 분산되어 있어 관리 및 모니터링이 힘들다.
2️⃣ 둘째,시스템이 커질수록 각각의 서비스가 독립적인 서비스이긴 힘들다. 즉, A 서비스가 B 서비스의 코드를 호출할 수 있는데 이 부분은 모놀리식과 비교하였을 때 어렵다.
→ 모놀리식일 경운 단순히 같은 시스템 내부에서의 method 호출,
→ but, MSA 일 경우, http 통신 등을 통해 호출해야 한다.
3️⃣ 셋째,모듈별로 애플리케이션과 통신하므로 다양한 통신에서 발생하는 오류가 상대적으로 잦다.
4️⃣ 넷째,하나의 시스템의 동작을 모두 확인하는 통합 테스트를 End-to-End로 진행하기 위해선 각각의 마이크로 모듈들과 UI, Gateway를 구동시켜야 한다. 즉, 상대적으로 통합테스트 하기 어렵다.
서비스에 적용시키기
서비스의 확장 가능성이 낮거나 시스템이 크지 않다면 모놀리식 아키텍처를 사용하고,
서비스의 확장 가능성이 높거나 시스템이 크다면 마이크로서비스 아키텍처를 사용하는 것이 나을 것이다.