7주차 Unit 7.2 — @Transactional 동작 원리

Psj·2026년 6월 5일

F-lab

목록 보기
235/240

Unit 7.2 — @Transactional 동작 원리

F-LAB JAVA · 7주차 · Phase 7 · @Transactional
★★★ 깊이 파기 — 7주차의 정점, 면접 정점, 5+6+7주차 응축의 결정체


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • @Transactional 동작 원리 전체 개요는?
  • Spring Boot 시작 흐름 은?
  • Bean Post Processor 의 역할은?
  • @Transactional 어노테이션 스캐닝 은?
  • InfrastructureAdvisorAutoProxyCreator 의 동작은?
  • 프록시 생성 과정 (CGLIB 의 자식 클래스 동적 생성) 은?
  • TransactionInterceptor 의 동작 은?
  • 메서드 호출 시 전체 흐름 (7 단계) 은?
  • PlatformTransactionManager 와 연동 은?

🎯 핵심 한 문장

@Transactional 의 동작 원리는 Spring Boot 시작 시 Bean Post Processor (InfrastructureAdvisorAutoProxyCreator) 가 빈을 스캔하면서 @Transactional 어노테이션을 감지하고 CGLIB 으로 자식 클래스를 동적 생성해 프록시 빈으로 등록하며, 호출 시점에는 (1) 클라이언트가 프록시 호출 (2) 프록시가 TransactionInterceptor 호출 (3) Interceptor 가 PlatformTransactionManager.getTransaction() 으로 EntityManager 생성 + tx.begin (4) 진짜 메서드 호출 (5) 정상이면 commit (em.flush 포함) / 예외면 rollback 의 7 단계 흐름이 모든 @Transactional 메서드에서 일어나는데, 이게 7주차 정점이자 5+6+7주차 (디자인 패턴 + DataSource + JPA) 응축의 결정체다.
@Transactional 동작 원리 는 Spring 백엔드의 핵심이자 면접의 정점 이다.
시작 단계 — Spring Boot 시작 시 @EnableTransactionManagement (Spring Boot 자동) 가 TransactionInterceptor + InfrastructureAdvisorAutoProxyCreator (Bean Post Processor) 를 등록 → 모든 빈 스캔하며 @Transactional 어노테이션 감지CGLIB 으로 자식 클래스 동적 생성 (ShipmentService$$EnhancerByCGLIB) → 프록시를 빈으로 등록 (진짜 X).
호출 단계 7 단계 — (1) 클라이언트 service.process(1L) 호출 → (2) CGLIB 프록시 의 오버라이드된 메서드 실행 → (3) TransactionInterceptor@Transactional 옵션 (propagation/isolation/timeout/readOnly/rollbackFor) 읽음 → (4) PlatformTransactionManager (JpaTransactionManager) .getTransaction() 호출 → EntityManager 생성 + tx.begin() + ThreadLocal 바인딩 → (5) 진짜 ShipmentService.process() 호출 (MethodProxy.invokeSuper 또는 joinPoint.proceed()) → 비즈니스 로직 실행 → (6) 정상 반환 시 TransactionInterceptor 가 commit() 호출 → em.flush() (Dirty Checking → UPDATE SQL) + tx.commit() + em.close() + ThreadLocal 정리 → (7) 예외 발생 시 rollback() 호출 → tx.rollback() + em.close() + 예외 throw.
이 모든 것이 — 5주차 (프록시 + AOP + DI + 템플릿+전략) + 6주차 (DataSource + ACID) + 7주차 (JPA + PlatformTM) 의 완벽한 응축으로, ILIC 의 1020 메서드가 모두 이 7 단계 흐름으로 동작하며 자바 백엔드의 정수다.

비유 — 자율주행 자동차의 내부 동작

@Transactional = 자율주행 자동차:

운전자 (개발자):
  - "@Transactional" 어노테이션 1줄 = "목적지 입력"
  - 비즈니스 로직 = "기본 운전 의도"
  - 나머지 모든 동작 자동

시작 (Spring Boot 시작):
  1. 자동차 시동 (Application 시작)
  2. 시스템 점검 (Bean Post Processor):
     - 모든 빈 스캔
     - @Transactional 검사
     - 검사 통과 빈 = 자율주행 가능
  3. 자율주행 모듈 설치 (CGLIB 프록시):
     - 자식 클래스 동적 생성
     - 운전대 가로채기 모듈
  4. 운행 준비

운행 (메서드 호출):
  1. 운전자 출발 명령 (service.process)
       ↓
  2. 자율주행 모듈 작동 (CGLIB 프록시):
       ↓
  3. 안전 점검 (TransactionInterceptor):
     - 어노테이션 옵션 확인
     - "어떤 모드?" (propagation, isolation)
       ↓
  4. 시동 + 출발 (PlatformTM.getTransaction):
     - EntityManager 생성
     - tx.begin()
       ↓
  5. 실제 주행 (진짜 메서드):
     - 비즈니스 로직
     - Dirty Checking 표시
       ↓
  6 또는 7. 목적지 도착 / 사고:
     6. 정상 → commit:
        - em.flush() (UPDATE SQL)
        - tx.commit()
        - em.close()
     7. 예외 → rollback:
        - tx.rollback()
        - em.close()
        - 예외 전파

5+6+7주차 응축:
  - 5주차: 자율주행 알고리즘 (디자인 패턴)
  - 6주차: 도로 인프라 (DataSource, ACID)
  - 7주차: GPS + 네비 (JPA + PlatformTM)
  - 모두 자동

ILIC:
  - 1020 메서드 = 1020 자율주행
  - 박승제 = 목적지만 입력
  - 모든 운전 자동

→ Spring 의 마법, 7 단계 흐름, 5+6+7주차 응축, 자바 백엔드 정점.


🧭 9개 섹션 로드맵

1. @Transactional 동작 원리 개요
2. Spring Boot 시작 흐름
3. Bean Post Processor 의 역할
4. 어노테이션 감지 + AutoProxyCreator
5. 프록시 생성 (CGLIB)
6. TransactionInterceptor 의 동작
7. 메서드 호출 전체 흐름 (7 단계)
8. 5+6+7주차 응축 종합
9. Phase 7.3 예고 (5가지 함정)

1️⃣ @Transactional 동작 원리 개요

1.1 두 단계 흐름

두 단계 흐름:

  ① 시작 단계 (Spring Boot 시작 시):
     - 빈 스캔
     - @Transactional 감지
     - 프록시 생성 (CGLIB)
     - 프록시를 빈으로 등록

  ② 호출 단계 (런타임):
     - 메서드 호출 시 7 단계
     - 프록시 → Interceptor → TM → 진짜

1.2 핵심 컴포넌트 5개

