데이터베이스
JPA 성능 최적화
PSW
2025. 2. 25. 19:46
1. 단순 조회 (기본 키를 이용한 조회)
- 특정 엔티티 하나를 조회할 때, 기본 키(PK)를 사용하면 가장 효율적이다
- findById()는 기본적으로 1차 캐시(영속성 컨텍스트)를 먼저 조회하기 때문에 성능이 좋음.
최적화 방법:
- EntityManager의 1차 캐시 활용
- Spring Data JPA의 findById()
// 기본 키(PK)로 Task 엔티티 조회 (1차 캐시 활용)
Optional<Task> task = taskRepository.findById(taskId);
-> 불필요한 SQL실행 없이,JPA의 1차 캐시에서 데이터를 가져올 수 있다.
2. 특정 컬럼만 조회할 때 (DTO Projection)
- 엔티티의 전체 필드를 가져오는 것이 아니라, 특정 필드만 필요한 경우
- 엔티티 조회 시 불필요한 데이터 로딩을 방지
최적화 방법:
- DTO Projection을 사용하여 필요한 필드만 조회
// Task 제목만 가져오는 DTO 조회
@Query("SELECT new com.To_Do.List.demo.task.dto.TaskResponseDto(t.title, t.status) FROM Task t WHERE t.id = :taskId")
TaskResponseDto findTaskSummary(@Param("taskId") Long taskId);
이점:
- 불필요한 엔티티 데이터 조회 방지
- DTO Projection을 사용하면 select 절에서 필요한 필드만 가져옴
- 페이징 처리 시 성능이 향상된다
3. 연관된 엔티티 조회 시 (JOIN FETCH 사용)
- 한 엔티티와 연관된 다른 엔티티를 한 번의 쿼리로 가져와야 할 때.
- 지연 로딩(LAZY)으로 설정된 연관 엔티티를 즉시 로딩하고 싶을 때.
최적화 방법:
- JOIN FETCH를 사용하여 N+1 문제 해결
- 즉시 로딩이 필요한 경우 EntityGraph 활용
@Query("SELECT t FROM Task t JOIN FETCH t.member WHERE t.id = :taskId")
Task findTaskWithMember(@Param("taskId") Long taskId);
이점:
- 한 번의 JOON FETCH 쿼리로 Task와 Member를 같이 로딩
- N+1 문제 해결
- Lazy Loading을 강제로 즉시 로딩(Eager Fetching)으로 전환
4. 대량 데이터 조회 시 (페이징 적용)
- 엔티티 목록을 조회할 때 데이터가 많아 성능 문제가 발생
- 무작위로 데이터를 조회하는 것이 아니라, 페이징 처리가 필요
최적화 방법:
- Spring Data JPA의 Pageable을 활용하여 페이징 처리
- COUNT 쿼리를 최적화하기 위해 @Query 사용 가능
@Query("SELECT t FROM Task t WHERE t.status = :status")
Page<Task> findByStatus(@Param("status") TaskStatus status, Pageable pageable);
// 서비스에서 페이징 적용
Page<Task> tasks = taskRepository.findByStatus(TaskStatus.PENDING, PageRequest.of(0, 10, Sort.by("createdAt").descending()));
이점:
- 한 번에 많은 데이터를 가져오는 대신 페이지 단위로 로드
- 쿼리 성능 최적화
- 정렬 및 페이징 기능 제공