본문 바로가기
개발진행목록/예약 서비스

예약 조회 API 개발

by o3oppp 2024. 10. 24.
엔티티 직접 노출
@GetMapping("/api/v1/simple-reserves")
public List<Reserve> reserve1(){
    List<Reserve> all = reserveRepository.findAllByString(new ReserveSearch());
    for (Reserve reserve : all) {
        reserve.getMember().getName(); // Lazy 강제 초기화
    }
    return all;
}

API 호출 결과

  • 엔티티를 직접 노출하는 것은 좋지 않음

엔티티를 DTO로 변환
@GetMapping("/api/v2/simple-reserves")
public List<SimpleReserveDto> reserveV2(){
    List<Reserve> reserves = reserveRepository.findAllByString(new ReserveSearch());
    List<SimpleReserveDto> result = reserves.stream()
            .map(r -> new SimpleReserveDto(r))
            .collect(Collectors.toList());
    return result;
}
...
@Data
static class SimpleReserveDto{
    private Long reserveId;
    private String name;
    private LocalDateTime reserveData;
    private ReserveStatus reserveStatus;
    private Address address;

    public SimpleReserveDto(Reserve reserve) {
        reserveId = reserve.getId();
        name = reserve.getMember().getName();
        reserveData = reserve.getReserveDate();
        reserveStatus = reserve.getStatus();
        address = reserve.getMember().getAddress();
    }
}

API 호출 결과
1 + N 문제 발생

  • 1 + N 문제 발생
  • 최악의 경우 총 1 + N번 실행
  • reserve 조회 1번 + reserve -> member 지연 로딩 조회 N 번
    • reserve 조회 시 2개의 예약이 조회
    • 예약 1개당 reserve.getMember.getName()과 reserve.getMember.getAddress()로 인해 member 조회
  • 만약 첫번째 예약에서 찾은 member와 두번째 예약에서 찾는 member가 같은 경우 N번이 아닌 1번만 실행
    • 지연로딩은 영속성 컨텍스트에서 조회하믈, 이미 조회된 경우 쿼리를 생략

페치 조인 최적화
// Repository
public List<Reserve> findAllWithMember() { // 페치 조인 적용
        return em.createQuery(
                "select r from Reserve r" +
                        " join fetch r.member m", Reserve.class
        ).getResultList();
}
...
// Controller
@GetMapping("/api/v3/simple-reserves")
public List<SimpleReserveDto> reserveV3(){
    List<Reserve> reserves = reserveRepository.findAllWithMember();
    List<SimpleReserveDto> result = reserves.stream()
            .map(r -> new SimpleReserveDto(r))
            .collect(Collectors.toList());
    return result;
}
...
@Data
static class SimpleReserveDto{
    private Long reserveId;
    private String name;
    private LocalDateTime reserveData;
    private ReserveStatus reserveStatus;
    private Address address;

    public SimpleReserveDto(Reserve reserve) {
        reserveId = reserve.getId();
        name = reserve.getMember().getName();
        reserveData = reserve.getReserveDate();
        reserveStatus = reserve.getStatus();
        address = reserve.getMember().getAddress();
    }
}

API 호출 결과
1 + N 문제 해결

  • Repository의 findAllWithMember 메소드 선언하여 페치 조인 사용
  • 페치 조인으로 reserve -> member는 이미 조회 된 상태이므로 더 이상 조회 쿼리 X

'개발진행목록 > 예약 서비스' 카테고리의 다른 글

예약 리스트 조회 API 순환참조 해결  (0) 2024.10.29
회원 관련 API 개발  (0) 2024.10.17
설계  (0) 2024.09.20