핵심 컴포넌트 5개:

  1. Bean Post Processor:
     - 빈 생성 후 후처리
     - @Transactional 감지

  2. InfrastructureAdvisorAutoProxyCreator:
     - 특수 Bean Post Processor
     - 프록시 자동 생성

  3. TransactionInterceptor:
     - 메서드 호출 가로채기
     - 트랜잭션 관리

  4. PlatformTransactionManager:
     - 실제 트랜잭션 수행
     - JpaTM / DataSourceTM 등

  5. EntityManager / Connection:
     - 실제 DB 작업
     - JPA / JDBC

1.3 시각화

시각화:

  [Spring Boot 시작]
   ↓
  Bean Post Processor 등록
   ↓
  빈 스캔 + @Transactional 감지
   ↓
  CGLIB 프록시 생성
   ↓
  프록시를 빈으로 등록

  [런타임 - 메서드 호출]
  Client
   ↓
  CGLIB Proxy (Bean)
   ↓
  TransactionInterceptor
   ↓
  PlatformTransactionManager (JpaTM)
   ↓
  EntityManager
   ↓
  Real Service (비즈니스 로직)
   ↓ (정상)
  PlatformTransactionManager.commit
   ↓
  em.flush() + tx.commit() + em.close()

1.4 ILIC 의 맥락

ILIC 의 @Transactional 동작

ILIC 의 1020 메서드:
  - 모두 @Transactional 어노테이션
  - 모두 위 두 단계 흐름

  시작 시:
    - Spring Boot 시작
    - 102 ShipmentService (등 모든 @Service) 빈 등록
    - 각 빈 검사
    - @Transactional 감지
    - 프록시 1020개 생성

  런타임:
    - 매 메서드 호출 = 위 7 단계
    - 박승제 의 코드 = 비즈니스만
    - Spring 이 모두 자동

1.5 자기 점검 답변

@Transactional 동작 원리 전체 개요는?

:
1. 두 단계:

  • 시작 / 호출
  1. 5 컴포넌트:

    • PostProcessor / AutoProxyCreator / Interceptor / TM / EM
  2. 시작:

    • 프록시 생성
  3. 호출:

    • 7 단계 흐름

2️⃣ Spring Boot 시작 흐름

2.1 Spring Boot 시작 시점

Spring Boot 시작 시점:

  main():
    SpringApplication.run(App.class, args);
         ↓
  ApplicationContext 생성
         ↓
  Auto Configuration 실행 (@EnableAutoConfiguration)
         ↓
  TransactionAutoConfiguration 활성화
         ↓
  @EnableTransactionManagement 자동
         ↓
  TransactionInterceptor + AutoProxyCreator 등록
         ↓
  빈 스캔 + 프록시 생성
         ↓
  Application 시작 완료

2.2 @EnableTransactionManagement

@EnableTransactionManagement:

  Spring Boot 가 자동 활성화 (Auto Configuration):
    - 명시 불필요
    - spring-boot-starter-data-jpa 의존성으로 자동

  내부 동작:
    1. TransactionInterceptor 빈 등록
    2. InfrastructureAdvisorAutoProxyCreator 빈 등록
    3. 트랜잭션 어노테이션 처리 준비

  옵션:
    - proxyTargetClass (true = CGLIB)
    - mode (PROXY / ASPECTJ)

2.3 자동 구성 코드 (Spring Boot 내부)

// TransactionAutoConfiguration (Spring Boot 내부)
@AutoConfiguration
@ConditionalOnClass(PlatformTransactionManager.class)
public class TransactionAutoConfiguration {
    
    @Configuration
    @ConditionalOnBean(TransactionManager.class)
    @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
    public static class EnableTransactionManagementConfiguration {
        
        @Configuration
        @EnableTransactionManagement(proxyTargetClass = true)
        public static class CglibAutoProxyConfiguration { }
    }
}

// → Spring Boot 가 자동
// → 우리는 @EnableTransactionManagement 명시 불필요
class PlatformTransactionManager {}
class TransactionManager {}
class AbstractTransactionManagementConfiguration {}
@interface AutoConfiguration {}
@interface ConditionalOnClass { Class<?> value(); }
@interface Configuration {}
@interface ConditionalOnBean { Class<?> value(); }
@interface ConditionalOnMissingBean { Class<?> value(); }
@interface EnableTransactionManagement { boolean proxyTargetClass(); }

2.4 빈 생성 흐름

빈 생성 흐름:

  ① @ComponentScan (Spring Boot 자동):
     - @Service / @Repository / @Component 스캔
     - BeanDefinition 등록

  ② AbstractApplicationContext.refresh():
     - 빈 생성 시작

  ③ 각 빈 생성:
     - new Instance()
     - 의존성 주입 (DI)

  ④ Bean Post Processor 호출:
     - postProcessBeforeInitialization()
     - postProcessAfterInitialization()
     - ← 여기서 프록시 생성!

  ⑤ 빈 등록 (DI 컨테이너):
     - 진짜 빈 또는 프록시

2.5 컴포넌트 등록 순서

컴포넌트 등록 순서:

  1. Infrastructure 빈 (먼저):
     - PlatformTransactionManager
     - InfrastructureAdvisorAutoProxyCreator
     - TransactionInterceptor

  2. 일반 빈 (@Service 등):
     - 위 인프라가 준비된 후
     - 프록시 생성 가능

→ 순서 중요!

2.6 ILIC 의 시작

ILIC 의 Spring Boot 시작

1. main() 호출
2. ApplicationContext 생성
3. Auto Configuration:
   - DataSource (HikariCP)
   - EntityManagerFactory
   - JpaTransactionManager (PlatformTM)
   - TransactionInterceptor
   - InfrastructureAdvisorAutoProxyCreator
4. 빈 스캔:
   - @Service 102 개 (ShipmentService, CustomerService, ...)
   - @Repository
   - @Controller
5. 각 @Service 빈 생성 중:
   - Bean Post Processor 호출
   - @Transactional 감지
   - CGLIB 프록시 생성
6. 1020 프록시 등록
7. ApplicationContext 시작 완료

→ 모두 자동
→ application.yml 만으로

2.7 자기 점검 답변

Spring Boot 시작 흐름은?

:
1. main():

  • SpringApplication.run
  1. Auto Configuration:

    • TransactionAutoConfiguration
  2. 빈 스캔:

    • @Service / @Component
  3. Bean Post Processor:

    • 프록시 생성

3️⃣ Bean Post Processor 의 역할

3.1 Bean Post Processor

Bean Post Processor:

  Spring 의 핵심 확장 메커니즘:
    - 빈 생성 후 (또는 초기화 전후) 후처리
    - "빈을 가로채서 변경 가능"
    - 프록시 생성의 기반

  인터페이스:
    BeanPostProcessor:
      - postProcessBeforeInitialization (초기화 전)
      - postProcessAfterInitialization (초기화 후, 여기서 프록시!)

3.2 인터페이스

