WMS(창고관리시스템)를 개발하면서, "각 창고별 남은 공간(remainSpace)을 계산해서 DTO에 담아 반환"하는 기능을 구현하게 되었다.
창고(warehouse_table)와 공간(area_table)은 1:N 관계다. 각 창고에 등록된 공간의 총합(sum(area_space))을 빼서 남은 공간을 계산해야 했다.
처음엔 그냥 남은 공간 계산하는 메소드를 하나 만들고, 창고 리스트에서 루프 돌면서 호출하면 되겠지? 하고 생각했다.
// AdminWarehouseService.java
private Optional<Integer> getRemainingSpace(Integer warehouseId) {
return adminAreaRepository.getAllAdminWarehouseSpaceUsage().stream()
.filter(adminRemainSpace ->
adminRemainSpace.getWarehouseId().equals(warehouseId))
.map(AdminWarehouseSpaceRemainVo::getRemainSpace)
.findFirst();
}
@Override
public List<AdminWarehouseDto> getAllWarehouses() {
return adminWareHouseRepository.adminGetAllWarehouses().stream()
.map(adminWarehouseVo -> {
Integer remainSpace = getRemainingSpace(adminWarehouseVo.getWarehouseId())
.orElse(0); // 못 찾으면 0
return AdminWarehouseDto.builder()
.warehouseId(adminWarehouseVo.getWarehouseId())
.warehouseName(adminWarehouseVo.getWarehouseName())
.warehouseSpace(remainSpace)
.warehouseAddress(adminWarehouseVo.getWarehouseAddress())
.warehouseAmount(adminWarehouseVo.getWarehouseAmount())
.build();
})
.collect(Collectors.toList());
}
getRemainingSpace()가 내부적으로 adminAreaRepository.getAllAdminWarehouseSpaceUsage()를 매번 호출 하게되고 이는 창고 수만큼 쿼리를 반복 실행하게 된다. 이 경우 창고가 100개면 area_table 전체를 100번 훑는 셈이 되었다. 한 번만 쿼리로 전체 사용공간 데이터를 불러오고, Map으로 보관하자
@Override
public List<AdminWarehouseDto> getAllWarehouses() {
// 1. 남은 공간 정보 한번에 조회 (창고 ID → 남은 공간)
Map<Integer, Integer> remainSpaceMap = adminAreaRepository.getAllAdminWarehouseSpaceUsage().stream()
.collect(Collectors.toMap(
AdminWarehouseSpaceRemainVo::getWarehouseId,
AdminWarehouseSpaceRemainVo::getRemainSpace
));
// 2. 창고 리스트 조회 및 DTO 매핑
return adminWareHouseRepository.adminGetAllWarehouses().stream()
.map(adminWarehouseVo -> {
Integer remainSpace = remainSpaceMap.getOrDefault(adminWarehouseVo.getWarehouseId(), 0);
return AdminWarehouseDto.builder()
.warehouseId(adminWarehouseVo.getWarehouseId())
.warehouseName(adminWarehouseVo.getWarehouseName())
.warehouseSpace(remainSpace) // ✅ 여기 변경됨
.warehouseAddress(adminWarehouseVo.getWarehouseAddress())
.warehouseAmount(adminWarehouseVo.getWarehouseAmount())
.build();
})
.collect(Collectors.toList());
}
Map으로 처리할 수 없을지3가지를 고려해야겠다.
특히 다음번 기능으로 구역에 들어간 제품들의 크기들을 합한 것을 가져와서 구역 최대공간에서 차를 구하여 남은 공간을 계산하는 기능도 개발해야하는데 같은 방법으로 하면 쿼리 반복 호출 문제.. 해결 할 수 있을 것이다.
💡 Stream은 깔끔하지만, 쿼리와 함께 쓰일 땐 쿼리 수를 꼭 고려하자!