ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring/SpringBoot] νŠΈλžœμž­μ…˜μ΄λž€? μŠ€ν”„λ§μ˜ Transactional(feat: AOP)
    πŸ’»Programming/SpringBoot 2023. 9. 25. 00:05
    λ°˜μ‘ν˜•

    νŠΈλžœμž­μ…˜(Transaction)μ΄λž€?

    : DB μƒνƒœλ₯Ό λ³€ν™˜μ‹œν‚€λŠ” ν•˜λ‚˜μ˜ 논리적 κΈ°λŠ₯을 μˆ˜ν–‰ν•˜κΈ° μœ„ν•œ μž‘μ—…μ˜ λ‹¨μœ„

    = μͺΌκ°€ 수 μ—†λŠ” μ—¬λŸ¬ μž‘μ—…λ“€μ„ λ…Όλ¦¬μ μœΌλ‘œ μ΅œμ†Œ λ‹¨μœ„λ‘œ 묢은 것

     

    νŠΈλžœμž­μ…˜μ˜ μ˜ˆμ‹œ 상황

    온라인 μ‡Όν•‘λͺ°μ„ κ²°μ œν•  λ•Œ, 두가지 μž‘μ—…μ„ κ±°μ³μ•Όν•œλ‹€.

    1. νŒλ§€μ²˜μ— λˆμ„ 보내기

    2. νŒλ§€μ²˜μ—μ„œ 고객이 보낸 λˆμ„ λ°›κΈ°

     

    μ € 두 μž‘μ—…μ΄ ν•œ νŠΈλžœμž­μ…˜μ΄λΌκ³  ν•˜μž. 이 경우 고객이 νŒλ§€μ²˜μ— λˆμ„ λ³΄λƒˆλŠ”λ° νŒλ§€μ²˜μ—μ„œ 고객이 보낸 λˆμ„ 받지 λͺ»ν•˜λŠ” κ²½μš°λ‚˜, 고객이 λˆμ„ 보내지 μ•Šμ•˜λŠ”λ° νŒλ§€μ²˜μ—μ„œ λˆμ„ λ°›λŠ” κ²½μš°κ°€ 생기면 μ•ˆλœλ‹€. λͺ¨λ“  μž‘μ—…μ΄ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ˜μ–΄μ•Ό μž‘μ—… κ²°κ³Όλ₯Ό 적용(commit)ν•˜κ³ , νŠΈλžœμž­μ…˜μ— μ†ν•œ λͺ¨λ“  μž‘μ—… 쀑에(1λ²ˆμ΄λ‚˜ 2번 λ‘˜μ€‘μ—) ν•˜λ‚˜λΌλ„ 였λ₯˜κ°€ λ°œμƒν•˜λŠ” κ²½μš°μ—λŠ” μž‘μ—…μ„ μ‹€ν–‰ν•˜κΈ° μ „μ˜ μƒνƒœλ‘œ μ™„λ²½ν•˜κ²Œ λŒμ•„κ°€μ•Ό ν•˜λŠ” 것(rollback)이 νŠΈλžœμž­μ…˜μ˜ κ°œλ…μ΄λ‹€. 

     

    νŠΈλžœμž­μ…˜μ˜ νŠΉμ§•(Transaction ACID)

    "A"tomicity(μ›μžμ„±)

    : νŠΈλžœμž­μ…˜μ˜ 연산은 DB에 λͺ¨λ‘ λ°˜μ˜λ˜κ±°λ‚˜, λͺ¨λ‘ λ°˜μ˜λ˜μ§€ μ•Šμ•„μ•Όν•œλ‹€. ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•œλ‹€λ©΄, μ•žμ„œ μ„±κ³΅ν•œ 것듀을 λ³΅κ΅¬μ‹œμΌœμ•Όν•œλ‹€. 

    "C"onsistency(일관성)

    : νŠΈλžœμž­μ…˜μ˜ μž‘μ—… 처리 κ²°κ³ΌλŠ” 항상 일관성 μžˆμ–΄μ•Ό ν•œλ‹€. = λͺ¨λ“  νŠΈλžœμž­μ…˜μ€ 일관성 μžˆλŠ” DB μƒνƒœλ₯Ό μœ μ§€ν•œλ‹€.(ex: DB의 무결성 μ œμ•½ 쑰건 항상 만쑱)

    "I"solation(격리성)

    : μ–΄λ–€ νŠΈλžœμž­μ…˜λ„ λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ˜ μž‘μ—…μ— λΌμ–΄λ“€κ±°λ‚˜ μ„œλ‘œ 영ν–₯을 쀄 수 μ—†λ‹€. (ex: λ™μ‹œμ— 같은 데이터 μˆ˜μ • X)

    "D"urability(지속성)

    : νŠΈλžœμž­μ…˜μ΄ μ™„λ£Œλœλ‹€λ©΄, κ²°κ³ΌλŠ” μ˜κ΅¬μ μ΄μ–΄μ•Όν•œλ‹€. 

    * 격리성을 μ™„λ²½ν•˜κ²Œ 보μž₯ν•˜λ €λ©΄ νŠΈλžœμž­μ…˜μ„ μˆœμ„œλ₯Ό μ •ν•΄μ„œ ν•˜λ‚˜μ”© μ‹€ν–‰ν•΄μ•Όν•˜λŠ”λ°, μ„±λŠ₯이 λ„ˆλ¬΄ λ‚˜λΉ μ§€κΈ° λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€μ΄ 쑴재.

    νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€(Isolation Level)

    λΉ„μ¦ˆλ‹ˆμŠ€ 둜직과 νŠΈλžœμž­μ…˜ 처리 둜직이 λ™μ‹œμ— μ‘΄μž¬ν•œλ‹€λ©΄ μ½”λ“œμ˜ 쀑볡이 λ°œμƒν•  μˆ˜λ„ 있고, λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—λ§Œ μ§‘μ€‘λœ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κΈ° μ–΄λ ΅λ‹€. μ΄λ ‡κ²Œ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—λ§Œ μ§‘μ€‘ν•˜κΈ° μœ„ν•΄ Spring은 크게 2가지 νŠΈλžœμž­μ…˜ κΈ°μˆ μ„ μ§€μ›ν•œλ‹€. 

     

    1. TransactionTemplate(TransactionManager을 μ£Όμž…λ°›μ•„ μ‚¬μš©)

    데이터 μ ‘κ·Ό κΈ°μˆ μ—λŠ” JdbcTemplate, JPAλ“± μ—¬λŸ¬κ°€μ§€κ°€ μ‘΄μž¬ν•˜λŠ”λ° 이 κΈ°μˆ λ§ˆλ‹€ νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜λŠ” 방법도 달라진닀. λ”°λΌμ„œ Springμ—μ„œλŠ” 이것을 κ³ λ €ν•΄ νŠΈλžœμž­μ…˜μ— λŒ€ν•œ 좔상화λ₯Ό μ§€μ›ν•œλ‹€. PlatformTransactionManager μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•˜λ©΄ λœλ‹€. ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λŠ” 총 3가지 λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

       - getTransaction(): ν˜„μž¬ TransactionStatusλ₯Ό return

       - commit(): λ³€κ²½ λ‚΄μ—­ commit

       - rollback(): λ³€κ²½ λ‚΄μ—­ rollback

    -> TransactionManager 클래슀λ₯Ό μ΄μš©ν•΄ κ°œλ°œμžκ°€ νŠΈλžœμž­μ…˜ μ‹œμž‘/μ’…λ£Œ 지점을 λͺ…μ‹œμ μœΌλ‘œ κ²°μ • κ°€λŠ₯.

     

    νŠΈλžœμž­μ…˜μ€ λ‘œμ§μ„ μˆ˜ν–‰ν•˜κ³  λͺ¨λ“  둜직이 μ„±κ³΅μ μœΌλ‘œ μˆ˜ν–‰λ˜μ—ˆμ„ κ²½μš°μ—λŠ” λͺ¨λ“  κ²°κ³Όλ₯Ό DB에 μΌκ΄„μ μœΌλ‘œ commitν•˜κ³ , ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•œλ‹€λ©΄ λͺ¨λ“  μž‘μ—…μ„ 원상볡ꡬ(rollback)μ‹œν‚¨λ‹€.

    // ν”„λ‘œκ·Έλž˜λ°μ  νŠΈλžœμž­μ…˜ 관리
    public void sendMoney(Long senderId, Long receiverId, Long value) {
    	Connection connection = dataSource.getConnection();
    	connection.setAutoCommit(false);
    
    	try {
        	// ν•œλ²ˆμ— μ²˜λ¦¬λ˜μ–΄μ•Ό ν•˜λŠ” 둜직
            1. 고객이 νŒλ§€μ²˜μ— 돈 보내기
            2. νŒλ§€μ²˜μ—μ„œ 고객이 보낸 돈 λ°›κΈ°
    
            connection.commit();	
        } catch(SQLException e) {
            connection.rollback();
            throw new RemittanceException();
        }
    }
    • Connection 클래슀: Javaμ—μ„œ μ„ λ‚˜νƒ€λ‚΄λŠ” 클래슀. JDBC(Java Database Connectivity) API의 μΌλΆ€λ‘œ 제곡되며, λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ 톡신을 μœ„ν•œ 핡심 역할을 μˆ˜ν–‰.
    • λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ μ—°κ²°
    • tryλ¬Έμ—μ„œ νŠΈλžœμž­μ…˜μ— μ†ν•œ μ—¬λŸ¬ μž‘μ—…λ“€ λͺ¨λ‘κ°€ λ¬Έμ œμ—†μ΄ 처리 μ™„λ£Œλ˜λ©΄, commit.
    • ν•˜λ‚˜μ˜ μž‘μ—…μ—μ„œλΌλ„ 였λ₯˜κ°€ λ°œμƒν•œλ‹€λ©΄ catchλ¬Έμ—μ„œ rollback.
    • 보톡 μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ @Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•˜μ—¬ νŠΈλžœμž­μ…˜ 처리λ₯Ό ν•΄μ£ΌλŠ”λ°, ν…ŒμŠ€νŠΈλ₯Ό ν•˜λŠ” κ²½μš°μ— @Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•΄ Testλ₯Ό ν•΄μ•Όν•˜λŠ” λ©”μ„œλ“œμ˜ Transaction은 λ¬΄μ‹œκ°€ 되기 λ•Œλ¬Έμ— 100% ν…ŒμŠ€νŠΈλ₯Ό ν†΅κ³Όν–ˆλ‹€κ³  ν•˜κΈ΄ μ–΄λ ΅λ‹€. μš”μ•½ν•΄μ„œ λ§ν•˜μžλ©΄ ν…ŒμŠ€νŠΈλ₯Ό ν•  λ•Œ μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•˜λ©΄ νŠΈλžœμž­μ…˜μ— κ΄€λ ¨λœ ν…ŒμŠ€νŠΈλŠ” λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것이닀.

    (https://velog.io/@byeongju/Spring-Transaction에-λŒ€ν•œ-κΈ°λ³Έ-κ°œλ… μ— 더 μžμ„Ένžˆ μ„€λͺ…이 λ˜μ–΄μžˆλ‹€.)

    2. @Transactional μ–΄λ…Έν…Œμ΄μ…˜-AOPλ₯Ό 보μž₯ν•΄μ€€λ‹€!

    DB와 κ΄€λ ¨λœ νŠΈλžœμž­μ…˜μ΄ ν•„μš”ν•œ 클래슀 ν˜Ήμ€ λ©”μ„œλ“œμ— @Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ λ‹¬μ•„μ£ΌκΈ°λ§Œ ν•˜λ©΄λœλ‹€.

    (+ ν΄λž˜μŠ€μ— 달면 ν•΄λ‹Ή 클래슀 및 ν•˜μœ„ ν΄λž˜μŠ€κΉŒμ§€ 적용됨.)

    @Transactional은 ν΄λž˜μŠ€μ™€ λ©”μ„œλ“œ λͺ¨λ‘ 적용 κ°€λŠ₯ν•˜λ‹€. @Transactional 이 μ„ μ–Έλœ 클래슀 μ•ˆμ˜ λ©”μ„œλ“œμ— 또 @Transactionalκ°€ μ„ μ–Έλ˜μ—ˆλ‹€λ©΄, λ©”μ„œλ“œ 레벨의 @Transactional 선언을 μš°μ„  적용. (Spring은 기본적으둜 더 쒁은 λ²”μœ„λ₯Ό μš°μ„ μ‹œ ν•΄μ€Œ)

    @Transactional
    public void buy(Long money) { 
        userDao.loseMoney(money);
        sellerDao.gainMoney(money);
    }

    - loseMoney λ©”μ„œλ“œλ‚˜ gainMoney λ©”μ„œλ“œ 쀑 ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•œλ‹€λ©΄ 전체 μž‘μ—…μ„ μ·¨μ†Œ. 

    - μœ„μ˜ λ©”μ„œλ“œ λͺ¨λ‘ μ„±κ³΅ν•œλ‹€λ©΄ DB에 ν•΄λ‹Ή λ³€κ²½ λ‚΄μ—­ 반영.

    AOP(Aspect-Oriented Programming)

    μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κΈ°λŠ₯은 크게 핡심 κΈ°λŠ₯κ³Ό λΆ€κ°€ κΈ°λŠ₯으둜 λ‚˜λˆŒ 수 μžˆλ‹€.

    - 핡심 κΈ°λŠ₯: ν•΄λ‹Ή 객체가 μ œκ³΅ν•˜λŠ” κ³ μœ ν•œ κΈ°λŠ₯(λΉ„μ¦ˆλ‹ˆμŠ€ 둜직)(ex: UserService - μœ μ € κ΄€λ ¨ 둜직)

    - λΆ€κ°€ κΈ°λŠ₯: 핡심 κΈ°λŠ₯을 λ³΄μ‘°ν•˜λŠ” κΈ°λŠ₯(ex: 둜그 좔적, νŠΈλžœμž­μ…˜ 처리 λ“±)

     

    λΆ€κ°€ κΈ°λŠ₯듀은 μ—¬λŸ¬ μ½”λ“œμ—μ„œ μž¬μ‚¬μš©λ˜λŠ” κ²½μš°κ°€ λ§Žλ‹€. 예λ₯Όλ“€μ–΄ TransactionManagerλ₯Ό μ΄μš©ν•΄ νŠΈλžœμž­μ…˜ 처리λ₯Ό ν•΄μ€€λ‹€λ©΄ ν•œλ²ˆμ— μ²˜λ¦¬λ˜μ–΄μ•Ό ν•˜λŠ” μž‘μ—…λ“€μ΄ μžˆλŠ” λͺ¨λ“  λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ— commit ν•΄μ£Όκ³  rollbackν•΄μ£ΌλŠ” μ½”λ“œλ₯Ό λ°˜λ³΅ν•΄μ„œ μž‘μ„±ν•΄μ£Όμ–΄μ•Όν•œλ‹€. 

    -> μ΄λŸ¬ν•œ λΆ€κ°€ κΈ°λŠ₯듀을 λΆ„λ¦¬ν•˜κ³  어디에 μ μš©ν• μ§€ μ„ νƒν•˜λŠ” κΈ°λŠ₯을 합쳐 ν•˜λ‚˜μ˜ λͺ¨λ“ˆλ‘œ λ§Œλ“ κ²ƒμ΄ Aspect이닀.

    -> μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 바라볼 λ•Œ 핡심 κΈ°λŠ₯에 집쀑할 수 μžˆλ„λ‘ λ„μ™€μ£ΌλŠ” λͺ¨λ“ˆμ΄λΌκ³  μ΄ν•΄ν•˜λ©΄ μ’‹λ‹€.

     

    => @Transactional은 AOPλ₯Ό 지ν–₯ν•  수 μžˆλ„λ‘ μŠ€ν”„λ§μ—μ„œ μ§€μ›ν•΄μ£ΌλŠ” μ–΄λ…Έν…Œμ΄μ…˜μ΄λ‹€.

    ν”„λ‘œκ·Έλž˜λ°μ  νŠΈλžœμž­μ…˜ 관리 vs 선언적 νŠΈλžœμž­μ…˜ 관리

    ν”„λ‘œκ·Έλž˜λ°μ  νŠΈλžœμž­μ…˜ 관리(Programmatic Transaction Management)

    -ex: TransactionManager, TransactionTemplate

    : νŠΈλžœμž­μ…˜ κ΄€λ ¨ μ½”λ“œλ₯Ό 직접 μž‘μ„±ν•˜λŠ” 방법

    선언적 νŠΈλžœμž­μ…˜ 관리(Declarative Transaction Management)

    -ex: @Transactional

    : μŠ€ν”„λ§μ—μ„œ μ œκ³΅ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μ„ μ„ μ–Έν•΄μ£ΌκΈ°λ§Œ ν•˜λ©΄λœλ‹€!

    *μž₯점: ν”„λ‘œκ·Έλž˜λ° νŠΈλžœμž­μ…˜ 관리에 λΉ„ν•΄ κ°„νŽΈ

     

    @Transactioal λ™μž‘ ꡬ쑰

    μŠ€ν”„λ§μ˜ νŠΈλžœμž­μ…˜ AOPλŠ” @Transactional을 μΈμ‹ν•˜μ—¬ νŠΈλžœμž­μ…˜ ν”„λ‘μ‹œλ₯Ό 적용.

    ν”„λ‘μ‹œλŠ” 'λŒ€λ¦¬μΈ'μ΄λΌλŠ” 뜻으둜 Aspect와 @Transactional을 μ μš©ν•œ 클래슀 / λ©”μ„œλ“œμΈ Target을 μ—°κ²°ν•΄μ£ΌλŠ” μ—­ν• 

     

    1. ν΄λΌμ΄μ–ΈνŠΈκ°€ API 호좜

    2. ν”„λ‘μ‹œ μ‹€ν–‰

    3. νŠΈλžœμž­μ…˜ μ½”λ“œ μ‹€ν–‰

    4. λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰

    5. νŠΈλžœμž­μ…˜ μ½”λ“œ μ‹€ν–‰(commit / rollback)

     

    *μŠ€ν”„λ§μ—μ„œ AOPλ₯Ό κ΅¬ν˜„ν•˜λŠ” 방식은 JDK Dynamic Proxy와 CGLIBκ°€ μžˆλ‹€. 두 방법은 μ–΄λ–€ κΈ°μˆ μ„ 톡해 Proxy객체λ₯Ό μƒμ„±ν•˜λŠλƒμ— 차이가 μžˆλŠ”λ°, 더 μžμ„Έν•œ μ •λ³΄λŠ”

    https://velog.io/@byeongju/Spring-Transaction%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90

    에 μžˆμœΌλ‹ˆ μ°Έκ³ ν•˜λ©΄ μ’‹λ‹€.

    References

    https://yeonyeon.tistory.com/223

    https://velog.io/@byeongju/Spring-Transaction%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90

    λ°˜μ‘ν˜•
Designed by Tistory.