DTO(Data Transfer Object) - 데이터 전송 객체
비지니스 계층과 프레젠테이션 계층간 데이터 교환을 하기 위해 사용하는 객체이다.
- 비지니스 계층 : 데이터베이스나 외부 시스템과 상호 작용하여 데이터를 검색하거나 수정하는 작업 수행
- 프레젠테이션 계층 : 웹 클라이언트의 요청 및 응답 처리
DTO는 Domain model의 복사본으로, 다양한 표현 계층의 Logic(표현 방식)을 추가하여 사용된다.
더 자세한 내용은 ⬇️
https://shout-to-my-mae.tistory.com/303
DB 데이터 -> Java -> Front
DB Entity - Java Object
1 => 변수 (스칼라, 단일 데이터)
[1, 2, 3] => 벡터
{id=1, name="user", password="1234", email="asdkkwe@nate.com}
=> 클래스(차원이 다른 데이터 모음)로 표현 : 모델(Model)
모델링
데이터베이스의 테이블을 보고 모델을 만드는 과정
프론트엔드 개발자에게 모델이 아닌 DTO로 전달
✅ 모델의 데이터를 DTO로 가공해서 넘겨줄 수 있다.
ex) average_price=price/12
DTO 생성 시나리오
DTO에 넣을 데이터도 시나리오를 고려해서 순서대로 만드는 것이 좋다.
✅ 관리자 데이터 만든 후 사용자 데이터 만들기
✅ DTO 객체 생성 순서 : Product -> Option -> User -> Cart -> OrderItem
DTO 생성 방법
단일 파일 생성
CartItemDTO.java
@Getter @Setter
public class CartRespFindAllDTO {
private List<ProductDTO> products;
private int totalPrice;
@Builder
public CartRespFindAllDTO(List<ProductDTO> products, int totalPrice) {
this.products = products;
this.totalPrice = totalPrice;
}
}
✳️ 파일 개수가 너무 많아질 수 있으므로 패키지를 만들어 도메인 별로 관리하는 것이 좋다.
static inner 클래스로 생성
1. DTO를 사용하는 클래스 안에 생성
CartRestController.java
package com.example.kakaoshop.cart;
import com.example.kakaoshop._core.utils.ApiUtils;
import com.example.kakaoshop.cart.response.CartItemDTO;
import com.example.kakaoshop.cart.response.CartRespFindAllDTO;
import com.example.kakaoshop.cart.response.ProductOptionDTO;
import com.example.kakaoshop.cart.response.ProductDTO;
import lombok.Builder;
import lombok.Data;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/carts")
public class CartRestController {
//장바구니 수정
@PostMapping("/update") // /carts/update
public ResponseEntity<?> updateCart(@RequestBody List<CartUpdateRequestDTO> request) {
// 카트 Info 리스트 만들기
List<CartInfoDTO> cartInfoDTOList = new ArrayList<>();
// 카트 Info 리스트에 담기
CartInfoDTO cartInfoDTO1 = CartInfoDTO.builder()
.cartId(4)
.optionId(1)
.optionName("01. 슬라이딩 지퍼백 크리스마스에디션 4종")
.quantity(10)
.price(100000)
.build();
cartInfoDTOList.add(cartInfoDTO1);
CartInfoDTO cartInfoDTO2 = CartInfoDTO.builder()
.cartId(5)
.optionId(2)
.optionName("02. 슬라이딩 지퍼백 플라워에디션 5종")
.quantity(10)
.price(109000)
.build();
cartInfoDTOList.add(cartInfoDTO2);
//responseDTO 만들기
CartUpdateResponseDTO responseDTO = CartUpdateResponseDTO.builder()
.carts(cartInfoDTOList)
.totalPrice(209000)
.build();
return ResponseEntity.ok(ApiUtils.success(responseDTO));
}
@Data
static class CartUpdateRequestDTO{
private int cartId;
private int quantity;
@Builder
public CartUpdateRequestDTO(int cartId, int quantity) {
this.cartId = cartId;
this.quantity = quantity;
}
}
@Data
static class CartUpdateResponseDTO{
private List<CartInfoDTO> carts;
private int totalPrice;
@Builder
public CartUpdateResponseDTO(List<CartInfoDTO> carts, int totalPrice) {
this.carts = carts;
this.totalPrice = totalPrice;
}
}
@Data
static class CartInfoDTO{
private int cartId;
private int optionId;
private String optionName;
private int quantity;
private int price;
@Builder
public CartInfoDTO(int cartId, int optionId, String optionName, int quantity, int price) {
this.cartId = cartId;
this.optionId = optionId;
this.optionName = optionName;
this.quantity = quantity;
this.price = price;
}
}
}
장점 : 사용하는 곳에서 바로 DTO를 가져와 쓸 수 있다.
단점 : 생성해야할 DTO가 많아지면 로직과 DTO의 코드가 섞여 코드가 매우 길어진다.
2. DTO를 패키지로 묶어 생성 (추천) ⭐️
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.stream.Collectors;
public class ProductResponse {
@Getter @Setter
public static class FindAllDTO {
private int id;
private String productName;
private String description;
private String image;
private int price;
public FindAllDTO(Product product) {
this.id = product.getId();
this.productName = product.getProductName();
this.description = product.getDescription();
this.image = product.getImage();
this.price = product.getPrice();
}
}
@Getter @Setter
public static class FindByIdDTO {
private int id;
private String productName;
private String description;
private String image;
private int price;
private List<OptionDTO> options;
public FindByIdDTO(Product product, List<Option> optionList) {
this.id = product.getId();
this.productName = product.getProductName();
this.description = product.getDescription();
this.image = product.getImage();
this.price = product.getPrice();
this.options = optionList.stream().map(OptionDTO::new).collect(Collectors.toList());
}
@Getter @Setter
public class OptionDTO {
private int id;
private String optionName;
private int price;
public OptionDTO(Option option) {
this.id = option.getId();
this.optionName = option.getOptionName();
this.price = option.getPrice();
}
}
}
}
✳️ DTO를 inner class로 묶어 작성하면
1. 필요한 DTO를 빠르게 찾을 수 있다.
2. 코드의 응집도가 높아진다.
3. 하나의 DTO를 여러 곳에서 사용할 수 있다.
4. 파일의 개수도 적어 관리하기 쉽다.
✳️ 파라미터를 객체로 받으면
- 필드마다 하나하나 작성하지 않고 객체만 넘겨주어도 되므로 편하다.
DTO의 중복 사용
DTO는 도메인 모델을 기반으로 생성되므로 View가 요구하는 표현 방식도 중복될 수 있다.
이때 기존 DTO를 재사용해야할지, 새로운 DTO를 생성해야할지 고민할 수 있다.
결론적으로는 새로운 DTO를 생성하는 것을 권장한다.
✅ 변경 가능한 표현 방식(View)
DTO는 사용자의 요청, 응답을 받는 바구니와도 같다.
DTO의 표현 방식은 요구에 맞게 언제든지 바뀔 수 있다.
DTO 사용 목적을 생각해보면, 표현 방식에 따라 모델이 수정되는 것을 막기 위함이다.
당장 같은 표현 방식이더라도 후에 언제든 바뀔 수 있으므로, DTO를 공유해서 사용하게 되면
표현 방식이 서로 달라진 후, 그에 따라 DTO를 새로 생성해주어야한다. 😞
✅ 도메인 분리 X
각각 다른 도메인에서 사용되는 DTO를 하나로 공유해서 사용하면 코드를 유연하게 변경하기 어려우며 확장이 어렵다.
또한 한 도메인이 변경될 경우 다른 도메인에 영향을 미치며, 유지보수가 어렵다.
✚ 추상 클래스를 이용해 DTO의 중복을 제거하는 경우도 있다.
'Spring > Spring 개발 상식' 카테고리의 다른 글
JDBC, MyBatis, JPA (0) | 2023.07.12 |
---|---|
@Builder와 @Getter, @Setter (1) | 2023.07.10 |
DAO vs DTO vs VO (2) | 2023.07.10 |
Spring Security Test (0) | 2023.07.08 |
DB : 기본키, 외래키, 제약조건 (1) | 2023.07.06 |