BackEnd๐ŸŒฑ/Spring

Spring ํŠธ๋žœ์žญ์…˜์€ ์–ธ์ œ ์–ด๋–ป๊ฒŒ ๋กค๋ฐฑ ๋ ๊นŒ? -1ํŽธ

dkswnkk 2024. 10. 4. 17:21

๊ฐœ์š”

์Šคํ”„๋ง์—์„œ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค. ๋กœ์ง์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉด commit์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋˜๊ณ , ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด rollback์„ ํ†ตํ•ด ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ทจ์†Œ๋œ๋‹ค. ๋‹ค๋งŒ ์ด๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์œผ๋กœ ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋” ๋‚˜์•„๊ฐ€ ์„ธ๋ถ€์ ์œผ๋กœ ๋กค๋ฐฑ์ด ์ •ํ™•ํžˆ ์–ธ์ œ ๋ฐœ์ƒํ•˜๊ณ (1ํŽธ), ํŠน์ • ์ƒํ™ฉ์„ ์˜ˆ์‹œ๋กœ ํ•ด๋‹น ๋กœ์ง์ด ๋กค๋ฐฑ์ด ๋ ์ง€ ์•ˆ ๋ ์ง€์— ๋Œ€ํ•ด ๋งž์ถฐ๋ณด๋Š” ์‹(2ํŽธ)์œผ๋กœ ์•Œ์•„๋ณด๊ณ ์ž ํ•œ๋‹ค.

 

 

Check Exception, Unchecked Exception

๋จผ์ € ์ž๋ฐ”์—์„œ๋Š” ์˜ˆ์™ธ๋ฅผ ํฌ๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Exception๊ณผ Error ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋ˆ„๊ณ  ์žˆ๋‹ค.

Exception & Error ๊ณ„์ธต๊ตฌ์กฐ

Exception์€ ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฑฐ๋‚˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ค‘์— ์ฐธ์กฐ๋œ ๊ฐ’์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ์ธ ์˜ˆ์™ธ๋“ค์„ ์˜๋ฏธํ•˜๋ฉฐ, Error๋Š” OutOfMemmoryError๋‚˜ StackOverFlowError์™€ ๊ฐ™์ด ํ”„๋กœ๊ทธ๋žจ์ด ์ค‘๋‹จ๋  ์ˆ˜๋„ ์žˆ๋Š” ์‹ฌ๊ฐํ•œ ์—๋Ÿฌ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  Exception ๋‚ด๋ถ€์—์„œ๋Š” ํฌ๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ทธ๋ฃน์œผ๋กœ ๊ตฌ๋ถ„ ์ง“๋Š”๋‹ค.

  • Checked Exception: RuntimeException ํด๋ž˜์Šค์™€ ์ „ํ˜€ ๊ด€๊ณ„์—†๋Š” ์˜ˆ์™ธ๋“ค(IoException, FileNotFoundException..)
  • Unchecked Exception: RuntimeException ํด๋ž˜์Šค ๋ฐ ๊ทธ ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š” ์˜ˆ์™ธ๋“ค(NullPointerException, ClassCastException..)

Checked Exception์€ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ๋ฐ˜๋“œ์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์˜ˆ์™ธ๋กœ, ๋งŒ์•ฝ try-catch๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€์— throws๋กœ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š์œผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

unreported exception IOException; must be caught or declared to be thrown

๋ฐ˜๋ฉด Unchecked Exception์€ ์ปดํŒŒ์ผ ์‹œ์ ์ด ์•„๋‹Œ ์‹คํ–‰ ์ค‘์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋กœ, ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ•์ œํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š๋”๋ผ๋„ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค.

JAVA 11์„ ๊ธฐ์ค€์œผ๋กœ Chekced Exception, Unchecked Exception, ๊ทธ๋ฆฌ๊ณ  Error์— ํ•ด๋‹นํ•˜๋Š” ํด๋ž˜์Šค ๋ชฉ๋ก์€ ๋‹ค์Œ ๋ฌธ์„œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

