💻 Backend

Spring Boot 는 JPA 영속성 컨텍스트를 어떻게 관리할까?

date
slug
spring-boot-and-jpa-context
author
status
Public
tags
SpringBoot
Java
summary
[SpringBoot/JPA] 영속성 컨텍스트 이해하기
type
Post
thumbnail
category
💻 Backend
updatedAt
Jul 12, 2025 06:40 AM
 
 
 

Spring Boot 는 JPA 영속성 컨텍스트를 어떻게 관리할까?


 

1. Spring Boot 프레임워크는 어떻게 객체(Bean) 을 관리하는가?

ApplicationContext

Spring Boot 는 내부적으로 스프링 프레임워크의 ApplicationContext (IoC 컨테이너) 를 기반으로 함
ApplicationContext 는 모든 Bean의 상태와 생명주기를 책임지고, 객체 간 의존성을 주입하여 시스템 전체를 구성하는 역할을 함
 
  • 애플리케이션 실행 시 @Component @Service @Repository 등으로 정의된 클래스들을 Bean 으로 등록, 의존성 주입(DI) 를 통해 서로 연결해줌
    • Spring Boot 컨테이너에서 관리되는 객체를 ‘Bean’ 이라고 함
  • 객체의 생명주기스코프를 관리함
    • 예를 들어, @Service 로 선언된 클래스는 싱글톤으로 관리되며 모든 요청에 재사용됨
    • ApplicationContext 내 Bean 의 생명주기
      [Bean의 생성 → 주입 → 초기화 → 사용 → 종료]
      1. Bean 생성
          • @Component, @Service, @Repository, @Configuration 등 등록된 클래스들이 실행 시 스캔되어 Bean 으로 생성
      1. Bean 의존성 주입/초기화
          • 1번으로 생성될 때 생성자는 자동 실행되고, @Autowired 로 의존성 주입되며 초기화 됨
      1. Bean 사용
          • 보통 싱글톤 스코프(default): 하나의 인스턴스를 모든 요청에서 공유
          • @RequestScope, @SessionScope 등 범위 변경 가능
      1. Bean 사용 종료
          • 애플리케이션 종료 시 @PreDestroy, @DisposableBean 등 정리 작업 가능 (참고 링크)
       
 
 

2. Spring Boot 에서의 영속성 컨텍스트 (JPA)

PersistanceContext

영속성 컨텍스트는 JPA의 핵심 개념이지만, Spring Boot 는 이를 트랜잭션 단위로 자동으로 관리해줌
 
영속성 컨텍스트(PersistanceContext)란?
“JPA가 엔티티 객체를 저장하고 관리하는 1차 캐시 공간”
JPA 1차 캐시와 2차 캐시
 
[1차 캐시]
EntityManager 내부 → 요청/트랜잭션 단위
 
[2차 캐시]
EntityManagerFactory 수준에서 관리되는 전역 캐시 → 애플리케이션 전체 범위
 
[비교]
1차 캐시 (PersistenceContext)
기본적으로 활성화 EntityManager 내부에서 동작하며, 트랜잭션 범위 내 객체를 캐싱함 (영속성 컨텍스트에 담긴 객체) 동기화된 상태를 유지하며 Dirty Checking 대상이 됨
2차 캐시 (Second-Level Cache)
기본적으로 비활성화 EntityManagerFactory 수준에서 관리되며, 영속성 컨텍스트가 닫힌 이후에도 재사용 가능한 캐시 예시로 @Cacheable, 사용하려면 Hibernate 설정과 캐시 구현체(EHCache, Redis, Hazelcast 등)를 구성해야 함
 
 
데이터베이스와 동기화된 엔티티는 이 컨텍스트 안에서 관리되며 이 상태를 “영속 상태” 라고 함
그렇다면 Spring Boot 에서 영속성 컨텍스트는 언제 생성되고 어떻게 관리될까?
🟢 JPA를 사용하며 @Transactional이 선언 된 경우
  1. 트랜잭션 시작 (@Transactional 감지)
  1. Spring이 EntityManager 생성
    1. EntityManagerFactory 와 EntityManager
      EntityManager 는 직접 등록하는 것이 아니라 Spring Boot가 자동 설정(AutoConfiguration)
       
      [EntityManagerFactory]
      • EntityManagerFactory 는 DB 커넥션 풀과 설정 정보를 포함한 핵심 팩토리로, @Bean 으로 등록되며 싱글톤임
       
      [EntityManager]
      • EntityManager 는 실제로 영속성 컨텍스트를 갖고 DB 연산을 처리하는 객체이며, EntityManagerFactory 로부터 생성되고 요청마다 새로 생성함
      • EntityManager 는 트랜잭션이 시작될 때 DB 커넥션 풀에서 커넥션을 할당받고, 종료 시 반납
       
  1. EntityManager는 영속성 컨텍스트를 내부에 포함
    1. JPA 의 EntityManager 포함 핵심 인터페이스 목록
       
      인터페이스
      역할
      EntityManager
      핵심 엔티티 조작 API (persist, find, merge, remove 등)
      EntityTransaction
      트랜잭션 처리용 (Spring 없이 순수 JPA에서 사용)
      EntityManagerFactory
      EntityManager를 생성하는 팩토리
      Query / TypedQuery
      JPQL/Native 쿼리 실행을 위한 API
      CriteriaBuilder / CriteriaQuery
      타입 안전한 동적 쿼리 생성용 API (Criteria API)
      PersistenceUnitUtil
      프록시 초기화 여부 확인 등 보조 기능 제공
       
      [Query vs TypedQuery]
      // Query: 캐스팅 필요 Query q = em.createQuery("select u.name from User u"); List<String> names = q.getResultList(); // TypedQuery: 타입 안정성 보장 TypedQuery<User> tq = em.createQuery("select u from User u", User.class); List<User> users = tq.getResultList();
      항목
      Query
      TypedQuery<T>
      타입 안정성
      X
      O
      반환 타입
      Object, List<Object[]> 등
      T, List<T> 등 제네릭 적용
      사용 시점
      결과 타입을 컴파일 타임에 알 수 없는 쿼리
      타입이 명확한 쿼리 (select u from User u)
      사용법 예시
      em.createQuery(...)
      em.createQuery(..., User.class)
       
      [Criteria API]
      JPA 명세에 포함된 기본 기능으로 동적 쿼리를 생성할 수 있도록 설계된 API
      CriteriaBuilder, CriteriaQuery, Root, Predicate 등을 조합해서 쿼리를 구성함
      • 복잡하여 실무에서는 QueryDSL 또는 JPQL 사용
        • QueryDSL: 타입 안정성, 간결한 문법, 가장 많이 사용
        • JPQL: 간단한 쿼리에 유용하지만 복잡한 문법은 어려움
       
      [PersistenceUnitUtil]
      프록시 객체의 초기화 여부, 식별자(id) 추출 등에 사용
      Lazy 로딩 객체가 초기화되었는지 확인하거나, 엔티티의 식별자(PK)를 동적으로 가져올 때 사용
      // Lazy 로딩된 엔티티 초기화 여부 확인 Member member = em.find(Member.class, 1L); Address address = member.getAddress(); // LAZY 로딩 PersistenceUnitUtil util = em.getEntityManagerFactory().getPersistenceUnitUtil(); boolean isLoaded = util.isLoaded(address); System.out.println("초기화 여부: " + isLoaded); // 엔티티 식별자(PK) 추출 Object id = util.getIdentifier(member); System.out.println("ID 값: " + id);
       
  1. Service/Repository 계층에서 조회한 엔티티는 이 컨텍스트에 보관됨 → 영속 상태
  1. 트랜잭션 종료 시 동작
      • 변경된 엔티티 감지(Dirty Checking) → flush()
        • JPA 의 변경 감지(Dirty Checking)
          영속성 컨텍스트에 있는 엔티티 객체의 상태가 변경되었는지 감지하여
          변경된 경우, 트랜잭션 종료 시 자동으로 UPDATE SQL 을 실행하는 JPA의 기능
           
      • 커밋 후 컨텍스트 종료
 
