복잡한 조건의 세미나 조회 api 개발하기

손현경 (보름)·2023년 7월 21일

keeper-homepage

목록 보기
1/5

우선 홈페이지의 seminar 도메인을 설명하겠습니다.

Seminar

현재 키퍼 동아리 회원은 활동회원이 거의 60명 가까이 됩니다.
매주 혹은 2주에 한번 씩 진행하는 세미나에서 회원 출석 체크를 서기가 일일히 하는 수고를 덜기 위한 자동 출석 서비스를 제공하기 위해 만들어진 도메인 입니다.

Table

세미나 도메인의 테이블은 다음의 4개가 있습니다.

  • seminar
  • seminar_attendance_status
  • seminar_attendance_excuse
  • seminar_attendance

이번 글에서는 seminar 테이블을 소개하기로 하고, 나머지 테이블들은 기회가 될 때 소개하도록 하겠습니다.

seminar 테이블에는 세미나의 정보를 담기 위한 8개의 column이 있습니다. 그 중 아래 3개의 컬럼을 주목합시다.

  • open_time (세미나 시작 시간)
  • attendance_close_time (출석 마감 시간)
  • lateness_close_time (지각 마감 시간)

각 컬럼이 어떤 용도인 지 한눈에 파악이 가능한 것 같습니다.

요구사항

홈페이지 리뉴얼 프로젝트에서는 기획자가 있고, 각 페이지를 어떻게 구성할 것인 지 정하는 업무를 담당하고 있습니다. 그래서 기획이 끝나면 프론트/백엔드 개발 담당은 기획서를 보면서 기능을 구현하면 됩니다.

기획서를 보니, 3가지 종류의 세미나를 조회해야 합니다.

상세 설명을 보니 다음의 3가지 세미나를 조회해야 한다고 적혀있습니다.

  • 지난 세미나 중 가장 최근 세미나
  • 예정된 세미나 중 가장 가까운 세미나
  • 예정된 세미나 중 2번째 가까운 세미나

그냥 슥 봐도 뭔가 복잡한 쿼리문을 날려야 할 것 같습니다. 그보다 중요한 건 어디에 기준을 두느냐에 따라서 조회 결과가 달라질 수도 있기에 요구사항을 확실하게 짚고 넘어갈 필요가 있었습니다. 사실 이 글도 이 포인트에서 복잡한 생각을 정리해보고자 작성하게 되었습니다.

처음 백엔드 개발에 참여했을 때도, 이와 비슷한 기능 구현 요청이 있었는데 헷갈리고 어려워서 혼자 해결하지 못했었던 경험이 있었습니다 😅

기획에 대한 궁금점

예정된 세미나 중 2번째 가까운 세미나가 왜 필요한 지 처음에 의아했습니다. 기획자님께 관련해서 질문을 드렸고, 아래와 같은 답변을 받았습니다.

세미나 일정 페이지를 만들게 되면 앞으로의 세미나에 대한 정보가 일정에 등록해두기 위해 미리 앞으로 진행될 세미나에 대해서 추가해두는 상황을 염두해둔 것

바로 이해 할 수 있었습니다 👍

끝난 세미나와 예정된 세미나

끝난 세미나는 lateness_close_time (지각 마감 시간)이 현재보다 이전인 세미나 입니다.
예정된 세미나는 open_time (세미나 시작 시간)이 현재보다 이후인 세미나 입니다.

그래서 저는 다음 그림처럼 기능을 구현할 생각이였습니다.

그런데 뭔가 헷갈려서 이야기를 해 본 결과, 기획자님은 다음처럼 조회되길 생각하셨습니다.

즉, 오늘의 세미나가 마감 되었더라도 이를 포함하여 예정된 세미나를 불러와야 합니다.

결론적으로 조회해야 하는 세미나

  • 오늘 이전(~어제)의 끝난 세미나 중 가장 최신
    • 보통의 경우 끝난 세미나 일텐데, 추가 속성으로 끝난 세미나 인지도 확인해야 할 것 같습니다.
  • 오늘 이후(오늘 포함 ~)의 세미나 중 가장 최신
  • 오늘 이후(오늘 포함 ~)의 세미나 중 2번째 최신

구현

3개의 api를 호출해야 할 텐데, 한번에 3개의 세미나 정보를 던져주는 게 좋을 지 각각을 분리하는 게 좋을 지도 고민했습니다.

일단 오버헤드가 크지 않다면 각각의 api로 분리하는 게 좋다고 판단하였습니다.

가장 최근에 끝난 세미나 조회

@Query("SELECT s FROM Seminar s "
      + "WHERE s.id <> 1 " // virtual seminar data 제외
      + "AND DATE(s.openTime) < :localDate "
      + "AND DATE(s.latenessCloseTime) < :localDate "
      + "ORDER BY s.openTime DESC "
      + "LIMIT 1")
Optional<Seminar> findRecentlyDoneSeminar(@Param("localDate") LocalDate localDate);

저는 프로젝트에서 복잡한 조회를 해야 할 때는 거의JPQL로 쿼리를 작성하였습니다. JPA에서 제공하는 메서드보다는 사용할 때 가독성이 좋지만, 쿼리를 작성할 때 주의하여 작성해야 해서 (이게 생각보다 부담입니다.) 좋은 방법인지 고민입니다. 테스트도 거의 쿼리문에 대한 테스트를 작성한 것 같습니다.

그래서 Query DSL을 써보는 것도 좋을 것 같네요. 일단은 부채로 남겨 두겠습니다.

Query DSL 장점

  • 문법 오류를 컴파일 단계에서 발견할 수 있습니다.
  • 실행하고자 하는 JPQL을 직관적으로 알 수 있습니다.

예정된 세미나 중 가장 최근 2개 조회

@Query("SELECT s FROM Seminar s "
      + "WHERE s.id <> 1 " // virtual seminar data 제외"
      + "AND DATE(s.openTime) >= :localDate "
      + "ORDER BY s.openTime ASC "
      + "LIMIT 2")
List<Seminar> findRecentlyUpcomingSeminar(@Param("localDate") LocalDate localDate);

오늘을 포함해야 하므로 >= 으로 openTime을 비교해줍니다. 또 이번에는 openTime을 오름차순으로 정렬했을 때 최상위 2개의 세미나를 조회해야 합니다.

profile
빛나는 개발자가 되는 그날까지...

0개의 댓글