Backend/spring (Boot)

Spring Boot DTO ↔ Entity ๋ณ€ํ™˜ ๋ฐฉ๋ฒ•

dddzr 2026. 1. 25. 12:42

๐Ÿ“Œ ์™œ DTO ↔ Entity ๋ณ€ํ™˜์ด ํ•„์š”ํ•œ๊ฐ€?

Entity๋Š” DB ๋ชจ๋ธ์ด๊ณ , DTO๋Š” ์ž…์ถœ๋ ฅ·๋ทฐ ๋ชจ๋ธ์ด๊ธฐ ๋•Œ๋ฌธ.

์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœํ•˜๋ฉด ๋ณด์•ˆ·์ปคํ”Œ๋ง·์œ ํšจ์„ฑ ๊ฒ€์ฆ·API ์ŠคํŽ™ ๋ถ„๋ฆฌ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฏ€๋กœ,

DTO๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์™ธ๋ถ€์™€ ๋‚ด๋ถ€๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด Spring ์‹ค๋ฌด ํ‘œ์ค€์ด๋‹ค.

 

๐Ÿ“Œ ๊ฐ์ฒด ๋ณ€ํ™˜ 3๊ฐ€์ง€ ๋ฐฉ๋ฒ• ๋น„๊ต

Spring Boot์—์„œ DTO ↔ Entity ๋ณ€ํ™˜ ์‹œ ๋Œ€ํ‘œ์ ์œผ๋กœ ์•„๋ž˜ 3๊ฐ€์ง€๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ“ ๋น„๊ต ๊ธฐ์ค€

  1. ์ปฌ๋ ‰์…˜(List) ๋ณ€ํ™˜
  2. ์—ฐ๊ด€ ๊ด€๊ณ„ ํ•„๋“œ ๋ณ€ํ™˜ (์˜ˆ: User → Address ํฌํ•จ)
  3. ํƒ€์ž… ๋ณ€ํ™˜ (์˜ˆ: LocalDateTime → String)
๋ฐฉ๋ฒ• List ๋ณ€ํ™˜ ์—ฐ๊ด€ ๊ด€๊ณ„ ํ•„๋“œ ๋ณ€ํ™˜ ํƒ€์ž… ๋ณ€ํ™˜ (์˜ˆ: LocalDateTime → String)
BeanUtils.copyProperties() โŒ ์ˆ˜๋™ ์ฒ˜๋ฆฌ ํ•„์š” โŒ ์ž๋™ ๋ณ€ํ™˜ ๋ถˆ๊ฐ€ โŒ ์ง์ ‘ ๋ณ€ํ™˜ ํ•„์š”
ModelMapper โœ… ์ž๋™ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ ๐Ÿ˜ ์ผ๋ถ€ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ โœ… ์ปค์Šคํ…€ ๋ณ€ํ™˜ ์ง€์›
์ˆ˜๋™ ๋ณ€ํ™˜ (์ง์ ‘ ๋งคํ•‘) โœ… ์™„๋ฒฝํ•œ ์ œ์–ด ๊ฐ€๋Šฅ โœ… ์›ํ•˜๋Š” ๋Œ€๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ โœ… ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ

โœ… 1. BeanUtils.copyProperties()

Spring์˜ BeanUtils.copyProperties()๋Š” ๊ฐ„๋‹จํ•œ DTO ๋ณ€ํ™˜์— ์œ ์šฉ.
ํ•˜์ง€๋งŒ, ์—ฐ๊ด€ ๊ด€๊ณ„ ํ•„๋“œ(@OneToMany, @ManyToOne)๋‚˜ ํƒ€์ž… ๋ณ€ํ™˜(LocalDateTime → String)์ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์•„์„œ ์ง์ ‘ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

 

๐Ÿ”น ์˜ˆ์ œ ์ฝ”๋“œ

  • OrderItemDetails orderItemDetails = new OrderItemDetails();
  • BeanUtils.copyProperties(orderItem, orderItemDetails);

 

๐Ÿ”ฅ ์žฅ์  & ๋‹จ์ 

โœ” ์žฅ์ 
๋น ๋ฅด๊ณ  ๋‹จ์ˆœ
ํ•„๋“œ๋ช…์ด ๊ฐ™์œผ๋ฉด ์ž๋™ ๋ณ€ํ™˜

 

โŒ ๋‹จ์ 
์ปค์Šคํ…€/์ค‘์ฒฉ ํ•„๋“œ ์ฒ˜๋ฆฌ ๊ฑฐ์˜ ์—†์Œ

 

