๐ŸŒฑ Spring Events๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ํ†ต์‹  ๋งˆ์Šคํ„ฐํ•˜๊ธฐ! ๐Ÿš€

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒฑ Spring Events๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ํ†ต์‹  ๋งˆ์Šคํ„ฐํ•˜๊ธฐ! ๐Ÿš€

 

 

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์˜ค๋Š˜์€ ์ •๋ง ์žฌ๋ฏธ์žˆ๊ณ  ์œ ์šฉํ•œ ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. ๋ฐ”๋กœ Spring Events๋ฅผ ํ™œ์šฉํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ํ†ต์‹ ์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ๐Ÿ˜Ž ์ด ์ฃผ์ œ๋Š” Java ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ํŠนํžˆ ์ค‘์š”ํ•˜๋‹ˆ๊นŒ, ์šฐ๋ฆฌ ํ•จ๊ป˜ ์ฆ๊ฒ๊ฒŒ ๋ฐฐ์›Œ๋ณด์ž๊ณ !

๋จผ์ €, ์šฐ๋ฆฌ๊ฐ€ ์™œ ์ด๋Ÿฐ ๊ฑธ ๋ฐฐ์›Œ์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ? ๐Ÿค” ํ˜„๋Œ€์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋“ค์€ ์ ์  ๋” ๋ณต์žกํ•ด์ง€๊ณ  ์žˆ์–ด. ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์„œ๋กœ ์†Œํ†ตํ•˜๋ฉด์„œ ์ผ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๊ฑฐ๋“ . ๊ทธ๋Ÿฐ๋ฐ ์ด ์†Œํ†ต์„ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๊ฐ€์žฅ ํšจ์œจ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ๋ฐ”๋กœ ์—ฌ๊ธฐ์„œ Spring Events๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๊ฑฐ์•ผ!

Spring Events๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„๋“ค์ด ์„œ๋กœ ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋ฉด์„œ๋„ ํšจ๊ณผ์ ์œผ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์–ด. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ๋“ค์ด ์„œ๋กœ ์—ฐ๊ฒฐ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ. ๐Ÿ˜‰

๐Ÿ”‘ ํ•ต์‹ฌ ํฌ์ธํŠธ: Spring Events๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ ๋ถ€๋ถ„์ด ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฉด์„œ๋„ ํ•„์š”ํ•  ๋•Œ ์„œ๋กœ ์ •๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ์ด๊ฑด ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ๋“ค์ด ์„œ๋กœ์˜ ๋Šฅ๋ ฅ์„ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด!

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Spring Events์˜ ์„ธ๊ณ„๋กœ ๋›ฐ์–ด๋“ค์–ด๋ณผ ์ค€๋น„ ๋์–ด? ์šฐ๋ฆฌ๋Š” ์ด ์—ฌ์ •์„ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‚ด์šฉ๋“ค์„ ๋ฐฐ์šฐ๊ฒŒ ๋  ๊ฑฐ์•ผ:

  • Spring Events์˜ ๊ธฐ๋ณธ ๊ฐœ๋… ๐Ÿ’ก
  • ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž(Publisher)์™€ ๊ตฌ๋…์ž(Listener) ๋งŒ๋“ค๊ธฐ ๐ŸŽญ
  • ๋™๊ธฐ์‹ vs ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ โšก
  • ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ ๋‹ค๋ฃจ๊ธฐ ๐Ÿ’ผ
  • Spring Events์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค ๐Ÿš€
  • ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ๐Ÿ› ๏ธ

ํฅ๋ฏธ์ง„์ง„ํ•˜์ง€ ์•Š์•„? ๊ทธ๋Ÿผ ์ด์ œ ํ•˜๋‚˜์”ฉ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž๊ณ !

๐ŸŒŸ Spring Events์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

์ž, ์นœ๊ตฌ๋“ค! ์ด์ œ Spring Events์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ์ฐจ๋ก€์•ผ. ๐Ÿค“ Spring Events๋Š” ๋ง ๊ทธ๋Œ€๋กœ Spring ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ด๋ฒคํŠธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์•ผ. ์ด๊ฑธ ์ด์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„๋“ค์ด ์„œ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์ง€.

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ผ๊ณ  ๋“ค์–ด๋ดค์–ด? ์ด๊ฑด ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์ด ์ด๋ฒคํŠธ์˜ ๋ฐœ์ƒ๊ณผ ์ฒ˜๋ฆฌ์— ์˜ํ•ด ๊ฒฐ์ •๋˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์ด์•ผ. Spring Events๋„ ์ด ๊ฐœ๋…์„ ๋”ฐ๋ฅด๊ณ  ์žˆ์–ด.

๐ŸŒˆ ๋น„์œ ๋กœ ์ดํ•ดํ•˜๊ธฐ: Spring Events๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ํŒŒํ‹ฐ๋ฅผ ์ƒ์ƒํ•ด๋ณด๋Š” ๊ฑฐ์•ผ. ํŒŒํ‹ฐ์—์„œ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ "์ผ€์ดํฌ ์ž๋ฅด์ž!"๋ผ๊ณ  ์™ธ์น˜๋ฉด(์ด๋ฒคํŠธ ๋ฐœํ–‰), ์ด ์†Œ๋ฆฌ๋ฅผ ๋“ค์€ ์‚ฌ๋žŒ๋“ค(์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ)์ด ๋ชจ์—ฌ๋“ค์–ด ์ผ€์ดํฌ๋ฅผ ๋จน๊ฒ ์ง€? Spring Events๋„ ์ด์™€ ๋น„์Šทํ•ด. ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๋ฉด(์ด๋ฒคํŠธ), ๊ทธ ์ผ์— ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ€๋ถ„๋“ค์ด ๋ฐ˜์‘ํ•˜๋Š” ๊ฑฐ์•ผ.