Spring @Transactionl๊ณผ Exception

์ด์ œ ์•ž์—์„œ ์‚ดํŽด๋ณธ CheckedException๊ณผ UncheckedException์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” @Transactional ์• ๋„ˆํ…Œ์ด์…˜์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ๋‹ค.

์Šคํ”„๋ง์—์„œ๋Š” @Transactional ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „ํ›„๋กœ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ จ ๋กœ์ง์„ ์‚ฝ์ž…ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•œ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งํ•˜๋ฉด ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ค‘ Exception์ด ๋ฐœ์ƒํ•˜๋ฉด rollback์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉด commit์ด ์ด๋ฃจ์–ด์ง€๋Š” ๋ฐฉ์‹์ด๋‹ค.

๋‹ค๋งŒ ์—ฌ๊ธฐ์„œ ์ •ํ™•ํ•˜๊ฒŒ ์งš๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ๋ชจ๋“  Exception์— ๋Œ€ํ•ด์„œ ์ „๋ถ€ ๋กค๋ฐฑ์„ ์‹œํ‚ค๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ Error์™€ UncheckedException(์ฆ‰, RuntimeException)์— ๋Œ€ํ•ด์„œ๋งŒ ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜๋ฉฐ, CheckedException์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กค๋ฐฑ๋˜์ง€ ์•Š๋Š”๋‹ค.(https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/rolling-back.html)

๋˜์ ธ์ง„ ์˜ˆ์™ธ๊ฐ€ RuntimeException์˜ ์ธ์Šคํ„ด์Šค ๋˜๋Š” ์„œ๋ธŒํด๋ž˜์Šค์ธ ๊ฒฝ์šฐ(Error๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กค๋ฐฑ๋จ).

 

 

Rollback ๊ณผ์ •

์ด์ œ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๋Š” ๊ณผ์ •์— ๋Œ€ํ•ด ์‚ดํŽด๋ณผ ํ…๋ฐ, ํ•ต์‹ฌ์ด ๋˜๋Š” ๋ถ€๋ถ„๋“ค์€ ์ˆœ์„œ๋Œ€๋กœ ํฌ๊ฒŒ ์•„๋ž˜์˜ ๋„ค ๊ฐ€์ง€ ํ•จ์ˆ˜๋“ค์ด๋‹ค.

  1. TransactionAspectSupport.invokeWithinTransaction()
  2. TransactionAspectSupport.completeTransactionAfterThrowing()
  3. RuleBasedTransactionAttribute.rollbackOn()
  4. DefaultTransactionAttribute.rollbackOn()

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์˜ 5๊ฐ€์ง€ ๋™์ž‘์„ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค.

  1. UncheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘
  2. UncheckedException์„ try-catch๋กœ ์žก์„ ๋•Œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘
  3. CheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘
  4. CheckedException + rollbackFor์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘
  5. UncheckedException + noRollbackFor์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘

 

1. UncheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void processOrders(List<Order> orders) {
        for (Order order : orders) {
            validateOrder(order);
            orderRepository.save(order);
        }
    }

    private void validateOrder(Order order) {
        if (order.getAmount() < 0) {
            throw new IllegalArgumentException("Invalid order amount"); // Unchecked Exception
        }
    }
}

์œ„ ์ฝ”๋“œ์—์„œ processOrders ๋ฉ”์„œ๋“œ๋Š” ์ฃผ๋ฌธ ๋ชฉ๋ก์„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, order.getAmount()๊ฐ€ 0๋ณด๋‹ค ์ž‘์€ ๊ฒฝ์šฐ IllegalArgumentException์ด ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์˜ˆ์™ธ๋Š” UncheckedException์ด๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜์ด ์ž๋™์œผ๋กœ ๋กค๋ฐฑ๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

 

๋‚ด๋ถ€ ๋™์ž‘

์ผ๋‹จ @Transactional์ด ์ ์šฉ๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์Šคํ”„๋ง ๋‚ด๋ถ€์ ์œผ๋กœ TransactionAspectSupport.invokeWithinTransaction() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•˜๊ณ , ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

