Lambda, Optional, Stream API 모두 Java 1.8 (Java 8)에 새로 추가된 기능입니다.
1. 원본 DTO 코드
@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
for (OrderOption option : options) {
this.sumPrice += option.getTotalPrice();
}
// 3. products
Set<Integer> ids = new HashSet<>();
for (OrderOption option : options) {
ids.add(option.getProduct().getId());
}
// 3.2 중복된 상품의 크기만큼 반복하면서 주문 옵션 추가하기
for (Integer id : ids) {
// 임시 컬렉션
List<OrderOption> temp = new ArrayList<>();
for (OrderOption option : options) {
if (id == option.getProduct().getId()) {
temp.add(option);
}
}
OrderProductDTO product = new OrderProductDTO(temp);
// this.products로 안 적어도 된다. products 와 같은 이름의 변수가 없어서
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();
this.options = options.stream()
.map(orderOption -> new OrderOptionDTO(orderOption))
.toList();
}
}
@Data
class OrderOptionDTO {
private int 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();
}
}
}
}
2. sumPrice 리팩터링
for (OrderOption option : options) {
this.sumPrice += option.getTotalPrice();
}
// 리팩터링 후
this.sumPrice = options.stream()
.mapToInt(value -> value.getTotalPrice())
.sum();
- .mapToInt(value -> value.getTotalPrice())
- 스트림의 각 Option 객체를 int 값으로 변환해서 sum() 메서드를 실행 가능하게 변환합니다.
3. products 리팩터링
Set<Integer> ids = new HashSet<>();
for (OrderOption option : options) {
ids.add(option.getProduct().getId());
}
for (Integer id : ids) {
List<OrderOption> temp = new ArrayList<>();
for (OrderOption option : options) {
if (id == option.getProduct().getId()) {
temp.add(option);
}
}
OrderProductDTO product = new OrderProductDTO(temp);
products.add(product);
}
// 리팩터링 후
this.products = options.stream()
.collect(Collectors.groupingBy(op -> op.getProduct().getId()))
.entrySet() // Map타입을 Set타입으로 변경(컬렉션만 stream 가능)
.stream()
.map(entry -> new OrderProductDTO(entry.getValue())) // 2바퀴
.toList();
(1) options.stream()
- options 리스트를 스트림으로 변환합니다.
(2) collect(Collectors.groupingBy(op -> op.getProduct().getId()))
- groupingBy는 Collectors에서 제공하는 메서드로, 스트림 데이터를 그룹화합니다.
- 여기서 op -> op.getProduct().getId()는 각 OrderOption 객체에서 Product의 id를 추출하는 람다식입니다.
- 결과적으로, options 리스트가 Product ID를 기준으로 그룹화되어 아래와 같은 형태의 Map이 생성됩니다:
Map<Integer, List<OrderOption>>
(3) .entrySet()
- Map의 데이터를 Set 형태로 변환합니다.
- entrySet()은 Map의 모든 키-값 쌍을 반환합니다.
- 결과적으로, Set<Map.Entry<Integer, List<OrderOption>>> 형태가 됩니다.
(4) .stream()
- entrySet() 결과를 다시 스트림으로 변환합니다.
- 이렇게 하면 키-값 쌍 각각에 대해 후속 작업을 스트림 방식으로 처리할 수 있습니다.
(5) .map(entry -> new OrderProductDTO(entry.getValue()))
- 각 Map.Entry 객체를 처리하여 OrderProductDTO 객체로 변환합니다:
- entry.getValue()는 List<OrderOption> 값을 반환합니다.
- 이 값을 OrderProductDTO의 생성자에 전달하여 새로운 객체를 생성합니다.
- 즉, 그룹화된 각 Product ID에 대해 하나의 OrderProductDTO 객체가 만들어집니다.
(6) .toList()
- 스트림에서 생성된 모든 OrderProductDTO 객체를 List로 변환합니다.
- 최종적으로, 이 리스트는 products 필드에 저장됩니다.
전체 동작 요약
- options 리스트를 Product ID로 그룹화하여 Map 생성.
- 각 Product ID 그룹에 대해 OrderProductDTO 객체 생성.
- 결과를 List<OrderProductDTO>로 변환하여 products에 저장.
Share article