Spring Events์˜ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ๋“ค์„ ์‚ดํŽด๋ณผ๊นŒ?

  • ์ด๋ฒคํŠธ(Event): ๋ฐœ์ƒํ•œ ์‚ฌ๊ฑด์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์•ผ. ์˜ˆ๋ฅผ ๋“ค๋ฉด, "์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์ž…ํ–ˆ๋‹ค" ๊ฐ™์€ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์ง€.
  • ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž(Event Publisher): ์ด๋ฒคํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‹œ์Šคํ…œ์— ์•Œ๋ฆฌ๋Š” ์—ญํ• ์„ ํ•ด. ๋งˆ์น˜ ํŒŒํ‹ฐ์—์„œ "์ผ€์ดํฌ ์ž๋ฅด์ž!"๋ผ๊ณ  ์™ธ์น˜๋Š” ์‚ฌ๋žŒ์ด์•ผ.
  • ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ(Event Listener): ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐ˜์‘ํ•˜๋Š” ๊ฐ์ฒด์•ผ. ์ผ€์ดํฌ๋ฅผ ๋จน์œผ๋Ÿฌ ๋ชจ์—ฌ๋“  ์‚ฌ๋žŒ๋“ค์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.

์ด ์„ธ ๊ฐ€์ง€๋งŒ ์žˆ์œผ๋ฉด ์šฐ๋ฆฌ๋Š” Spring Events๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜ƒ

๊ทธ๋Ÿฐ๋ฐ ๋ง์ด์•ผ, Spring Events๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ค ์žฅ์ ์ด ์žˆ์„๊นŒ? ๐Ÿค”

  1. ๋Š์Šจํ•œ ๊ฒฐํ•ฉ(Loose Coupling): ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„๋“ค์ด ์„œ๋กœ ์ง์ ‘์ ์œผ๋กœ ์˜์กดํ•˜์ง€ ์•Š์•„๋„ ๋ผ. ์ด๊ฑด ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ ์ œ๊ณต์ž์™€ ๊ตฌ๋งค์ž๊ฐ€ ์ง์ ‘ ๋งŒ๋‚˜์ง€ ์•Š์•„๋„ ์„œ๋น„์Šค๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด.
  2. ํ™•์žฅ์„ฑ(Scalability): ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์„ ๋•Œ, ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ํฌ๊ฒŒ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์ƒˆ๋กœ์šด ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด.
  3. ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ(Separation of Concerns): ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ์˜ ์ฃผ์š” ๊ธฐ๋Šฅ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์–ด. ๋‹ค๋ฅธ ๋ถ€๋ถ„์— ์–ด๋–ค ์˜ํ–ฅ์„ ๋ฏธ์น ์ง€ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์ง€.
  4. ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ(Testability): ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ์€ ๊ฐ ๋ถ€๋ถ„์„ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์›Œ.

์™€! ๋ฒŒ์จ๋ถ€ํ„ฐ Spring Events๊ฐ€ ์–ผ๋งˆ๋‚˜ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ธ์ง€ ๋Š๊ปด์ง€์ง€ ์•Š์•„? ๐Ÿ˜ฒ

Spring Events ๊ธฐ๋ณธ ๊ตฌ์กฐ Spring Application Event Publisher Event Listener Event

์ด ๊ทธ๋ฆผ์„ ๋ณด๋ฉด Spring Events์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ํ•œ๋ˆˆ์— ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด. Event Publisher๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด, ๊ทธ ์ด๋ฒคํŠธ๋Š” Spring Application ๋‚ด์—์„œ ์ „๋‹ฌ๋˜์–ด Event Listener์—๊ฒŒ ๋„์ฐฉํ•ด. ๊ทธ๋Ÿฌ๋ฉด Listener๋Š” ๊ทธ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฑฐ์ง€.

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” Spring Events์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์•Œ๊ฒŒ ๋์–ด. ๐Ÿ‘ ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ๊ฐœ๋…์„ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ์ค€๋น„๋์ง€? ๊ณ„์† ๊ฐ€๋ณด์ž๊ณ ! ๐Ÿš€

๐ŸŽญ ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž(Publisher)์™€ ๊ตฌ๋…์ž(Listener) ๋งŒ๋“ค๊ธฐ

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด Spring Events์˜ ๊ฐœ๋…์„ ์‹ค์ œ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด๋ณผ ์‹œ๊ฐ„์ด์•ผ. ๐Ÿ˜ƒ ์šฐ๋ฆฌ๋Š” ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž(Publisher)์™€ ๊ตฌ๋…์ž(Listener)๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ๊ฑฐ์•ผ. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์‚ฌ๋žŒ๊ณผ ๊ทธ ์žฌ๋Šฅ์„ ํ•„์š”๋กœ ํ•˜๋Š” ์‚ฌ๋žŒ์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ!

1. ์ด๋ฒคํŠธ(Event) ํด๋ž˜์Šค ๋งŒ๋“ค๊ธฐ

๋จผ์ €, ์šฐ๋ฆฌ์˜ ์ด๋ฒคํŠธ๋ฅผ ์ •์˜ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ด. ์ด ํด๋ž˜์Šค๋Š” ์ด๋ฒคํŠธ์™€ ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์„ ๊ฑฐ์•ผ.


import org.springframework.context.ApplicationEvent;

public class UserRegisteredEvent extends ApplicationEvent {
    private final String username;

    public UserRegisteredEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

์—ฌ๊ธฐ์„œ UserRegisteredEvent๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋ก๋์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋‚˜ํƒ€๋‚ด. ์ด ์ด๋ฒคํŠธ๋Š” ๋“ฑ๋ก๋œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์„ ๋‹ด๊ณ  ์žˆ์–ด.

2. ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž(Publisher) ๋งŒ๋“ค๊ธฐ

๋‹ค์Œ์œผ๋กœ, ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ๊ฑฐ์•ผ. ์ด ํด๋ž˜์Šค๋Š” ApplicationEventPublisher๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•ด.


import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void registerUser(String username) {
        // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ๋กœ์ง...
        System.out.println("User registered: " + username);

        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
    }
}

์ด UserService ํด๋ž˜์Šค๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด. ์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋ก๋˜๋ฉด, ์ด ๋ฉ”์„œ๋“œ๋Š” UserRegisteredEvent๋ฅผ ๋ฐœํ–‰ํ•ด. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ƒˆ๋กœ์šด ์žฌ๋Šฅ์ด ๋“ฑ๋ก๋๋‹ค๊ณ  ์•Œ๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด!

3. ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ(Listener) ๋งŒ๋“ค๊ธฐ

๋งˆ์ง€๋ง‰์œผ๋กœ, ์šฐ๋ฆฌ์˜ ์ด๋ฒคํŠธ๋ฅผ ๋“ฃ๊ณ  ๋ฐ˜์‘ํ•  ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ๊ฑฐ์•ผ.


import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class UserRegistrationListener {

    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        System.out.println("Received spring event - user registered: " + event.getUsername());
        // ์—ฌ๊ธฐ์— ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์„ ๋„ฃ์„ ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค๋ฉด:
        // - ํ™˜์˜ ์ด๋ฉ”์ผ ๋ณด๋‚ด๊ธฐ
        // - ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ
        // - ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ๋“ฑ๋“ฑ
    }
}

์ด UserRegistrationListener ํด๋ž˜์Šค๋Š” @EventListener ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ UserRegisteredEvent๋ฅผ ๋“ฃ๊ณ  ์žˆ์–ด. ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, handleUserRegisteredEvent ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ผ.

๐ŸŽ‰ ์ถ•ํ•˜ํ•ด! ์ด์ œ ๋„ˆ๋Š” Spring Events์˜ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋์–ด. ์ด ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, UserService(๋ฐœํ–‰์ž)์™€ UserRegistrationListener(๊ตฌ๋…์ž)๋Š” ์„œ๋กœ ์ง์ ‘์ ์œผ๋กœ ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ํšจ๊ณผ์ ์œผ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฒŒ ๋ฐ”๋กœ Spring Events์˜ ๊ฐ•๋ ฅํ•œ ์ ์ด์•ผ!

์ž, ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๊ตฌ์กฐ๋ฅผ ์‹œ๊ฐํ™”ํ•ด๋ณผ๊นŒ?

Spring Events ๊ตฌํ˜„ ๊ตฌ์กฐ UserService UserRegisteredEvent UserRegistrationListener publishes listens to

์ด ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด๋ฉด, Spring Events์˜ ํ๋ฆ„์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด:

  1. UserService๊ฐ€ UserRegisteredEvent๋ฅผ ๋ฐœํ–‰ํ•ด.
  2. UserRegisteredEvent๋Š” Spring Application Context๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋ผ.
  3. UserRegistrationListener๊ฐ€ ์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•ด.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด UserService๋Š” ๋ˆ„๊ฐ€ ์ด ์ด๋ฒคํŠธ๋ฅผ ๋“ฃ๊ณ  ์žˆ๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์–ด. ๊ทธ์ € ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ผ. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ UserRegistrationListener๋„ ์ด ์ด๋ฒคํŠธ๊ฐ€ ์–ด๋””์„œ ์™”๋Š”์ง€ ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์–ด. ๊ทธ์ € ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ผ.

์ด๋Ÿฐ ๊ตฌ์กฐ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋” ์œ ์—ฐํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค˜. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‚˜์ค‘์— ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์‹œ ์ถ”๊ฐ€์ ์ธ ์ž‘์—…(์˜ˆ: ํ™˜์˜ ์ด๋ฉ”์ผ ๋ณด๋‚ด๊ธฐ)์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, UserService๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š” ์—†์ด ์ƒˆ๋กœ์šด ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ผ!

๐Ÿ’ก Pro Tip: Spring Events๋Š” ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์ •๋ง ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ƒˆ๋กœ์šด ์žฌ๋Šฅ์ด ๋“ฑ๋ก๋์„ ๋•Œ ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ฑฐ๋‚˜, ๊ฑฐ๋ž˜๊ฐ€ ์„ฑ์‚ฌ๋์„ ๋•Œ ๋‹ค์–‘ํ•œ ํ›„์† ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€. ์ด๋Ÿฐ ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ํ”Œ๋žซํผ์˜ ๊ฐ ๋ถ€๋ถ„์ด ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฉด์„œ๋„ ํšจ๊ณผ์ ์œผ๋กœ ํ˜‘๋ ฅํ•  ์ˆ˜ ์žˆ์–ด!

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” Spring Events์˜ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋์–ด. ๐Ÿ‘ ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ๋™๊ธฐ์‹๊ณผ ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๊ณ„์† ๋”ฐ๋ผ์™€! ๐Ÿš€

โšก ๋™๊ธฐ์‹ vs ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์ด๋ฒˆ์—๋Š” Spring Events์˜ ์•„์ฃผ ์ค‘์š”ํ•œ ์ฃผ์ œ์ธ ๋™๊ธฐ์‹๊ณผ ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๐Ÿ˜Ž ์ด ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

๋จผ์ € ๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ถ€ํ„ฐ ์‚ดํŽด๋ณผ๊นŒ? ๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Spring Events๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ.

๐Ÿ” ๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ๋ž€? ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ชจ๋“  ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ๊นŒ์ง€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐฉ์‹์ด์•ผ. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์žฌ๋Šฅ ๊ฑฐ๋ž˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, ๊ฒฐ์ œ, ์•Œ๋ฆผ, ๋ฆฌ๋ทฐ ์š”์ฒญ ๋“ฑ์˜ ๋ชจ๋“  ๊ณผ์ •์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด.

๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ์˜ ์žฅ๋‹จ์ ์„ ์‚ดํŽด๋ณผ๊นŒ?

  • ์žฅ์ :
    • ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋ผ. ๋ชจ๋“  ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๋‹ˆ๊นŒ.
    • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์›Œ. ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์ด๋ฃจ์–ด์ง€๋‹ˆ๊นŒ.
    • ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ„๋‹จํ•ด. ์–ด๋–ค ๋ฆฌ์Šค๋„ˆ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ.
  • ๋‹จ์ :
    • ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์–ด. ๋ชจ๋“  ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ๋งˆ์น  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋‹ˆ๊นŒ.
    • ๋ธ”๋กœํ‚น์ด ๋ฐœ์ƒํ•ด. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ค‘์—๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ.

