Skip to content

Comments

[FEAT/5week] JPA 활용(JPQL 방식으로 쿼리 리팩토링)#5

Open
nomorefifa wants to merge 1 commit intodevfrom
feat/5week-jpql
Open

[FEAT/5week] JPA 활용(JPQL 방식으로 쿼리 리팩토링)#5
nomorefifa wants to merge 1 commit intodevfrom
feat/5week-jpql

Conversation

@nomorefifa
Copy link
Owner

@nomorefifa nomorefifa commented Oct 27, 2025

🎋 작업중인 브랜치

#4 , feat/5week-jpql

⚡️ 작업동기

1. JPA와 영속성 컨텍스트 (Persistence Context)의 핵심 이해

JPA는 객체와 RDB 간의 패러다임 불일치를 해소하는 ORM 기술이며, 그 중심에는 영속성 컨텍스트가 있습니다.

  • 영속성 컨텍스트: 엔티티 객체를 영구적(persistent)으로 관리하고 변경을 추적하는 메모리상의 논리적 공간입니다. 이는 DB 접근 횟수를 줄이는 1차 캐시 역할을 합니다.
  • 영속 상태의 주요 이점: 엔티티가 관리 상태(Managed)가 되면 다음과 같은 장점을 얻습니다.
    1. 1차 캐시: 동일 트랜잭션 내 중복 조회 시 DB 접근 없이 캐시 데이터를 즉시 반환합니다.
    2. 변경 감지 (Dirty Checking): 엔티티 수정 시 트랜잭션 커밋 시점에 JPA가 자동으로 감지하여 UPDATE 쿼리를 생성하고 반영합니다.
    3. 지연 로딩 (Lazy Loading): 연관된 엔티티를 실제로 사용하는 시점에만 조회합니다. (N+1 문제 방지를 위해 EAGER 대신 권장)

2. Spring Data JPA를 통한 데이터 접근 및 쿼리 전략

Spring Data JPA는 Repository 인터페이스를 사용하여 DB 접근을 간소화할 수 있습니다. 이번 미션에서는 @Query 어노테이션으로 JPQL을 직접 작성하는 방식을 사용했습니다.

  • 기본 CRUD 및 save(): JpaRepository 상속으로 기본적인 CRUD가 제공됩니다. reviewRepository.save(review) 호출은 엔티티를 영속화하고, 트랜잭션 커밋 시점에 INSERT 쿼리를 자동 실행합니다.
  • @Query와 JPQL: 복잡한 조회 조건이나 커스터마이징을 위해 @Query를 사용하며, 테이블이 아닌 엔티티 객체를 대상으로 쿼리하는 JPQL을 작성했습니다.
    • 예시: findMemberById 메서드는 @Query("select m from Member m where m.id = :memberId")를 통해 특정 ID의 Member 엔티티를 조회합니다.

3. 성능 최적화 전략: N+1 문제와 커서 페이징

효율적인 데이터 조회와 대용량 처리를 위해 다음과 같은 최적화 전략을 적용했습니다.

① N+1 문제 해결을 위한 join fetch (페치 조인)

  • 문제: 즉시 로딩 (EAGER) 사용 시 발생하는 N+1 문제 (1개 메인 쿼리 후 N개 추가 쿼리)를 방지하기 위해 페치 조인을 사용했습니다.
  • 적용: findMemberMissionsfindAvailableMissions 쿼리에서 join fetch를 사용하여 연관된 엔티티 (mm.mission, m.store)를 하나의 쿼리로 함께 조회함으로써, N+1 문제를 근본적으로 해결하고 성능을 최적화했습니다.

② 커서 기반 페이징

  • 전략: 오프셋(Offset) 기반 페이징의 성능 저하를 피하기 위해 커서 기반 페이징을 적용했습니다.
  • 구현: WHERE mm.id < :cursor와 같이 이전에 조회된 마지막 레코드의 ID를 기준으로 다음 페이지를 조회하도록 구현했습니다.
  • 서비스 로직: MissionService에서 cursornull인 경우 Long.MAX_VALUE를 사용하여 첫 페이지부터 최신 데이터를 조회하도록 처리했습니다.

③ 비즈니스 로직 구현

  • 정렬 조건: findMemberMissions에서 order by case when mm.status = 'CHALLENGING' then 1 else 2 end를 사용하여 진행 중인 미션을 완료된 미션보다 우선적으로 정렬하는 로직을 JPQL 내에서 구현했습니다.
  • 필터링: findAvailableMissions에서 서브 쿼리를 이용해 and m.id not in (...) 조건을 적용하여, 이미 도전 중인 미션은 목록에서 제외하는 복잡한 필터링을 구현했습니다. 추후 QueryDSL과 같이 쿼리 빌더 라이브러리를 사용해서 리팩토링을 진행할수도 있겠습니다.

Check List

  • Reviewers 등록을 하였나요?
  • Assignees 등록을 하였나요?
  • 라벨(Label) 등록을 하였나요?
  • PR 머지하기 전 반드시 CI가 정상적으로 작동하는지 확인해주세요!

@nomorefifa nomorefifa linked an issue Oct 27, 2025 that may be closed by this pull request
@nomorefifa nomorefifa changed the title 5week jpql application [FEAT/5week] JPA 활용(JPQL 방식으로 쿼리 리팩토링) Oct 27, 2025
@jih023
Copy link

jih023 commented Nov 4, 2025

미션 너무 잘 수행하신 것 같습니다! 수고하셨습니다~!

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.

[FEAT/5week] JPA 활용(JPQL 방식으로 쿼리 리팩토링)

2 participants