
이 글은 JPA 연관관계가 뭐야?! 라는 질문에서 출발했다.
우테코 오픈 미션을 진행하면서 JPA 연관관계를 공부하던 중 “엔티티끼리는 분명히 참조로 연결해두었는데 이게 정확히 어떤 시점에 DB에 반영되는 걸까?” 라는 궁금증이 생겼다.
특히 연관관계를 설정할 때 “JPA는 어떤 기준으로 엔티티의 상태를 추적하고, 언제 DB에 쓰는 걸까?” 이 부분이 명확히 이해되지 않았다.
이 궁금증을 따라가다 보니 자연스럽게
영속성 컨텍스트(Persistence Context) 라는 개념과 마주하게 됐다.
처음에는 단순히 외워야 할 개념 정도로 생각했지만
공부할수록 이 개념이 JPA의 근본적인 동작 원리를 이해하는 핵심이라는 걸 깨달았다.
그래서 이 글은 “JPA가 어떻게 엔티티를 관리하며, 왜 그렇게 동작하는가”를 한눈에 볼 수 있도록 정리한 내용이다.
미래의 내가 다시 돌아왔을 때 “아, 그래서 JPA가 이렇게 작동하는 거였지”하고 바로 이해할 수 있도록 나를 위한 복습용 아카이브이기도 하다.
JPA의 모든 동작은 영속성 컨텍스트(Persistence Context)를 중심으로 이루어진다.
이 컨텍스트는 말 그대로 엔티티를 “영속” 상태로 관리하는 메모리 공간이다.
즉, 엔티티를 단순히 생성하는 것이 아니라, JPA가 해당 객체를 추적하고 변경 사항을 관리하도록 등록하는 영역이다.

JPA의 엔티티는 New → Managed → Detached → Removed 네 가지 상태를 가진다.
이 그림은 그 상태들이 어떤 메서드에 의해 이동하는지 그리고 JPA 내부에서 어떤 일이 일어나는지를 보여준다.
new로 생성된 객체Book book = new Book("자바의 정석", "남궁성");
- 내부 동작
EntityManager는 이 객체를 전혀 모름flush()나 commit()과 무관new로 만든 객체는 JPA 입장에서 그냥 일반 자바 객체일 뿐이다.
em.persist(book) 을 호출하면 비영속 → 영속 상태로 전환된다.
즉, 이제 JPA가 이 객체를 영속성 컨텍스트(1차 캐시) 안에서 관리한다.
em.persist(book);

- 내부 동작
book을 1차 캐시(Map 구조) 에 등록@Id(PK) 값을 키로 관리flush() 시점에만 DB에 실제 INSERT 실행book의 필드 변경을 감시(Dirty Checking)book.setTitle("자바의 정석 - 개정판"); // 변경만 해도 JPA가 감지
em.flush(); // UPDATE SQL 자동 실행
영속 상태가 되면 엔티티의 변경이 SQL로 자동 반영된다.
detach(), clear(), close()를 호출하면 영속성 컨텍스트에서 엔티티가 제거되어 더 이상 JPA가 관리하지 않는다.
em.detach(book); // 개별 엔티티 분리
- 내부 동작
Detached는 말 그대로 “영속성 컨텍스트와의 연결이 끊어진 상태”다.
다시 영속 상태로 복구하려면 merge()를 사용한다.
Book mergedBook = em.merge(book); // 준영속 → 영속
merge()는 기존 객체를 다시 관리 상태로 복구하지만 내부적으로는 새로운 영속 객체를 만들어 반환한다.
em.remove(book)을 호출하면 엔티티가 삭제 예약 상태가 된다.
즉시 삭제되지 않고, flush 시점에 DELETE SQL이 실행된다.
em.remove(book);
- 내부 동작
flush() 또는 commit() 시 실제 DELETE FROM book ... 쿼리 실행remove()는 즉시 DB에서 지워지는 게 아니라 트랜잭션 커밋 시점에 반영된다.
그림에서 보듯 Managed 상태의 엔티티만이 DB와 직접적인 연결을 가진다.
JPA는 INSERT, UPDATE, DELETE 쿼리를 즉시 보내지 않고 '쓰기 지연 SQL 저장소(Write-behind)'라는 공간에 모아둔다.
em.persist(bookA);
em.persist(bookB);
// 아직 INSERT 없음
em.flush(); // INSERT 2개 SQL 한 번에 실행
em.commit(); // 트랜잭션 커밋
flush() = 영속성 컨텍스트 ↔ DB 상태 동기화commit() = flush() + 실제 트랜잭션 종료영속성 컨텍스트를 이해하려면 하나 더 중요한 사실이 있다.
스프링 환경에서 우리가 흔히 사용하는
bookRepository.save(book);
이 코드는 내부적으로 JPA의 EntityManager.persist(book) 또는 merge(book)를 호출한다.
persist()merge()즉, save()가 호출되는 시점이 엔티티가 비로소 영속(Managed) 상태가 되는 순간이다.
나는 예전에는 코드가 돌아가기만 하면 된다고 생각했었다.
하지만 이제는 “왜 그렇게 동작하는가?, 그 안에서 어떤 원리가 작동하는가?”를 먼저 질문하게 되었다.
이 경험을 통해 우아한테크코스가 추구하는 이해를 기반으로 한 성장이라는 가치가 실제로 어떤 의미인지 직접 경험하게 되었다.
이제는 단순히 기능을 구현하는 개발자가 아니라 기능 뒤의 원리까지 스스로 설명할 수 있는 개발자로 성장하고 싶다는 마음이 생겼다.
스프링 영속성 관리 (영속성 컨텍스트)
JPA 영속성 컨텍스트란?
[JPA] 영속성 컨텍스트(Persistence Context)란? - 개넘 정리 및 사용법