์ž, ์ด์ œ ๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ์˜ ์ฝ”๋“œ๋ฅผ ํ•œ๋ฒˆ ๋ณผ๊นŒ?


@Component
public class SynchronousEventListener {

    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        System.out.println("Synchronously handling user registration: " + event.getUsername());
        // ์—ฌ๊ธฐ์„œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.
        try {
            Thread.sleep(2000);  // 2์ดˆ ๋™์•ˆ ๋Œ€๊ธฐ
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Finished handling user registration for: " + event.getUsername());
    }
}

์ด ์ฝ”๋“œ์—์„œ๋Š” @EventListener ์–ด๋…ธํ…Œ์ด์…˜๋งŒ ์‚ฌ์šฉํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋ผ. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— 2์ดˆ๊ฐ€ ๊ฑธ๋ฆฐ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ๋Š”๋ฐ, ์ด ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์€ ๋ธ”๋กœํ‚น๋ผ.

๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

์ด๋ฒˆ์—” ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ์•Œ์•„๋ณผ ์ฐจ๋ก€์•ผ! ๐Ÿ˜ƒ

๐Ÿš€ ๋น„๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ๋ž€? ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ฆฌ์Šค๋„ˆ๋“ค์ด ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด. ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•œ ์Šค๋ ˆ๋“œ๋Š” ๋ฆฌ์Šค๋„ˆ๋“ค์˜ ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์œผ๋กœ ๋น„์œ ํ•˜์ž๋ฉด, ์žฌ๋Šฅ ๊ฑฐ๋ž˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๊ฒฐ์ œ, ์•Œ๋ฆผ, ๋ฆฌ๋ทฐ ์š”์ฒญ ๋“ฑ์˜ ๊ณผ์ •์ด ๋™์‹œ์— ์ง„ํ–‰๋˜๋Š” ๊ฑฐ์•ผ!

๋น„๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ์˜ ์žฅ๋‹จ์ ๋„ ์‚ดํŽด๋ณผ๊นŒ?

  • ์žฅ์ :
    • ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ผ. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์™€ ๋‹ค๋ฅธ ์ž‘์—…์ด ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜๋‹ˆ๊นŒ.
    • ์‘๋‹ต์„ฑ์ด ์ข‹์•„์ ธ. ์ด๋ฒคํŠธ ๋ฐœํ–‰ ํ›„ ๋ฐ”๋กœ ๋‹ค์Œ ์ž‘์—…์œผ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ.
    • ํ™•์žฅ์„ฑ์ด ์ข‹์•„. ๋” ๋งŽ์€ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•ด๋„ ์ „์ฒด ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์•„.
  • ๋‹จ์ :
    • ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š์•„. ์–ด๋–ค ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋จผ์ € ์‹คํ–‰๋ ์ง€ ์•Œ ์ˆ˜ ์—†์–ด.
    • ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด. ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰๋˜๋Š” ๋ฆฌ์Šค๋„ˆ์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ๊ณ ๋ฏผํ•ด์•ผ ํ•ด.
    • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ. ๊ฐ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋ณ„๋„์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ์‹คํ–‰๋˜๋‹ˆ๊นŒ.

์ž, ์ด์ œ ๋น„๋™๊ธฐ์‹ ์ฒ˜๋ฆฌ์˜ ์ฝ”๋“œ๋ฅผ ๋ณผ๊นŒ?


@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

@Component
public class AsynchronousEventListener {

    @Async
    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        System.out.println("Asynchronously handling user registration: " + event.getUsername());
        // ์—ฌ๊ธฐ์„œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.
        try {
              Thread.sleep(2000);  // 2์ดˆ ๋™์•ˆ ๋Œ€๊ธฐ
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Finished asynchronously handling user registration for: " + event.getUsername());
    }
}

์—ฌ๊ธฐ์„œ๋Š” @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด ๋ฆฌ์Šค๋„ˆ๋Š” ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋ผ. ๊ทธ๋ฆฌ๊ณ  @EnableAsync๋กœ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ , TaskExecutor๋ฅผ ์„ค์ •ํ•ด์„œ ์Šค๋ ˆ๋“œ ํ’€์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์–ด.

๋™๊ธฐ์‹ vs ๋น„๋™๊ธฐ์‹: ์–ธ์ œ ๋ฌด์—‡์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

์ž, ์ด์ œ ๋‘ ๋ฐฉ์‹์„ ๋‹ค ๋ฐฐ์› ์œผ๋‹ˆ ์–ธ์ œ ์–ด๋–ค ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ์ง€ ์•Œ์•„๋ณด์ž!

๐Ÿค” ๋™๊ธฐ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ:
  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•  ๋•Œ
  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์ฆ‰์‹œ ํ•„์š”ํ•  ๋•Œ
  • ํŠธ๋žœ์žญ์…˜ ์ผ๊ด€์„ฑ์ด ์ค‘์š”ํ•  ๋•Œ
  • ์—๋Ÿฌ๋ฅผ ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋•Œ
๐Ÿš€ ๋น„๋™๊ธฐ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ:
  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ๋•Œ
  • ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • ์ด๋ฒคํŠธ ๋ฐœํ–‰์ž์˜ ์‘๋‹ต์„ฑ์„ ๋†’์ด๊ณ  ์‹ถ์„ ๋•Œ
  • ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์ฆ‰์‹œ ํ•„์š”ํ•˜์ง€ ์•Š์„ ๋•Œ

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ๋Š” ๋™๊ธฐ์‹์œผ๋กœ, ์•Œ๋ฆผ ๋ฐœ์†ก์€ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์„ ๊ฑฐ์•ผ. ๊ฒฐ์ œ๋Š” ์ฆ‰์‹œ ์ฒ˜๋ฆฌ๋˜๊ณ  ํ™•์ธ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ, ์•Œ๋ฆผ์€ ์กฐ๊ธˆ ๋Šฆ๊ฒŒ ๋ฐœ์†ก๋˜์–ด๋„ ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋‹ˆ๊นŒ!