TransactionAspectSupport.invokeWithinTransaction()

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    
    Object retVal = null;
    try {
        // ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        retVal = invocation.proceedWithInvocation();
    } catch (Throwable ex) {
        // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ ์ฒ˜๋ฆฌ
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // ํŠธ๋žœ์žญ์…˜ ์ •๋ฆฌ ์ž‘์—…
        cleanupTransactionInfo(txInfo);
    }
    commitTransactionAfterReturning(txInfo);
    return retVal;
}

invokeWithinTransaction()๋Š” proceedWithInvocation()์„ ํ˜ธ์ถœํ•ด ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค. ๊ทธ๋ฆฌ๊ณ  try-catch๋ฅผ ํ†ตํ•ด ์ด ๊ณผ์ •์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด completeTransactionAfterThrowing()๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ• ์ง€ ๋˜๋Š” ์ปค๋ฐ‹ํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

TransactionAspectSupport.completeTransactionAfterThrowing()

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        if (txInfo.transactionAttribute.rollbackOn(ex)) { // ๋กค๋ฐฑ ์—ฌ๋ถ€ ํŒ๋‹จ
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        } else {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }
}

completeTransactionAfterThrowing()์—์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ txInfo.transactionAttribute.rollbackOn(ex)๊ฐ€ ์ฃผ์–ด์ง„ ์˜ˆ์™ธ์— ๋”ฐ๋ผ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ• ์ง€ ์ปค๋ฐ‹ํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ด๋•Œ TransactionAttribute์˜ ๊ตฌํ˜„์ฒด๋Š” RuleBasedTransactionAttribute๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.

RuleBasedTransactionAttribute.rollbackOn()

@Override
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null; // ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋กค๋ฐฑ ๊ทœ์น™์„ ๋‹ด์„ ๋ณ€์ˆ˜
    int deepest = Integer.MAX_VALUE; // ๊ณ„์ธต ๊นŠ์ด๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜ (์ž‘์„์ˆ˜๋ก ์ƒ์œ„ ํด๋ž˜์Šค์— ๊ฐ€๊นŒ์›€)
 
    // ๋กค๋ฐฑ ๊ทœ์น™์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ
    if (this.rollbackRules != null) {
        for (RollbackRuleAttribute rule : this.rollbackRules) {
            // ํ˜„์žฌ ์˜ˆ์™ธ์˜ ์ƒ์† ๊ณ„์ธต์—์„œ ์ด ๊ทœ์น™๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
            int depth = rule.getDepth(ex); 
            // ์ผ์น˜ํ•˜๋Š” ๊ทœ์น™์ด ์žˆ์œผ๋ฉด, ํ•ด๋‹น ๊ทœ์น™์˜ ๊นŠ์ด๋ฅผ ํ™•์ธ
            if (depth >= 0 && depth < deepest) {
                // ๋” ์–•์€(๊ฐ€๊นŒ์šด) ๊นŠ์ด์˜ ๊ทœ์น™์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ winner๋กœ ์„ค์ •
                deepest = depth;
                winner = rule;
            }
        }
    }
 
    // winner๊ฐ€ ์—†๋‹ค๋ฉด ๊ธฐ๋ณธ ๊ทœ์น™(UncheckedException ๋˜๋Š” Error์ผ ๋•Œ ๋กค๋ฐฑ)์„ ์ ์šฉ
    if (winner == null) {
        return super.rollbackOn(ex); // ๊ธฐ๋ณธ ๋กค๋ฐฑ ๊ทœ์น™: RuntimeException ๋˜๋Š” Error์ผ ๊ฒฝ์šฐ ๋กค๋ฐฑ
    }
 
    // winner๊ฐ€ NoRollbackRuleAttribute๊ฐ€ ์•„๋‹ˆ๋ฉด ๋กค๋ฐฑ, ๋งž๋‹ค๋ฉด ์ปค๋ฐ‹
    return !(winner instanceof NoRollbackRuleAttribute);
}

