[스프링부트] Model을 DTO로 만들기

이나겸's avatar
Nov 19, 2024
[스프링부트] Model을 DTO로 만들기

 
💡

Model

  • MVC 패턴에서 데이터를 처리하는 부분으로, 데이터베이스와 상호작용하고 데이터를 가져와서 Controller에 전달
💡

DTO(Data Transfer Object)

  • 계층 간 데이터를 주고받기 위해 사용되는 객체
  • DTO를 활용하여 불필요한 데이터 전송을 막고 필요한 데이터만 전송함으로써 성능을 최적화

데이터 구조 설계 (DB에서 데이터를 가져왔다고 가정)

1. Product 클래스 - 상품

  • 제품 id
  • 제품명
    • 바지
    • 티셔츠
package model; public class Product { private int id; // 제품 id private String name; // 제품명(바지, 티) // 생성자 public Product(int id, String name) { this.id = id; this.name = name; } // getter setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

2. ProductOption 클래스 - 상품 옵션

  • 옵션 id
  • 옵션명
  • 가격
  • 수량
 
  • ProductOpton의 fk가 될 상품 id
package model; public class ProductOption { private int id; // 옵션 id private String name; // 옵션명 private int price; // 가격 private int qty; // 수량(qantity) private Product product; // DB에서는 product_id가 될것(fk) // 생성자 public ProductOption(int id, String name, int price, int qty, Product product) { this.id = id; this.name = name; this.price = price; this.qty = qty; this.product = product; } // getter setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public int getQty() { return qty; } public void setQty(int qty) { this.qty = qty; } public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } }

3. Order 클래스 - 주문

  • 주문 번호(id)
package model; public class Order { private int id; // 주문 번호(id) // 생성자 public Order(int id) { this.id = id; } // getter setter public int getId() { return id; } public void setId(int id) { this.id = id; } }

4. OrderOption 클래스 - 주문 옵션

  • 주문 옵션 id
  • 주문 옵션명 (ex. 하얀티)
  • 주문 수량 (ex. 5개)
  • 주문 금액 (ex. 10000원)
 
  • OrderOption의 fk가 될 상품 id
  • OrderOption의 fk가 될 주문 번호(id)
package model; public class OrderOption { private int id; // 주문 옵션 id private String optionName; // 주문 옵션명 ex)하얀티 private int qty; // 주문량 ex)5개 private int totalPrice; // 주문 금액 ex)10000원 private Product product; // DB에서는 product_id가 될것(fk) private Order order; // DB에서는 order_id가 될것(fk) // 생성자 public OrderOption(int id, String optionName, int qty, int totalPrice, Product product, Order order) { this.id = id; this.optionName = optionName; this.qty = qty; this.totalPrice = totalPrice; this.product = product; this.order = order; } // getter setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getOptionName() { return optionName; } public void setOptionName(String optionName) { this.optionName = optionName; } public int getQty() { return qty; } public void setQty(int qty) { this.qty = qty; } public int getTotalPrice() { return totalPrice; } public void setTotalPrice(int totalPrice) { this.totalPrice = totalPrice; } public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }

5. App1 클래스 (main) - 실습 문제

  • 문제 별 DTO에 넣을 데이터 주어짐
  • 문제 1. 상품 목록 화면 구현
  • 문제 2-1, 2-2. 상품 상세 화면 구현
  • 문제3. 주문 확인 상세 화면 구현 (주문 번호 2)
  • 문제 4. 주문 확인 상세 화면 구현 (주문 번호 1)
import com.google.gson.Gson; import dto.OrderDetailDTO; import dto.ProductDTO; import dto.ProductDetailDTO; import model.Order; import model.OrderOption; import model.ProductOption; import model.Product; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class App1 { public static void main(String[] args) { // 1. 상품 2개 Product p1 = new Product(1, "바지"); Product p2 = new Product(2, "티"); // 2. 옵션 4개 생성 (2-1, 2-2) ProductOption op1 = new ProductOption(1, "파란바지", 1000, 10, p1); ProductOption op2 = new ProductOption(2, "빨간바지", 2000, 10, p1); ProductOption op3 = new ProductOption(3, "노랑티", 1000, 10, p2); ProductOption op4 = new ProductOption(4, "하얀티", 2000, 10, p2); // 3. 구매 Order or1 = new Order(1); OrderOption orOption1 = new OrderOption(1, "파란바지", 2, 2000, p1, or1); OrderOption orOption2 = new OrderOption(2, "빨간바지", 2, 4000, p1, or1); OrderOption orOption3 = new OrderOption(3, "하얀티", 5, 10000, p2, or1); // 각 상품 별 구매 수량을 재고에서 빼줌 op1.setQty(op1.getQty() - 2); op2.setQty(op2.getQty() - 2); op4.setQty(op4.getQty() - 5); Order or2 = new Order(2); OrderOption orOption4 = new OrderOption(4, "노랑티", 7, 7000, p2, or2); // 각 상품 별 구매 수량을 재고에서 빼줌 op3.setQty(op3.getQty() - 7); // 4. 상품 목록 화면 (products) -> List<ProductDTO> List<Product> products = Arrays.asList(p1, p2); // 1번 문제 -> products DTO로 옮기기 // 알고리즘 // 4-1(1번 문제). product -> new ProductDTO(product); List<ProductDTO> productDTOS = new ArrayList<>(); // 5. 상품 상세 화면 (p1Options) -> ProductDetailDTO // 2-1번 문제 -> p1, p1Options DTO로 옮기기 List<ProductOption> p1Options = Arrays.asList(op1, op2); // 2-2번 문제 -> p2, p2Options DTO로 옮기기 List<ProductOption> p2Options = Arrays.asList(op3, op4); // 6-1. 주문 확인 상세 화면 (or2Options) -> OrderDetailDTO // 3번문제 데이터 (이것만 넣고 DTO 만들기) List<OrderOption> or2Options = Arrays.asList(orOption4); // 6-2. 주문 확인 상세 화면 (or1Options) -> OrderDetailDTO // 4번문제 데이터 (이것만 넣고 DTO 만들기) List<OrderOption> or1Options = Arrays.asList(orOption1, orOption2, orOption3); } }
 

풀이

  • 구현 예상 화면을 보고 나타낼 데이터를 생각하기
  • 각 클래스들의 정보를 어느 DTO에 어떻게 넣을지 생각하기
  • 라이브러리 lombok, gson 활용

1. 프로젝트에 라이브러리 설정

  • 프로젝트 우클릭 - 모듈 설정 열기
notion image
  • 라이브러리 - +아이콘 클릭 - lombok 검색 - 버전 찾아서 선택
notion image
  • 라이브러리 - +아이콘 클릭 - gson 검색 - 버전 찾아서 선택
notion image
  • 라이브러리 추가된 것 확인
notion image

2. ProductDTO 클래스

  • 문제1. 상품 목록 화면 구현
notion image
package dto; // 1번 문제(App1의 main 참고) import lombok.Data; import model.Product; @Data // getter setter toString 다 포함 public class ProductDTO { private int id; // 제품 id private String name; // 제품명(바지, 티) // 생성자 public ProductDTO(Product product) { this.id = product.getId(); this.name = product.getName(); } }

3. ProductDetailDTO 클래스

  • 문제2-1, 2-2. 상품 상세 보기 화면 구현
    • 해당 상품과 옵션들 나타내기
      • 옵션 id(3), 옵션명(노란티), 가격(1000), 수량(3)
      • 옵션 id(4), 옵션명(하얀티), 가격(2000), 수량(5)
notion image
package dto; // 2번 문제(App1의 main 참고) import lombok.Data; import model.ProductOption; import java.util.ArrayList; import java.util.List; @Data public class ProductDetailDTO { private int productId; private String productName; private List<ProductOptionDTO> options = new ArrayList<>(); // 생성자 public ProductDetailDTO(List<ProductOption> options) { this.productId = options.get(0).getProduct().getId(); this.productName = options.get(0).getProduct().getName(); // for-each문으로 순회 // models(ProductOption)를 dtos에 옮기기 for (ProductOption option : options) { this.options.add(new ProductOptionDTO(option)); } } @Data class ProductOptionDTO { private int id; // 옵션 id private String name; // 옵션명 private int price; // 가격 private int qty; // 수량(qantity) // 생성자 // ProductOption의 정보를 통째로 가져옴 (생성자의 파라미터) public ProductOptionDTO(ProductOption option) { this.id = option.getId(); this.name = option.getName(); this.price = option.getPrice(); this.qty = option.getQty(); } } }

4. OrderDetailDTO 클래스

  • 문제3. 주문 상세 보기 화면 구현 (주문 번호 2)
    • 주문 번호 2의 구매 내역 상세 나타내기
      • 상품 2번(티셔츠), 옵션(옵션id = 3 / 노란티), 수량(7), 총 금액(7000원)
      • 주문 합계 금액(sumPrice) 7000원
notion image
  • 문제4. 주문 상세 보기 화면 구현 (주문 번호 1)
    • 주문 번호 1의 구매 내역 상세 나타내기
      • 상품 1번(바지), 옵션(옵션id=1 / 파랑, 옵션 id = 2 / 빨강), 수량(파란바지2, 빨간바지2), 총 금액(파란바지 2000원, 빨강바지 4000원)
      • 상품 2번(티셔츠), 옵션(옵션id=3 / 하얀티), 수량(5), 총 금액(10000원)
      • 주문 합계 금액(sumPrice) 16000원
notion image
package dto; // 3번 문제(App1의 main 참고) // 4번 문제(App1의 main 참고) import lombok.Data; import model.OrderOption; import java.util.*; @Data public class OrderDetailDTO { private int orderId; private List<OrderProductDTO> products = new ArrayList<>(); private int sumPrice; // 생성자 public OrderDetailDTO(List<OrderOption> options) { // 1. orderId this.orderId = options.get(0).getOrder().getId(); // 2. sumPrice // options의 크기만큼 OrderOption 순회해서 정보 가져와서 합연산 for (OrderOption option : options) { this.sumPrice += option.getTotalPrice(); } // 3. products // 3-1. 주문 옵션들 productId [1,1,2] -> [1,2] 2개 만들기 Set<Integer> ids = new HashSet<>(); // Set은 중복을 알아서 제거해줌 for (OrderOption option : options) { ids.add(option.getProduct().getId()); // [1,2]를 들고있게됨 } // 3-2. 중복된 상품의 크기만큼 반복하면서 주문 옵션 추가하기 // ids의 크기(이 경우 2)만큼 순회 for (Integer id : ids) { List<OrderOption> temp = new ArrayList<>(); for (OrderOption option : options) { // if문의 중괄호 생략하고 간소화 if (id == option.getProduct().getId()) temp.add(option); } OrderProductDTO product = new OrderProductDTO(temp); products.add(product); } } @Data class OrderProductDTO { private int productId; private List<OrderOptionDTO> options = new ArrayList<>(); public OrderProductDTO(List<OrderOption> options) { this.productId = options.get(0).getProduct().getId(); // OrderOprion의 fk인 Product의 id // options의 크기만큼 OrderOption 순회해서 정보 가져옴 for (OrderOption option : options) { this.options.add(new OrderOptionDTO(option)); } } @Data class OrderOptionDTO { private int id; // 주문 옵션 id private String optionName; // 주문 옵션명 private int qty; // 주문량 private int totalPrice; // 주문 금액 public OrderOptionDTO(OrderOption option) { this.id = option.getId(); this.optionName = option.getOptionName(); this.qty = option.getQty(); this.totalPrice = option.getTotalPrice(); } } } }

5. App1 클래스 (main) - 결과 확인

  • gson을 이용한 결과 콘솔 출력
  • JSON Viewer 사이트에서 콘솔 출력된 문자열(gson)을 text에 복사붙여넣기 하고 Viewer를 확인하면 JSON 형태로 보여줌
import com.google.gson.Gson; import dto.OrderDetailDTO; import dto.ProductDTO; import dto.ProductDetailDTO; import model.Order; import model.OrderOption; import model.ProductOption; import model.Product; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class App1 { public static void main(String[] args) { // 1. 상품 2개 Product p1 = new Product(1, "바지"); Product p2 = new Product(2, "티"); // 2. 옵션 4개 생성 (2-1, 2-2) ProductOption op1 = new ProductOption(1, "파란바지", 1000, 10, p1); ProductOption op2 = new ProductOption(2, "빨간바지", 2000, 10, p1); ProductOption op3 = new ProductOption(3, "노랑티", 1000, 10, p2); ProductOption op4 = new ProductOption(4, "하얀티", 2000, 10, p2); // 3. 구매 Order or1 = new Order(1); OrderOption orOption1 = new OrderOption(1, "파란바지", 2, 2000, p1, or1); OrderOption orOption2 = new OrderOption(2, "빨간바지", 2, 4000, p1, or1); OrderOption orOption3 = new OrderOption(3, "하얀티", 5, 10000, p2, or1); // 각 상품 별 구매 수량을 재고에서 빼줌 op1.setQty(op1.getQty() - 2); op2.setQty(op2.getQty() - 2); op4.setQty(op4.getQty() - 5); Order or2 = new Order(2); OrderOption orOption4 = new OrderOption(4, "노랑티", 7, 7000, p2, or2); // 각 상품 별 구매 수량을 재고에서 빼줌 op3.setQty(op3.getQty() - 7); // gson Gson gson = new Gson(); // 4. 상품 목록 화면 (products) -> List<ProductDTO> List<Product> products = Arrays.asList(p1, p2); // 1번 문제 -> products DTO로 옮기기 // 알고리즘 // 4-1(1번 문제). product -> new ProductDTO(product); List<ProductDTO> productDTOS = new ArrayList<>(); // for-each로 Product의 정보를 담은 리스트 products를 순회해서 productsDTOs에 add for (Product product : products) { productDTOS.add(new ProductDTO(product)); } String r1 = gson.toJson(productDTOS); // JSON 형태의 스트링으로 출력 System.out.println(r1); // 5. 상품 상세 화면 (p1Options) -> ProductDetailDTO // 2-1번 문제 -> p1, p1Options DTO로 옮기기 List<ProductOption> p1Options = Arrays.asList(op1, op2); ProductDetailDTO productDetailDTO1 = new ProductDetailDTO(p1Options); String r2 = gson.toJson(productDetailDTO1); System.out.println(r2); // 2-2번 문제 -> p2, p2Options DTO로 옮기기 List<ProductOption> p2Options = Arrays.asList(op3, op4); ProductDetailDTO productDetailDTO2 = new ProductDetailDTO(p2Options); String r3 = gson.toJson(productDetailDTO2); System.out.println(r3); // 6-1. 주문 확인 상세 화면 (or2Options) -> OrderDetailDTO // 3번문제 데이터 (이것만 넣고 DTO 만들기) List<OrderOption> or2Options = Arrays.asList(orOption4); OrderDetailDTO orderDetailDTO1 = new OrderDetailDTO(or2Options); String r4 = gson.toJson(orderDetailDTO1); System.out.println(r4); // 6-2. 주문 확인 상세 화면 (or1Options) -> OrderDetailDTO // 4번문제 데이터 (이것만 넣고 DTO 만들기) List<OrderOption> or1Options = Arrays.asList(orOption1, orOption2, orOption3); OrderDetailDTO orderDetailDTO2 = new OrderDetailDTO(or1Options); String r5 = gson.toJson(orderDetailDTO2); System.out.println(r5); } }
  • 문제 1 결과
Q1 콘솔 출력 [{"id":1,"name":"바지"},{"id":2,"name":"티"}]
notion image
  • 문제 2 결과
Q2 콘솔 출력 {"productId":1,"productName":"바지","options":[{"id":1,"name":"파란바지","price":1000,"qty":8},{"id":2,"name":"빨간바지","price":2000,"qty":8}]}
notion image
  • 문제 3 결과
Q3 콘솔 출력 {"orderId":2,"products":[{"productId":2,"options":[{"id":4,"optionName":"노랑티","qty":7,"totalPrice":7000}]}],"sumPrice":7000}
notion image
  • 문제 4 결과
Q4 콘솔 출력 {"orderId":1,"products":[{"productId":1,"options":[{"id":1,"optionName":"파란바지","qty":2,"totalPrice":2000},{"id":2,"optionName":"빨간바지","qty":2,"totalPrice":4000}]},{"productId":2,"options":[{"id":3,"optionName":"하얀티","qty":5,"totalPrice":10000}]}],"sumPrice":16000}
notion image
 
 
Share article

Nakyeom's Study