๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŒฑ Backend/Spring

@Transactional ์˜ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ

by kekeyo 2025. 5. 21.
728x90

ํŠธ๋žœ์žญ์…˜?

: DBMS ๋˜๋Š” ์œ ์‚ฌํ•œ ์‹œ์Šคํ…œ์—์„œ ์ƒํ˜ธ์ž‘์šฉ์˜ ๋‹จ์œ„ (ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์ ์ธ ์ž‘์—… ๋‹จ์œ„)

ํŠน์ง•

  • Atomic
  • Consistent
  • Isolation
  • Durability

์—ฐ์‚ฐ

  • ๋กค๋ฐฑ
  • ์ปค๋ฐ‹

⇒ ๊ทธ๋Ÿผ Spring์—์„œ๋Š” ์ด ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”๊ฐ€?

@Transactional

์Šคํ”„๋ง์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

@Transactional ๊ณผ ๊ฐ™์ด ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹์œผ๋กœ ์ด๋ฅผ ์ง€์›ํ•œ๋‹ค.

Spring์—์„œ ๋ฐ์ดํ„ฐ ์ž‘์—…์˜ ์‹คํ–‰๋‹จ์œ„๋ฅผ ๋ช…์‹œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํด๋ž˜์Šค / ๋ฉ”์„œ๋“œ ์œ„์— @Transactional ์„ ๋ถ™์ด๋ฉด AOP(๊ด€์ ์ง€ํ–ฅํ”„๋กœ๊ทธ๋ž˜๋ฐ)๋ฅผ ํ†ตํ•ด ํƒ€๊ฒŸ์ด ์ƒ์†ํ•˜๊ณ  ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๋˜๋Š” ํƒ€๊ฒŸ์„ ์ƒ์†ํ•œ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ์ด๋•Œ ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํƒ€๊ฒŸ ๋ฉ”์†Œ๋“œ ์ „ ํ›„๋กœ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

ํ•„์š”ํ•œ ์ด์œ 

