반응형
1️⃣ 영속성 ?
- 영속성( 永(길게) 續(속하다) - Persistence)이란, 프로그램이 종료된 이후에도 데이터가 사라지지 않고 저장되는 성질
- 즉, 메모리(휘발성 공간)가 아닌 디스크(비휘발성 저장소)에 데이터를 영구히 보존하겠다라는 강력한 의지가 나타내기 위한 것
2️⃣ 영속성은 JPA 개념이 ❌
- 우리가 DB에 데이터를 저장한다면, 이 데이터는 프로그램이 종료되어도 남아 있는다. 이 처럼 데이터가 장기적으로 저장되는 상태를 영속(Persistent) 하다고 표현함.
- 반대로, 자바 객체나 컬렉션에만 데이터를 담는 경우는 프로그램이 종료되면 사라지므로 비영속(Transient) 상태라고 함
3️⃣ JPA에서 '영속성' ?
- JPA는 객체(Entity)와 DB 테이블간 중간자 역할을 하며, 자바 객체를 DB에 '영속' 시키는 과정을 관리하기 위한 라이브러리임.
- 1명의 유치원 선생님이 여러 유치원생을 관리하려면 유치원이라는 환경이 필요하듯 여러 객체를 관리하기 위한 환경이 바로 영속성 컨텍스트(Persistence Context) 라는 메모리 영역을 필요로 함.
- JPA에서의 영속성은 영속성 컨텍스트에 등록한 객체의 추이를 관찰하다 DB에 어떻게 영구히 저장할지 저장하는 과정임