๋™๊ธฐ์‹ vs ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ๋ฆฌ์Šค๋„ˆ 1 ์ฒ˜๋ฆฌ ๋ฆฌ์Šค๋„ˆ 2 ์ฒ˜๋ฆฌ ๋ฆฌ์Šค๋„ˆ 3 ์ฒ˜๋ฆฌ ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ๋ฆฌ์Šค๋„ˆ 1 ์ฒ˜๋ฆฌ ๋ฆฌ์Šค๋„ˆ 2 ์ฒ˜๋ฆฌ ๋ฆฌ์Šค๋„ˆ 3 ์ฒ˜๋ฆฌ

์ด ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ๋™๊ธฐ์‹๊ณผ ๋น„๋™๊ธฐ์‹์˜ ์ฐจ์ด๋ฅผ ํ•œ๋ˆˆ์— ์•Œ ์ˆ˜ ์žˆ์–ด. ๋™๊ธฐ์‹์—์„œ๋Š” ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€์ง€๋งŒ, ๋น„๋™๊ธฐ์‹์—์„œ๋Š” ์ด๋ฒคํŠธ ๋ฐœํ–‰ ํ›„ ๋ชจ๋“  ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋™์‹œ์— ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์ง€.

๐Ÿ’ก Pro Tip: ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋™๊ธฐ์‹๊ณผ ๋น„๋™๊ธฐ์‹์„ ์ ์ ˆํžˆ ํ˜ผํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„. ์ค‘์š”ํ•˜๊ณ  ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์€ ๋™๊ธฐ์‹์œผ๋กœ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ฒ˜๋ฆฌํ•ด๋„ ๋˜๋Š” ์ž‘์—…์€ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹์ด์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‘๋‹ต์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋ชจ๋‘ ์žก์„ ์ˆ˜ ์žˆ์–ด!

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋™๊ธฐ์‹๊ณผ ๋น„๋™๊ธฐ์‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์˜ ์ฐจ์ด์™€ ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ , ๊ทธ๋ฆฌ๊ณ  ์–ธ์ œ ์–ด๋–ค ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ๊ฒŒ ๋์–ด. ๐Ÿ‘ ์ด ์ง€์‹์„ ํ™œ์šฉํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ์ด๊ฑด ์ •๋ง ์žฌ๋ฏธ์žˆ๊ณ  ์œ ์šฉํ•œ ์ฃผ์ œ๋‹ˆ๊นŒ ๊ธฐ๋Œ€ํ•ด๋„ ์ข‹์•„! ๊ณ„์† ๋”ฐ๋ผ์™€! ๐Ÿš€

๐Ÿ’ผ ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ ๋‹ค๋ฃจ๊ธฐ

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์ด๋ฒˆ์—๋Š” Spring Events์˜ ์•„์ฃผ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ธ ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๐Ÿ˜Ž ์ด ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ๋”์šฑ ์ž˜ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ๋ž€?

๐Ÿ” ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ๋ž€? ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ์˜ ๋ฐœํ–‰ ์—ฌ๋ถ€๊ฐ€ ๊ฒฐ์ •๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋งํ•ด. ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœํ–‰๋˜๊ณ , ์‹คํŒจํ•˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœํ–‰๋˜์ง€ ์•Š์•„. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๊ฒฐ์ œ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์—๋งŒ ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด!

์ด ๊ธฐ๋Šฅ์ด ์™œ ์ค‘์š”ํ• ๊นŒ? ๐Ÿค” ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž ๋“ฑ๋ก ๊ณผ์ •์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ์ด ์‹คํŒจํ•˜๋ฉด ํ™˜์˜ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด์ง€ ์•Š์•„์•ผ ํ•ด. ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ์ƒํ™ฉ์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด!

ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ

์ž, ์ด์ œ ์ฝ”๋“œ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ๋ณผ๊นŒ?


import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
public class UserService {

    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public void registerUser(String username) {
        // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ๋กœ์ง...
        System.out.println("User registered: " + username);

        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
    }
}

@Component
public class TransactionalEventListenerExample {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        System.out.println("Handling user registration after transaction commit: " + event.getUsername());
        // ์—ฌ๊ธฐ์„œ ํ™˜์˜ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ›„์† ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด.
    }
}

์ด ์ฝ”๋“œ์—์„œ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ์ ์€ @TransactionalEventListener ์–ด๋…ธํ…Œ์ด์…˜์ด์•ผ. ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ํŠธ๋žœ์žญ์…˜์˜ ํŠน์ • ๋‹จ๊ณ„์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์ง€์ •ํ•ด. ์—ฌ๊ธฐ์„œ๋Š” AFTER_COMMIT์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ด๋Š” ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ ํ›„์— ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ๋œป์ด์•ผ.

ํŠธ๋žœ์žญ์…˜ ๋‹จ๊ณ„ (TransactionPhase)

@TransactionalEventListener๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ๋‹จ๊ณ„๋ฅผ ์ง€์›ํ•ด:

  • AFTER_COMMIT (๊ธฐ๋ณธ๊ฐ’): ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ ํ›„
  • AFTER_ROLLBACK: ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋œ ํ›„
  • AFTER_COMPLETION: ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋œ ํ›„ (์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑ)
  • BEFORE_COMMIT: ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜๊ธฐ ์ง์ „
๐Ÿ’ก Pro Tip: BEFORE_COMMIT์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ด. ์ด ๋‹จ๊ณ„์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ!

ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์˜ ์žฅ์ 

  1. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€: ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋งŒ ์ด๋ฒคํŠธ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ, ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด.
  2. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๊ฐ„์†Œํ™”: ํŠธ๋žœ์žญ์…˜ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ์ทจ์†Œ๋˜๋ฏ€๋กœ, ๋ณ„๋„์˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋กœ์ง์ด ํ•„์š” ์—†์–ด.
  3. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ: ์ฃผ์š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ€๊ฐ€์ ์ธ ์ž‘์—…(์˜ˆ: ์•Œ๋ฆผ ๋ฐœ์†ก)์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
  4. ์„ฑ๋Šฅ ์ตœ์ ํ™”: AFTER_COMMIT์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์™„๋ฃŒ ํ›„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ์ „์ฒด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด.

