reservation 기능 구현 중 마주친 문제와 해결과정

개발하는 구황작물·2022년 10월 9일
0

메인 프로젝트

목록 보기
7/10

메인 프로젝트 진행하던 도중 고객이 선택한 기간에 예약이 다 찼는지 확인하는 로직을 짜야 했었다.

처음엔 단순히 기간에 해당하는 예약들을 찾아 예약된 강아지의 수들을 합치기만 하면 된다는 생각으로 jqpl을 이용하여 강아지 수의 합을 구했다.

    @Query("select COALESCE(sum(r.dogCount),0) from Reservation r where r.companyId = :companyId and " +
            "((:checkIn < r.checkIn and r.checkIn < :checkOut) or " +
            "(:checkIn between r.checkIn and r.checkOut) or " +
            "(:checkIn < r.checkIn and r.checkIn < :checkOut))") //COALESCE  -> 값이 없을 경우 두번째 파라미터를 리턴한다고 합니다...
    Integer findByCheckInCheckOut(LocalDate checkIn, LocalDate checkOut, Long companyId);

위의 쿼리문으로 기간 내에 예약된 강아지의 수와 호텔의 총 강아지 수용 가능 마리 수를 비교하는 방식으로 로직을 짰으나 모든 예약된 강아지의 마리 수를 count하면 기존의 날짜가 다른 예약된 강아지들 까지도 계산이 된다는 사실을 놓치고 말았다.

결국 위의 @Query는 지우고 다른 방식을 찾아보기로 하였다.


해결방법 1 : reservation 엔티티에 1 : N 관계의 엔티티를 만든 다음 checkInDate ~ checkOutDate 사이의 모든 날짜별로 엔티티를 만든다 ( 기각 )

어떻게 해야 할지 고민하던 도중 팀원 중 한 분이 아이디어를 냈다.
reservation 엔티티에 1 : N 관계의 엔티티를 만든 다음 checkInDate ~ checkOutDate 사이의 모든 날짜별로 엔티티를 저장하는 방식으로 진행하자는 아이디어였다.

위의 방법대로 하면 기간 사이에 위치한 날짜들 중 예약된 강아지들의 마리 수가 가장 큰 값을 구하기가 쉬워져서 처음은 위의 방식을 채택 하였다.

하지만 계산 하나만을 위해 엔티티를 많이 만들어 저장한다는 점이 문제였다.


해결방법 2 : Map을 이용해 날짜별로 예약된 총 마리 수를 구하는 방법 (채택)

먼저 고객이 선택한 날짜 사이에 기존의 예약들을 조회하였다.

// 고객이 선택한 호텔 중 고객의 체크아웃 날짜보다 체크인할 예약 && 예약이 확정된 예약들 조회
@Query("select " +
            " r from Reservation r " +
            " where r.companyId = :companyId and r.checkInDate >= :checkOut and r.status = confirmed")
    List<Reservation> findByCheckInCheckOut(Long companyId, LocalDateTime checkOut);

그 다음 날짜별로 예약된 강아지의 총 합을 구한 다음 Map에 저장하였고 날짜들 중 가장 예약이 많은 날의 강아지 마리 수를 구하여 호텔의 최대 수용 가능 마리 수와 비교하는 방식으로 로직을 짰다.

public Integer occupiedRoomCount(List<Reservation>reservations,
                                      LocalDate checkInDate,
                                      LocalDate checkOutDate) {
        Map<LocalDate, Integer> occupiedMap = getOccupiedMap(reservations, checkInDate, checkOutDate);
        Set<LocalDate> dateSet = occupiedMap.keySet();

        Integer max = 0;
        for (LocalDate localDate : dateSet) {
            Integer bookedQuantity = occupiedMap.get(localDate);

            if(max <= bookedQuantity) {
                max = bookedQuantity;
            }
        }
        return max;
    }

    //선택한 날짜 사이에 일별마다 예약된 강아지수
    public Map<LocalDate, Integer> getOccupiedMap(List<Reservation>reservations,
                                               LocalDate checkInDate,
                                               LocalDate checkOutDate) {

        Map<LocalDate, Integer> occupiedMap = new HashMap<>();
        for(LocalDate date = checkInDate; date.isBefore(checkOutDate); date = date.plusDays(1)) {
            LocalDate currentDate = date;
            Integer occupied = reservations.stream()
                    .filter(reservation -> isBetween(currentDate, reservation))
                    .mapToInt(ReservationList::getDogCount).sum();

            occupiedMap.put(currentDate, occupied);
        }
        return occupiedMap;
    }

    public boolean isBetween(LocalDate date, Reservation reservation) {
        LocalDate checkInDate = reservation.getCheckInDate();
        LocalDate checkOutDate = reservation.getCheckOutDate();
        return date.isBefore(checkOutDate) && date.isAfter(checkInDate);
    }

위와 같이 로직을 짠 결과 1 : N 연관관계 엔티티를 만들 때 보다 성능 개선을 할 수 있었고 DB 공간 또한 절약할 수 있게 되었다.

개편 이전
개편 이전

개편 이후

profile
어쩌다보니 개발하게 된 구황작물

0개의 댓글