더티 체킹(Dirty Checking)
한눈에 보기
더티 체킹은 영속성 컨텍스트가 엔티티 변경을 감지해 플러시 시점에 UPDATE 쿼리를 자동 생성하는 JPA 기능이다. 스냅샷 비교 원리, 플러시 시점, 준영속 엔티티와 벌크 연산의 주의점을 다룬다.
최종 업데이트 2026.09.17
더티 체킹(Dirty Checking)은 JPA(Java Persistence API)의 영속성 컨텍스트(Persistence Context)가 관리 중인 엔티티(Entity)의 변경을 스스로 감지해서, 플러시(Flush) 시점에 UPDATE 쿼리를 만들어 데이터베이스에 반영하는 기능이다. 개발자가 별도의 저장 메서드를 호출하지 않아도 트랜잭션 안에서 엔티티의 필드 값만 바꾸면 수정이 반영되므로, 수정 로직을 객체지향적인 코드로 쓸 수 있게 해 준다.
왜 변경 감지가 필요한가
SQL 중심 개발에서는 데이터를 수정할 때마다 UPDATE 문을 작성하고 어떤 컬럼을 바꿀지 일일이 나열해야 한다. 반면 JPA는 데이터베이스의 행을 자바 객체로 옮겨 와서 다루는 것을 목표로 하므로, 객체의 상태를 바꾸면 그 변화가 저장소에도 반영되는 것이 자연스럽다. 더티 체킹은 이 간극을 메우는 장치다. 조회한 엔티티의 세터(Setter)나 비즈니스 메서드로 상태를 바꾸기만 하면, 무엇이 어떻게 바뀌었는지 추적해 SQL로 번역하는 일은 영속성 컨텍스트가 맡는다.
스냅샷 비교로 동작한다
영속성 컨텍스트는 엔티티를 1차 캐시에 넣는 순간, 그 시점의 필드 값을 복사한 스냅샷(Snapshot)을 함께 보관한다. 이후 플러시가 일어나면 다음 과정이 진행된다.
- 1차 캐시에 있는 모든 영속 엔티티를 순회하면서 현재 값과 스냅샷을 필드 단위로 비교한다.
- 달라진 엔티티가 있으면 UPDATE SQL을 생성해 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 저장소에 쌓인 SQL을 데이터베이스로 전송한다.
- 트랜잭션이 커밋되면 변경이 확정된다.
이때 하이버네이트(Hibernate)가 생성하는 UPDATE 문은 기본적으로 바뀐 필드만이 아니라 엔티티의 모든 컬럼을 포함한다. 컬럼이 고정된 쿼리를 재사용하면 구문을 미리 준비해 둘 수 있고 캐싱 효율이 좋기 때문이다. 컬럼이 매우 많거나 변경 폭이 좁은 테이블이라면 @DynamicUpdate를 붙여 바뀐 컬럼만 포함한 쿼리를 만들 수 있지만, 매번 쿼리를 새로 만드는 비용이 생기므로 측정 후에 적용하는 것이 좋다.
플러시 시점과 트랜잭션
더티 체킹은 아무 때나 일어나는 것이 아니라 플러시 시점에 일어난다. 플러시는 트랜잭션 커밋 직전, JPQL(Java Persistence Query Language) 쿼리 실행 직전, 그리고 flush()를 직접 호출할 때 발생한다. 따라서 스프링에서는 @Transactional 범위 안에서 엔티티를 수정해야 하며, 트랜잭션이 커밋되면서 변경이 자동으로 반영된다. 플러시는 SQL을 전송하는 것이지 트랜잭션을 커밋하는 것이 아니므로, 플러시 이후에도 롤백하면 변경은 취소된다는 점을 구분해야 한다.
코드 예시
회원 이름을 수정하는 서비스 코드는 다음과 같다. save()나 update() 같은 호출이 없다는 점이 핵심이다.
@Service
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Transactional
public void changeName(Long memberId, String newName) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("회원이 없습니다."));
member.changeName(newName); // 필드 값만 변경한다
// 트랜잭션 커밋 시점에 플러시가 일어나며
// 스냅샷과 비교해 UPDATE 쿼리가 자동 실행된다
}
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public void changeName(String newName) {
this.name = newName;
}
}
changeName처럼 의도가 드러나는 메서드로 상태를 바꾸면, 수정 로직이 엔티티 안에 응집되어 도메인 주도 설계와도 잘 어울린다.
주의할 점과 다른 수정 방식 비교
더티 체킹은 영속 상태의 엔티티에만 동작한다. 트랜잭션이 끝나 준영속(Detached)이 된 엔티티나 new로 만든 비영속 객체는 값을 바꿔도 아무 일도 일어나지 않는다. 준영속 객체를 넘겨받아 수정해야 한다면 식별자로 다시 조회한 뒤 값을 옮기는 방식이 안전하며, merge()는 모든 필드를 통째로 덮어써서 의도하지 않은 null 갱신을 일으킬 수 있으므로 신중히 써야 한다.
또한 수천 건 이상을 한 번에 수정할 때 더티 체킹을 쓰면 엔티티를 전부 메모리에 올리고 건별 UPDATE가 나가므로 비효율적이다. 이때는 JPQL 벌크 연산이 낫다.
| 방식 | 동작 | 적합한 상황 |
|---|---|---|
| 더티 체킹 | 스냅샷 비교로 건별 UPDATE 자동 생성 | 소수 엔티티의 비즈니스 로직 중심 수정 |
| merge() | 준영속 객체의 모든 필드를 영속 엔티티에 덮어씀 | 전체 필드를 교체하는 명확한 경우 |
| 벌크 연산 | UPDATE JPQL을 데이터베이스에 직접 실행 | 대량 일괄 수정 |
벌크 연산은 영속성 컨텍스트를 거치지 않으므로, 실행 후에는 1차 캐시와 데이터베이스가 어긋날 수 있다. Spring Data JPA의 @Modifying(clearAutomatically = true)처럼 컨텍스트를 비워 정합성을 맞추는 처리가 필요하다. 마지막으로 읽기 전용 조회에는 @Transactional(readOnly = true)를 붙이면 하이버네이트가 스냅샷 비교를 생략해 메모리와 CPU 비용을 아낄 수 있다.
면접에서 이렇게 물어본다
- 더티 체킹이 무엇이고 내부적으로 어떤 원리로 변경을 감지하는지 설명해 보라.
- 더티 체킹으로 생성되는 UPDATE 쿼리가 기본적으로 모든 컬럼을 포함하는 이유와 @DynamicUpdate의 트레이드오프를 말해 보라.
- 준영속 엔티티는 왜 더티 체킹의 대상이 아니며, 준영속 객체를 수정해야 할 때는 어떻게 하는지 설명해 보라.
- 플러시와 커밋의 차이, 그리고 플러시가 일어나는 세 가지 시점을 말해 보라.
- 대량 데이터 수정에 더티 체킹 대신 벌크 연산을 쓰는 이유와 벌크 연산 후 주의할 점을 설명해 보라.
Spring / JPA에서 같이 읽기
- JPA(Java Persistence API)JPA는 자바 객체와 관계형 데이터베이스 테이블의 매핑을 표준으로 정의한 ORM 명세다. 명세와 구현체의 관계, 영속성 컨텍스트의 기능, 엔티티 생명주기, N+1 문제까지 면접 관점에서 정리한다.
- SpringApplication과 부트스트랩 과정SpringApplication은 스프링 부트의 main 메서드에서 부트스트랩을 총괄하는 클래스다. run() 내부의 환경 준비, 컨텍스트 생성, refresh, 러너 실행 순서와 자동 구성 원리를 설명한다.
- 스프링 빈 스코프(Bean Scope)빈 스코프는 스프링 컨테이너가 빈을 언제 생성하고 어디까지 공유할지 정하는 규칙이다. 싱글턴과 프로토타입의 동작 차이, 웹 스코프, 스코프 혼용 시 문제와 ObjectProvider, 프록시 해결법을 정리한다.
- Spring Boot 테스트 코드 작성Spring Boot 테스트는 컨테이너를 어디까지 띄우느냐에 따라 통합 테스트와 슬라이스 테스트로 나뉜다. @SpringBootTest, @WebMvcTest, @DataJpaTest 활용법과 컨텍스트 캐싱 주의점을 다룬다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기