๋กค๋ฐฑ์˜ ์—ฌ๋ถ€๋Š” rollbackOn()์—์„œ ๊ฒฐ์ •๋˜๋Š”๋ฐ, rollbackOn()์€ ์Šคํ”„๋ง์€ ์˜ˆ์™ธ ํƒ€์ž…์„ ๊ฒ€์‚ฌํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋ฉฐ ๊ธฐ๋ณธ์ ์œผ๋กœ RuntimeException ๋˜๋Š” Error๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋กค๋ฐฑ์ด ์ˆ˜ํ–‰๋œ๋‹ค. 

์—ฌ๊ธฐ์„œ winner๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค.

  • ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋กค๋ฐฑ ๊ทœ์น™์„ ์ฐพ๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • ์˜ˆ์™ธ์˜ ์ƒ์† ๊ณ„์ธต์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋กค๋ฐฑ ๊ทœ์น™(์ฆ‰, ์˜ˆ์™ธ ๊ณ„์ธต ๊ตฌ์กฐ์ƒ ๊ฐ€์žฅ ์–•์€ ๊นŠ์ด์— ์žˆ๋Š” ๊ทœ์น™)์„ ์„ ํƒํ•œ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์˜ˆ์™ธ๊ฐ€ IllegalArgumentException์ผ ๋•Œ, IllegalArgumentException์— ๋Œ€ํ•œ ๊ทœ์น™์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ๊ทœ์น™์ด winner๊ฐ€ ๋œ๋‹ค. ๊ทœ์น™์ด ์—†๋‹ค๋ฉด ์ƒ์œ„ ํด๋ž˜์Šค์ธ RuntimeException์— ๋Œ€ํ•œ ๊ทœ์น™์„ ์ฐพ๋Š”๋‹ค.

๋งŒ์•ฝ ์šฐ๋ฆฌ ์˜ˆ์‹œ์ฒ˜๋Ÿผ winner๊ฐ€ ์—†์œผ๋ฉด(null์ด๋ฉด) ๊ธฐ๋ณธ์ ์œผ๋กœ UncheckedException(RuntimeException)์ด๋‚˜ Error์— ๋Œ€ํ•ด ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•˜๋Š” super.rollbackOn(ex) ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๋™์ž‘์€ DefaultTransactionAttribute.rollBackOn()์—์„œ ์ฒ˜๋ฆฌ๋œ๋‹ค.

DefaultTransactionAttribute.rollBackOn()

@Override
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error); // UncheckedException ๋˜๋Š” Error ์‹œ ๋กค๋ฐฑ
}

rollbackOn()์€ true๋กœ ๋ฐ˜ํ™˜๋˜๊ณ  ๋”ฐ๋ผ์„œ @Transactional ์‚ฌ์šฉ ์‹œ UncheckedException(RuntimeException)์„ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ์ž๋™์œผ๋กœ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๊ฒŒ ๋œ๋‹ค.

 

2. UncheckedException์„ try-catch๋กœ ์žก์„ ๋•Œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘

์ด๋ฒˆ์—๋Š” @Transactional์„ ์‚ฌ์šฉํ•˜์—ฌ UncheckedException(์ฆ‰, RuntimeException)์ด ๋ฐœ์ƒํ–ˆ์ง€๋งŒ, ์ด๋ฅผ try-catch๋กœ ์ฒ˜๋ฆฌํ–ˆ์„ ๋•Œ ํŠธ๋žœ์žญ์…˜์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void processOrders(List<Order> orders) {
        for (Order order : orders) {
            validateOrderWithTryCatch(order);
            orderRepository.save(order);
        }
    }

    private void validateOrderWithTryCatch(Order order) {
        if (order.getAmount() < 0) {
            try {
                throw new IllegalArgumentException("Invalid order amount"); // Unchecked Exception
            } catch (IllegalArgumentException e) {
                e.printStackTrace(); // ์˜ˆ์™ธ๋ฅผ try-catch๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ณต๊ตฌ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ
            }
        }
    }
}

 

