Skip to content

Conversation

@scars97
Copy link
Contributor

@scars97 scars97 commented Mar 24, 2025

검색 기능 추가 & 성능 테스트

📝작업 내용

  • JPA 연관관계 설정 제거
  • 인덱스 설정
  • 로컬 캐시(Caffeine) & 글로벌 캐시(Redis) 적용
  • 인덱스 및 캐시 성능 테스트 문서 작성

🔒해결하려는 문제 혹은 고민이 되었던 부분을 남겨주세요.(문제 수 만큼 복사해서 사용할 것)

  • JPA 연관관계 설정으로 인한 불필요한 join 수행
  • Like 연산자 사용 시 풀 테이블 스캔되는 문제 발생

🔑해당 문제를 어떻게 해결했나요? 그리고 왜 그렇게 생각했는지 이유를 남겨주세요.(자세하게 남겨주실 수록 좋습니다.)

🔹JPA 연관관계 설정 제거

배경
상영 중인 영화 목록 조회 시 테이블을 각각 조회하여 UseCase에서 조합하는 방식으로 구현하였습니다.
테이블 조회 흐름은 아래와 같습니다.

  • 영화 조회 -> 조회된 영화의 상영일정 목록 조회 -> 상영관 조회

현재 물리적 FK를 맺지 않고 있으며, TheaterSchedule(상영일정) 엔티티에 JPA 연관관계만 설정되어 있는 상태입니다.

문제 상황
상영일정 목록 조회 시,
이미 조회된 영화 데이터가 다시 조회되어 불필요한 join이 수행되는 문제가 발생하였습니다.

해결
JPA 연관관계 설정으로 인해 발생한 문제라 판단하여 설정을 제거하고, id 값만 가지도록 수정하였습니다.
해당 내용은 문서로 작성하였습니다.
Link : JPA 연관관계 설정에 대한 의문

🔹 FullText Index 설정

배경
영화 제목 검색 시 LIKE 연산자를 활용한 중간 검색을 사용합니다. -> LIKE '%keyword%'
중간 검색의 경우, 인덱스가 활용되지 않아 풀 스캔이 발생하였습니다.
이를 해결하기 위해 텍스트 필드의 모든 단어를 인덱싱하는 FullText Index를 적용하였습니다.

문제 상황
FullText Index 는 Mysql의 부가 기능으로, JPA 표준에서는 해당 기능을 제공하지 않았습니다.

해결
FullText Index 문법을 사용할 수 있도록 커스텀 함수를 설정하였습니다.
Link : MySqlFullTextDialect.kt

🔹성능 테스트 진행

아래와 같은 내용이 정리되어 있습니다.

  • 전체적인 성능 테스트 결과
  • 특정 컬럼에 인덱스를 적용한 이유
  • Caffeine 캐시 선택 이유(EhCache vs Caffeine)
  • 글로벌 캐시를 적용했음에도 적용 전과 동일한 성능이 나오는 이유(추측)
    Link : 성능 테스트 문서

scars97 added 21 commits March 16, 2025 22:28
- 영화, 상영관, 상영일정 조회
- Mapper 추가 : Entity -> Domain Model
- 아키텍처, 멀티모듈 설계, ERD, 시퀀스 다이어그램 추가
- 기존 api 모듈 -> infra 모듈로 통합
- infra 모듈 : 외부 시스템 연동 역할 수행
- AvailableMovieResult: 상영 중인 영화 정보 Dto
- MovieResult: 전체 영화 정보 Dto
- Theater, Schedule Dto 생성
- 제거한 이유에 대한 문서 작성
- QueryDsl 설정
- FullText Index 사용을 위한 MysqlDialect 커스텀
import org.springframework.context.annotation.Configuration
import java.util.concurrent.TimeUnit

@Configuration

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로컬 캐시를 사용하지 않는다면 해당 파일은 제거해주는 것이 좀 더 깔끔할 것 같습니다.

private val scheduleService: TheaterScheduleService
){

@Cacheable(value = ["available-movies"], key = "'movie-list'")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 Get method만 있어서 상관이 없겠지만 이후 post, put 같은 method도 같이 사용되고 캐싱한 데이터가 변경할 일이 생길 경우 해당 키 값으로 관리하는 건 위험 할 수도 있습니다. DB 데이터가 변경되어도 캐시 가 남아있으면 그대로 최신화 반영이 안될 것이니까요.

추가적으로 영화 상영 데이터 같이 일별로 관리되는 데이터의 경우 key에 오늘 날짜를 넣어서 생명주기를 관리하는 방법도 있을 것 같아요. 영화 데이터라고 함은 극장 오픈 이후에 마감 할 때 까지 정말 특별한 일이 없으면 변경이 되지 않을것이니 key의 생명 주기를 1일 로 주고 key 값에 날짜를 추가해서 관리하는 방법도 좋을 것 같습니다.
ex) 2025-03-25일 상영중인 영화 목록 조회
key = movie-list-20250325

@Test
fun getAvailableMovies() {
// given
val movie = fixture.createMovie(1L, "영화 A", LocalDate.now().minusDays(1))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 테스트 코드 작성할 때 더미 데이터를 한곳에 모아서 관리해서 사용하는 편입니다. test 코드에 더미데이터 파일을 만들고 거기에 테스트에서 사용한 모든 객체들을 모아서 관리해요. 이렇게 관리 할 경우 지금 테스트 마다 생성하는 영화 A, A관 등의 데이터를 더미데이터 파일에서 가져오기만하면 중복되는 내용 없이 사용이 가능하기 떄문입니다.

또한 프로젝트가 성숙해질 수록 더미 데이터 또한 같이 고도화 될 것이니 이후에 유지보수 또한 가성비가 좋아진다는 장점이 있습니다.

@DongHyunKIM-Hi
Copy link

잘한점

  • 문서 정리가 너무 잘되어 있어서 보기 편했습니다.
  • 고민한 내용에 대해서 하이퍼링크를 통해 가독성 있게 잘 표현하여 좋았습니다. (제가 가장 선호하는 프로젝트 어필 스타일)

아쉬운점

  • 없습니다.

총평

이번 주차 학습 목표에 부합하는 내용으로 잘 진행해주셔서 좋았습니다. 특히 가장 인상 깊었던 것은 문서 정리 능력이 좋았습니다. 리뷰어 혹은 평가자 입장에서 이 사람이 어떤것을 고민해고 해결 했는지 왜 이런식으로 구현했는지에 대해서 일반적으로는 이해하기 어렵습니다. 그때 현재 진행해주신 것 처럼 문서로 내가 왜 이런 고민을 했고 이렇게 구현했는지에 대해서 설명해준다면 보다 쉽고 깊은 이해도로 프로젝트를 볼 수 있어서 어필하기 아주 좋은 방법이라고 생각합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants