본문 바로가기
공부/Spring Boot

JPA의 더티체킹과 영속성 컨텍스트.. 의 관계 ! 완벽이해

by son_i 2025. 4. 22.
728x90

 

더티체킹을 적용하려고 개념을 되짚어 보다가 다시 정리해보려고 한다 !

 

더티체킹이란 ? )

JPA에서 영속성 컨텍스트가 관리 중인 엔티티 객체의 변경을 감지하고 자동으로 DB에 반영해주는 기능이다.

트랜잭션이 끝나기 전에 엔티티의 값이 바뀌었는지 감지하고, 바뀐 경우에만 UPDATE 쿼리를 자동으로 날린다. 

 

나는 이 기능을 주로 정보 수정 API에 유용하게 사용하고 있었다.

 

더티체킹이 없다면 회원 정보 중에서 이름을 수정한다고 했을 때

@Transcational
public void updatedMember() {
	Member member = memberRepository.findById(1L);

	member.setName("newName");
	memberRepository.save(member);
}

이렇게 바뀐 항목에 대해 다시 save를 통해 변경된 엔티티를 다시 저장해줘야 한다.

 

반면 더티체킹을 사용한다면 save() 없이도 JPA가 알아서 변경된 내용을 감지해 UPDATE 쿼리를 자동으로 날린다. 

@Transcational
public void updatedMember() {
	Member member = memberRepository.findById(1L); // 1. 영속상태

	member.setName("newName"); // 2. 값 변경
}// 3. 트랜잭션 종료 시 -> JPA가 변경 감지 -> UPDATE 쿼리 실행

 

더티체킹의 조건

1. @Transactional이 필수적 :

    트랜잭션 범위내에서 영속성 컨텍스트가 살아있어야 한다. (그래야만 트랜잭션이 커밋될 때 JPA가 변경사항을 감지해 반영) 

2. 엔티티가 영속 상태여야 한다.

3. set등으로 값이 변경되어야 한다. 

   단순 조회로는 동작 X 

 

그럼 여기서 자주 듣는 영속성 컨텍스트가 또 머냐 ,.... 하면

 

영속성 컨텍스트란 ? )

JPA에서 엔티티를 관리하는 일종의 1차캐시.

자바 객체(Entity)를 DB에 매핑하면 JPA가 그 객체를 관리해야하는데 그 객체가 관리되는 공간이 영속성 컨텍스트 !

  

이 저장소에 들어가야 == 영속성 컨텍스트에 올라가야 JPA가 변경감지, 자동 업데이트, 쿼리 생성을 해줄 수 있다.

 

영속성 컨텍스트의 특징

1. 1차 캐시 : 같은 ID로 엔티티를 두 번 조회하면 DB가 아닌 메모리에서 가져온다.

2. 동일성 보장 : 같은 트랜잭션 안에서는fidnById(1L) 두 번 해도 동일 객체 반환

3. Dirty Checking : 값만 바꿔도 자동으로 변경을 감지해서DB에 업데이트

4. 지연 로딩(Lazy Loading) : 연관된 엔티티는 필요할 때만 쿼리로 불러옴

5. 쓰기 지연(Write-behind) : insert/update 쿼리를 모아서 트랜잭션 종료시 한 번에 날림(flush)  

 

엔티티의 생명주기(상태)

1. 비영속 (Transient) :

   - DB와 아무 관련 없는 단순 자바 객체

   - 아직 영속성 컨텍스트에 저장되지 않은 상태

   - new 키워드를 통해 객체를 생성하면 이 상태임.

   - 영속성 컨텍스트가 관리하지 않고 DB에 저장되지 않음.

 

2. 영속 (managed) :

   - 영속성 컨텍스트에 의해 관리되는 상태

   - EntityManager.persist() 또는 reposioty.save()하면 영속 상태가 됨.

 

3. 준영속 (detached) :

   - 영속성 컨텍스트에서 관리가 중단된 상태

   - 영속 상태에서 EntityManager.detach(), em.clear(), 트랜잭션 종료로 관리가 끊어지면 준영속.

   - 더이상 변경 감지가 이뤄지지 않음. 수동으로 다시 영속화 해야 변경사항 반영 가능.

 