๋‚ด๋ถ€ ๋™์ž‘

1๋ฒˆ๊ณผ ๋™์ผํ•˜๊ฒŒ TransactionAspectSupport.invokeWithinTransaction()๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

TransactionAspectSupport.invokeWithinTransaction()

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    
    Object retVal = null;
    try {
        // ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        retVal = invocation.proceedWithInvocation();
    } catch (Throwable ex) {
        // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ ์ฒ˜๋ฆฌ
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // ํŠธ๋žœ์žญ์…˜ ์ •๋ฆฌ ์ž‘์—…
        cleanupTransactionInfo(txInfo);
    }
    commitTransactionAfterReturning(txInfo); // ์˜ˆ์™ธ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋จ
    return retVal;
}

๋‹ค๋งŒ ์ด๋ฒˆ์—๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์˜ˆ์™ธ๊ฐ€ try-catch๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, invokeWithinTransaction() ๋ฉ”์„œ๋“œ์˜ catch ๋ธ”๋ก์—์„œ ๊ฐ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ completeTransactionAfterThrowing() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š์•„ ํŠธ๋žœ์žญ์…˜์€ ์ •์ƒ์ ์œผ๋กœ ์ปค๋ฐ‹๋œ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ UncheckedException(์ฆ‰, RuntimeException)์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์ด๋ฅผ try-catch๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์€ ๋กค๋ฐฑ๋˜์ง€ ์•Š๊ณ  ์ปค๋ฐ‹๋œ๋‹ค.

 

3. CheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘

CheckedException์€ ์„œ๋‘์—์„œ ์–ธ๊ธ‰ํ–ˆ๋‹ค์‹œํ”ผ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ๋ฐ˜๋“œ์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์˜ˆ์™ธ๋กœ, try-catch๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€์— throws๋กœ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š์œผ๋ฉด ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ฒฐ๋ก ๋จผ์ € ๋ณด๋ฉด ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋“  ๊ฐ„์— ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ๋Š” ํŠธ๋žœ์žญ์…˜์—์„œ CheckedException์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ํŠธ๋žœ์žญ์…˜์€ ๋กค๋ฐฑ๋˜์ง€ ์•Š๊ณ  ์ปค๋ฐ‹๋œ๋‹ค. ๋‹ค๋งŒ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‚ด๋ถ€์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ณผ์ •์€ ๋‹ค๋ฅด๋‹ˆ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์ž.

ChckedException์€ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • try-catch๋กœ ์ฒ˜๋ฆฌ
  • throws๋กœ ์ฒ˜๋ฆฌ

 

try-catch๋กœ ์ฒ˜๋ฆฌ

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void processOrders(List<Order> orders) {
        for (Order order : orders) {
            try {
                validateOrder(order);
                orderRepository.save(order);
            } catch (IOException e) {
                e.printStackTrace(); // CheckedException์„ try-catch๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ
            }
        }
    }

    private void validateOrder(Order order) throws IOException {
        // ์ฃผ๋ฌธ ์„ค๋ช…์ด ์—†์œผ๋ฉด IOException์„ ๋ฐœ์ƒ์‹œํ‚ด (Checked Exception)
        if (order.getDescription() == null || order.getDescription().isEmpty()) {
            throw new IOException("Order description cannot be empty"); // Checked Exception
        }
    }
}

์ด ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘ ๋ฐฉ์‹์€ 2. UncheckedException์„ try-catch๋กœ ์žก์„ ๋•Œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘๊ณผ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

TransactionAspectSupport.invokeWithinTransaction()๊ฐ€ ์‹คํ–‰๋˜๋”๋ผ๋„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์˜ˆ์™ธ๊ฐ€ try-catch๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, invokeWithinTransaction() ๋ฉ”์„œ๋“œ์˜ catch ๋ธ”๋ก์—์„œ ๊ฐ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ completeTransactionAfterThrowing() ๋ฉ”์„œ๋“œ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์œผ๋ฉฐ, ํŠธ๋žœ์žญ์…˜์€ ์ •์ƒ์ ์œผ๋กœ ์ปค๋ฐ‹๋œ๋‹ค.

 