package org.springframework.beans.factory.config;

public interface BeanPostProcessor {
    
    // 초기화 전 (@PostConstruct 전)
    default Object postProcessBeforeInitialization(
            Object bean, String beanName) {
        return bean;
    }
    
    // 초기화 후 (@PostConstruct 후)
    default Object postProcessAfterInitialization(
            Object bean, String beanName) {
        return bean;   // ← 여기서 프록시로 감쌀 수 있음!
    }
}

3.3 동작 흐름

동작 흐름:

  ① 빈 객체 생성:
     ShipmentService bean = new ShipmentService();

  ② BeanPostProcessor.postProcessBeforeInitialization():
     return bean;   // 변경 X

  ③ 초기화 (@PostConstruct):
     bean.init();

  ④ BeanPostProcessor.postProcessAfterInitialization():
     // ★ 여기서 프록시 생성!
     if (bean 에 @Transactional 메서드 있음) {
         return createProxy(bean);   // 프록시로 교체
     }
     return bean;

  ⑤ DI 컨테이너에 등록 (프록시 또는 진짜)

3.4 핵심 BPP — InfrastructureAdvisorAutoProxyCreator

핵심 Bean Post Processor:

  InfrastructureAdvisorAutoProxyCreator:
    - extends AbstractAutoProxyCreator
    - extends ProxyProcessorSupport
    - implements BeanPostProcessor

  역할:
    - 빈 후처리
    - @Transactional 감지
    - 프록시 생성

→ @Transactional 의 핵심 엔진

3.5 BPP 등록

// @EnableTransactionManagement 가 내부적으로 등록
// (사용자 코드 X)

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class TransactionManagementConfiguration {
    
    @Bean
    public InfrastructureAdvisorAutoProxyCreator autoProxyCreator() {
        return new InfrastructureAdvisorAutoProxyCreator();
    }
    
    @Bean
    public TransactionInterceptor transactionInterceptor(
            PlatformTransactionManager tm) {
        return new TransactionInterceptor(tm, ...);
    }
    
    @Bean
    public BeanFactoryTransactionAttributeSourceAdvisor advisor(
            TransactionInterceptor interceptor) {
        return new BeanFactoryTransactionAttributeSourceAdvisor(
            interceptor, ...);
    }
}

// → 3 핵심 빈:
//   1. AutoProxyCreator (BPP)
//   2. Interceptor (가로채기)
//   3. Advisor (Pointcut + Advice 묶음)
class InfrastructureAdvisorAutoProxyCreator {}
class TransactionInterceptor { TransactionInterceptor(PlatformTransactionManager tm, Object... rest) {} }
class PlatformTransactionManager {}
class BeanFactoryTransactionAttributeSourceAdvisor {
    BeanFactoryTransactionAttributeSourceAdvisor(TransactionInterceptor i, Object... rest) {}
}
class BeanDefinition { static int ROLE_INFRASTRUCTURE = 2; }
@interface Configuration {}
@interface Role { int value(); }
@interface Bean {}

3.6 BPP 의 일반 활용

BPP 의 일반 활용:

  Spring 의 다양한 어노테이션:
    - @Transactional (TransactionInterceptor)
    - @Async (AsyncAnnotationBeanPostProcessor)
    - @Cacheable (CacheInterceptor)
    - @PreAuthorize (MethodSecurityInterceptor)

  모두 BPP 기반:
    - 어노테이션 감지
    - 프록시 생성
    - 가로채기

→ Spring 의 확장 메커니즘

3.7 ILIC 의 맥락

ILIC 의 BPP 동작

ILIC 의 Spring Boot 시작 시:

  1. InfrastructureAdvisorAutoProxyCreator (BPP) 등록

  2. ShipmentService 빈 생성:
     ShipmentService bean = new ShipmentService();

  3. BPP.postProcessAfterInitialization(bean):
     - bean 에 @Transactional 있나?
     - YES (process, get, findAll 등)
     - createProxy(bean) → CGLIB 프록시

  4. 프록시 등록:
     - 빈 이름 "shipmentService"
     - 진짜가 아니라 프록시!

  5. @Autowired 시:
     - ShipmentService service = ...  // 프록시 주입
     - service.process() = 프록시.process() = 트랜잭션 자동

  → 102 @Service × 평균 10 메서드 = 1020 프록시

3.8 자기 점검 답변

Bean Post Processor 의 역할은?

:
1. BPP:

  • 빈 생성 후 후처리
  1. 두 메서드:

    • Before / After Initialization
  2. AutoProxyCreator:

    • 프록시 생성
  3. Spring 확장:

    • 어노테이션 처리

4️⃣ 어노테이션 감지 + AutoProxyCreator

4.1 InfrastructureAdvisorAutoProxyCreator

InfrastructureAdvisorAutoProxyCreator:

  AbstractAutoProxyCreator 의 자손:
    - BPP 구현
    - Advisor 들을 자동 적용
    - "Infrastructure" Advisor 만

  역할:
    1. 모든 빈 검사
    2. Advisor 매칭 (Pointcut)
    3. 매칭되면 프록시 생성

4.2 Advisor 의 의미

Advisor:

  AOP 의 핵심:
    - Advisor = Pointcut + Advice
    - Pointcut: 어디에 적용?
    - Advice: 무엇을 할까?

  TransactionAdvisor:
    - Pointcut: @Transactional 있는 메서드
    - Advice: TransactionInterceptor

→ "@Transactional 메서드를 TransactionInterceptor 가 가로채라"

4.3 매칭 알고리즘

매칭 알고리즘:

  AutoProxyCreator 의 postProcessAfterInitialization():
  
    for (each bean) {
        for (each Advisor) {
            if (Advisor.matches(bean)) {
                // 프록시 생성
                proxy = createProxy(bean, advisors);
                return proxy;
            }
        }
        return bean;   // 매칭 X = 진짜 빈
    }

  → @Transactional 있는 메서드 = 매칭 = 프록시
  → 없는 = 진짜 빈

4.4 Pointcut 의 동작

// TransactionAttributeSourcePointcut (간략)
public class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut {
    
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        
        // @Transactional 어노테이션이 있는가?
        TransactionAttribute attr = tas.getTransactionAttribute(method, targetClass);
        return attr != null;
    }
}

// 동작:
// - 모든 메서드 검사
// - @Transactional 있으면 true
// - true 면 → 프록시 적용
class Method {}
class StaticMethodMatcherPointcut { boolean matches(Method m, Class<?> c) { return false; } }
class TransactionAttributeSource { TransactionAttribute getTransactionAttribute(Method m, Class<?> c) { return null; } }
class TransactionAttribute {}
TransactionAttributeSource getTransactionAttributeSource() { return null; }

4.5 어노테이션 스캐닝

