@Transactional μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­

κΉ€μ˜ν•œλ‹˜ μŠ€ν”„λ§ DB 2편 - 데이터 μ ‘κ·Ό 기술 ν™œμš© κ°•μ˜λ₯Ό 기반으둜 μ •λ¦¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

@Transactional을 μ‚¬μš©ν•˜λ©΄ μŠ€ν”„λ§ νŠΈλžœμž­μ…˜ AOPκ°€ μ μš©λœλ‹€. νŠΈλžœμž­μ…˜ AOPλŠ” 기본적으둜 ν”„λ‘μ‹œ λ°©μ‹μ˜ AOPλ₯Ό μ‚¬μš©ν•œλ‹€. @Transactional을 μ γ…ˆμš©ν•˜λ©΄ ν”„λ‘μ‹œ 객체가 μš”μ²­μ„ λ¨Όμ € λ°›μ•„μ„œ νŠΈλžœμž­μ…˜μ„ λ¨Όμ € μ²˜λ¦¬ν•˜κ³ , μ‹€μ œ 객체λ₯Ό ν˜ΈμΆœν•΄μ€€λ‹€. λ”°λΌμ„œ νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜λ €λ©΄ 항상 ν”„λ‘μ‹œλ₯Ό ν†΅ν•΄μ„œ λŒ€μƒ 객체(Target)을 ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€. μ΄λ ‡κ²Œ ν•΄μ•Ό ν”„λ‘μ‹œμ—μ„œ λ¨Όμ € νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜κ³ , 이후에 λŒ€μƒ 객체λ₯Ό ν˜ΈμΆœν•˜κ²Œ λœλ‹€. λ§Œμ•½ ν”„λ‘μ‹œλ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  λŒ€μƒ 객체λ₯Ό 직접 ν˜ΈμΆœν•˜κ²Œ 되면 AOPκ°€ μ μš©λ˜μ§€ μ•Šκ³  λ”°λΌμ„œ νŠΈλžœμž­μ…˜λ„ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€. AOPλ₯Ό μ μš©ν•˜λ©΄ μŠ€ν”„λ§μ€ λŒ€μƒ 객체 λŒ€μ‹œμ— ν”„λ‘μ‹œλ₯Ό μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•œλ‹€. λ”°λΌμ„œ μŠ€ν”„λ§μ€ μ˜μ‘΄κ΄€κ³„ μ£Όμž… μ‹œμ— 항상 μ‹€μ œ 객체 λŒ€μ‹ μ— ν”„λ‘μ‹œ 객체λ₯Ό μ£Όμž…ν•œλ‹€. ν”„λ‘μ‹œ 객체가 μ£Όμž…λ˜κΈ° λ•Œλ¬Έμ— λŒ€μƒ 객체λ₯Ό 직접 ν˜ΈμΆœν•˜λŠ” λ¬Έμ œλŠ” 일반적으둜 λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€. ν•˜μ§€λ§Œ λŒ€μƒ 객체의 λ‚΄λΆ€μ—μ„œλ„ λ©”μ„œλ“œ 호좜이 λ°œμƒν•˜λ©΄ ν”„λ‘μ‹œλ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  λŒ€μƒ 객체λ₯Ό 직접 ν˜ΈμΆœν•˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•œλ‹€. μ΄λ ‡κ²Œ 되면 @Transactional이 μžˆμ–΄λ„ νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€.

  
  @Service
  @Slf4j
  class TxTest {

    @Transactional
    public void internalCall() {
    
    }

    public void externalCall() {
      internalCall(); 
    }
  }

  ...

  @Autowired
  TxTest txTest;

  txTest.internalCall(); // νŠΈλžœμž­μ…˜ 적용됨
  txTest.externalCall(); // externalCallμ—μ„œ λ‚΄λΆ€μ—μ„œ ν˜ΈμΆœν•˜λŠ” internalCall() νŠΈλžœμž­μ…˜ 적용 μ•ˆ 됨

txTest.internalCall()

  1. txTest.internalCall()을 ν˜ΈμΆœν•˜λŠ”λ°, μ—¬κΈ°μ„œ txTestλŠ” νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œμ΄λ‹€.
  2. txTest의 νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œκ°€ ν˜ΈμΆœλœλ‹€.
  3. internalCall λ©”μ„œλ“œμ— @Transactional이 λΆ™μ–΄ μžˆμœΌλ―€λ‘œ ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜μ„ μ μš©ν•œλ‹€.
  4. νŠΈλžœμž­μ…˜ 적용 후에 μ‹€μ œ txTest 객체 μΈμŠ€ν„΄μŠ€μ˜ internalCall() 호좜
  5. μ‹€μ œ txTestκ°€ 처리λ₯Ό μ™„λ£Œν•˜λ©΄ 응닡이 νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œλ‘œ λŒμ•„μ˜€κ³ , νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜μ„ μ™„λ£Œν•œλ‹€.

txTest.externalCall()

  1. txTest.externalCall()을 ν˜ΈμΆœν•˜λŠ”λ°, μ—¬κΈ°μ„œ txTestλŠ” νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œμ΄λ‹€.
  2. txTest의 νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œκ°€ ν˜ΈμΆœλœλ‹€.
  3. externalCall λ©”μ„œλ“œμ— @Transactional이 μ—†λ―€λ―€λ‘œ νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.
  4. νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜μ§€ μ•Šκ³ , μ‹€μ œ txTest 객체 μΈμŠ€ν„΄μŠ€μ˜ externalCall()을 ν˜ΈμΆœν•œλ‹€.
  5. externalCall() λ‚΄λΆ€μ—μ„œ internalCall() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ§€λ§Œ 이 internalCall() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄λ„ νŠΈλžœμž­μ…˜μ€ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€. 큰일났닀.

문제의 원인 externalCall()μ—μ„œλŠ” internalCall()을 ν˜ΈμΆœν•œλ‹€. μžλ°”μ—μ„œλŠ” λ©”μ„œλ“œ μ•žμ— λ³„λ„μ˜ μ°Έμ‘°κ°€ μ—†μœΌλ©΄ thisλΌλŠ” 뜻으둜 자기 μžμ‹ μ˜ μΈμŠ€ν„΄μŠ€ μ£Όμ†Œλ₯Ό 가리킨닀. 결과적으둜 자기 μžμ‹ μ˜ λ‚΄λΆ€ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” this.internalCall()이 λ˜λŠ”λ° thisλŠ” 자기 μžμ‹ μ„ κ°€λ¦¬ν‚€λ―€λ‘œ μ‹€μ œ 객체(target)을 λœ»ν•œλ‹€. 결과적으둜 μ΄λŸ¬ν•œ λ‚΄λΆ€ ν˜ΈμΆœμ€ ν”„λ‘μ‹œλ₯Ό κ±°μΉ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜ μ—­μ‹œ 적용될 수 μ—†λ‹€.

λŒ“κΈ€λ‚¨κΈ°κΈ°