🔍 영속성과 Transactional

2025. 5. 4. 23:34·[내일배움캠프-Sparta]/Spring 6기
반응형

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 종료
    }
}

@Transactional 이 달려있는 save메소드

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로 전송 ( 일괄 처리 )
    1. em.persist(entity) 호출 : 엔티티를 1차 캐시에 저장 + INSERT SQL 생성 후 쓰기지연 SQL 저장소에 보관
    2. transaction.commit() 호출 : flush() 자동 호출 ➡ 쓰기 지연 SQL 저장소의 쿼리들을 DB에 전송 ➡ 실제 커밋
  • commit 전 flush() 를 통해 DB에 반영되는 경우
    • repository.flush() 직접 호출 시
    • JPQL 쿼리 실행 전 (자동)
    • 식별자 생성 전략이 GenerationType.IDENTITY인 경우 save() 호출 시 즉시 INSERT SQL 실행

✅ 변경 감지 ( Dirty Checking )

  • 트랜잭션 내 엔티티 값이 변경되면, commit() 시점에 자동으로 UPDATE SQL이 생성함.
  • 별도의 update() 메소드 호출이 필요 없는 것이 JPA의 큰 특징
    1. 트랜잭션 시작 시 엔티티의 최초 상태 스냅샷을 저장
    2. 플러시 시점에 현재 엔티티와 스냅샷을 비교하여 변경된 엔티티 탐지
    3. 변경된 엔티티가 있으면 UPDATE SQL 생성 후 DB에 전송

  • 변경 감지 최적화 관점:
    • JPA는 기본적으로 엔티티의 모든 필드를 업데이트하는 SQL을 생성해요.
    • 필드가 많은 엔티티의 경우 변경 필드만 업데이트하도록 설정 가능

6️⃣ 영속성 컨텍스트 범위 OSIV 패턴?

  • 영속성 컨텍스트가 @Service, @Repository 레이어 밖에서 작동하지 않나? 라고 생각 ???
  • OSIV는 영속성 컨텍스트를 Controller, filter, View등이 끝날 때까지 개방된 상태로 유지하는 방식임.
  • @Transactional을 붙이지 않아도 지연로딩을 사용할 수 있는데, OSIV 기능이 기본적으로 활성화가 되어 있기 때문

OSIV 동작 순서

  1. 클라이언트의 요청이 들어오면 영속성 컨텍스틀 생성
  2. 트랜잭션의 범위에 진입시 미리 생성해둔 영속성 컨텍스틀 찾아와 트랜잭션을 시작함
  3. 트랜잭션이 종료되면 커밋을 하고 영속성 컨텍스트를 Flush, 영속성 컨텍스트는 유지
  4. 영속성 컨텍스트가 유지되므로 Entity는 영속상태로 관리함.
  5. 요청 종료시, 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)
      1. Atomicity (원자성) - 전부 성공 or 전부 실패
      2. Consistency (일관성) - 트랜잭션 전/후 데이터 일관성 유지
      3. Isolation (격리성) - 동시에 실행되는 트랜잭션 간 간섭방지
      4. Durabilty (지속성) - 트랜잭션이 완료된 내용은 반드시 저장 (영구적)
  • 우리가 로그인을 위해 Session을 활용하듯 DB 서버에도 Session이 존재함.
    • transcaction 동작순서
      • 웹 서버는 DB 서버 연결을 요청 후 연결
      • DB 서버는 세션을 만들어 전달
      • 웹 서버는 커넥션을 통해 SQL을 전달하면 세션은 SQL을 실행
      • 세션은 DB서버 내 트랜잭션을 시작하고 커밋, 롤백으로 트랜잭션을 종료
        • 이때 한 세션은 여러 트랜잭션을 가질 수 있고, 트랜잭션은 세션 안에서 수행
      • 사용자가 커넥션을 닫거나 DB 관리자가 세션을 종료하면 세션이 종료 후 커넥션 해제

 

  • 스프링에서는 트랜잭션과 영속성 컨텍스트의 생명주기가 동일하게 관리되요. 그 이유는 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는 각각 하나의 영속성 컨텍스트를 관리하죠. 즉 트랜잭션당 하나와 엔터티 매니저와 영속성 컨텍스트 하나가 맵핑되는 것이죠.
  • 트랜잭션 처리 과정:
    1. EntityManagerFactory로부터 EntityManager 생성
    2. JpaTransactionObject에서 EntityManager 획득
    3. EntityTransaction을 통한 트랜잭션 처리
    4. 예외 발생 시 적절한 예외 변환 처리

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
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • ⚓Spring Security ( 스탠다드 )
  • 📅 연관관계와 N+1 문제 💥
  • TIL 50 [ JPA 심화 - ( 테이블 객체 다루는법, JpaRepository 쿼리,페이징처리, JPQL ,QueryDSL, Auditing, 필요한부분 갱신 ) , 트랜잭션 복습 ]
  • 🚧 트랜잭션 (스탠다드)
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Python
    알고리즘
    웹
    spring
    SQLD
    배포
    내일배움캠프
    네트워크
    web
    docker
    SQL
    개발자
    db
    세션
    메모리
    코딩테스트
    It
    AWS
    KPT
    트랜잭션
    백엔드
    OS
    CPU
    Testcode
    Til
    network
    운영체제
    컴퓨터구조
    Java
    cs
  • hELLO· Designed By정상우.v4.10.3
dimenshun
🔍 영속성과 Transactional
상단으로

티스토리툴바