어노테이션 스캐닝:

  Spring 의 검사 위치:
    1. 메서드 위 @Transactional
    2. 클래스 위 @Transactional (모든 public 메서드)
    3. 부모 인터페이스의 @Transactional (proxyTargetClass=false 시)

  예:
    @Transactional   ← 클래스
    public class ShipmentService {
        public void method1() { }   // ← 적용
        public void method2() { }   // ← 적용
        
        @Transactional(readOnly = true)   ← 메서드 (오버라이드)
        public void method3() { }
    }

4.6 어떤 메서드 프록시?

어떤 메서드 프록시?:

  ✅ public 메서드:
    - 프록시 적용

  ❌ private 메서드:
    - CGLIB / JDK 동적 모두 X
    - 함정 1 (Unit 7.3)

  ❌ protected 메서드:
    - CGLIB 만 가능 (이론)
    - 보통 안 함

  ❌ package-private:
    - CGLIB 만 가능 (이론)
    - 보통 안 함

  ❌ final 메서드:
    - 상속 X → 오버라이드 X
    - 무시

4.7 ILIC 의 맥락

// ILIC 의 어노테이션 감지

@Service
public class ShipmentService {
    
    @Transactional   // ← 감지!
    public void process(Long id) { }   // ✅ 프록시 적용
    
    @Transactional(readOnly = true)   // ← 감지!
    public Shipment get(Long id) { }   // ✅ 프록시 적용
    
    public void publicMethod() { }   // ❌ 감지 X (어노테이션 X)
    
    @Transactional
    private void privateMethod() { }   // ❌ private (함정 1)
    
    @Transactional
    public final void finalMethod() { }   // ❌ final
}

// Spring 의 동작:
// 1. ShipmentService 빈 생성
// 2. BPP 가 검사:
//    - process: @Transactional + public → 적용
//    - get: @Transactional + public → 적용
//    - publicMethod: 어노테이션 X → 적용 X
//    - privateMethod: private → 적용 X (함정!)
//    - finalMethod: final → 적용 X
// 3. 위 결과로 프록시 생성:
//    - 적용 메서드 = 트랜잭션 자동
//    - 적용 X = 트랜잭션 X
class Shipment {}
@interface Service {}
@interface Transactional { boolean readOnly() default false; }

4.8 자기 점검 답변

어노테이션 감지 + InfrastructureAdvisorAutoProxyCreator 의 동작은?

:
1. AutoProxyCreator:

  • BPP
  1. Advisor:

    • Pointcut + Advice
  2. 매칭:

    • @Transactional 검사
  3. public 메서드:

    • 적용

5️⃣ 프록시 생성 (CGLIB)

5.1 프록시 생성 시점

프록시 생성 시점:

  Bean Post Processor 의 후처리 중:
    BPP.postProcessAfterInitialization() {
        if (@Transactional 메서드 있음) {
            // ★ 여기서 프록시 생성
            return createProxy(bean);
        }
        return bean;
    }

5.2 ProxyFactory 사용

// 프록시 생성 코드 (개념)
private Object createProxy(Object bean, List<Advisor> advisors) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(bean);             // 진짜 객체
    proxyFactory.addAdvisors(advisors);       // Advisor (Pointcut + Interceptor)
    proxyFactory.setProxyTargetClass(true);   // CGLIB
    
    return proxyFactory.getProxy();            // 프록시 객체
}
class ProxyFactory {
    void setTarget(Object o) {}
    void addAdvisors(java.util.List<Advisor> a) {}
    void setProxyTargetClass(boolean b) {}
    Object getProxy() { return null; }
}
class Advisor {}

5.3 CGLIB 의 자식 클래스 생성

CGLIB 의 자식 클래스 생성:

  원본:
    class ShipmentService {
        @Transactional
        public void process(Long id) {
            // 비즈니스
        }
    }

  CGLIB 가 동적 생성:
    class ShipmentService$$EnhancerByCGLIB$xyz extends ShipmentService {
        
        private MethodInterceptor[] interceptors;
        
        @Override
        public void process(Long id) {
            // 가로채기 → TransactionInterceptor 호출
            MethodInvocation invocation = new ReflectiveMethodInvocation(
                this,                          // 프록시
                target,                        // 진짜 객체
                method,                        // process Method
                new Object[]{id},              // 인자
                targetClass,                   // ShipmentService.class
                interceptors                   // [TransactionInterceptor]
            );
            invocation.proceed();              // 가로채기 시작
        }
        
        // 진짜 메서드도 호출 가능 (super)
        // MethodProxy.invokeSuper(this, args)
    }

→ 바이트코드 동적 생성
→ 클래스명: ShipmentService$$EnhancerByCGLIB$xyz

5.4 프록시 객체의 특징

프록시 객체의 특징:

  타입:
    - ShipmentService 의 자식
    - instanceof ShipmentService = true

  메모리:
    - 진짜 객체 + 프록시 객체 = 2개
    - 진짜는 프록시 내부에 참조

  메서드:
    - 모든 public 메서드 오버라이드
    - 각 메서드 = 가로채기

  identity:
    - proxy != target
    - proxy.equals(target) = false (보통)

5.5 빈 등록

빈 등록:

  BPP.postProcessAfterInitialization() 반환값:
    - 진짜 빈 또는 프록시
    - 반환된 객체가 빈으로 등록

  결과:
    - "shipmentService" 빈 = 프록시
    - 진짜는 프록시 내부

  @Autowired:
    - 프록시 주입
    - 사용자는 모름 (구분 X)

5.6 디버깅 시 확인

// 디버깅 시 프록시 확인
@Autowired ShipmentService service;

System.out.println(service.getClass().getName());
// 출력 예:
// com.ilic.ShipmentService$$EnhancerByCGLIB$$abc123

// 또는 Spring Boot Actuator
// /actuator/beans
class ShipmentService {}
ShipmentService service;

5.7 ILIC 의 맥락

// ILIC 의 프록시 생성

@Service
public class ShipmentService {
    @Autowired ShipmentRepository repo;
    
    @Transactional
    public void process(Long id) {
        Shipment s = repo.findById(id).orElseThrow();
        s.markAsShipped();
    }
}

// Spring 시작 시:
// 1. ShipmentService bean = new ShipmentService();
// 2. repo 의존성 주입
// 3. BPP 가 검사 → @Transactional 발견
// 4. CGLIB 프록시 생성:
//    - ShipmentService$$EnhancerByCGLIB$$xyz extends ShipmentService
//    - process(Long id) 오버라이드
//      → TransactionInterceptor 호출
// 5. 빈 "shipmentService" = 프록시
// 6. 진짜 ShipmentService 는 프록시 내부에 참조

// 사용 시:
// @Autowired ShipmentService service;  ← 프록시
// service.process(1L);                  ← 가로채기 시작
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Service {}
@interface Autowired {}
@interface Transactional {}

5.8 자기 점검 답변

