[JPA] 더티 체킹(Dirty Checking)

도두맨·2025년 4월 1일

공부

목록 보기
9/23
post-thumbnail

🗓 ScheduleApp 개발 중 Dirty Checking 개념 정리

ScheduleApp develop 과제를 진행하다가,
SQL을 직접 썼을 때는 아래처럼 원하는 필드만 쿼리로 쉽게 처리했다.

UPDATE table SET deleted_at = NOW() WHERE id = ?

하지만 JPA는 처음이라서 어떻게 원하는 필드만 업데이트할 수 있을지 고민하게 됐다.
그 과정에서 Dirty Checking이라는 개념을 알게 되었고,
조금 어려웠기 때문에 이 내용을 정리해서 velog에 기록해본다.


❓ Dirty Checking이란?

JPA가 DB의 상태 변경을 자동으로 감지해서 필요한 SQL만 날려주는 기능!

  • 이 검사는 영속성 컨텍스트 안에서만 일어난다.
    • ❌ 비영속 / 준영속 상태에서는 동작하지 않음
  • 영속성 컨텍스트는 Entity 객체를 보관하고 관리하는 임시 저장소라고 생각하면 된다.
  • 이 모든 과정은 트랜잭션 안에서만 일어나므로, @Transactional필수다!

🧠 내가 헷갈렸던 부분들

Dirty Checking을 이해하는 데 방해가 되었던 오해 두 가지:

  1. 트랜잭션은 DB에 왔다 갔다 하는 거 아닌가?
  2. @Transactional 안 써도 save(user)는 잘 되던데?

🔍 설명해보자면

  • 트랜잭션은 DB 작업을 하나의 논리적 단위로 묶는 개념.
    → 트랜잭션이 커밋될 때 DB에 최종 반영된다.
  • 예를 들어 한 트랜잭션 안에서 findById()를 여러 번 호출해도 DB에 한 번만 요청되는데 그 이유는,
    → 첫 조회 결과가 영속성 컨텍스트에 캐싱되기 때문이다.
  • @Transactional 없이 save(user)가 작동하는 이유는?
    JpaRepositoryCrudRepository의 메서드에 이미 @Transactional붙어있기 때문이다.
    → 복수의 로직을 묶을 때는 직접 @Transactional을 사용해야 한다.

🔁 Dirty Checking의 전체 과정

1. 트랜잭션 시작 (@Transactional)
   → 영속성 컨텍스트 & EntityManager 생성

2. findById로 엔티티 조회
   → 조회한 객체 + Snapshot(초기 상태) 저장
   → 내부적으로는 Map<Field, Value> 형태

3. 엔티티 필드 변경
   → 객체는 변경되지만 아직 DB 반영 X

4. flush() 발생
   → 현재 객체 상태 vs 스냅샷 비교
   → 변경된 필드만 추려서 UPDATE SQL 생성

5. DB에 COMMIT → 변경 사항 최종 반영

💡 참고
save(user)를 사용할 때 user 객체에 ID가 없다면 JPA는 기존 데이터를 조회하지 않고 바로 INSERT를 실행함 → Dirty Checking 아님!


✅ 한눈에 정리

개념설명
Dirty Checking영속성 컨텍스트 내부에서 스냅샷과 변경된 엔티티를 비교해 변경된 필드만 UPDATE
트랜잭션DB에 대한 하나의 논리적 작업 단위. 커밋 시 반영됨
영속성 컨텍스트엔티티와 스냅샷을 저장하는 임시 저장소. Dirty Checking의 핵심 요소

📝 마무리

이렇게 글로 정리하니 이제서야 머릿속에 개념이 좀 들어오는 것 같다 😌
처음엔 헷갈렸던 개념들도 정리하면서 자연스럽게 연결되었다.
JPA는 처음일수록 더티 체킹 개념을 꼭 짚고 넘어가는 것이 중요한 것 같다!

0개의 댓글