📝 TIL (Today I Learned) — 2025.09.24. 수요일
✍️ 내일배움캠프에서 Spring 학습 중 정리한 내용입니다.
왜 알아야 할까?
백엔드 개발에서는 항상 자바 객체와 데이터베이스 행(Row) 사이를 오가야 합니다.
매번 SQL을 직접 작성하면 코드가 복잡해지고 유지보수가 어려워집니다.
👉 이 과정을 단순화해 주는 것이 JPA이며, 그 핵심에 영속성 컨텍스트(Persistence Context) 가 있습니다.
영속성 컨텍스트는 엔티티(Entity)를 보관하고 상태를 추적하는 공간입니다.
덕분에,
- 같은 데이터를 반복 조회해도 DB를 다시 찍지 않고 캐시에서 가져올 수 있고,
- 엔티티 변경을 자동으로 감지해서 SQL을 만들어주며,
- 여러 SQL을 모아 효율적으로 한 번에 전송할 수 있습니다.
1. 영속성 컨텍스트란?
한 문장으로 정리하면,
“JPA가 엔티티를 캐싱하고, 변경을 추적하며, DB 반영 시점을 관리하는 작업 공간”입니다.
📌 영속성 컨텍스트 주요 기능
- 1차 캐시: 동일한 엔티티를 여러 번 조회해도 DB가 아닌 캐시에서 반환합니다.
- 동일성 보장: 같은 영속성 컨텍스트 내에서는 == 비교가 true입니다.
- 변경 감지(Dirty Checking): 엔티티의 필드를 바꾸면 커밋 시점에 자동으로 UPDATE SQL이 생성됩니다.
- 쓰기 지연(Write-Behind): INSERT, UPDATE, DELETE SQL을 모아 두었다가 트랜잭션 커밋 시 한 번에 실행합니다.
2. JPA와 데이터베이스 관계
JPA는 애플리케이션 ↔ 데이터베이스 사이의 중간 계층 역할을 합니다.
개발자는 엔티티만 다루면 되고, JPA가 SQL로 변환해 DB와 통신합니다.
3. EntityManager와 EntityManagerFactory
- EntityManagerFactory (EMF)
- 무겁고 스레드 안전합니다.
- 보통 애플리케이션 시작 시 DB당 하나만 생성해서 전체에서 공유합니다.
- EntityManager (EM)
- 가볍습니다.
- 요청/트랜잭션 단위로 생성합니다.
- 하나의 EntityManager는 하나의 영속성 컨텍스트를 가집니다.
- 스레드 안전하지 않으므로 사용 후 닫아야 합니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo");
EntityManager em = emf.createEntityManager();
📌 Spring Boot에서는?
이 과정을 직접 하지 않습니다.
spring-boot-starter-data-jpa가 자동으로 EMF를 만들고, 요청마다 EM을 주입합니다.
개발자는 @Transactional만 선언하면 됩니다.
4. EntityManagerFactory 생성 과정
EntityManagerFactory는 persistence.xml 설정을 기반으로 생성됩니다.
<persistence-unit name="memo">
<class>com.sparta.entity.Memo</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value="비밀번호"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
5. 트랜잭션과 쓰기 지연
트랜잭션은 "전부 성공하거나 전부 실패해야 하는 하나의 작업 단위"입니다.
JPA는 엔티티 변경 내용을 즉시 DB에 보내지 않고, 쓰기 지연 저장소(Action Queue) 에 쌓아둡니다.
그리고 트랜잭션이 커밋될 때 flush()가 발생하면서 SQL이 한 번에 실행됩니다.
flush가 발생하는 시점은 다음과 같습니다.
- 트랜잭션 커밋 직전 (자동)
- 명시적으로 em.flush() 호출 시
- JPQL 실행 직전 (기본 AUTO 모드일 경우)
6. 저장과 롤백 예제
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Memo memo = new Memo();
memo.setId(1L); // @GeneratedValue 사용 시에는 직접 지정하지 않습니다.
memo.setUsername("Robbie");
memo.setContents("영속성 컨텍스트 테스트");
em.persist(memo); // DB에는 즉시 반영되지 않음
tx.commit(); // commit 시점에 INSERT SQL 실행
} catch (Exception e) {
tx.rollback(); // 오류 발생 시 되돌림
} finally {
em.close();
emf.close();
}
- em.persist() 호출만으로는 DB에 반영되지 않습니다.
- tx.commit() 시점에 flush()가 실행되며, INSERT SQL이 DB에 전달됩니다.
- 오류가 발생하면 rollback()으로 모든 변경이 취소됩니다.
7. 실무에서는 이렇게 사용합니다
Spring Boot 환경에서는 @Transactional만 붙이면 영속성 컨텍스트와 트랜잭션이 자동으로 열리고 닫힙니다.
@Service
@RequiredArgsConstructor
public class MemoService {
private final EntityManager em;
@Transactional
public void save(Memo memo) {
em.persist(memo); // commit 시점에 DB 반영
}
}
- 조회 전용 메서드에는 @Transactional(readOnly = true)를 붙이면 불필요한 flush 방지로 성능 최적화에 도움이 됩니다.
마무리
영속성 컨텍스트는 JPA의 가장 중요한 개념입니다.
“트랜잭션 동안 엔티티를 캐시하고, 변경을 추적하며, SQL 실행 시점을 조절하는 공간”
Spring Boot 환경에서는 @Transactional만 올바르게 사용해도 이 모든 기능이 자동으로 동작합니다.
'TIL > 내일배움캠프' 카테고리의 다른 글
| [AI] 제미나이(Gemini) API 키 발급받기 (1) | 2025.10.13 |
|---|---|
| [JPA] 영속성 컨텍스트의 기능 (0) | 2025.09.25 |
| [Spring] IoC 컨테이너와 Bean — 객체를 관리하는 Spring의 방식 (0) | 2025.09.23 |
| [Spring] IoC와 DI — 좋은 코드의 핵심 원칙 (0) | 2025.09.22 |
| [Java] 문자열 다루기 — String, StringBuffer, StringBuilder 차이 (0) | 2025.09.19 |