프록시 생성 (CGLIB 의 자식 클래스 동적 생성) 은?

:
1. 시점:

  • BPP 의 후처리
  1. ProxyFactory:

    • setTarget + Advisor
  2. 자식 클래스:

    • 바이트코드 생성
  3. 클래스명:

    • $$EnhancerByCGLIB

6️⃣ TransactionInterceptor 의 동작

6.1 TransactionInterceptor

TransactionInterceptor:

  Spring 트랜잭션의 핵심 클래스:
    - MethodInterceptor 구현
    - invoke() 메서드가 핵심
    - PlatformTransactionManager 와 연동

  역할:
    1. 메서드 호출 가로채기
    2. @Transactional 옵션 읽기
    3. PlatformTM 호출
    4. 진짜 메서드 호출
    5. 결과 처리 (commit / rollback)

6.2 invoke 메서드 (핵심)

// TransactionInterceptor.invoke (개념)
public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = invocation.getThis().getClass();
    Method method = invocation.getMethod();
    
    // 1. @Transactional 옵션 읽기
    TransactionAttribute txAttr = 
        getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    
    // 2. PlatformTransactionManager 결정
    PlatformTransactionManager tm = determineTransactionManager(txAttr);
    
    // 3. 트랜잭션 시작
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, ...);
    
    Object retVal;
    try {
        // 4. 진짜 메서드 호출 (proceed)
        retVal = invocation.proceed();
    } catch (Throwable ex) {
        // 5. 예외 → rollback
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // 정리
        cleanupTransactionInfo(txInfo);
    }
    
    // 6. 정상 → commit
    commitTransactionAfterReturning(txInfo);
    return retVal;
}
class MethodInvocation {
    Object getThis() { return null; }
    Method getMethod() { return null; }
    Object proceed() throws Throwable { return null; }
}
class Method {}
class TransactionAttribute {}
class TransactionAttributeSource { TransactionAttribute getTransactionAttribute(Method m, Class<?> c) { return null; } }
TransactionAttributeSource getTransactionAttributeSource() { return null; }
class PlatformTransactionManager {}
PlatformTransactionManager determineTransactionManager(TransactionAttribute t) { return null; }
class TransactionInfo {}
TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute t, Object... rest) { return null; }
void completeTransactionAfterThrowing(TransactionInfo i, Throwable t) {}
void cleanupTransactionInfo(TransactionInfo i) {}
void commitTransactionAfterReturning(TransactionInfo i) {}

6.3 핵심 호출 분석

핵심 호출 분석:

  invocation.proceed():
    - 다음 Advisor 호출 또는
    - 진짜 메서드 호출
    - CGLIB 의 MethodProxy.invokeSuper(target, args)

  createTransactionIfNecessary():
    - txAttr 의 propagation 따라
    - tm.getTransaction(txDefinition) 호출
    - TransactionInfo 반환

  completeTransactionAfterThrowing():
    - rollbackOn 검사
    - tm.rollback() 호출

  commitTransactionAfterReturning():
    - tm.commit() 호출

6.4 propagation 처리

// propagation 따른 분기 (간략)
TransactionInfo createTransactionIfNecessary(...) {
    // txAttr.getPropagationBehavior()
    
    switch (propagation) {
        case REQUIRED:
            // 기존 있으면 참여, 없으면 새로
            if (기존 트랜잭션) participate();
            else newTransaction();
            break;
        case REQUIRES_NEW:
            // 항상 새 트랜잭션 (기존 보류)
            suspend(기존);
            newTransaction();
            break;
        case NESTED:
            // 기존 있으면 SAVEPOINT
            if (기존) createSavepoint();
            else newTransaction();
            break;
        case SUPPORTS:
            // 기존 있으면 사용, 없으면 X
            break;
        case MANDATORY:
            // 기존 필수 (없으면 예외)
            break;
        case NOT_SUPPORTED:
            // 기존 보류, 트랜잭션 없이
            break;
        case NEVER:
            // 기존 없어야 (있으면 예외)
            break;
    }
}
// → 7 가지 propagation
class TransactionInfo {}
void participate() {}
void newTransaction() {}
void suspend(Object o) {}
void createSavepoint() {}
int propagation;

6.5 rollback 결정

rollback 결정:

  기본 (rollback 안 함):
    - Checked Exception (Exception 상속)
    - 트랜잭션 commit!

  기본 (rollback 함):
    - RuntimeException + Error
    - 또는 그 자손

  명시:
    @Transactional(rollbackFor = {Exception.class})
    - Checked 도 rollback

    @Transactional(noRollbackFor = {SomeException.class})
    - 특정만 commit

6.6 ILIC 의 맥락

// ILIC 의 TransactionInterceptor 동작

@Transactional(rollbackFor = Exception.class)
public void process(Long id) {
    Shipment s = repo.findById(id).orElseThrow();
    s.markAsShipped();
    
    if (s.getWeight().compareTo(BigDecimal.ZERO) <= 0) {
        throw new BusinessException("Invalid weight");
        // Checked → rollbackFor 명시 → rollback
    }
}

// TransactionInterceptor 의 동작:
// 1. @Transactional 옵션 읽기:
//    - rollbackFor = Exception.class
// 2. PlatformTransactionManager (JpaTM) 결정
// 3. createTransactionIfNecessary:
//    - propagation REQUIRED (기본)
//    - JpaTM.getTransaction() 호출
//    - EntityManager 생성, tx.begin
// 4. invocation.proceed():
//    - 진짜 process() 호출
// 5. BusinessException 발생:
//    - completeTransactionAfterThrowing:
//    - rollbackOn 검사 → Exception → rollback OK
//    - JpaTM.rollback() 호출
// 6. 예외 전파 (throw)
class Shipment {
    void markAsShipped() {}
    java.math.BigDecimal getWeight() { return null; }
}
class BusinessException extends Exception { BusinessException(String s) {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Transactional { Class<? extends Throwable>[] rollbackFor() default {}; }

6.7 자기 점검 답변

TransactionInterceptor 의 동작은?

:
1. TransactionInterceptor:

  • MethodInterceptor 구현
  1. invoke:

    • 6 단계
  2. proceed:

    • 진짜 메서드 호출
  3. propagation:

    • 7 가지

7️⃣ 메서드 호출 전체 흐름 (7 단계)

7.1 전체 흐름

전체 흐름 (7 단계):

  ① 클라이언트 → 프록시 호출
  ② 프록시 → TransactionInterceptor
  ③ TransactionInterceptor → PlatformTM.getTransaction()
  ④ PlatformTM → EntityManager 생성, tx.begin
  ⑤ 진짜 메서드 호출 (proceed)
  ⑥ 정상 반환 → PlatformTM.commit
  ⑦ 예외 → PlatformTM.rollback

7.2 단계 1 — 클라이언트 호출

단계 1 — 클라이언트 호출:

  @Service
  public class OrderService {
      @Autowired ShipmentService shipmentService;   // 프록시!
      
      public void createOrder() {
          shipmentService.process(1L);   // 프록시 메서드 호출
      }
  }

  - shipmentService 는 진짜가 아니라 프록시
  - process() 호출 = 프록시의 process()
  - 가로채기 시작

7.3 단계 2 — 프록시 → Interceptor

단계 2 — 프록시 → Interceptor:

  ShipmentService$$EnhancerByCGLIB$$xyz.process(1L):
    // CGLIB 가 생성한 메서드
    
    // 1. MethodInvocation 생성
    MethodInvocation invocation = new ReflectiveMethodInvocation(
        this,                          // proxy
        realTarget,                    // 진짜 객체
        Method.process,                // 메서드 정보
        new Object[]{1L},              // 인자
        ShipmentService.class,         // 타겟 클래스
        interceptors                   // [TransactionInterceptor]
    );
    
    // 2. invocation.proceed()
    //    → 첫 Interceptor.invoke() 호출
    //    → TransactionInterceptor.invoke(invocation)
    
    return invocation.proceed();

7.4 단계 3 — Interceptor → PlatformTM

단계 3 — Interceptor → PlatformTM:

  TransactionInterceptor.invoke(invocation):
    
    // @Transactional 옵션 읽기
    TransactionAttribute txAttr = ... (@Transactional 의 옵션);
    
    // PlatformTransactionManager 결정
    PlatformTransactionManager tm = (JpaTransactionManager);
    
    // 트랜잭션 시작
    TransactionStatus status = tm.getTransaction(txAttr);
    //   ↓ 다음 단계 (PlatformTM 내부)

7.5 단계 4 — PlatformTM → EM 생성

단계 4 — PlatformTM → EntityManager 생성:

  JpaTransactionManager.doBegin(transaction, definition):
    
    // 1. EntityManager 생성
    EntityManager em = entityManagerFactory.createEntityManager();
    
    // 2. tx.begin() 호출
    em.getTransaction().begin();
    
    // 3. ThreadLocal 에 바인딩
    TransactionSynchronizationManager.bindResource(
        entityManagerFactory,
        new EntityManagerHolder(em)
    );
    
    // (옵션: DataSource 도 바인딩 → JdbcTemplate 혼용 가능)
    
    // 4. 트랜잭션 시작 완료

7.6 단계 5 — 진짜 메서드 호출

단계 5 — 진짜 메서드 호출:

  TransactionInterceptor.invoke() 안:
    
    // invocation.proceed() 호출
    Object result = invocation.proceed();
    
    // 내부적으로:
    //   - 다른 Advisor 없으면
    //   - MethodProxy.invokeSuper(target, args)
    //   - CGLIB 가 진짜 메서드 호출
    
    // 진짜 ShipmentService.process(1L):
    //   Shipment s = repo.findById(1L).orElseThrow();
    //   s.markAsShipped();
    //   // Dirty Checking 표시
    
    // 반환

7.7 단계 6 — 정상 반환 (commit)

단계 6 — 정상 반환 (commit):

  TransactionInterceptor:
    // 진짜 메서드 정상 반환
    
    commitTransactionAfterReturning(txInfo):
      tm.commit(status);
        ↓
    JpaTransactionManager.doCommit(status):
      EntityManager em = ThreadLocal 에서 가져옴
      
      // 1. Dirty Checking → UPDATE SQL
      em.flush();
      
      // 2. 트랜잭션 commit
      em.getTransaction().commit();
      
      // 3. EntityManager 정리
      em.close();
      
      // 4. ThreadLocal 정리
      TransactionSynchronizationManager.unbindResource(emf);

  return result;   // 반환

7.8 단계 7 — 예외 (rollback)

단계 7 — 예외 (rollback):

  TransactionInterceptor:
    // invocation.proceed() 에서 예외
    