4. 삭제 (removed) :

   - 영속성 컨텍스트에서 삭제가 예약된 상태

   - em.remove() 또는 Repository.delete() 호출 시

   - 트랜잭션이 커밋되면 DB에서 해당 데이터가 삭제됨.

 


아하 ! 그러면 JPA는 영속성 컨텍스트를 통해 엔티티를 관리해주고 있었던 거고 
엔티티가 영속상태에 있어야 DirtyChecking등의 여러 동작들이 가능해지는구나 ~

 

memberRepository.findByXx 머시기로 조회해서 오면 find()가 호출되어 영속상태가 되는거라고 한다.

이제 개념은 알겠다 ! 

 

근데 직접적으로 영속성 컨텍스트를 다루지 않고 JPA 내부에서 자동으로 상호작용되니 어느 상황에 영속 or 준영속 상태가 되는지 와닿지가 않는 느낌이었다.

 

따라서 프로젝트에서 사용하는 코드 기준으로 정리해보려고 한다 !

 

영속상태가 되는 경우 -> save()나 fidnByXxx() 같은 조회 메소드를 사용할 때 !

1. memberRepository.save(entity) 

Member member = new Member();
member.setName("버니");
memberRepository.save(member); //INSERT

 

신규엔티티면 persist()가 내부에서 호출됨.

영속성 컨텍스트에 등록되며 영속 상태가 됨.

 

Member member = memberRepository.fidnById(1L).get();
member.setName("번번이");
memberRepository.save(member)//Dirty Checking으로 자동 반영. (save 호출 안 해도 반영됨!)

기존 엔티티면 이미 영속 상태.

값 변경만 하면 Dirty checking에 의해 자동 업데이트

 

 2. findById()등으로 조회할 때 

Member member = memberRepository.findById(1L).get();

JPA가 내부적으로 EntityManager.find()를 사용

DB에서 조회되면 영속성 컨텍스트에 등록되어 영속상태로 관리됨.

 


이제 어느정도 이해가 된다 ! JPA가 엔티티를 관리하려고 영속성컨텍스트라는 일종의 1차 캐시공간을 사용하는데 
프로젝트에서는 직접적으로 EntityManger를 호출해서 쓰지 않지만
레포지토리에 save()나 find()를 하면 해당 엔티티가 영속상태가 된다는 것 !

 

그런데 이게 @Transactional이랑은 무슨 관계이지 ?!?!?! 라는 의문이 또 들었다.

 

@Transcational은 영속성 컨텍스트의 생명 주기를 결정한다.
== 영속성 컨텍스트는 트랜잭션 안에서만 동작한다 !

 

트랜잭션이 시작되면 영속성 컨텍스트가 생기고, 트랜잭션이 끝나면 영속성 컨텍스트도 종료(=flush + detach) 된다 !

@Transactional 이 없으면 변경사항은 감지돼도 반영할 타이밍이 없기 때문이 무시된다.

 

트랜잭션 커밋 시점에 flush()가 일어나고 실제 SQL이 실행된다.

 

다시 더티체킹으로 돌아가서... ~ 

더티체킹을 위해서는 ? 

 1. @Transactional이 필수적.

  Y?) JPA가 변경감지를 하려면 엔티티가 영속상태가 되어야 한다. 영속성 컨텍스트는 트랜잭션 안에서만 동작하므로!

 

2. 엔티티가 영속상태여야 함

 Y?) 당연함. 결국은 다 똑같은 얘기를 다르게 표현하고있던 것 ! 

 

 

궁금했던 것들

Q. 단순 조회로도 엔티티가 영속 상태가 되는지 

-> ㅇㅇ save()나 findBy() 쿼리 시 영속상태가됨.

 

 Q. 영속성 컨텍스트에서 관리되지 않는 엔티티는 변경사항이 반영되지 않음?

-> ㅇㅇ 당연 JPA가 관리해주지 않기 때문에. 변경사항을 감지하도록 하려면 다시 영속화 하거나 새로운 엔티티 만들어 저장해야함.

 

+ Spring data JPA에서 조회 후 영속 상태를 벗어나는 경우

 1. 조회된 엔티티가 영속성 컨텍스트의 범위를 벗어남 == 트랜잭션 종료일 때

 2. Lazy Loading 필드 접근 시 준영속 문제 발생.

728x90