JPA 에서 영속은 영속성 컨텍스트에 객체가 등록된 상태를 의미함.
- 영속 (managed)
- 영속성 컨텍스트에 등록된 상태
- find(), save() 시
- 비영속 (new/transient)
- 영속성 컨텍스트에 등록되지 않은 상태
- 준영속 (detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없음
- clear(), refresh() 시
- 삭제 (removed)
- 영속성 컨텍스트에서 제외되며 SQL 저장소에 delete문을 저장
- delete() 시
- 반영 (flush)
- 현재 영속성 컨텍스트에 상태를 확인하여 SQL 생성 후 데이터베이스에 query를 전송
- 단, commit 전이기 때문에 데이터베이스에 실제로 저장되지 않은 상태
- flush() 시
4️⃣ 객체 관리는 누가?
- 유치원생인 객체를 관리하는 선생님이 바로 EntityManager 에요.
- 선생님을 배출하는 곳은 EntityManagerFactory 이며 선생님을 배출하는 시점은 transaction이 시작될 때에요.
- 생성된 EntityManager 는 스프링은 내부적으로 발행된 TransactionThread에 바인딩되기 때문에 해당 트랜잭션 동안 동일한 EntityManager 인스턴스를 재사용할 수 있게되요.
@Service
@RequiredArgsConstructor
public class MemberService {
private MemberRepository memberRepository;
public void joinMember(Member member) {
// 트랜잭션 start → EntityManager 생성
memberRepository.save(member);
// 트랜잭션 commit → flush → EntityManager 종료
}
}

5️⃣ 영속성 컨텍스트 특징
✅ 1차캐시 & 동등성 보장
- 영속성 컨텍스트 내부에는 캐시라는 이름의 Map<@Id, Entity> 형태의 자료구조가 존재
- 이 캐시는 동일 트랜잭션 내에서만 유효한 로컬 캐시

// 최초 조회: DB에서 조회 후 1차 캐시에 저장
Set<Member> members = repository.findAll(); // DB 쿼리 발생
// 두 번째 조회: 1차 캐시에서 바로 조회
Member member = repository.findByName("member1"); // DB 쿼리 발생하지 않음
System.out.println(
members.stream().anyMatch(m -> m.equals(member)) // 동등 비교 시 true
);
- 같은 엔티티를 반복 조회할 때 DB 접근 횟수를 줄여줌 ❗❗
- 실무 고려사항: 대량의 데이터를 처리하는 배치 작업에서는 주기적으로 영속성 컨텍스트를 초기화 clear()하여 메모리 사용량 관리 필요가 있어요. 다만 clear() 했다고해서 대량의 데이터가 stack memory에서 없어지는건 아니기 때문에 OutOfMemory를 조심
✅ 쓰기 지연
- JPA는 SQL를 즉시 실행하지 않고 영속성 컨텍스트 내 쓰기 지연 SQL 저장소에 모아둠.
- 이후 transaction.commit() 시점에 모아둔 SQL을 한 번에 DB로 전송 ( 일괄 처리 )
- em.persist(entity) 호출 : 엔티티를 1차 캐시에 저장 + INSERT SQL 생성 후 쓰기지연 SQL 저장소에 보관
- transaction.commit() 호출 : flush() 자동 호출 ➡ 쓰기 지연 SQL 저장소의 쿼리들을 DB에 전송 ➡ 실제 커밋
- commit 전 flush() 를 통해 DB에 반영되는 경우
- repository.flush() 직접 호출 시
- JPQL 쿼리 실행 전 (자동)
- 식별자 생성 전략이 GenerationType.IDENTITY인 경우 save() 호출 시 즉시 INSERT SQL 실행
✅ 변경 감지 ( Dirty Checking )
- 트랜잭션 내 엔티티 값이 변경되면, commit() 시점에 자동으로 UPDATE SQL이 생성함.
- 별도의 update() 메소드 호출이 필요 없는 것이 JPA의 큰 특징
- 트랜잭션 시작 시 엔티티의 최초 상태 스냅샷을 저장
- 플러시 시점에 현재 엔티티와 스냅샷을 비교하여 변경된 엔티티 탐지
- 변경된 엔티티가 있으면 UPDATE SQL 생성 후 DB에 전송

- 변경 감지 최적화 관점:
- JPA는 기본적으로 엔티티의 모든 필드를 업데이트하는 SQL을 생성해요.
- 필드가 많은 엔티티의 경우 변경 필드만 업데이트하도록 설정 가능
6️⃣ 영속성 컨텍스트 범위 OSIV 패턴?
- 영속성 컨텍스트가 @Service, @Repository 레이어 밖에서 작동하지 않나? 라고 생각 ???
- OSIV는 영속성 컨텍스트를 Controller, filter, View등이 끝날 때까지 개방된 상태로 유지하는 방식임.
- @Transactional을 붙이지 않아도 지연로딩을 사용할 수 있는데, OSIV 기능이 기본적으로 활성화가 되어 있기 때문

OSIV 동작 순서
- 클라이언트의 요청이 들어오면 영속성 컨텍스틀 생성
- 트랜잭션의 범위에 진입시 미리 생성해둔 영속성 컨텍스틀 찾아와 트랜잭션을 시작함
- 트랜잭션이 종료되면 커밋을 하고 영속성 컨텍스트를 Flush, 영속성 컨텍스트는 유지
- 영속성 컨텍스트가 유지되므로 Entity는 영속상태로 관리함.
- 요청 종료시, Flush(DB와 동기화)를 호출 하지 않고 종료
장점
- 편리한 지연 로딩 : Service, Repository 외에서도 지연 로딩 사용 가능
- 유연한 설계 : 복잡한 화면 구성에도 필요한 시점에 데이터 로딩 가능
- 개발 생산성 : LazyInitializationException, TransactionRequireException 걱정 없이 개발
단점
- DB 커넥션 점유 : 컨트롤러와 뷰 렌더링이 끝날 때까지 DB 커넥션을 유지
- 실시간 트래픽 : 커넥션 보유 시간이 길어져 서버 자원을 많이 사용
- 성능 이슈 : 과도한 DB 커넥션 사용으로 인해 성능 저하 가능성
Service 외 Entity자체를 반환하는 방법은 보호받아야할 Entity를 노출시키기 때문에 좋은 방법은 아니며 DB 커넥션을 오랫동안 물고 있는 것은 서버 성능에 영향을 미치는 부분이니 OSIV를 비활성화 하여 사용하는 것이 좋음.
// OSIV 비활성화 시 권장 패턴 ✅
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository MemberRepository;
public MemberDto retrive(Long id) {
Member member = MemberRepository.findById().orElseThrow(...);
// 트랜잭션 내에서 DTO 변환
return new MemberDto(
member.getId(),
member.getMember(),
member.getDept().getName()
);
}
}
7️⃣ Transactional ❔
- 영속과 마찬가지로 Transactional도 DB에서 나온 개념임.
- 데이터가 영구적으로 저장되려면, 그 과정이 정확하고 안정적이어야 함. 그것을 보장해주기 위한 기술임.
- 예시 1 ) 고객이 주문 정보를 저장하려는데, 중간에 시스템 오류가 나면 ?
- 에시 2 ) 계좌 이체 중 송금 했는데 입금이 안 되면?
- Transactional 은 모든 작업 성고 시에만 commit, 하나라도 실패 시 rollback하여 일관성을 유지함.
- ⭐ 기술면접 단골 질문 트랜잭션 4대속성 (ACID)
- Atomicity (원자성) - 전부 성공 or 전부 실패
- Consistency (일관성) - 트랜잭션 전/후 데이터 일관성 유지
- Isolation (격리성) - 동시에 실행되는 트랜잭션 간 간섭방지
- Durabilty (지속성) - 트랜잭션이 완료된 내용은 반드시 저장 (영구적)
- ⭐ 기술면접 단골 질문 트랜잭션 4대속성 (ACID)
- 우리가 로그인을 위해 Session을 활용하듯 DB 서버에도 Session이 존재함.
- transcaction 동작순서
- 웹 서버는 DB 서버 연결을 요청 후 연결
- DB 서버는 세션을 만들어 전달
- 웹 서버는 커넥션을 통해 SQL을 전달하면 세션은 SQL을 실행
- 세션은 DB서버 내 트랜잭션을 시작하고 커밋, 롤백으로 트랜잭션을 종료
- 이때 한 세션은 여러 트랜잭션을 가질 수 있고, 트랜잭션은 세션 안에서 수행
- 사용자가 커넥션을 닫거나 DB 관리자가 세션을 종료하면 세션이 종료 후 커넥션 해제
- transcaction 동작순서

