JPA의 FlushMode, Flush, 변경 감지 개념 및 동작 원리
1. JPA에서 영속성 컨텍스트란?
JPA를 쓰면 엔티티를 데이터베이스에서 가져와 객체로 다루게 된다. 이때 객체를 단순히 메모리에 올리는 게 아니라, 영속성 컨텍스트라는 곳에서 관리하게 된다. 쉽게 말해, JPA가 엔티티를 보관하고 변경 사항을 추적하는 1차 캐시 같은 개념이다.
엔티티를 조회하면 JPA는 먼저 영속성 컨텍스트에서 찾고, 없으면 데이터베이스에서 가져와서 저장한다. 덕분에 같은 트랜잭션 내에서는 동일한 엔티티를 여러 번 조회해도 쿼리가 추가로 실행되지 않는다.
2. Flush란?
영속성 컨텍스트에 있는 엔티티의 변경 사항은 곧바로 데이터베이스에 반영되지 않는다. 대신 쓰기 지연 저장소에 모아뒀다가 특정 시점에 한꺼번에 반영하는데, 이 작업을 flush라고 한다.
하지만 flush는 데이터베이스에 커밋을 하는 게 아니라, SQL을 생성해서 데이터베이스와 동기화하는 것일 뿐이다. 실제 데이터 반영은 트랜잭션이 커밋될 때 이루어진다.
Flush가 발생하는 조건
- EntityManager.flush() 메서드를 직접 호출할 때
- 트랜잭션이 커밋될 때 (commit 시 자동 실행)
- JPQL 또는 네이티브 쿼리를 실행하며, 해당 쿼리가 변경된 엔티티가 포함된 테이블을 조회할 때
3. FlushMode란?
FlushMode는 언제 flush를 실행할지 결정하는 설정이다.
FlushMode의 종류
- FlushModeType.AUTO (기본값)
- 트랜잭션이 커밋될 때 자동 flush
- JPQL 실행 시 변경된 엔티티가 포함된 테이블을 조회하면 flush
- FlushModeType.COMMIT
- JPQL 실행 시 flush가 발생하지 않음
- 오직 트랜잭션이 커밋될 때만 flush 실행
4. 변경 감지(Dirty Checking)란?
변경 감지는 JPA가 영속성 컨텍스트 내 엔티티의 변경 사항을 자동으로 감지하고 반영하는 기능이다. 엔티티를 수정하면 JPA가 알아서 UPDATE 쿼리를 생성해준다.
변경 감지 동작 방식
- 엔티티 조회 (1차 캐시 또는 데이터베이스에서 불러옴)
- 엔티티 필드 변경 (entity.setXXX() 호출)
- 트랜잭션이 커밋되거나 flush가 실행될 때 1차캐시 엔티티와 스냅샷의 변경사항을 비교
- update 쿼리 생성 및 쓰기 지연 저장소에 저장 후 쿼리 실행
5. Flush, FlushMode, 변경 감지 동작 예제
// 1. PK로 엔티티 조회
Member member = MemberRepository.findById(id);
// 엔티티 필드 수정
member.setGrade(5);
// 2. PK가 아닌 컬럼으로 엔티티 조회
Member member = MemberRepository.findByPhoneNum(phoneNum);/ ㅇ 이후 쿼리 실행 로직 (생략)
실행되는 SQL 쿼리 순서
- SELECT (PK 기반 조회)
- UPDATE 실행
- SELECT (컬럼 기반 조회)
왜 UPDATE 쿼리가 실행될까?
findByPhoneNum()은 PK가 아닌 컬럼을 기준으로 엔티티를 조회하는 메서드이므로, 1차 캐시가 아닌 데이터베이스 테이블을 직접 조회한다.
따라서 JPA는 **데이터베이스 일관성을 유지하기 위해 조회 전에 변경 사항을 flush**한다. flush 과정에서 변경 감지가 수행되어 UPDATE 쿼리가 생성되고, 쓰기 지연 저장소에 저장된 후 실행된다.
6. Flush가 발생하지 않는 경우
만약 findByPhoneNum()이 아니라 전혀 관련 없는 다른 테이블을 조회한다면, flush는 실행되지 않는다.
즉, flush는 영속성 컨텍스트에 보류 중인 변경이 포함된 테이블을 조회할 때만 발생한다. 다른 테이블을 조회할 경우, 변경 사항과 관련이 없으므로 flush가 실행되지 않는다.
7. 영속성 컨텍스트 - Flush - Commit 과정
이해하기 쉽게 그림으로 정리해보자.

이렇게 보면 감이 올 것이다. flush는 변경 사항을 데이터베이스에 밀어 넣지만, 트랜잭션이 커밋되기 전까지 확정되지 않는다.
8. 정리
- 영속성 컨텍스트: JPA가 엔티티를 관리하는 메모리 공간 (1차 캐시 역할)
- Flush: 변경 사항을 데이터베이스에 반영하는 과정 (트랜잭션 내에서만 적용됨)
- Commit: 트랜잭션이 종료되며, 변경 사항이 데이터베이스에 확정됨
- FlushMode: AUTO는 JPQL 실행 시 flush, COMMIT은 트랜잭션 커밋 시에만 flush
- 변경 감지: JPA가 변경된 엔티티를 감지하여 UPDATE 쿼리를 생성함
이제 JPA의 flush가 언제, 왜 실행되는지 감이 좀 잡혔을 거다. 더 깊이 알고 싶다면 직접 실험해보는 게 최고다. JPA의 동작을 로그로 확인하면서 실행 순서를 보는 것도 추천한다!
'Spring' 카테고리의 다른 글
| 로깅 - SLF4J, Logback, Log4j2 차이 (1) | 2025.06.18 |
|---|---|
| JPA에서 save()는 언제 의미가 있을까? – 더티 체킹과의 차이 (1) | 2025.06.17 |
| Spring Boot 통합 테스트 가이드 (1) | 2025.06.13 |
| SpringBoot static 변수에 @value 사용 (0) | 2025.01.17 |