์ž, ์ด์ œ ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์‹œ๊ฐํ™”ํ•ด๋ณผ๊นŒ?

ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ ํ๋ฆ„ ํŠธ๋žœ์žญ์…˜ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

์ด ๋‹ค์ด์–ด๊ทธ๋žจ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ์ด๋ฒคํŠธ๋Š” ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๋ฐœํ–‰๋˜์ง€๋งŒ, ์‹ค์ œ ์ฒ˜๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋œ ํ›„์— ์ด๋ฃจ์–ด์ ธ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ์‹คํŒจํ–ˆ์„ ๋•Œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋„ ์ž๋™์œผ๋กœ ์ทจ์†Œ๋˜๋Š” ๊ฑฐ์ง€!

๐ŸŽ‰ ์‹ค์ „ ํŒ: ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ์œ ์šฉํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด:
  • ๊ฑฐ๋ž˜ ์™„๋ฃŒ ํ›„ ํŒ๋งค์ž์—๊ฒŒ ์ •์‚ฐ ์ฒ˜๋ฆฌ ์ด๋ฒคํŠธ ๋ฐœํ–‰
  • ์ƒˆ๋กœ์šด ์žฌ๋Šฅ ๋“ฑ๋ก ํ›„ ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์•Œ๋ฆผ ์ด๋ฒคํŠธ ๋ฐœํ–‰
  • ์‚ฌ์šฉ์ž ๋ฆฌ๋ทฐ ์ž‘์„ฑ ํ›„ ํŒ๋งค์ž ํ‰์  ์—…๋ฐ์ดํŠธ ์ด๋ฒคํŠธ ๋ฐœํ–‰
์ด๋Ÿฐ ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด!

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์˜ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์žฅ์ ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด. ๐Ÿ‘ ์ด ๊ธฐ๋Šฅ์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•œ์ธต ๋” ๋†’์ผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” Spring Events์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๊ณ„์† ๋”ฐ๋ผ์™€! ๐Ÿš€

๐Ÿš€ Spring Events์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” Spring Events์˜ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•๊ณผ ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด. ์ด์ œ ๋” ๊นŠ์ด ๋“ค์–ด๊ฐ€์„œ, Spring Events์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์‚ดํŽด๋ณผ ๊ฑฐ์•ผ. ์ด ๊ธฐ๋Šฅ๋“ค์„ ๋งˆ์Šคํ„ฐํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ํ•œ์ธต ๋” ๊ฐ•๋ ฅํ•ด์งˆ ๊ฑฐ์•ผ! ๐Ÿ˜Ž

1. ์กฐ๊ฑด๋ถ€ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

๋•Œ๋กœ๋Š” ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด. Spring์€ ์ด๋ฅผ ์œ„ํ•ด @EventListener์˜ condition ์†์„ฑ์„ ์ œ๊ณตํ•ด.


@EventListener(condition = "#event.username.startsWith('admin')")
public void handleAdminUserRegisteredEvent(UserRegisteredEvent event) {
    System.out.println("Admin user registered: " + event.getUsername());
}

์ด ์ฝ”๋“œ๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„์ด 'admin'์œผ๋กœ ์‹œ์ž‘ํ•  ๋•Œ๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด. ์ด๋Ÿฐ ๊ธฐ๋Šฅ์€ ์žฌ๋Šฅ๋„ท์—์„œ ํŠน๋ณ„ํ•œ ์œ ํ˜•์˜ ์‚ฌ์šฉ์ž๋‚˜ ์žฌ๋Šฅ์— ๋Œ€ํ•ด์„œ๋งŒ ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•  ๊ฑฐ์•ผ.

2. ์ด๋ฒคํŠธ ์ˆœ์„œ ์ง€์ •

์—ฌ๋Ÿฌ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๊ฐ™์€ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ, ์ฒ˜๋ฆฌ ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์–ด. ์ด๋•Œ๋Š” @Order ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.


@EventListener
@Order(1)
public void handleUserRegisteredEventFirst(UserRegisteredEvent event) {
    System.out.println("First listener: " + event.getUsername());
}