throws๋กœ ์ฒ˜๋ฆฌ

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void processOrders(List<Order> orders) throws IOException {
        for (Order order : orders) {
            validateOrder(order);
            orderRepository.save(order);
        }
    }

    private void validateOrder(Order order) throws IOException {
        // ์ฃผ๋ฌธ ์„ค๋ช…์ด ์—†์œผ๋ฉด IOException์„ ๋ฐœ์ƒ์‹œํ‚ด (Checked Exception)
        if (order.getDescription() == null || order.getDescription().isEmpty()) {
            throw new IOException("Order description cannot be empty"); // Checked Exception
        }
    }
}

์ด๋ฒˆ์—๋Š” ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€์— throws๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์™ธ๋ฅผ ์ƒ์œ„ ๊ณ„์ธต์œผ๋กœ ๊ณ„์†ํ•ด์„œ ๋˜์กŒ๋‹ค. ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ผ ์„œ๋น„์Šค๋งŒ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ ์ปจํŠธ๋กค๋Ÿฌ๊นŒ์ง€ throws๋ฅผ ๋˜์กŒ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์ด๋ฒˆ์—๋Š” IOException์ด TransactionAspectSupport.invokeWithinTransaction() ๋‚ด๋ถ€์˜ try-catch ๋ธ”๋ก์—์„œ ์žกํ˜”๊ธฐ ๋•Œ๋ฌธ์— completeTransactionAfterThrowing()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

TransactionAspectSupport.invokeWithinTransaction()

๊ทธ๋ฆฌ๊ณ  @Transactional์— rollbackFor ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, RuleBasedTransactionAttribute.rollbackRules์—๋„ ์•„๋ฌด ๋กค๋ฐฑ ๊ทœ์น™์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ winner๊ฐ€ null์ด ๋˜์–ด 1. UncheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘๊ณผ ๋™์ผํ•˜๊ฒŒ DefaultTransactionAttribute.rollBackOn()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

DefaultTransactionAttribute.rollBackOn()

๋‹ค๋งŒ ์ด๋ฒˆ์—๋Š” ๋„˜์–ด์˜ค๋Š” ์˜ˆ์™ธ๊ฐ€ RuntimeException์ด๋‚˜ Error ํƒ€์ž…์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— rollbackOn() ๋ฉ”์„œ๋“œ๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜์ง€ ์•Š๊ณ  ์ปค๋ฐ‹๋œ๋‹ค.

 

4. CheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘(rollbakFor ์‚ฌ์šฉ)

์ง€๊ธˆ๊นŒ์ง€ ๋‚ด์šฉ์„ ์ž˜ ์ดํ•ดํ–ˆ๋‹ค๋ฉด, ๋ณ„๋„๋กœ try-catch๋กœ ์žก๋Š” ์ˆœ๊ฐ„ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ TransactionAspectSupport.invokeWithinTransaction() ๋ฉ”์„œ๋“œ์˜ catch ๋ธ”๋ก์—์„œ ์˜ˆ์™ธ๊ฐ€ ๊ฐ์ง€๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— completeTransactionAfterThrowing() ๋ฉ”์„œ๋“œ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ด๋กœ ์ธํ•ด ํŠธ๋žœ์žญ์…˜์€ ์ •์ƒ์ ์œผ๋กœ ์ปค๋ฐ‹๋œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋ฒˆ์—๋Š” throws๋กœ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ๋งŒ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค.

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional(rollbackFor = IOException.class) // rollbackFor ์ง€์ •!
    public void processOrders(List<Order> orders) throws IOException {
        for (Order order : orders) {
            validateOrder(order);
            orderRepository.save(order);
        }
    }

    private void validateOrder(Order order) throws IOException {
        // ์ฃผ๋ฌธ ์„ค๋ช…์ด ์—†์œผ๋ฉด IOException์„ ๋ฐœ์ƒ์‹œํ‚ด (Checked Exception)
        if (order.getDescription() == null || order.getDescription().isEmpty()) {
            throw new IOException("Order description cannot be empty"); // Checked Exception
        }
    }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” @Transactional์—์„œ rollbackFor ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด IOException์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. (@Transactional์—์„œ๋Š” rollbackFor์ด๋ผ๋Š” ํ•„๋“œ๋กœ Throwable์˜ ๋ชจ๋“  ํ•˜์œ„ ์˜ˆ์™ธ ํด๋ž˜์Šค๋“ค์˜ ๋กค๋ฐฑ์„ ์œ ๋ฐœํ•˜๋„๋ก ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. - https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html)