- 스프링에서는 트랜잭션과 영속성 컨텍스트의 생명주기가 동일하게 관리되요. 그 이유는 Spring TransactionManager가 EntityManger를 관리하기 때문이에요.
- 스프링은 기본적으로 PlatformTransactionManager를 통해 transaction을 관리해요.
- PlatformTransactionManager은 트랜잭션 시작과 종료, 커밋, 롤백 등을 수행해요.
- 주요 구현체는 아래와 같으며 우린 그중에서 JpaTransactionManager를 사용하고 있는 거에요.
- DataSourceTransactionManager: JDBC 기반 트랜잭션 관리
- JpaTransactionManager: JPA 기반 트랜잭션 관리
- HibernateTransactionManager: Hibernate 기반 트랜잭션 관리
- JpaTransactionManager는 EntityManagerFactory를 관리하며 EntityManager를 사용하여 트랜잭션이 이루어지게함
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory)
JpaTranscationManager transactionManager = new JpaTransactionManager () ;
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
- EntityManagerFactory는 요청당 발생하는 thread를 안전하게 다루기 위해 DB당 하나씩 공유해 사용해요.
- EntityManager는 각각 하나의 영속성 컨텍스트를 관리하죠. 즉 트랜잭션당 하나와 엔터티 매니저와 영속성 컨텍스트 하나가 맵핑되는 것이죠.
- 트랜잭션 처리 과정:
- EntityManagerFactory로부터 EntityManager 생성
- JpaTransactionObject에서 EntityManager 획득
- EntityTransaction을 통한 트랜잭션 처리
- 예외 발생 시 적절한 예외 변환 처리
JpaTransactionManager는 트랜잭션 범위 내에서 영속성 컨텍스트를 관리하며, 이를 통해 엔티티의 상태 변화를 추적하고 트랜잭션 커밋 시 변경 내용을 데이터베이스에 반영
8️⃣ Spring Transaction
✅ 만약 이게 없다면?
- 변경한 자바 객체는 영속성 컨텍스트 메모리에만 존재 (비영속 상태로 끝날 수 있음)하게 됨.
- 여러 쿼리가 실행 중 하나 실패하더라도 실패했을 때 데이터 불일치 발생
✅ 어떻게 지정?
- TransactionManager가 실제로 트랜잭션의 시작, 커밋, 롤백을 처리하는 반면, @Transactional은 개발자가 간편하게 트랜잭션 범위를 정의함.
- @Transactional이 선언된 메소드가 호출될 때 자동으로 트랜잭션 관련 작업 수행하게 AOP가 동작하도록 구현되어 있음.
실무 주의사항
- 트랜잭션은 가능한 한 짧게 유지하는 것이 좋아요. 트랜잭션 범위가 넓으면 데이터베이스 락(Iock)으로 인 해 동시성 문제, 무한 대기와 같은 이슈가 발생할 수 있음.
@Transactional
public void order(Object input) {
// 주요 서비스 로직 처리
order(input);
// 외부 서비스 호출
payment.pay();
// 외부 서비스 호출
point.establish();
// 외부 서비스 호출
delivary.call();
// 외부 서비스 호출 - transaction 제외해야 함
alarm.send();
}
9️⃣ Transactional 특징
- transaction 주요 특징은 전파, 격리, 락 등이 있음.
- 그중 옵션으로 선별하여 구현할 수 있어요.
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
rollbackFor = Exception.class,
noRollbackFor = IllegalArgumentException.class,
readOnly = false,
timeout = 30
)
1. propagation (전파 속성)
- 하나의 트랜잭션이 실행 중 일때, 그 안에서 호출되는 다른 트랜잭션 메소드가 기존 트랜잭션을 어떻게 이어받거나 새롭게 실행할지를 결정하는 설정임. 주로 Spring Framework에서 사용되는 개념
- 기본값은 Propagation.REQUIRED 으로 상위 트랜잭션이 하위 트랜잭션을 포함하여 하나의 트랜잭션으로 간주함.
@Transactional(propagation = Propagation.REQUIRED)
public void method();
2. isolation (격리 수준)
- 동시에 여러 트랜잭션이 실행될 때 트랜잭션 간의 간섭 정도를 조절하는 설정임.
- 기본값은 연결한 DB 기본 설정을 따르며, MySQL은 REPEATABLE_READ 가 기본임.
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void method();
3. rollbackFor / noRollbackFor
- 특정 예외에 대해 롤백 여부 지정할 수 있음
- 업무 흐름상 예외가 발생해도 그대로 처리되어야 하는가? noRollbackFor
- 예외 발생 시 재시도하거나 되돌려야 하는가 ? rollbackFor
@Transactional(
rollbackFor = {CustomException.class, IOException.class}
noRollbackFor = NotEnoughBalanceException.class
)
public void method();
4. readOnly
- 읽기 전용 트랜잭션 여부 지정하는 설정으로 혹시 모를 쓰기 발생을 막아주기도 하지만 더티 체킹을 위한 스냅샷을 유지하지 않거나 DB에 쓰기를 위한 자원을 개방하지 않아 필요한 리소스를 절약할 수 있음.
@Transactional(readOnly = true)
public void method();
5. timeout
- 트랜잭션 수행 시간 제한하기 위한 설정이며, 초과시 TransactionTimedOutException
@Transactional(timeout = 10)
public void method();
⭐ 실무 자주 사용하는 경우
- 읽기 전용 서비스
@Transactional(readOnly = true)
public void method();
- 예외에 따라 롤백 전략 변경
@Transactional(
rollbackFor = {CustomException.class, IOException.class}
noRollbackFor = NotEnoughBalanceException.class
)
public void method();
- 독립 실행되는 보조 로직 (예 : 로그 저장, 메일 발송)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method();
✅ 트랜잭션 전파(Propagation)
| 전파 타입 | 설명 | 사용 예시 |
| REQUIRED (기본값) ⭐ | 현재 트랜잭션이 있으면 참여, 없으면 새로 생성 | 대부분의 비즈니스 로직에 적합 |
| REQUIRES_NEW ⭐ | 기존 트랜잭션이 있다면 일시 중단후 새 트랜잭션 실행 외부 트랜잭션의 롤백에 영향 ❌ |
독립적인 로깅, 알림, 보상 트랜잭션 등 |
| NESTED | 현재 트랜잭션 내 중첩 트랜잭션 시작 | 실패 시 일부 롤백하고 나머지는 유지하고 싶을 때 |
| SUPPORTS | 트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행 | 필수는 아니지만 트랜잭션이 있으면 같이 쓰는 경우 |
| NOT_SUPPORTED | 트랜잭션 없이 실행, 기존 트랜잭션은 일시 중단 | 성능이 중요한 조회작업 등 |
| NEVER | 트랜잭션이 있으면 예외 발생 | 트랜잭션을 쓰면 안 되는 경우 (안정성 이슈방지) |
| MANDATORY | 트랜잭션이 없으면 예외 발생 | 반드시 기존 트랜잭션 안에서만 실행되어야 할때 |
✅ 트랜잭션 격리 수준(Isolation)
- 동시에 여러 트랜잭션이 실행될 때 트랜잭션 간의 간섭 정도를 조절하는 설정 (DMBS마다 다름!!)
- 트랜잭션이 간에 변경된 데이터를 조회 할 수 있는 level을 정리한 것
- 격리 수준에 따라 데이터의 일관성과 동시에 처리되는 성능이 영향을 받으며, 격리 수준이 높아질수록 동시성은 낮아지고, 데이터의 일관성은 높아짐
반응형
'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글
| ⚓Spring Security ( 스탠다드 ) (0) | 2025.05.06 |
|---|---|
| 📅 연관관계와 N+1 문제 💥 (0) | 2025.05.05 |
| TIL 50 [ JPA 심화 - ( 테이블 객체 다루는법, JpaRepository 쿼리,페이징처리, JPQL ,QueryDSL, Auditing, 필요한부분 갱신 ) , 트랜잭션 복습 ] (0) | 2025.05.02 |
| 🚧 트랜잭션 (스탠다드) (0) | 2025.05.02 |
| 🔒 JWT 인증/인가 (0) | 2025.05.01 |