TransactionalEventListener

@TransactionalEventListener

Event๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” @EventListener๋Š” event๋ฅผ publishing ํ•˜๋Š” ์ฝ”๋“œ ์‹œ์ ์— ๋ฐ”๋กœ publishingํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์šฐ๋ฆฌ๋Š” event๋ฅผ ํผ๋ธ”๋ฆฌ์‹ฑ ํ• ๋•Œ๋Š” ๋Œ€๋ถ€๋ถ„ ๋ฉ”์ธ ์ž‘์—…์ด ์•„๋‹Œ ์„œ๋ธŒ์˜ ์ž‘์—…์ด ๋งŽ๊ณ  ๋น„๋™๊ธฐ๋กœ ์ง„ํ–‰ํ•ด๋„ ๋˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋„๋ฉ”์ธ ๋กœ์ง์ธ ๊ฒฝ์šฐ๋„ ์žˆ์ฃ . ์ด๋Ÿด ๊ฒฝ์šฐ ์กฐ๊ธˆ ์• ๋งคํ•ด์ง€๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์ œ๋กœ ์ƒํ™ฉ์„ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์ฝ”๋“œ๋Š” @Transactional๋กœ ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค. 1๋ฒˆ๊ณผ 2๋ฒˆ์ด ์ •์ƒ์ ์œผ๋กœ ๋งˆ๋ฌด๋ฆฌ๋˜๊ณ  3๋ฒˆ์ด ๋ฐœ์ƒํ•˜๋Š” ๋„์ค‘์— ์˜ˆ์™ธ์ฒ˜๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š” ? 3๋ฒˆ์€ ์‹คํŒจํ–ˆ์œผ๋ฉฐ 1๋ฒˆ๋„ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์—ฌ ์žˆ๊ธฐ๋•Œ๋ฌธ์— ์‹คํŒจํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ 2๋ฒˆ์€ rollback์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๊ธฐ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ๋ฐ–์— ์—†๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@Transactional
public void function() {

    reviewRepository.save() // 1. A ์ €์žฅ

    applicationEventPublisher.publishEvent(); // 2. A์— ์˜ํ•œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ

    userRepository.save() // 3. B ์ €์žฅ

}

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ @TransactionEventListener๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. @TransactionEventListener๋Š” Event์˜ ์‹ค์งˆ์ ์ธ ๋ฐœ์ƒ์„ ํŠธ๋žœ์žญ์…˜์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ผ๋Š”๊ฒƒ์ž…๋‹ˆ๋‹ค.

@TransactionalEventListener ์˜ต์…˜

@TransactionalEventListener์„ ์ด์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์˜ ์–ด๋–ค ํƒ€์ด๋ฐ์— ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ง€ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ TransactionPhase์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • AFTER_COMMIT (๊ธฐ๋ณธ๊ฐ’) - ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ๋ฌด๋ฆฌ(commit)๋ฌ์„ ๋•Œ ์ด๋ฒคํŠธ ์‹คํ–‰

  • AFTER_ROLLBACK โ€“ ํŠธ๋žœ์žญ์…˜์ด rollback ๋ฌ์„ ๋•Œ ์ด๋ฒคํŠธ ์‹คํ–‰

  • AFTER_COMPLETION โ€“ ํŠธ๋žœ์žญ์…˜์ด ๋งˆ๋ฌด๋ฆฌ ๋ฌ์„ ๋•Œ(commit or rollback) ์ด๋ฒคํŠธ ์‹คํ–‰

  • BEFORE_COMMIT - ํŠธ๋žœ์žญ์…˜์˜ ์ปค๋ฐ‹ ์ „์— ์ด๋ฒคํŠธ ์‹คํ–‰

์‹ค์Šต ์‚ฌ์ „ ์ค€๋น„

