TIL/내일배움캠프

[JPA] 영속성 컨텍스트의 기능

bu119 2025. 9. 25. 18:20
728x90
반응형

📝 TIL (Today I Learned) — 2025.09.25. 목요일
✍️ 내일배움캠프에서 Spring 학습 중 정리한 내용입니다.


왜 알아야 할까?

JPA의 핵심은 영속성 컨텍스트(Persistence Context) 입니다.

 

영속성 컨텍스트는 단순히 엔티티를 보관하는 공간이 아니라,

  • 조회 최적화 (캐시 제공)
  • 동일성 보장
  • SQL 실행 시점 제어 (쓰기 지연)
  • 변경 자동 감지 (Dirty Checking)

와 같은 기능을 통해 개발자가 SQL을 직접 관리하지 않아도 안정적이고 효율적인 데이터 처리를 가능하게 합니다.
즉, 성능 최적화와 개발 생산성을 동시에 돕는 핵심 메커니즘입니다.

 

👉 이번 글에서는 영속성 컨텍스트가 제공하는 주요 기능을 살펴보겠습니다.


1. 1차 캐시 (First-Level Cache)

영속성 컨텍스트는 내부적으로 Map<Id, Entity> 형태의 1차 캐시를 가집니다.

  • Key: @Id 로 매핑된 식별자 값
  • Value: 엔티티 객체

📌 동작 방식

em.persist(entity); // 엔티티 → 1차 캐시에 저장
em.find(Entity.class, id); // 캐시에 있으면 DB 조회 없이 반환
 

예시:

Memo memo1 = em.find(Memo.class, 1L); // 캐시에 없으면 DB 조회 후 캐시에 저장
Memo memo2 = em.find(Memo.class, 1L); // 캐시에 있으므로 DB 조회 없음

System.out.println(memo1 == memo2); // true (동일 객체 보장)

👉 같은 데이터를 반복 조회할 때 DB 부하를 줄이고, 항상 같은 객체를 반환하여 객체 동일성을 보장합니다.


2. 엔티티 저장과 조회

저장

Memo memo = new Memo();
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("1차 캐시 테스트");

em.persist(memo); // 1차 캐시에 저장됨

조회

  • 캐시에 없으면 DB 조회 후 캐시에 저장
  • 캐시에 있으면 DB 조회 생략
Memo memo1 = em.find(Memo.class, 1L); // DB 조회 → 캐시에 저장
Memo memo2 = em.find(Memo.class, 1L); // 캐시에서 바로 조회

3. 엔티티 삭제

엔티티를 삭제할 때도 캐시를 활용합니다.

  1. 삭제 대상 엔티티 조회 (캐시에 없으면 DB에서 불러와 저장)
  2. em.remove(entity) 호출 → 엔티티 상태가 DELETED로 변경
  3. 트랜잭션 commit 시 DELETE SQL 실행
Memo memo = em.find(Memo.class, 2L); // MANAGED 상태
em.remove(memo);                     // DELETED 상태
tx.commit();                         // DELETE SQL 실행

⚠️ 삭제하려는 엔티티는 반드시 MANAGED 상태여야 합니다.


4. 쓰기 지연 저장소 (Write-Behind)

JPA는 SQL을 곧바로 실행하지 않고, 쓰기 지연 저장소(Action Queue) 에 모아둡니다.

Memo m1 = new Memo(2L, "Robbert", "쓰기 지연 저장소");
em.persist(m1);

Memo m2 = new Memo(3L, "Bob", "SQL 모으기");
em.persist(m2);

// 아직 INSERT SQL 실행되지 않음
tx.commit(); // commit 시점에 INSERT SQL 2개 한 번에 실행

👉 SQL을 모았다가 한 번에 실행함으로써 성능 최적화가 가능합니다.


5. flush()

flush()는 영속성 컨텍스트의 변경 내용을 쓰기 지연 저장소에 쌓인 SQL을 DB에 반영하는 역할을 합니다.

  • 자동 실행 시점
    1. 트랜잭션 커밋 직전
    2. JPQL 실행 직전 (FlushModeType.AUTO일 경우)
  • 수동 실행
    필요하다면 em.flush()를 직접 호출할 수도 있습니다.
    (단, 트랜잭션 내부에서만 호출 가능)
Memo memo = new Memo(4L, "Flush", "flush 직접 호출");
em.persist(memo);

em.flush(); // 즉시 INSERT SQL 실행

6. 변경 감지 (Dirty Checking)

JPA에는 em.update() 메서드가 없습니다.

대신 변경 감지(Dirty Checking) 로 UPDATE를 처리합니다.

 

📌 동작 방식

  1. 엔티티를 영속성 컨텍스트에 저장할 때 최초 상태(Snapshot)를 함께 보관
  2. 트랜잭션 커밋 시 flush 실행
  3. 현재 상태와 스냅샷 비교
  4. 변경이 있으면 UPDATE SQL 생성
Memo memo = em.find(Memo.class, 4L); // 최초 상태 스냅샷 저장
memo.setUsername("Update");
memo.setContents("변경 감지 확인");

tx.commit(); // UPDATE SQL 자동 실행

👉 엔티티의 값을 바꾸는 것만으로 SQL이 자동 실행되므로, 개발자가 직접 UPDATE SQL을 작성할 필요가 없습니다.


마무리

영속성 컨텍스트는 단순 저장소가 아니라,

1차 캐시, 동일성 보장, 삭제 관리, 쓰기 지연, flush, 변경 감지

 

를 통해 SQL 작성 부담을 줄이고 성능까지 최적화합니다.

 

Spring Boot 환경에서는 @Transactional만 올바르게 선언해도 이 모든 기능이 자동으로 동작하므로, 개발자는 비즈니스 로직에 집중할 수 있습니다.

728x90
반응형