🟢 JPA를 사용하며 @Transactional이 선언되지 않은 경우
  • Spring Boot 트랜잭션 자동 관리 X
    • EntityManager는 트랜잭션이 없는 상태로 동작하게 되며, 문제 발생 야기
      • find()
        동작은 함. 단순 조회는 트랜잭션 없어도 가능
        persist(), merge()
        반영되지 않거나, 예외 발생 가능
        변경 감지 (Dirty Checking)
        ❌ 작동 안 함 (flush가 안 됨)
    • 트랜잭션이 없으면 영속성 컨텍스트가 생성되지 않거나, DB 커밋이 안 되므로 JPA의 주요 기능이 제대로 작동하지 않음
🔵 JPA를 사용하지 않으며 @Transactional이 선언된 경우 (예: MyBatis, JDBC만 쓸 때)
  • Spring이 DataSourceTransactionManager 등을 통해 JDBC 레벨에서 트랜잭션을 관리
    • 내부적으로는 Connection.setAutoCommit(false)commit() 또는 rollback() 로직이 적용됨
 
🟡 JPA를 사용하지 않으며 @Transactional이 선언되지 않은 경우
  • Spring Boot ApplicationContext 에서 객체를 Bean으로 관리
    • 이 Bean은 기본적으로 싱글톤 스코프로, DI로 주입되어 사용됨
    • DB 트랜잭션이나 롤백은 수동으로 처리해야 함
      • 예시 코드
        // 트랜잭션이 없는 상태에서는 자동 커밋 jdbcTemplate.update("INSERT INTO ..."); // 또는 // 트랜잭션 수동 관리 Connection conn = dataSource.getConnection(); try { conn.setAutoCommit(false); PreparedStatement stmt = conn.prepareStatement("UPDATE member SET name = ? WHERE id = ?"); stmt.setString(1, "홍길동"); stmt.setInt(2, 1); stmt.executeUpdate(); conn.commit(); // 수동 커밋 } catch (Exception e) { conn.rollback(); // 예외 발생 시 롤백 } finally { conn.close(); }
    • 자동 커밋 되긴 하지만, 트랜잭션 단위로 묶이지 않기에 rollback 불가
      • 복수 쿼리를 하나의 단위로 묶고 싶다면 반드시 @Transactional 필요
 
 

 
 
Spring Boot 는 내부적으로 JPA의 EntityManager와 PersistenceContext를 관리하며,
트랜잭션 단위로 영속성 컨텍스트의 생성/종료를 자동으로 처리
 
 
JPA 객체의 생명주기는 EntityManager 내부에서 관리되는 영속성 컨텍스트와 연결되어 있음
즉, 일반적으로 JPA의 객체 상태(비영속, 영속, 준영속, 삭제)는 요청마다 새로 생성되는 EntityManager 단위로 결정
 
 
EntityManager는 매 트랜잭션마다 생성되고 사라지므로,
영속성 컨텍스트도 함께 매 요청 단위로 생성되고 사라지는 것