TransactionAspectSupport.invokeWithinTransaction()

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ IOException์ด invokeWithinTransaction() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€ try-catch์— ์žกํ˜”๊ธฐ ๋•Œ๋ฌธ์— completeTransactionAfterThrowing()์ด ํ˜ธ์ถœ๋œ๋‹ค.

RuleBasedTransactionAttribute.rollbackOn()

๋‹ค๋งŒ ์ด๋ฒˆ์—๋Š” @Transactional์— rollbackFor ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— RuleBasedTransactionAttribute.rollbackRules ์—๋Š” IOException์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ winner๊ฐ€ null์ด ๋˜์ง€ ์•Š์•„ rollbackOn์ด true๋กœ ๋ฐ˜ํ™˜๋˜์–ด TransactionAspectSupport.completeTransactionAfterThrowing() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ๋กค๋ฐฑ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ @Transactional(rollbackFor = IOException.class)์„ ์‚ฌ์šฉํ•˜๋ฉด IOException์„ throw ํ–ˆ์„ ๋•Œ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚œ๋‹ค.

 

5. UncheckedException + noRollbackFor์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘

์ด๋ฒˆ์—๋Š” rollbackFor์ด ์•„๋‹Œ noRollbackFor์˜ ์‚ฌ์šฉ ์‹œ ๋™์ž‘ ๊ณผ์ •์ด๋‹ค. 

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional(noRollbackFor = IllegalArgumentException.class) // noRollbackFor ์ง€์ •!
    public void processOrders(List<Order> orders) {
        for (Order order : orders) {
            validateOrder(order);
            orderRepository.save(order);
        }
    }

    private void validateOrder(Order order) {
        // ์ฃผ๋ฌธ ๊ธˆ์•ก์ด 0๋ณด๋‹ค ์ž‘์œผ๋ฉด IllegalArgumentException ๋ฐœ์ƒ (Unchecked Exception)
        if (order.getAmount() < 0) {
            throw new IllegalArgumentException("Order amount cannot be negative"); // Unchecked Exception
        }
    }
}

noRollbackFor์ด ์—†๋”๋ผ๋ฉด ์œ„ ๋กœ์ง์€ ๋กค๋ฐฑ๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ noRollbackFor ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • UncheckedException์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜์ง€ ์•Š๊ณ  ์ปค๋ฐ‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋™์ž‘๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

TransactionAspectSupport.invokeWithinTransaction()

๋‹น์—ฐํ•˜๊ฒŒ๋„ IllegalArgumentException์ด TransactionAspectSupport.invokeWithinTransaction() ๋‚ด๋ถ€์˜ try-catch ๋ธ”๋ก์—์„œ ์žกํ˜”๊ธฐ ๋•Œ๋ฌธ์— completeTransactionAfterThrowing()์ด ํ˜ธ์ถœ๋œ๋‹ค.

RuleBasedTransactionAttribute.rollbackOn()

