루틴 전체 리스트 조회 개선

// 루프 전체 리스트 조회
    @Override
    @Transactional(readOnly = true)
    public PageResponse<LoopWithCheckListResponse> getAllLoop(Pageable pageable, CurrentUserDto currentUser) {
        checkPageSize(pageable.getPageSize());

        // 1) 루프 페이지 조회 (DB에서 NULLS LAST 처리)
        Page<Loop> mainPage = loopRepository.findByMemberIdWithOrder(currentUser.getId(), pageable);
        // 2) 해당 페이지에 포함된 루프 ID 추출
        List<Long> mainIds = mainPage.getContent().stream().map(Loop::getId).toList();
        
        // 3) 체크리스트 벌크 조회 (DB에서 loop.id ASC, deadline ASC NULLS LAST 처리)
        List<LoopCheckList> loopCheckLists = mainIds.isEmpty()
                ? List.of()
                : loopCheckListRepository.findByLoopIdIn(mainIds);

        // 4) 체크리스트를 loopId 기준으로 그룹핑
        Map<Long, List<LoopCheckList>> subByMainId = new LinkedHashMap<>();
        for (LoopCheckList sg : loopCheckLists) {
            Long mid = sg.getLoop().getId();
            subByMainId.computeIfAbsent(mid, k -> new ArrayList<>()).add(sg);
        }
        // 5) DTO 변환
        List<LoopWithCheckListResponse> content = new ArrayList<>(mainPage.getNumberOfElements());
        for (Loop mg : mainPage.getContent()) {
            LoopResponse mainDto = convertToLoopResponse(mg);
            List<LoopCheckListResponse> subDtos = subByMainId.getOrDefault(mg.getId(), List.of())
                    .stream()
                    .map(this::convertToCheckListResponse)
                    .toList();
            content.add(LoopWithCheckListResponse.builder()
                    .loop(mainDto)
                    .build());
        }
        // 6) Page로 감싸서 반환
        return PageResponse.of(new PageImpl<>(content, mainPage.getPageable(), mainPage.getTotalElements()));
    }
// 루프 전체 리스트 조회
    @Override
    @Transactional(readOnly = true)
    public PageResponse<LoopSimpleResponse> getAllLoop(Pageable pageable, CurrentUserDto currentUser) {
        checkPageSize(pageable.getPageSize());

        // 1. Loop 엔티티 페이지를 DB에서 조회
        Page<Loop> loopPage = loopRepository.findByMemberIdWithOrder(currentUser.getId(), pageable);
        List<Long> loopIds = loopPage.stream().map(Loop::getId).toList();

        // 2. 모든 체크리스트를 한 번에 조회해서 Map으로 그룹핑
        Map<Long, List<LoopCheckList>> checklistsMap = loopCheckListRepository.findByLoopIdIn(loopIds)
                .stream()
                .collect(Collectors.groupingBy(cl -> cl.getLoop().getId())); // Stream의 groupingBy를 사용해 한 줄로 그룹핑

        // 3. 엔티티 페이지를 DTO 페이지로 직접 변환
        Page<LoopSimpleResponse> simpleDtoPage = loopPage.map(loop ->
                convertToSimpleResponse(loop, checklistsMap.getOrDefault(loop.getId(), List.of()))
        );

        return PageResponse.of(simpleDtoPage);
    }

모든 체크리스트를 한번에 조회하여 N+1 문제 해결 (loopCheckListRepository.findByLoopIdIn(loopIds))

→ 기존의 “5) DTO 변환”은 for문을 돌면서 수동으로 list를 만들었으나, Page.map() 함수를 사용하여 간편하게 엔티티가 담긴 페이지를 DTO가 담긴 페이지로 변환했다.

<aside> 💡

groupingBy

현재 모든 체크리스트를 한번에 조회했기에 각 루프의 id를 기준으로 그룹핑해줌.

Collectors.groupingBy(cl -> cl.getLoop().getId()) ← 분류 기준을 루프의 id로 설정

ex) 1번 루프의 체크리스트끼리 그룹핑해서 map으로 저장

</aside>

루프 추가 API 구현

<aside> 💡

LocalDate 클래스

→ java.time 패키지에 속한 날짜 클래스 (오직 ‘연-월-일’ 정보만 표현하는 불변 객체)