목차

    ⏳ Time Log/1. One Day (Daily · TIL)

    [TIL] Day 67 — JPA 영속성 컨텍스트 심화 (1/22)

    this.Serena 2026. 6. 8. 21:32

    [TIL] Day 67 — JPA 영속성 컨텍스트 심화 (1/22)

    날짜: 2026-01-22
    기술 스택: Spring Boot JPA Hibernate 영속성 컨텍스트


    한 줄 요약: JPA 영속성 컨텍스트의 핵심 개념 — 1차 캐시, 변경 감지(Dirty Checking), 지연 로딩 동작 원리 정리


    JPA 영속성 컨텍스트 (Persistence Context)

    엔티티를 저장하는 논리적 공간. EntityManager가 관리하는 1차 캐시 역할

    엔티티 생명주기

    비영속 (new)
    ↓ em.persist()
    영속 (managed)    ← 1차 캐시에 저장, 변경 감지 대상
    ↓ em.detach() / em.clear() / em.close()
    준영속 (detached) ← 컨텍스트에서 분리, 변경 감지 안 됨
    
    영속 (managed)
    ↓ em.remove()
    삭제 (removed)    ← DB 삭제 예약

    1차 캐시 — 같은 트랜잭션 내 DB 조회 최소화

    // 첫 번째 find() → DB 쿼리 발생
    Member member1 = em.find(Member.class, 1L);
    
    // 두 번째 find() → 1차 캐시에서 반환, DB 쿼리 없음
    Member member2 = em.find(Member.class, 1L);
    
    System.out.println(member1 == member2); // true (동일 인스턴스)

    변경 감지 (Dirty Checking)

    em.update() 같은 메서드 없이 필드만 바꿔도 트랜잭션 커밋 시 자동으로 UPDATE 쿼리 발생

    @Transactional
    public void changePrice(Long itemId, int newPrice) {
        Item item = itemRepository.findById(itemId).orElseThrow();
        item.setPrice(newPrice); // 별도 save() 호출 없이 자동 반영
    }

    원리: 영속성 컨텍스트는 엔티티를 처음 조회할 때 스냅샷을 저장
    → 커밋 시점에 현재 상태와 스냅샷 비교 → 다르면 UPDATE 쿼리 생성

    지연 로딩 (Lazy Loading) 주의사항

    @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
    private List<OrderItem> orderItems; // 실제 접근 시 쿼리 발생

    트랜잭션 밖에서 지연 로딩된 컬렉션에 접근하면 LazyInitializationException 발생
    → 해결책: @Transactional 범위 안에서 처리하거나 fetch join 사용


    더 알아볼 것

    • @Transactional(readOnly = true) — 읽기 전용 트랜잭션에서 Dirty Checking 비활성화 효과
    • OSIV (Open Session In View) 패턴과 그 문제점
    • N+1 문제 해결 — fetch join vs @EntityGraph
    • em.flush()em.clear()를 명시적으로 사용하는 케이스