    completeTransactionAfterThrowing(txInfo, exception):
      // rollback 해야?
      if (txAttr.rollbackOn(exception)) {
        tm.rollback(status);
          ↓
      JpaTransactionManager.doRollback(status):
        EntityManager em = ThreadLocal 에서
        
        // 1. 트랜잭션 rollback
        em.getTransaction().rollback();
        
        // 2. EntityManager 정리
        em.close();
        
        // 3. ThreadLocal 정리
      
      // 예외 다시 throw
      throw exception;

7.9 시각화 (전체 흐름)

시각화 (전체 흐름):

  Client.createOrder():
    shipmentService.process(1L)
       ↓
  ┌───────────────────────────────────────┐
  │ CGLIB 프록시:                          │
  │  ShipmentService$$EnhancerByCGLIB     │
  │  .process(1L)                          │
  │   ↓                                    │
  │  invocation.proceed()                  │
  └───────────────────────────────────────┘
       ↓
  ┌───────────────────────────────────────┐
  │ TransactionInterceptor.invoke():       │
  │  1. @Transactional 옵션 읽기            │
  │  2. PlatformTM (JpaTM) 결정             │
  │  3. tm.getTransaction(txAttr)          │
  └───────────────────────────────────────┘
       ↓
  ┌───────────────────────────────────────┐
  │ JpaTransactionManager.doBegin():       │
  │  - em = emf.createEntityManager()     │
  │  - em.getTransaction().begin()         │
  │  - ThreadLocal bind                    │
  └───────────────────────────────────────┘
       ↓
  ┌───────────────────────────────────────┐
  │ 진짜 ShipmentService.process(1L):      │
  │  - Shipment s = repo.findById(1L)     │
  │  - s.markAsShipped()                   │
  │  - // Dirty Checking 표시              │
  └───────────────────────────────────────┘
       ↓
       ↙             ↘
  [정상]          [예외]
       ↓              ↓
  doCommit:      doRollback:
  - em.flush()   - tx.rollback()
  - tx.commit()  - em.close()
  - em.close()   - throw e

7.10 ILIC 의 맥락

// ILIC 의 흐름

// 클라이언트 코드
@RestController
public class ShipmentController {
    @Autowired ShipmentService shipmentService;   // ← 프록시
    
    @PostMapping("/shipments/{id}/ship")
    public void ship(@PathVariable Long id) {
        shipmentService.process(id);   // ← 7 단계 흐름 시작
    }
}

// 서비스 코드
@Service
public class ShipmentService {
    @Autowired ShipmentRepository repo;
    
    @Transactional
    public void process(Long id) {
        // 진짜 메서드 (단계 5)
        Shipment s = repo.findById(id).orElseThrow();
        s.markAsShipped();
        // Dirty Checking → 트랜잭션 commit 시 UPDATE
    }
}

// 7 단계 전체:
// 1. Controller → service.process(1L) (프록시)
// 2. CGLIB 프록시 → invocation.proceed()
// 3. TransactionInterceptor.invoke()
// 4. JpaTransactionManager.doBegin()
//    - EntityManager 생성
//    - tx.begin
// 5. 진짜 process() 실행:
//    - findById (SQL: SELECT)
//    - markAsShipped (메모리 변경)
// 6. 정상 반환 → doCommit:
//    - em.flush() → UPDATE SQL
//    - tx.commit()
//    - em.close()
// 7. (예외 없음, commit 됨)

// → 박승제 의 코드 = @Transactional + 비즈니스 3 줄
// → Spring 의 동작 = 모든 7 단계 자동
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
ShipmentService shipmentService;
class ShipmentService { void process(Long id) {} }
@interface RestController {}
@interface Autowired {}
@interface PostMapping { String value(); }
@interface PathVariable {}
@interface Service {}
@interface Transactional {}

7.11 자기 점검 답변

메서드 호출 시 전체 흐름 (7 단계) 은?

:
1. 7 단계:

  • 클라이언트 → 프록시 → Interceptor → TM → EM → 진짜 → commit/rollback
  1. 핵심:

    • 프록시 + Interceptor + TM
  2. commit:

    • flush + tx.commit + em.close
  3. rollback:

    • tx.rollback + em.close + throw

8️⃣ 5+6+7주차 응축 종합

8.1 5주차 응축

5주차 응축:

  - 프록시 패턴: CGLIB 자식 클래스
  - DI: @Autowired (프록시 주입)
  - DIP: PlatformTransactionManager 인터페이스
  - OCP: 새 Advisor 추가 (코드 변경 X)
  - 템플릿+전략:
    - 템플릿: TransactionInterceptor.invoke
    - 전략: PlatformTransactionManager 구현체
  - 데코레이터: 프록시가 부가 기능

→ 5주차 디자인 패턴 완벽 응축

8.2 6주차 응축

6주차 응축:

  - DataSource: HikariCP (Connection)
  - ACID: 트랜잭션 의 원칙
  - JdbcTemplate: 자원 자동 관리
  - JDBC: Connection 사용

  @Transactional 의 활용:
    - DataSource 가 Connection 제공
    - tx.commit / rollback = ACID
    - Connection 자동 관리

→ 6주차 인프라 활용

8.3 7주차 응축

7주차 응축:

  - JPA: EntityManager, Dirty Checking
  - Spring Data JPA: Repository
  - Querydsl: 동적 쿼리
  - PlatformTransactionManager: 추상화
  - @Transactional: 자동화

  7주차 Part B 의 정점:
    - Phase 5 (수동의 한계)
    - Phase 6 (PlatformTM 추상화)
    - Phase 7 (@Transactional 자동화)
    - = @Transactional 동작 원리

→ 7주차 모든 학습 응축

8.4 전체 응축

전체 응축:

  @Transactional 의 1 줄 안에:
    
    [5주차]
    - 프록시 패턴 ✓
    - DI / DIP / OCP ✓
    - 템플릿+전략 ✓
    - 데코레이터 ✓
    - AOP ✓
    
    [6주차]
    - DataSource ✓
    - ACID ✓
    - HikariCP ✓
    - JDBC ✓
    
    [7주차]
    - JPA / EntityManager ✓
    - PlatformTransactionManager ✓
    - 추상화 ✓
    - 자동화 ✓

  → 자바 백엔드의 결정체
  → 박승제 의 ILIC 코드 1020 메서드
  → 모두 이 응축 활용

8.5 학습 가치

학습 가치:

  1. 면접 정점:
     - "@Transactional 어떻게 동작?"
     - 위 흐름 설명
     - 시니어 시험 통과

  2. 실무 디버깅:
     - 함정 회피 (Unit 7.3)
     - 트러블슈팅
     - 성능 분석

  3. 깊은 이해:
     - 5+6+7주차 정수
     - 자바 진영 응축

  4. 코드 리뷰:
     - 의도 파악
     - 효율적 패턴

8.6 박승제 의 학습 의의

박승제 의 학습 의의

ILIC 의 1020 메서드:
  - 매일 사용
  - @Transactional 1 줄
  - 7 단계 흐름

학습 후:
  - 동작 원리 완전 이해
  - 디버깅 능력
  - 함정 회피
  - 면접 정점

박승제 의 자산:
  - 5+6+7주차 응축 마스터
  - 자바 백엔드 시니어
  - 면접 / 운영 / 코드 리뷰

8.7 ILIC 의 맥락

ILIC 의 매일

ILIC = 1020 @Transactional 메서드:

  - 모두 위 7 단계 흐름
  - Spring 의 마법 자동
  - 박승제 의 비즈니스 코드만

  매일 무수한 호출:
    - shipmentService.process()
    - customerService.update()
    - bookingService.create()
    - ...
    
    각자 위 7 단계
    동시에 (멀티 쓰레드)
    ThreadLocal 격리

  운영 안정:
    - 함정 회피 (다음 Unit)
    - 모니터링
    - 트러블슈팅

→ 박승제 의 운영 자산

8.8 자기 점검 답변

5+6+7주차 응축의 의미는?

:
1. 5주차:

  • 디자인 패턴 (프록시/DI/AOP)
  1. 6주차:

    • DataSource / ACID
  2. 7주차:

    • JPA / PlatformTM
  3. 응축:

    • @Transactional 1줄

9️⃣ Phase 7.3 예고 (5가지 함정)

9.1 Phase 7.3 의 가치

Phase 7.3 — @Transactional 5가지 함정 ★:

  학습 후의 깊이:
    - 7.2 동작 원리 이해 →
    - 7.3 함정 회피 →
    - 운영 안전

  면접 단골:
    - "@Transactional 의 함정은?"
    - 5가지 답변

9.2 5가지 함정

5가지 함정 (Unit 7.3):

  1. private 메서드:
     - CGLIB / JDK 모두 X
     - 프록시 적용 X
     - @Transactional 무시

  2. self-invocation:
     - 같은 클래스 내 호출
     - this.method() = 진짜 객체
     - 프록시 우회

  3. checked exception:
     - 기본 rollback X
     - rollbackFor 명시 필요

  4. 트랜잭션 전파:
     - REQUIRED / REQUIRES_NEW / NESTED 등
     - 7가지

  5. readOnly:
     - 진짜 읽기 전용?
     - 최적화 X 케이스

9.3 함정 1 미리보기

// 함정 1: private 메서드
@Service
public class ShipmentService {
    public void process(Long id) {
        // 프록시 안 통과 (같은 클래스 내!)
        doPrivate(id);   // ← 진짜 객체의 메서드 호출
    }
    
    @Transactional   // ← 무시됨!
    private void doPrivate(Long id) {
        // 프록시 적용 X
        // 트랜잭션 X
    }
}

// 함정:
// - @Transactional 보고 안전하다고 생각
// - 실제로는 트랜잭션 X
// - 운영 사고
@interface Service {}
@interface Transactional {}

9.4 함정 2 미리보기

// 함정 2: self-invocation
@Service
public class ShipmentService {
    public void outerMethod() {
        this.innerMethod();   // ← 진짜 객체!
        // 프록시 우회
    }
    
    @Transactional
    public void innerMethod() {
        // 외부에서 호출 시 OK
        // 같은 클래스 내 호출 시 X
    }
}

// 외부에서:
service.outerMethod();
// → outerMethod 는 프록시 통과
// → 안에서 this.innerMethod() 호출
// → this = 진짜 객체 (프록시 X)
// → innerMethod 의 @Transactional 무시!
@interface Service {}
@interface Transactional {}
ShipmentService service;
class ShipmentService {
    void outerMethod() {}
}

9.5 종합 졸업 시험 예고

종합 졸업 시험 24문항 (Unit 7.3 마지막):

  SQL JOIN (Phase 1): 3 문항
  ORM (Phase 2): 6 문항
  JPA 매핑 (Phase 3-4): 4 문항
  수동 트랜잭션 (Phase 5): 2 문항
  PlatformTM (Phase 6): 3 문항
  @Transactional (Phase 7): 6 문항

  → 7주차 종합 졸업!

9.6 7주차 진행

🗂️ Part A (완주)
  ✅ Phase 1-4 (16)

🔄 Part B — 트랜잭션 추상화의 진화
  ✅ Phase 5 (2)
  ✅ Phase 6 (3)
  ✨ Phase 7 — @Transactional (2/3)
    ✅ Unit 7.1 프록시 패턴 ★
    ✅ Unit 7.2 @Transactional 동작 원리 ★★★ ← 여기
    ⏭ Unit 7.3 5가지 함정 ★ + 종합 졸업 시험 24문항

총: 23/24 Unit (96%)

9.7 면접 단골 질문 매핑

Q핵심 답변
@Transactional 동작?7 단계 흐름
Bean Post Processor?프록시 생성
CGLIB 자식 클래스?동적 생성
TransactionInterceptor?MethodInterceptor
invoke 메서드?6 단계
invocation.proceed?진짜 메서드
PlatformTM 호출?단계 3-4
EntityManager?단계 4
commit 시?flush + tx.commit
5+6+7 응축?@Transactional 1줄

9.8 자기 점검 체크리스트

개요

  • 두 단계

Spring Boot 시작

  • 흐름

Bean Post Processor

  • 역할

AutoProxyCreator

  • 매칭

CGLIB

  • 자식 클래스

TransactionInterceptor

  • invoke

7 단계

  • 전체 흐름

5+6+7 응축

  • 종합

Phase 7.3

  • 5 함정 예고

9.9 추가 심화 질문

Q1: AspectJ vs Spring AOP?

답:

  • Spring AOP: 프록시 (메서드 호출만)
  • AspectJ: 바이트코드 위빙 (필드/생성자/private 등)
  • AspectJ 가 더 강력 (함정 회피)
  • Spring 도 AspectJ 사용 가능 (mode = ASPECTJ)

Q2: ThreadLocal 의 위험?

답:

  • 메모리 누수 가능 (스레드풀)
  • Spring 이 자동 정리
  • 직접 ThreadLocal 사용 시 주의
  • TransactionSynchronizationManager

Q3: TransactionInterceptor 의 캐시?

답:

  • TransactionAttribute 캐시
  • 매 호출 어노테이션 파싱 X
  • 첫 호출 시 캐시
  • 성능 최적화

Q4: 프록시 체인?

답:

  • 여러 어노테이션 (@Transactional + @Async 등)
  • Advisor 여러 개
  • proceed() 가 다음 Advisor 호출
  • 마지막에 진짜 메서드

Q5: 디버깅 방법?

답:

  • 로그 (logging.level.org.springframework.transaction = DEBUG)
  • 객체 클래스명 확인 (CGLIB 포함)
  • Actuator /actuator/beans
  • 브레이크포인트 (TransactionInterceptor)

🎯 핵심 요약 — 3줄 정리

1. 시작 단계 (Spring Boot 시작 시)

  • @EnableTransactionManagement 자동 → TransactionInterceptor + InfrastructureAdvisorAutoProxyCreator (BPP) 등록
  • BPP 가 모든 빈 스캔 → @Transactional 감지 → CGLIB 으로 자식 클래스 동적 생성 (ShipmentService$$EnhancerByCGLIB)
  • 프록시를 빈으로 등록 (진짜는 프록시 내부에 참조)

2. 호출 단계 (메서드 호출 시 7 단계)

  • (1) 클라이언트 → (2) CGLIB 프록시 → (3) TransactionInterceptor.invoke
  • (4) PlatformTransactionManager (JpaTM) .getTransaction → EntityManager 생성 + tx.begin
  • (5) invocation.proceed() → 진짜 메서드 호출 (Dirty Checking 표시)
  • (6) 정상 → tm.commit() → em.flush() (UPDATE SQL) + tx.commit() + em.close()
  • (7) 예외 → tm.rollback() → tx.rollback() + em.close() + throw

3. 5+6+7주차 응축의 정점

  • 5주차: 프록시 + AOP + DI + DIP + OCP + 템플릿+전략
  • 6주차: DataSource + ACID + HikariCP
  • 7주차: JPA + EntityManager + PlatformTransactionManager
  • = @Transactional 1줄 안에 모두 응축 → 자바 백엔드의 결정체

📚 다음으로...

Unit 7.3 — @Transactional 5가지 함정 ★ + 종합 졸업 시험 24문항 (Phase 7 완주 + 7주차 완주)

이번 Unit에서 동작 원리를 봤다면, 다음은 5가지 함정 (마지막 Unit).

  • 함정 1: private 메서드
  • 함정 2: self-invocation
  • 함정 3: checked exception
  • 함정 4: 트랜잭션 전파 (7가지)
  • 함정 5: readOnly
  • 종합 졸업 시험 24문항
  • 7주차 완주!

Phase 7 진행 상황

✨ Phase 7 — @Transactional (모두 ★ 깊이)
  ✅ Unit 7.1 프록시 패턴 ★
  ✅ Unit 7.2 @Transactional 동작 원리 ★★★ ← 여기
  ⏭ Unit 7.3 5가지 함정 ★ — Phase 7 완주 + 7주차 완주

7주차 누적 진행

🗂️ Part A (완주)
  ✅ Phase 1-4 (16)

🔄 Part B
  ✅ Phase 5 (2)
  ✅ Phase 6 (3)
  ✨ Phase 7 (2/3)

총: 23/24 Unit (96%)

★★★ 깊이 파기 — @Transactional 동작 원리, 7주차의 정점

profile
Software Developer

0개의 댓글