1. 개요
알림(Notification) 패키지 고도화 및 리팩토링 과정에서 적극적으로 활용된 JPA(Java Persistence API)의 데이터베이스 성능 최적화 기법에 대한 집약적 회고록입니다.
자바 백엔드 개발 시 빈번히 발목을 잡는 고질병인 N+1 문제의 근원을 조명하고 이를 파쇄하기 위한 @EntityGraph 활용법, 대량 데이터의 효율적인 처리를 돕는 벌크(Bulk) 연산 및 일괄 삭제(Batch Delete) 기법, 그리고 JPA의 핵심 구동 메커니즘인 **Dirty Checking(변경 감지)**의 오동작 방지 원리까지 알기 쉬운 비유와 함께 일목요연하게 조명합니다.
2. 핵심 내용
2-1. JPA N+1 문제 격파를 위한 EntityGraph와 벌크(Bulk) 연산 최적화
- N+1 문제 상황 (지연 로딩의 양날의 검):
알림 목록 20개를 화면에 뿌려주기 위해 단 한 번의 조회를 날렸지만(1회), 각 알림의 송신자(Sender) 상세 정보를 렌더링하기 위해 뒤늦게 루프를 돌며 유저 테이블을 추가로 20번 조회하여 총 21번의 쿼리가 나가는 병목 현상(N+1 문제)이 있었습니다.
- 해결 방안 (택배 기사 비유): 택배 배송 기사님(JPA)에게 택배(알림)를 배송할 때 옆에 딸린 송신자 이름표(Sender) 정보도 애초에 한 박스에 묶어서 한 번에 배달해 달라고 지시하는 것과 같은
@EntityGraph(attributePaths = {"sender"})를 레포지토리에 부여했습니다. 내부적으로 단 1방의LEFT OUTER JOIN쿼리가 발생하면서 21회의 DB 통신 비용을 1회로 극적 단축했습니다.
- 해결 방안 (택배 기사 비유): 택배 배송 기사님(JPA)에게 택배(알림)를 배송할 때 옆에 딸린 송신자 이름표(Sender) 정보도 애초에 한 박스에 묶어서 한 번에 배달해 달라고 지시하는 것과 같은
- 벌크(Bulk) 연산의 구원:
회원 탈퇴나 일괄 삭제 시 데이터를 루프 돌며
delete()를 호출하는 것은SELECT1번 +DELETE10,000번 등 재앙적인 네트워크 지연을 초래합니다. 메모리를 거치지 않고 DB 엔진에게 직접DELETE FROM notification WHERE receiver_id = ?쿼리 한 방을 쏴서 해결하도록 레포지토리에@Query기반 대량 벌크 처리를 접목해 순식간에 데이터를 증발시켰습니다.
2-2. 영속성 컨텍스트의 마법인 Dirty Checking 원리와 일괄 삭제 Batch 활용
- Dirty Checking (변경 감지) 원리 (호텔 룸 서비스 비유):
영속성 컨텍스트를 **‘호텔 매니저’**에 비유할 수 있습니다.
- 투숙객(엔티티)이 체크인(
findById조회)할 때 매니저는 방의 초기 상태를 사진(Snapshot)으로 몰래 남겨 둡니다. - 투숙 기간 중에 객체 속의 값을 바꾸면 매니저는 눈치를 보고 있다가, 최종 체크아웃 시점(트랜잭션 커밋)에 촬영했던 사진과 현재 방의 바뀐 상태를 비교(Dirty 감지)하여, 달라진 가구의 위치 정보만큼을 워크 오더(
UPDATESQL)로 자동 생성하여 DB에 조용히 밀어 넣습니다. 따라서 서비스 단에 명시적으로save()나update()를 남발하지 않아도 데이터 정합성이 예술적으로 동기화됩니다.
- 투숙객(엔티티)이 체크인(
- 일괄 삭제 Batch (
deleteAllInBatch): 보존 가치가 없는 잔여 고아 알림 객체들을 처리할 때, JPA 본연의 자식 Cascade 관계나 메모리 정리를 다소 우회하더라도 극도의 성능이 필요할 때는 일반deleteAll대신deleteAllInBatch를 활용했습니다. 이것은IN쿼리(id IN (1, 2, 3...))로 일괄 사멸을 단행하므로 불필요한 루프 오버헤드를 가볍게 제압했습니다.
2-3. 수정 쿼리 정합성(clearAutomatically) 보장 및 추천 라이브러리 검토
JPA의 영속성 컨텍스트 1차 캐시를 거치지 않고 다이렉트로 DB 테이블 데이터만 갱신하는 @Query 벌크 연산 후에는, 반드시 @Modifying(clearAutomatically = true) 옵션을 동봉해야 합니다. 이 옵션은 캐시 창고를 강제로 싹 비워주어, 다음 조회 시 옛날 복사본 데이터가 아닌 최신 DB 데이터를 반환하도록 데이터 정합성을 철저히 지켜줍니다.
- 추천 오픈소스 라이브러리:
P6Spy: 백엔드 스프링 부트에서 JPA를 다룰 때, 실제로 런타임에 날아가는 SQL 쿼리의 가독성이 나쁜 파라미터 바인딩(? 표시)을 실제 변수 값으로 매핑하여 정돈해 보여주고 SQL 수행 시간까지 포맷팅하여 콘솔에 뿜어주는 대표적인 SQL 프로파일링 라이브러리입니다. 로컬 및 스테이징 개발 시 쿼리 비효율을 포착하는 데 가히 필수적인 가치를 지닙니다.Spring Data Envers: JPA 엔티티들의 생성, 수정, 삭제 이력(Audit)을 자동으로 기록하고 추적해주는 프레임워크 수준의 감시 도구입니다. 알림 이력이나 코칭 관계 변동 내역을 실무 차원에서 완전한 이력 보존용 감사 데이터베이스 테이블로 적치해야 할 때 매우 강력하고 안전하게 구동됩니다.