@EventListener
@Order(2)
public void handleUserRegisteredEventSecond(UserRegisteredEvent event) {
    System.out.println("Second listener: " + event.getUsername());
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฒซ ๋ฒˆ์งธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ํ•ญ์ƒ ๋‘ ๋ฒˆ์งธ ๋ฆฌ์Šค๋„ˆ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋ผ. ์žฌ๋Šฅ๋„ท์—์„œ ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์‚ฌ์šฉ์ž ๋“ฑ๋ก ํ›„ ๋จผ์ € ํ™˜์˜ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๊ณ , ๊ทธ ๋‹ค์Œ์— ์ถ”์ฒœ ์žฌ๋Šฅ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•˜๋Š” ์‹์œผ๋กœ ์ˆœ์„œ๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€?

3. ์ œ๋„ค๋ฆญ ์ด๋ฒคํŠธ

๋•Œ๋กœ๋Š” ์—ฌ๋Ÿฌ ์œ ํ˜•์˜ ์ด๋ฒคํŠธ๋ฅผ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ ์ œ๋„ค๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ ์ค‘๋ณต์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด.


public class EntityCreatedEvent<t> {
    private final T entity;

    public EntityCreatedEvent(T entity) {
        this.entity = entity;
    }

    public T getEntity() {
        return entity;
    }
}

@EventListener
public void handleEntityCreatedEvent(EntityCreatedEvent> event) {
    System.out.println("Entity created: " + event.getEntity());
}
</t>

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž ์ƒ์„ฑ, ์žฌ๋Šฅ ๋“ฑ๋ก, ๊ฑฐ๋ž˜ ์™„๋ฃŒ ๋“ฑ ๋‹ค์–‘ํ•œ "์ƒ์„ฑ" ์ด๋ฒคํŠธ๋ฅผ ํ•˜๋‚˜์˜ ๋ฆฌ์Šค๋„ˆ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•ด์ง€๊ฒ ์ง€?

4. ์ด๋ฒคํŠธ ๋ฐœํ–‰ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

๋•Œ๋กœ๋Š” ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐœํ–‰์ž์—๊ฒŒ ๋Œ๋ ค์ฃผ๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด. Spring 4.2๋ถ€ํ„ฐ๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด.


@EventListener
public String handleUserRegisteredEvent(UserRegisteredEvent event) {
    String welcomeMessage = "Welcome, " + event.getUsername() + "!";
    System.out.println(welcomeMessage);
    return welcomeMessage;
}

// ์ด๋ฒคํŠธ ๋ฐœํ–‰
List<string> results = (List<string>) eventPublisher.publishEvent(new UserRegisteredEvent(this, "newuser"));
System.out.println("Event processing results: " + results);
</string></string>

์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ฆ‰์‹œ ํ™•์ธํ•˜๊ณ  ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์—์„œ ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์ƒˆ ์žฌ๋Šฅ ๋“ฑ๋ก ์‹œ ๊ด€๋ จ ํƒœ๊ทธ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€?

5. ์—๋Ÿฌ ํ•ธ๋“ค๋ง

์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์šฐ์•„ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด. @EventListener์— ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด.


@EventListener
public void handleUserRegisteredEvent(UserRegisteredEvent event) {
    try {
        // ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋กœ์ง
    } catch (Exception e) {
        System.err.println("Error processing event for user: " + event.getUsername());
        // ์—๋Ÿฌ ๋กœ๊น…, ์•Œ๋ฆผ ๋ฐœ์†ก ๋“ฑ์˜ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ
    }
}

๋˜๋Š” ์ „์—ญ ์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด ๋ชจ๋“  ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์—๋Ÿฌ๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜๋„ ์žˆ์–ด:


@Component
public class GlobalEventErrorHandler {

    @EventListener
    public void handleException(Exception exception) {
        System.err.println("An error occurred during event processing: " + exception.getMessage());
        // ์ถ”๊ฐ€์ ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋กœ์ง
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์—๋Ÿฌ๋กœ ์ธํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ค‘๋‹จ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ด๋Ÿฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ์ •๋ง ์ค‘์š”ํ•˜์ง€!

๐Ÿ’ก Pro Tip: ์ด๋Ÿฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์กฐ๊ฑด๋ถ€ ์ฒ˜๋ฆฌ์™€ ์ˆœ์„œ ์ง€์ •์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊น”๋”ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€. ์žฌ๋Šฅ๋„ท์—์„œ VIP ์‚ฌ์šฉ์ž์˜ ์žฌ๋Šฅ ๋“ฑ๋ก์„ ์ตœ์šฐ์„ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ:

@EventListener(condition = "#event.user.isVIP()")
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handleVIPTalentRegistration(TalentRegisteredEvent event) {
    // VIP ์‚ฌ์šฉ์ž์˜ ์žฌ๋Šฅ ๋“ฑ๋ก ์ฒ˜๋ฆฌ
}

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” Spring Events์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด. ๐Ÿ‘ ์ด ๊ธฐ๋Šฅ๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋”์šฑ ์œ ์—ฐํ•˜๊ณ , ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์งˆ ๊ฑฐ์•ผ!

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ๋ชจ๋“  ๊ฐœ๋…๋“ค์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๊ธฐ๋Œ€๋˜์ง€ ์•Š์•„? ๊ณ„์† ๋”ฐ๋ผ์™€! ๐Ÿš€

๐Ÿ› ๏ธ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๊ธฐ

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ๋“œ๋””์–ด ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๋ชจ๋“  ๊ฒƒ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณผ ์‹œ๊ฐ„์ด ์™”์–ด. ๐Ÿ˜ƒ ์šฐ๋ฆฌ๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด Spring Events์˜ ๋ชจ๋“  ๊ฐœ๋…์„ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž!

1. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์„ค๊ณ„

๋จผ์ €, ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ ์šฉํ•œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•ด๋ณผ๊ฒŒ:

  com.talentnet
โ”œโ”€โ”€ config
โ”‚   โ””โ”€โ”€ AsyncConfig.java
โ”œโ”€โ”€ event
โ”‚   โ”œโ”€โ”€ TalentRegisteredEvent.java
โ”‚   โ”œโ”€โ”€ UserRegisteredEvent.java
โ”‚   โ””โ”€โ”€ TransactionCompletedEvent.java
โ”œโ”€โ”€ listener
โ”‚   โ”œโ”€โ”€ TalentEventListener.java
โ”‚   โ”œโ”€โ”€ UserEventListener.java
โ”‚   โ””โ”€โ”€ TransactionEventListener.java
โ”œโ”€โ”€ service
โ”‚   โ”œโ”€โ”€ TalentService.java
โ”‚   โ”œโ”€โ”€ UserService.java
โ”‚   โ””โ”€โ”€ TransactionService.java
โ””โ”€โ”€ controller
    โ”œโ”€โ”€ TalentController.java
    โ”œโ”€โ”€ UserController.java
    โ””โ”€โ”€ TransactionController.java

์ด๋Ÿฐ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„ํ•˜๋ฉด ๊ฐ ๋„๋ฉ”์ธ(์žฌ๋Šฅ, ์‚ฌ์šฉ์ž, ๊ฑฐ๋ž˜)๋ณ„๋กœ ์ด๋ฒคํŠธ์™€ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.

2. ์ด๋ฒคํŠธ ์ •์˜

๊ฐ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ •์˜ํ•ด๋ณด์ž:


public class TalentRegisteredEvent extends ApplicationEvent {
    private final String talentId;
    private final String userId;

    public TalentRegisteredEvent(Object source, String talentId, String userId) {
        super(source);
        this.talentId = talentId;
        this.userId = userId;
    }

    // getters...
}

public class UserRegisteredEvent extends ApplicationEvent {
    private final String userId;
    private final String email;

    public UserRegisteredEvent(Object source, String userId, String email) {
        super(source);
        this.userId = userId;
        this.email = email;
    }

    // getters...
}

public class TransactionCompletedEvent extends ApplicationEvent {
    private final String transactionId;
    private final String buyerId;
    private final String sellerId;
    private final BigDecimal amount;

    public TransactionCompletedEvent(Object source, String transactionId, String buyerId, String sellerId, BigDecimal amount) {
        super(source);
        this.transactionId = transactionId;
        this.buyerId = buyerId;
        this.sellerId = sellerId;
        this.amount = amount;
    }

    // getters...
}

3. ์„œ๋น„์Šค ๊ตฌํ˜„

์ด์ œ ๊ฐ ์„œ๋น„์Šค์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด๋ณด์ž:


@Service
public class TalentService {
    private final ApplicationEventPublisher eventPublisher;

    public TalentService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public void registerTalent(String talentId, String userId) {
        // ์žฌ๋Šฅ ๋“ฑ๋ก ๋กœ์ง...
        
        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publishEvent(new TalentRegisteredEvent(this, talentId, userId));
    }
}

@Service
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public void registerUser(String userId, String email) {
        // ์‚ฌ์šฉ์ž ๋“ฑ๋ก ๋กœ์ง...
        
        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publishEvent(new UserRegisteredEvent(this, userId, email));
    }
}

@Service
public class TransactionService {
    private final ApplicationEventPublisher eventPublisher;

    public TransactionService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public void completeTransaction(String transactionId, String buyerId, String sellerId, BigDecimal amount) {
        // ๊ฑฐ๋ž˜ ์™„๋ฃŒ ๋กœ์ง...
        
        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publishEvent(new TransactionCompletedEvent(this, transactionId, buyerId, sellerId, amount));
    }
}

4. ๋ฆฌ์Šค๋„ˆ ๊ตฌํ˜„

์ด์ œ ๊ฐ ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž:


@Component
public class TalentEventListener {

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleTalentRegistered(TalentRegisteredEvent event) {
        System.out.println("์ƒˆ๋กœ์šด ์žฌ๋Šฅ์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค: " + event.getTalentId());
        // ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์—…๋ฐ์ดํŠธ
        // ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ ๋ฐœ์†ก
    }
}

@Component
public class UserEventListener {

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserRegistered(UserRegisteredEvent event) {
        System.out.println("์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค: " + event.getUserId());
        // ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
        // ์ดˆ๊ธฐ ์ถ”์ฒœ ์žฌ๋Šฅ ๋ชฉ๋ก ์ƒ์„ฑ
    }
}

@Component
public class TransactionEventListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleTransactionCompleted(TransactionCompletedEvent event) {
        System.out.println("๊ฑฐ๋ž˜๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค: " + event.getTransactionId());
        // ํŒ๋งค์ž์—๊ฒŒ ์ •์‚ฐ ์ฒ˜๋ฆฌ
        // ๊ตฌ๋งค์ž์—๊ฒŒ ๋ฆฌ๋ทฐ ์š”์ฒญ ๋ฐœ์†ก
        // ๊ฑฐ๋ž˜ ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ
    }
}

5. ๋น„๋™๊ธฐ ์„ค์ •

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž:


@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("TalentNet-");
        executor.initialize();
        return executor;
    }
}

6. ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด ๋ชจ๋“  ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž:


@RestController
@RequestMapping("/talents")
public class TalentController {
    private final TalentService talentService;

    public TalentController(TalentService talentService) {
        this.talentService = talentService;
    }

    @PostMapping
    public ResponseEntity> registerTalent(@RequestBody TalentRegistrationRequest request) {
        talentService.registerTalent(request.getTalentId(), request.getUserId());
        return ResponseEntity.ok().build();
    }
}

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public ResponseEntity> registerUser(@RequestBody UserRegistrationRequest request) {
        userService.registerUser(request.getUserId(), request.getEmail());
        return ResponseEntity.ok().build();
    }
}

@RestController
@RequestMapping("/transactions")
public class TransactionController {
    private final TransactionService transactionService;

    public TransactionController(TransactionService transactionService) {
        this.transactionService = transactionService;
    }

    @PostMapping("/complete")
    public ResponseEntity> completeTransaction(@RequestBody TransactionCompletionRequest request) {
        transactionService.completeTransaction(request.getTransactionId(), request.getBuyerId(), request.getSellerId(), request.getAmount());
        return ResponseEntity.ok().build();
    }
}
๐ŸŽ‰ ์ถ•ํ•˜ํ•ด! ์ด์ œ ์šฐ๋ฆฌ๋Š” Spring Events๋ฅผ ํ™œ์šฉํ•œ ์™„์ „ํ•œ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ตฌํ˜„ํ–ˆ์–ด. ์ด ๊ตฌ์กฐ์˜ ์žฅ์ ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด:
  • ๊ฐ ๋„๋ฉ”์ธ(์žฌ๋Šฅ, ์‚ฌ์šฉ์ž, ๊ฑฐ๋ž˜)์ด ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ.
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋˜๊ณ  ํ™•์žฅ์„ฑ์ด ์ข‹์•„์ ธ.
  • ํŠธ๋žœ์žญ์…˜ ๋ฐ”์šด๋“œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด.
  • ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ์‰ฌ์›Œ. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ƒˆ๋กœ์šด ์•Œ๋ฆผ ์‹œ์Šคํ…œ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ๋ฆฌ์Šค๋„ˆ๋งŒ ๋งŒ๋“ค๋ฉด ๋ผ!

์ด ๊ตฌ์กฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์„ ํšจ๊ณผ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด:

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

์ด๋ ‡๊ฒŒ Spring Events๋ฅผ ํ™œ์šฉํ•˜๋ฉด, ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ๋„ˆ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ ์  ๋” ๊ฐ•๋ ฅํ•ด์ง€๋Š” ๊ฑธ ๋Š๋‚„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ‘

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” Spring Events์˜ ๋ชจ๋“  ๊ฒƒ์„ ๋ฐฐ์šฐ๊ณ  ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ดค์–ด. ์ด ์ง€์‹์„ ๋ฐ”ํƒ•์œผ๋กœ ๋„ˆ๋งŒ์˜ ๋ฉ‹์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด๋Š” ๊ฑด ์–ด๋•Œ? ํ™”์ดํŒ…! ๐Ÿš€