@Override
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null; // ๋กค๋ฐฑ ๊ทœ์น™์„ ๊ฒฐ์ •ํ•  ๋ณ€์ˆ˜
    int deepest = Integer.MAX_VALUE; // ๊ณ„์ธต ๊นŠ์ด๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜

    if (this.rollbackRules != null) {
        for (RollbackRuleAttribute rule : this.rollbackRules) {
            // ์˜ˆ์™ธ๊ฐ€ ๋กค๋ฐฑ ๊ทœ์น™๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
            int depth = rule.getDepth(ex);
            // ์˜ˆ์™ธ์˜ ๊นŠ์ด๊ฐ€ ๋” ์–•๋‹ค๋ฉด(๊ณ„์ธต์ ์œผ๋กœ ๋” ๊ฐ€๊นŒ์šด ๊ทœ์น™์ด๋ผ๋ฉด) ํ•ด๋‹น ๊ทœ์น™์„ winner๋กœ ์„ค์ •
            if (depth >= 0 && depth < deepest) {
                deepest = depth;
                winner = rule;
            }
        }
    }

    // IllegalArgumentException์— ๋Œ€ํ•œ ๋กค๋ฐฑ ๋ฐฉ์ง€ ๊ทœ์น™์ด ์žˆ์œผ๋ฏ€๋กœ winner๊ฐ€ NoRollbackRuleAttribute
    if (winner == null) {
        return super.rollbackOn(ex); // ๊ธฐ๋ณธ์ ์œผ๋กœ RuntimeException๊ณผ Error์ผ ๋•Œ ๋กค๋ฐฑ
    }

    return !(winner instanceof NoRollbackRuleAttribute); // NoRollbackRuleAttribute๋ฉด false ๋ฐ˜ํ™˜
}

RuleBasedTransactionAttribute.rollbackOn()

๋‹ค๋งŒ ์›๋ž˜๋ผ๋ฉด 1. UncheckedException ๋ฐœ์ƒ ์‹œ ํŠธ๋žœ์žญ์…˜ ๋™์ž‘์ฒ˜๋Ÿผ winner๊ฐ€ null์ด ๋˜์–ด rollbackOn()์ด true๋กœ ๋ฐ˜ํ™˜๋˜์–ด ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๊ฒ ์ง€๋งŒ ์ด๋ฒˆ์—๋Š” rollbackRules์— IllegalArgumentException์— ๋Œ€ํ•œ ๋กค๋ฐฑ ๋ฐฉ์ง€ ๊ทœ์น™์ด ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— rollbackOn() ๋ฉ”์„œ๋“œ๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋กค๋ฐฑ๋˜์ง€ ์•Š๊ณ  ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ @Transactional(noRollbackFor = RuntimeException.class)์„ ์‚ฌ์šฉํ•˜๋ฉด RuntimeException์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๊ณ , ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ๋‹ค.

 

 

์ •๋ฆฌ

Java์˜ ์˜ˆ์™ธ ์ข…๋ฅ˜์™€ Spring์—์„œ @Transactional์ด ์˜ˆ์™ธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋กค๋ฐฑํ•˜๊ฑฐ๋‚˜ ์ปค๋ฐ‹ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์•˜๋‹ค. ์ด ๊ธ€์€ ์ด 2๋ถ€๋กœ ๋‚˜๋‰˜๋ฉฐ, ์ด๋ฒˆ 1ํŽธ์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜๊ณผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ๋™์ž‘ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด์•˜๋‹ค. ๋‹ค์Œ 2ํŽธ์—์„œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ํŠธ๋žœ์žญ์…˜์„ ํ˜ธ์ถœํ•  ๋•Œ, Propagation.REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋“ฑ์„ ์˜ˆ์‹œ๋กœ ๋“ค์–ด ๋” ๋‹ค์–‘ํ•œ ์ƒํ™ฉ๋“ค์„ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด, ํ•ด๋‹น ๋กœ์ง์ด ๋กค๋ฐฑ๋ ์ง€ ์ปค๋ฐ‹๋ ์ง€ ๋งž์ถฐ๋ณด๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค.

์œ„ ๋‚ด์šฉ์˜ ์ „์ฒด์ ์ธ ํ๋ฆ„์€ https://sup2is.github.io/2021/03/04/java-exceptions-and-spring-transactional.html๋ฅผ ๋งŽ์ด ์ฐธ๊ณ ํ–ˆ๋‹ค.