๐Ÿ”น ๋ฆฌ์ŠคํŠธ ๋ณ€ํ™˜ ๋ฐ ์—ฐ๊ด€ ๊ด€๊ณ„ ๋ณ€ํ™˜ ์˜ˆ์ œ (์ˆ˜๋™ ์ฒ˜๋ฆฌ ํ•„์š”)

orderItemDetails.setOrderId(orderItem.getOrder().getOrderId()); // ์—ฐ๊ด€ ๊ด€๊ณ„ ํ•„๋“œ ์ง์ ‘ ๋งคํ•‘
orderItemDetails.setProductName(orderItem.getProduct().getName());

 


โœ… 2. ModelMapper

ModelMapper๋Š” List ๋ณ€ํ™˜, ์—ฐ๊ด€ ๊ด€๊ณ„ ๋ณ€ํ™˜, ํƒ€์ž… ๋ณ€ํ™˜์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ.
ํ•˜์ง€๋งŒ ์„ฑ๋Šฅ์ด ๋‹ค์†Œ ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Œ.

 

๐Ÿ”น ์˜ˆ์ œ ์ฝ”๋“œ

  • ModelMapper modelMapper = new ModelMapper();
  • OrderItemDetails orderItemDetails = modelMapper.map(orderItem, OrderItemDetails.class);

 

๐Ÿ”ฅ ์žฅ์  & ๋‹จ์ 

โœ” ์žฅ์ 
์ž๋™ ๋งคํ•‘, ์ปจ๋ฒค์…˜ ๊ธฐ๋ฐ˜

 

โŒ ๋‹จ์ 
์„ค์ • ๋ณต์žก, ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ ์ด์Šˆ ๊ฐ€๋Šฅ

 

๐Ÿ”น ๋ฆฌ์ŠคํŠธ & ์—ฐ๊ด€ ๊ด€๊ณ„ ์ž๋™ ๋ณ€ํ™˜ ์˜ˆ์ œ

modelMapper.createTypeMap(Product.class, ProductDTO.class)
    .addMappings(mapper -> {
        mapper.map(Product::getName, ProductDTO::setProductName);
        mapper.map(src -> src.getCategory().getCategoryName(), ProductDTO::setCategoryName);
    });

 


โœ… 3. ์ˆ˜๋™ ๋ณ€ํ™˜

์—ฐ๊ด€ ๊ด€๊ณ„(@OneToMany, @ManyToOne) ํ•„๋“œ ๋ณ€ํ™˜, ๋ฆฌ์ŠคํŠธ ๋ณ€ํ™˜, ํƒ€์ž… ๋ณ€ํ™˜์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Œ.

 

๐Ÿ”น ์˜ˆ์ œ ์ฝ”๋“œ

public static ProductDTO convertToDto(Product product) {
    ProductDTO productDTO = new ProductDTO();
    productDTO.setId(product.getId());
    productDTO.setName(product.getName());

    // ์—ฐ๊ด€ ๊ด€๊ณ„ ๋ณ€ํ™˜
    productDTO.setCategoryName(product.getCategory().getCategoryName());

    // ๋ฆฌ์ŠคํŠธ ๋ณ€ํ™˜
    List<ProductDetailDTO.ProductStockDto> stockDTOs = product.getStocks().stream()
        .map(stock -> new ProductDetailDTO.ProductStockDto(
            stock.getSize().getSizeName(),
            stock.getColor().getColorName(),
            stock.getStockQuantity()))
        .collect(Collectors.toList());

    productDTO.setProductStocks(stockDTOs);

    return productDTO;
}

 

๐Ÿ”ฅ ์žฅ์  & ๋‹จ์ 

โœ” ์žฅ์ 
๊ฐ€์žฅ ๋ช…ํ™•, ์œ ์ง€๋ณด์ˆ˜ ์‰ฌ์›€

 

โŒ ๋‹จ์ 
์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Œ


๐Ÿš€ ์ตœ์ข… ๊ฒฐ๋ก : ์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์„ ํƒํ• ๊นŒ?

๋ฐฉ๋ฒ• ์ถ”์ฒœ ์‚ฌ์šฉ ๊ฒฝ์šฐ
BeanUtils.copyProperties() ํ•„๋“œ๋ช…์ด ๋™์ผํ•œ ๊ฐ„๋‹จํ•œ DTO ๋ณ€ํ™˜์ผ ๋•Œ
ModelMapper ์ž๋™ ๋ณ€ํ™˜์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ (์—ฐ๊ด€ ๊ด€๊ณ„ ํฌํ•จ)
์ˆ˜๋™ ๋ณ€ํ™˜ ์ตœ๋Œ€ ์„ฑ๋Šฅ & ์ •ํ™•ํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