// ๊ฐ€๋…์„ฑ ๋†’๊ณ 

  • ๊ฐ€๋…์„ฑ์ด ๋†’์€ ์ด์œ 
      public static void main(String[] args) {
          //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ƒ์„ฑ
          EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
          EntityManager em = emf.createEntityManager(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ƒ์„ฑ
          EntityTransaction tx = em.getTransaction(); //ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ ํš๋“
    
          try {
              tx.begin(); //ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
              logic(em); //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
              tx.commit();//ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
          } catch (Exception e) {
              tx.rollback(); //ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
          } finally {
              em.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ข…๋ฃŒ
          }
          emf.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ข…๋ฃŒ
      }
    ๊ทผ๋ฐ ์ด๋ฅผ @Transactional ์„ ์ด์šฉํ•˜๋ฉด ๋˜์–ด์„œ ๊ฐ€๋…์„ฑ์ด ๋†’๋‹ค. ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Œ?
  • ๊ณผ๊ฑฐ์—๋Š” ๋กค๋ฐฑ์„ ํ•˜๊ธฐ ์œ„ํ•ด try-catch์™€ ๊ฐ™์€ ๋กค๋ฐฑ์„ ์ด์šฉํ•ด์•ผ ํ–ˆ์Œ

์œ ์ง€๋ณด์ˆ˜ ์ข‹๋‹ค

์ž‘๋™

ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฝ์šฐ, ํŠธ๋žœ์žญ์…˜์˜ ์‹œ์ž‘๊ณผ ์—ฐ์‚ฐ ์ข…๋ฃŒ ์‹œ์˜ ์ปค๋ฐ‹ ๊ณผ์ •์ด ํ•„์š”ํ•˜๋ฏ€๋กœ, ํ”„๋ก์‹œ(ํŠธ๋žœ์žญ์…˜ AOP)๋ฅผ ์ƒ์„ฑํ•ด ํ•ด๋‹น ๋ฉ”์„œ๋“œ์˜ ์•ž๋’ค์— ํŠธ๋žœ์žญ์…˜์˜ ์‹œ์ž‘๊ณผ ๋์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋˜ํ•œ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ „๋žต์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

์„œ๋น„์Šค ํด๋ž˜์Šค์—์„œ @Transactional์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ํ•ด๋‹น ์ฝ”๋“œ ๋‚ด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ƒ๊ธด๋‹ค๋Š” ๋œป์ด๋‹ค. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ ์ƒ๊ฒจ๋‚˜๊ณ , ๋ฉ”์„œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๊ฒฝ์šฐ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ flush๋˜๋ฉด์„œ ํ•ด๋‹น ๋‚ด์šฉ์ด ๋ฐ˜์˜๋œ๋‹ค.

์ดํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์—ญ์‹œ ์ข…๋ฃŒ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

// ์ด ๋ง์ด 1์ฐจ ์บ์‹œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค๋Š” ๊ฑฐ๋ƒ

๋™์ž‘ ์›๋ฆฌ

AOP (๊ด€์ ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ)

spring์€ @Transactional์„ ๋ณด๊ณ  AOP ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.

์Šคํ”„๋ง AOP๋Š” ํ”„๋ก์‹œ ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ๋Œ€์ƒ ๊ฐ์ฒด ๋Œ€์‹  ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ œ๊ณต.

⇒ ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ๋ชจ๋“ˆ์„ฑ์ด ํ™•์žฅ

  • AOP ์ฃผ์š” ๊ฐœ๋…
    • Aspect: Advice + PointCut๋กœ AOP์˜ ๊ธฐ๋ณธ ๋ชจ๋“ˆ
    • Advice: Target์— ์ œ๊ณตํ•  ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ๋Š” ๋ชจ๋“ˆ
    • Target: Advice์ด ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ๋Œ€์ƒ (Advice๊ฐ€ ์ ์šฉ๋  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)
    • JointPoint: Advice๊ฐ€ ์ ์šฉ๋  ์œ„์น˜
      • ๋ฉ”์„œ๋“œ ์ง„์ž… ์ง€์ , ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์‹œ์ , ํ•„๋“œ์—์„œ ๊ฐ’์„ ๊บผ๋‚ด์˜ฌ ๋•Œ ๋“ฑ ๋‹ค์–‘ํ•œ ์‹œ์ ์— ์ ์šฉ ๊ฐ€๋Šฅ
    • PointCut: Target์„ ์ง€์ •ํ•˜๋Š” ์ •๊ทœ ํ‘œํ˜„์‹advice: ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋กœ์ง๋งŒ ๋‹ด๋‹น
    • pointcut: ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•˜๋Š” ํ•„ํ„ฐ ์—ญํ• ๋งŒ ๋‹ด๋‹น
    • ⇒ ์—ญํ• ๊ณผ ์ฑ…์ž„์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•จ

Proxy

AOP๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ์ˆ  ์ค‘ ํ•˜๋‚˜, ๋Œ€์ƒ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ œ์–ดํ•˜๊ณ  ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ๊ฐ์‹ธ๊ณ  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ด๊ฒŒ ์ง„์งœ์ธ์ง€ ๊ฐ€์งœ์ธ์ง€ ๋ชจ๋ฅด๊ฒŒํ•˜๋ฉด์„œ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Œ

  • [Proxy ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ๋ฐฉ๋ฒ•]
    1. JDK Dynamic Proxy
       public interface MyService {
           void doSomething();
       }
       @Service
       public class MyServiceImpl implements MyService {
           @Transactional 
           public void doSomething() {
               System.out.println("์‹ค์ œ ๋กœ์ง ์‹คํ–‰");
           }
       }
      ⇒ @Tranactional์ด ์ ์šฉ๋˜๋ฉด spring์€ MyServiceImpl์œผใ…ข ํ”„๋ก์‹œ ๊ฐ์ฒด ์ƒ์„ฑ. doSomething์„ ํ˜ธ์ถœํ•˜๋ฉด ํ”„๋ก์‹œ๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜๋ฉด์„œ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๋ฅผ ํ•จ
    2. ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ํ”„๋ก์‹œ ๊ฐ์ฒด ์ƒ์„ฑ
    3. CGLIBํด๋ž˜์Šค ์ž์ฒด๋ฅผ ์ƒ์†ํ•˜์—ฌ ํ”„๋ก์‹œ ์ƒ์„ฑ
    4. : ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์›๋ž˜ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ
    5. ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ

  1. @Transactional ์ ์šฉ์‹œ์ : Bean ํ›„์ฒ˜๋ฆฌ ๊ณผ์ •Spring ์ปจํ…Œ์ด๋„ˆ๋Š” BeanFactoryPostProcessor์™€ AnnotationTransactionAttributeSource๋ฅผ ์ด์šฉํ•ด @Transactional์„ ๊ฐ์ง€ํ•˜๊ณ , ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•œ๋‹ค.
  2. Spring Application์ด ์‹œ์ž‘ํ•  ๋•Œ, ์ปจํ…Œ์ด๋„ˆ๋Š” ๊ฐ Bean์„ ์ƒ์„ฑํ•˜๊ณ  BeanPostProcessor ๋‹จ๊ณ„์—์„œ @Transactional ๋“ฑ AOP ๊ด€๋ จ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ Bean์„ ์Šค์บ”ํ•จ
  3. Caller์—์„œ AOP ํ”„๋ก์‹œ๋ฅผ ํƒ„๋‹ค. ์ด๋•Œ ํƒ€๊ฒŸ์„ ํ˜ธ์ถœํ•˜์ง€๋Š” ์•Š๊ณ , ํ”„๋ก์‹œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    • Caller: ์•ฑ ๋‚ด๋ถ€/์™ธ๋ถ€์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฃผ์ฒด (@Autowired๋กœ ์ฃผ์ž…๋ฐ›์€ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ์‹ค์ œ๋กœ๋Š” ๊ทธ ๊ฐ์ฒด์˜ “ํ”„๋ก์‹œ”๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. )
  4. ๊ทธ AOP ํ”„๋ก์‹œ๋Š” Advisor๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.DB์—ฐ๊ฒฐ์žก๊ณ  ์‹ค์ œ ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋œ๋‹ค.
    • Transaction Advisor: @Transactional์„ ํ•ด์„ํ•˜๊ณ  ์‹œ์ž‘/์ปค๋ฐ‹/๋กค๋ฐฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ด€๋ฆฌ์ž.
    • Custom Advisor๊ฐ€ ์žˆ๋‹ค๋ฉด, ํŠธ๋žœ์žญ์…˜ Advisor ์‹คํ–‰ ์ „ํ›„๋กœ ๋™์ž‘ํ•œ๋‹ค.
    • CustomAdvisor: ๋ณ„๋„๋กœ ๋งŒ๋“  AOP์„ค์ •์ด ์žˆ๋‹ค๋ฉด, TransactionAdvisor ์•ž๋’ค๋กœ ๋ผ์–ด๋“ค์–ด ๋™์ž‘ํ•˜๋Š” ์ถ”๊ฐ€์ ์ธ ์–ด๋“œ๋ฐ”์ด์ €
  5. → ์ด๋•Œ @Transactional์˜ ์„ธ๋ถ€ ์„ค์ •์ด ๋ฐ˜์˜๋จ (propagation, isolation ๋“ฑ)
  6. @Transactional์ด ์žˆ์œผ๋ฏ€๋กœ ์ด ๋ฉ”์„œ๋“œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ํ•„์š”ํ•˜๋‹ค. ์‹œ์ž‘ํ•ด๋‹ฌ๋ผ๊ณ  ํ•œ๋‹ค.
  7. ์‹ค์ œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ (Targer Method)์ด๋•Œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ˆ˜ํ–‰๋˜๊ณ , DB์ ‘๊ทผ (์ฟผ๋ฆฌ)๋„ @Transactional ๊ฒฝ๊ณ„ ์•ˆ์—์„œ ์ง„ํ–‰๋จ
    • ์˜ˆ์™ธ ๋ฐœ์ƒ → ๋กค๋ฐฑ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
    • ์˜ค๋ฅ˜ ์—†์Œ → ์ปค๋ฐ‹ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ์Œ
  8. ๋ชจ๋“  Advisor๋“ค์ด ์‹คํ–‰ ์ „์— ํ•ด์•ผํ•  ์ผ์„ ๋งˆ์น˜๋ฉด ์‹ค์ œ Target ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋จ
  9. ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
    • ๊ฐ advisor์˜ after ํŒŒํŠธ ์‹คํ–‰ ๊ฐ€๋Šฅ
    • TransactionalAdvisor๋Š” ๋กค๋ฐฑ / ์ปค๋ฐ‹ํ• ์ง€ ์ตœ์ข…ํŒ๋‹จํ•˜์—ฌ ์ง„ํ–‰
    • JPA ์‚ฌ์šฉ ์‹œ flush() ์‹คํ–‰ ํ›„ ์‹ค์ œ ์ปค๋ฐ‹ ์ˆ˜ํ–‰
    • ์ปค๋ฐ‹ ์™„๋ฃŒ๋˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ํ›„ Connection ๋ฐ˜ํ™˜
    • ๋กค๋ฐฑ ์‹œ์—๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ดˆ๊ธฐํ™”
    • ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜๋ฉด ๊ฒฐ๊ณผ๊ฐ’์„ AOP Proxy๊ฐ€ caller์—๊ฒŒ ๋ฐ˜ํ™˜ํ•จ
  10. ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด, ๋‹ค์‹œ CustomAdvisor→TransactionAdvisor ์ˆœ์œผ๋กœ ๋Œ์•„์˜ด

์ฃผ์˜์ 

  • ๋‚ด๋ถ€ ํ˜ธ์ถœ์„ ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ๊ฑธ๋ฆฌ์ง€ ์•Š์Œ
      @Service
      public class dkdkdkdService {
      @Transactional
      public void methodA{
          methodB;         // ๋‚ด๋ถ€ ํ˜ธ์ถœ -> ํŠธ๋žœ์žญ์…˜ X
      }
    
      @Transactional
      public void methodB{
          // ํŠธ๋žœ์žญ์…˜ ๊ฑธ๋ฆฌ๊ณ  ์‹ถ์—ˆ์ง€๋งŒ ์‹คํŒจ
      }
  • ๋ฐ˜๋“œ์‹œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๊ฑฐ์ณ์•ผ ํŠธ๋žœ์žญ์…˜ AOP ์ ์šฉ
728x90