๊ทธ๋ ‡๋‹ค๋ฉด ์‚ฌ์šฉํ•˜๋ฉด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ์‚ฌ์šฉํ•  ์‹ค์Šต ์˜ˆ์ œ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด์ „ ์ฝ”๋“œ์—์„œ Model์€ ๋™์ผํ•˜๋ฉฐ Service์™€ Listener์˜ ์ฝ”๋“œ๋Š” ์ผ๋ถ€ ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„์ด ์žˆ์œผ๋‹ˆ ํ™•์ธํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

Service ์ฝ”๋“œ

์•„๋ž˜๋Š” Service ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. @Transactional์„ ์ด์šฉํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋ฉฐ review์™€ user๋ฅผ DB์— ์ ‘๊ทผํ•˜๋ฉด์„œ ๊ทธ ์‚ฌ์ด์— ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Listener ์ฝ”๋“œ

์•„๋ž˜๋Š” Listener ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. @EventListener ๋Œ€์‹  @TransactionalEventListener์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Model ์ฝ”๋“œ

์•„๋ž˜๋Š” ๋ชจ๋ธ ์ฝ”๋“œ๋กœ ์ด์ „ ์ฝ”๋“œ์™€ ๋‹ฌ๋ผ์ง„์ ์€ ์—†์Šต๋‹ˆ๋‹ค.

์‹ค์Šต

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์‹ค์Šต์„ ์ง„ํ–‰ํ•ด๋ณป๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ์ƒํ™ฉ

์œ„ ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ log๋ฅผ ์‹ฌ์–ด๋†“๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ ์ˆœ์„œ๋ฅผ ๋ณด๋ฉด publishEvent์˜ ์ˆœ๊ฐ„ Event๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Event๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ˆœ๊ฐ„์€ ๋ฐ”๋กœ ๋ฉ”์„œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์„œ ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋˜๋Š” ์ˆœ๊ฐ„์ด๋ผ๋Š” ์‚ฌ์‹ค์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ฐœ์ƒ

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š” ? ์œ„ ๋ฉ”์„œ๋“œ์—์„œ isException์„ true๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋กœ๊ทธ๊ฐ€ ์ฐํž™๋‹ˆ๋‹ค. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜์€ ๋กค๋ฐฑ๋˜๋ฉฐ Event๋„ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ต์…˜ ๋ณ€๊ฒฝ

๋ชจ๋“  ์˜ต์…˜์„ ํ•˜๋‚˜์”ฉ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธํ•  TransactionPhase๋ฅผ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์„ฑ๊ณต์˜ ๊ฒฝ์šฐ 3๊ฐ€์ง€ ํƒ€์ž…์˜ Event๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๊ฒƒ์„ ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด๋ฒคํŠธ๋“ค์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ˆœ์„œ์ ์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋…ธ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. AFTER_COMMIT๊ณผ AFTER_COMPLETION์€ ORDER์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹คํŒจ์˜ ๊ฒฝ์šฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ Event๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์œผ๋ฉฐ ์ˆœ์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค. ์ด ์ˆœ์„œ์—ญ์‹œ ORDER์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Transactional ์—†์„ ๊ฒฝ์šฐ

๋งˆ์ง€๋ง‰์œผ๋กœ๋Š” @Transactional์ด ์—†์„๊ฒฝ์šฐ๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. @TransactionalEventListener๋Š” ํŠธ๋žœ์žญ์…˜์— ์˜์กดํ•˜์—ฌ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ @Trasnactional์ด ์—†์„๋•Œ๋Š” Event๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์˜ค๋Š˜์€ ์ด๋ ‡๊ฒŒ TrnasactionalEventListener์— ๋Œ€ํ•ด์„œ ์ด๋ก ์ ์ธ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์‹ค์Šต์„ ํ†ตํ•ด ์–ด๋–ค ํƒ€์ด๋ฐ์— ์ด๋ฒคํŠธ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š”์ง€ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ฐธ์กฐ

baeldung_spring-events#transaction-bound-events

baeldung_transaction-configuration-with-jpa-and-spring

stackoverflow_transactionaleventlistener-doesnt-works-where-as-eventlistener-works-like-cha

Last updated