-
[SpringBoot] ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ฅผ ์์๋ณด๊ณ ์คํ๋ง๋ถํธ์ ์ด๋ฒคํธ๋ฅผ ๊ตฌํํด๋ณด์SpringBoot 2024. 3. 1. 08:07
๐ ๋ค์ด๊ฐ๋ฉฐ
๊ต๋ด์ ์คํฌ์ธ ๊ฒฝ๊ธฐ ์ํ์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๋ ์๋น์ค 'ํ์น์น'์์๋ ์ฌ์ฉ์๋ค์ด ์์ํ๋ ํ์ ๋ํด ์์ ๋๊ธ์ ๋จ๊ธธ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ์๋ค.
1์ฐจ ๋ฆด๋ฆฌ์ฆ ์ดํ ์๋น์ค์ ๋ํ ํผ๋๋ฐฑ์ ๋ฐ์์ ๋น์, ์๋ก๊ณ ์นจ์ ํด์ผ๋ง ์๋ก์ด ๋๊ธ์ด ๋ฐ์๋๋ ๊ฒ์ ๋ํ ์ง์ ์ด ์์๋ค.
์ด์ ๊น์ง๋ '๋๊ธ'์ ๊ฐ๊น๊ฒ ํด๋น ๊ธฐ๋ฅ์ ์ ์ํ์ง๋ง, ํด๋น ํผ๋๋ฐฑ๊ณผ ์ด๋ฒคํธ ์คํ ๋ฐ์ ํตํด ๋๋ฉ์ธ ์ฉ์ด๋ฅผ ์ ๋ฆฌํ ๋ค์๋ ํด๋น ๊ธฐ๋ฅ์ด '์ฑํ '์ ๊ฐ๊น๋ค๊ณ ์ ์๋ฅผ ๋ด๋ฆฌ๊ณ '์์ํก'์ด๋ผ๊ณ ๋ช ๋ช ํ๊ธฐ๋ก ํ๋ค.
๋ฐ๋ผ์ ์ค์๊ฐ์ผ๋ก ์๋ก ๋ฑ๋ก๋ ๋๊ธ์ด ๋ฐ์๋๋ ๊ฒ์ผ๋ก ๊ธฐํ์ด ๋ฐ๋์ด ์๋ก ๊ตฌํ์ ํ๊ฒ ๋๋ค.
๊ทธ ๊ณผ์ ์ค์์๋ ์ด๋ฒ์๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ ๋ฌด์์ด๋ฉฐ, ์ ์ด ๊ธฐ๋ฅ์ ๊ตฌํ ๊ณผ์ ์์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ฅผ ์ฌ์ฉํ๋์ง ๊ทธ๋ฆฌ๊ณ ์คํ๋ง๋ถํธ์ ์ด๋ฒคํธ๋ ์ด๋ป๊ฒ ๊ตฌํ๋์ด์ผ ํ๋์ง์ ๋ํด ์์ฑํด๋ณด๊ฒ ๋ค.
๐ฑ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ (EDA)
์ดํ๋ฆฌ์ผ์ด์ ๊ฐ ์ค๊ณ๋ฅผ ์ํ ์ํํธ์จ์ด ์ํคํ ์ฒ ๋ฐ ๋ชจ๋ธ์ด๋ค.
์ด๋ ๋ถ๋ฆฌ๋ ์๋น์ค๋ค ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด์ ์ค๊ณ ๋์๋ค.
์ด๋ฒคํธ๋?
๋ชจ๋ ์ค์ํ ๋ฐ์ ํน์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํ ๊ธฐ๋ก์ด๋ค.
์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ๊ตฌ์ฑ ์์
1. event producers (publishers)
- ์ด๋ฒคํธ๋ฅผ ์ธ์งํ๊ฑฐ๋ ์ฐพ๋๋ค.
- ์ด๋ฒคํธ๋ฅผ ๋ฉ์์ง๋ก ํํํ๋ค.
- decoupling ํ๊ฒ ๊ตฌ์ฑ๋๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ์ consumer ์ ์ด๋ฒคํธ์ ๊ฒฐ๊ณผ๋ฅผ ์์ง ๋ชปํ๋ค.
- ์ด๋ฒคํธ์ channel ์ ํตํด์ consumer ์๊ฒ ์ ๋ฌ๋๋ค.
2. event consumers (subscribers)
- ์ด๋ฒคํธ๊ฐ ๋ฐํ๋๋ฉด ์๊ฒ ๋๋ค.
- ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋ ์ํฅ์ ๋ฐ๋๋ค.
3. event processing platform
- ์ด๋ฒคํธ์ ๋ํ ์ฌ๋ฐ๋ฅธ ์๋ต์ ์คํํ๋ค.
์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ๋ชจ๋ธ
1. pub/sub model
- ํน์ ๋ถ๋ถ์ ๊ตฌ๋ ํ๊ณ ์๋ค๊ฐ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๊ฑฐ๋ ๋ฐํ๋๋ฉด ๊ตฌ๋ ์๋ค์ ์ด๋ฅผ ์๊ฒ ๋๊ณ ์ด๋ฒคํธ๋ฅผ ์์ ํ๊ฒ ๋๋ ๋ฐฉ์
2. event streaming model
- ์ด๋ฒคํธ๊ฐ ๋ก๊ทธ๋ก ๊ธฐ๋ก๋๋ค.
- event consumer ์ ๊ตฌ๋ ํ์ง ์๋๋ค.
- consumer ๋ค์ stream ์ ์ด๋ค ๋ถ๋ถ์ด๋ ์ฝ์ ์ ์๊ณ , ์ธ์ ๋ ์ง stream ์ ์กฐ์ธํ ์ ์๋ค.
- apache kafka ์ ๊ฐ์ streaming platform ์ ์ด์ฉํ๋ค.
Event ๋ฅผ ์ฌ์ฉํ๋ ์ด์
event ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ฌ ์ํคํ ์ฒ๋ฅผ ๊ณ ๋ คํ๊ฒ ๋๋ฉด, ์๋น์ค ๊ฐ์ ์์กด์ฑ์ด ๋ถ๋ฆฌ๋๋ค.
์ฐ๋ฆฌ ์๋น์ค๋ฅผ ์๋ก ๋ค์ด ์ค๋ช ํ์๋ฉด, ์์ํก์ ๋ฑ๋กํ๊ณ ๋์ ์น์์ผ์ ์ด์ฉํด ํด๋น ๊ฒ์(๊ฒฝ๊ธฐ) ํ์ด์ง์ ๋จธ๋ฌด๋ฅด๊ณ ์๋ ์ฌ์ฉ์๋ค์๊ฒ ์๋ก ๋ฑ๋ก๋ ์์ํก์ ์ ์กํด์ค์ผ ํ๋ค.
๊ทธ๋ฌ๋, ์์ํก์ ๋ฑ๋กํ๋ ๋ก์ง๊ณผ ์์ผ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ๋ก์ง์ ์์ ํ ๋ค๋ฅธ ๋ก์ง์ด๋ค. ๋ฐ๋ผ์ ์ด๋ ์๋ก ์์กดํ์ง ์๋ ๊ฒ์ด ์ ์ ํ๋ค.
๋ฐ๋ผ์ ์์ํก์ด ๋ฑ๋ก๋๋ฉด, ์ด๋ฅผ ์ด๋ฒคํธ๋ก ๋ฐํํ๊ณ ์ด๋ฒคํธ๋ฅผ ํธ๋ค๋งํ๋ ๋ก์ง์์๋ ์ด๋ฒคํธ๊ฐ ๋ฐํ๋์์ ์๊ฒ ๋ ๋ค ์ด๋ฅผ ์น์์ผ์ ์ด์ฉํด ํด๋ผ์ด์ธํธ ์ธก์ ์ ์กํด ์๋ก ๋ฑ๋ก๋ ์์ํก์ด ํ๋ฉด์ ์ ์ ํ ๋ํ๋๋๋ก ํ๋ค.
์ฆ, ์ด๋ฒคํธ๋ฅผ ์ด์ฉํ๊ฒ ๋๋ฉด ์๋ก ์ง์ ์ ์ผ๋ก ์ฐ๊ด์ด ์๋ ๋ก์ง์ด ์๋ก ์์กดํ์ง ์๋๋ก ํ ์ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด, ์๋ก ์ฐ๊ด์ด ์๋ ๋ก์ง์ด ์์กดํ๋ค๋ ๊ฑด ์ด๋ค ์ํฉ์ผ๊น?
์๋ฅผ ๋ค์ด์, ์ด์ปค๋จธ์ค ์๋น์ค์์
1. ์ฃผ๋ฌธ์ ํ๋ฉด
2. ํฌ์ธํธ๋ฅผ ์ง๊ธํ๊ณ
3. ์ถ์ฒ ์ํ์ ๋ํ ์๊ณ ๋ฆฌ์ฆ์ ๋ก์ง์ ๋ณ๊ฒฝํด์ผ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
์์์ ์ธ๊ธํ 3๊ฐ์ง์ ๋ก์ง์ ๋ชจ๋ ์๋ก ์ฐ๊ด์ด ์์ผ๋ฏ๋ก ๋ณ๊ฒฝ ์ฌํญ์ ์ ํํด์๋ ์๋๋ค๋ ๊ฒ์ด ๋๊ปด์ง ๊ฒ์ด๋ค.
๋ง์ฝ ์ด๋ฒคํธ๋ฅผ ์ด์ฉํด ์๋ก ๋ถ๋ฆฌํ์ง ์๋๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๊ฐ ์์ฑ๋๋ค.
public class OrderService{ public void order(OrderRequestDto orderRequestDto){ ์ฃผ๋ฌธ(orderRequestDto); ํฌ์ธํธ_์ง๊ธ(ํฌ์ธํธ ์ง๊ธ์ ํ์ํ ํ๋ผ๋ฏธํฐ); ์ถ์ฒ_์ํ_์๊ณ ๋ฆฌ์ฆ_๋ณ๊ฒฝ(์๊ณ ๋ฆฌ์ฆ ๋ณ๊ฒฝ์ ํ์ํ ํ๋ผ๋ฏธํฐ); } }
๋ง์ฝ, ํฌ์ธํธ ์ง๊ธ์ ํ์ํ๋ ์ ๋ณด๊ฐ ๋ฐ๋๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๊ทธ๋ ๊ฒ ๋๋ฉด OrderService ์ฆ ์ฃผ๋ฌธ์ ๋ํ ๋ก์ง๊น์ง ๋ณ๊ฒฝ์ด ์ ํ๋๋ค.
์๋ก ์ฐ๊ด์ด ์๋ ๋ก์ง๋ผ๋ฆฌ์ ์์กด์ด ์๊ธฐ๊ฒ ๋๋ ๊ฒ์ด๋ค. ์ด๋ฐ ๊ฒฝ์ฐ์ ์ด๋ฒคํธ์ ๋ฐํ์ด ํ์ํด์ง๋ ๊ฒ์ด๋ค.
๐ฑ ์คํ๋ง๋ถํธ์์์ ์ด๋ฒคํธ ๋ฐํ๊ณผ ๊ตฌ๋ ๊ตฌํํด๋ณด๊ธฐ
1. Event
public record ExampleEvent(ExampleDomain exampleDomain) { }
๊ธฐ์กด์๋ ์ด๋ฒคํธ์ ํด๋นํ๋ ํด๋์ค๊ฐ ๋ฐ๋์ ApplicationEvent ๋ฅผ ์์๋ฐ์์ผ ํ๋ค.
๊ทธ๋ฌ๋, ๋ ์ด์์ ์์๋ฐ์ง ์์๋ ๋๋ค.
2. EventListener
@Component public class ExampleEventListener { @EventListener public void listen(ExampleEvent event) { // .. do sth with event } }
Event Listener ์ @EventListener ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๋ฉด ๋๋ค.
3. EventPublisher
@Service @RequiredArgsConstructor public class ExampleEventPublisher { private final ApplicationEventPublisher eventPublisher; public void publish() { eventPublisher.publishEvent(new ExampleEvent(new ExampleDomain())); } }
EventPublisher ๋ ApplicationEventPublisher ์ ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ์ฌ ๊ตฌํํ๋ค.
์ด์ ๋ํ ์ค์ ๊ตฌํ์ ApplicationContext ์์ ์ด๋ฃจ์ด์ง๋ค.
ํด๋น ์ธํฐํ์ด์ค์ publishEvent ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ Event ๋ฅผ ๋ฐํํ๋ฉด ๋๋ค.
๐ฑ ์ด๋ฒคํธ ๋ฐํ๊ณผ ๊ตฌ๋ ์์์ ํธ๋์ญ์
ํธ๋์ญ์ ๊ณผ ํธ๋ค๋ฌ์ ๋์ ์์ ์ ์์ธํ๊ฒ ์กฐ์ ํ ์ ์๋ค.
TransactionPhase.BEFORE_COMMIT
- ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๊ธฐ ์ ์ ์ด๋ฒคํธ๋ฅผ ํธ๋ค๋งํ๋ค.
TransactionPhase.AFTER_COMPLETION
- ํธ๋์ญ์ ์ด ์๋ฃ๋ ์ดํ์ ์ด๋ฒคํธ๋ฅผ ํธ๋ค๋งํ๋ค.
TransactionPhase.AFTER_COMMIT (default)
- ํธ๋์ญ์ ์ด ์ปค๋ฐ๋ ์ดํ์ ์ด๋ฒคํธ๋ฅผ ํธ๋ค๋งํ๋ค.
TransactionPhase.AFTER_ROLLBACK
- ๋กค๋ฐฑ๋ ์ดํ์ ํธ๋ค๋ง์ด ์ด๋ฃจ์ด์ง๋ค.
๐ฑ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ธฐ
๋ง์ฝ, ์์ ๊ณผ์ ์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ์ด์
@Service @RequiredArgsConstructor public class ExampleEventPublisher { private final ApplicationEventPublisher eventPublisher; @Transactional public void register() throws InterruptedException { registerNewMember(); eventPublisher.publishEvent(new ExampleEvent(new ExampleDomain("example"))); sendEmail(); } public void registerNewMember() throws InterruptedException { System.out.println("ํ์๊ฐ์ ์๋ฃ!"); } public void sendEmail() throws InterruptedException { System.out.println("์ด๋ฉ์ผ ์ ์ก ์๋ฃ!"); } } @Component @RequiredArgsConstructor @Slf4j public class ExampleEventListener { private final ExampleEventPublisher publisher; @EventListener public void listen(ExampleEvent event) throws InterruptedException { System.out.println("๊ฐ์ ์ถํ๊ธ ์ง๊ธ ์๋ฃ!"); } }
์์ ๊ฐ์ ์ํฉ์ ์์๋ก ๋ค ์ ์๋ค.
์๋ก์ด ์ฌ์ฉ์๊ฐ ํ์๊ฐ์ ์ ํ ๋
1. ์๋ก์ด ํ์์ ์ ๋ณด ์ ์ฅ
2. ๊ฐ์ ์ถํ ์ด๋ฉ์ผ ์ ์ก
3. ๊ฐ์ ์ถํ ํฌ์ธํธ ๋ถ์ฌ ๊ฐ ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
๋ง์ฝ ์ด๊ฐ ๋๊ธฐ๋ก ์ฒ๋ฆฌ๋๋ค๋ฉด, ๊ฐ์ ์ถํ๊ธ์ด ๋ถ์ฌ๋ ๋๊น์ง ์ด๋ฉ์ผ ์ ์ก ๋ก์ง์ด ๋๊ธฐ๋ฅผ ํด์ผ ํ๋ค.
๊ทธ๋ฌ๋, ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ๋๋ค๋ฉด ์ด๋ฉ์ผ ์ ์ก ๋ก์ง์ด ๋๊ธฐ๋ฅผ ํ ํ์๊ฐ ์์ด์ง๋ค.
๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
@Component public class ExampleEventListener { @EventListener @Async public void listen(ExampleEvent event) { // .. do sth with event } }
๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ์๋, ์์ฒ๋ผ @Async ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
๐ฑ @Async, @EventListener, @TransactionalEventListener
์ฒ์ ์์ ์ด๋ ธํ ์ด์ ๋ค์ ์ ํ์ ๋ ๋๋ฌด ํท๊ฐ๋ ธ๋ค.
๊ทธ๋์ ๋ค์ํ ์กฐํฉ์ ๊ณ ๋ คํด์ ์์๋ค์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
1. ๋๊ธฐ + @EventListener
@Service @RequiredArgsConstructor public class ExampleEventPublisher { private final ApplicationEventPublisher eventPublisher; @Transactional public void register() throws InterruptedException { registerNewMember(); eventPublisher.publishEvent(new ExampleEvent(new ExampleDomain("example"))); sendEmail(); } public void registerNewMember() throws InterruptedException { System.out.println("ํ์๊ฐ์ ์๋ฃ!"); } public void sendEmail() throws InterruptedException { System.out.println("์ด๋ฉ์ผ ์ ์ก ์๋ฃ!"); } } @Component @RequiredArgsConstructor @Slf4j public class ExampleEventListener { private final ExampleEventPublisher publisher; @EventListener public void listen(ExampleEvent event) throws InterruptedException { Thread.sleep(10000); System.out.println("๊ฐ์ ์ถํ๊ธ ์ง๊ธ ์๋ฃ!"); } }
- ํธ๋์ญ์ : ํ๋์ ํธ๋์ญ์
- ์ฐ๋ ๋ : ํ๋์ ์ฐ๋ ๋
- ์คํ ์์ : ํ์์ ์ ๋ณด ์ ์ฅ ๋ก์ง์ด ์คํ๋ ์ดํ์ ๊ฐ์ ์ถํ๊ธ ๋ถ์ฌ ๋ก์ง์ด ๋๋ ๋์ ์ด๋ฉ์ผ ์ ์ก ๋ก์ง์ ๋๊ธฐํ๊ณ ์๋ค๊ฐ ๊ฐ์ ์ถํ๊ธ ๋ถ์ฌ ๋ก์ง์ด ์๋ฃ๋ ์ดํ์ ์คํ๋๋ค.
2. ๋น๋๊ธฐ(@Async) + @EventListener
@EventListener @Async public void listen(ExampleEvent event) throws InterruptedException { Thread.sleep(10000); System.out.println("๊ฐ์ ์ถํ๊ธ ์ง๊ธ ์๋ฃ!"); }
listen ๋ฉ์๋์์๋ง ์ฐจ์ด๊ฐ ๋ฐ์ํ๋ค. @Async ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๋ฉด ๋๋ค.
- ํธ๋์ญ์ : ๋ถ๋ฆฌ๋ ํธ๋์ญ์
- ์ฐ๋ ๋ : ๋ถ๋ฆฌ๋ ์ฐ๋ ๋
- ์คํ ์์ ์๋ก์ด ํ์์ ์ ๋ณด ์ ์ฅ ๋ก์ง์ด ์คํ๋๊ณ , ์ด๋ฒคํธ ๋ฐํ ์ดํ์ ๊ฐ์ ์ถํ๊ธ ๋ถ์ฌ ๋ก์ง์ด ์คํ๋๋ค. ์ด ๋, register ๋ฉ์๋๋ listen ๋ฉ์๋์ ์คํ ์๋ฃ๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ listen ๋ฉ์๋์๋ ๋ณ๊ฐ๋ก ์ด๋ฉ์ผ ์ ์ก ๋ก์ง์ด ๋ฐ๋ก ์คํ ๋๋ค.
3. ๋๊ธฐ + @TransactionalEventListener
@TransactionalEventListener public void listen(ExampleEvent event) throws InterruptedException { Thread.sleep(10000); System.out.println("๊ฐ์ ์ถํ๊ธ ์ง๊ธ ์๋ฃ!"); }
- ํธ๋์ญ์ : ํ๋์ ํธ๋์ญ์
- ์ฐ๋ ๋ : ํ๋์ ์ฐ๋ ๋
- ์คํ ์์ : ์ด๋ฒคํธ ๋ฐํ ์ชฝ์์์ ์ปค๋ฐ์ ๊ธฐ๋ค๋ ธ๋ค๊ฐ listen ๋ฉ์๋๊ฐ ์คํ๋๋ค.
๋๊ธฐ์ ์ผ๋ก ์คํ์ด ๋์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ๊ฐ์ ์ฐ๋ ๋์์ ์คํ๋๋ค.
๋ง์ฝ, ์ปค๋ฐ์ด ์ด๋ฃจ์ด์ง์ง ์๋๋ค๋ฉด?
public void register() throws InterruptedException { registerNewMember(); eventPublisher.publishEvent(new ExampleEvent(new ExampleDomain("example"))); sendEmail(); }
์์ ๊ฐ์ด @Transactional ์ด๋ ธํ ์ด์ ์ด ๋๋ฝ๋์๊ณ , ๋ช ์์ ์ผ๋ก ์ปค๋ฐ๋ ํ์ง ์์๋ค๊ณ ๊ฐ์ ํด๋ณด์.
์ด๋ฌํ ๊ฒฝ์ฐ์๋ ์ด๋ ํ ์ปค๋ฐ๋ ์ผ์ด๋์ง ์๊ธฐ ๋๋ฌธ์ listen ๋ฉ์๋๋ ๋ฌดํ์ ์ผ๋ก ๋๊ธฐํ๊ฒ ๋๋ค.
4. ๋น๋๊ธฐ + @TransactionalEventListener
@TransactionalEventListener @Async public void listen(ExampleEvent event) throws InterruptedException { long threadId = Thread.currentThread().getId(); System.out.println("listen ์ฐ๋ ๋: " + threadId); System.out.println("๊ฐ์ ์ถํ๊ธ ์ง๊ธ ์๋ฃ!"); }
- ํธ๋์ญ์ : ๋ณ๊ฐ์ ํธ๋์ญ์
- ์ฐ๋ ๋ : ๋ณ๊ฐ์ ์ฐ๋ ๋
- ์คํ ์์ : ์๋ก์ด ํ์์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ , ์ปค๋ฐ์ด ๋ ๋๊น์ง listen ๋ฉ์๋๋ ๋๊ธฐํ๋ค๊ฐ ์ปค๋ฐ์ด ๋ ์ดํ์ listen ๋ฉ์๋๊ฐ ์คํ๋๋ค.
๋ค์ ๊ธ์์ ์ด์ด์ง๋๋ค..
https://dev-seunghee.tistory.com/14
[SpringBoot] ๋๋ฉ์ธ ์ด๋ฒคํธ๋ฅผ ์์๋ณด๊ณ AbstractAggregateRoot ๋ฅผ ์ด์ฉํด ์ฑํ ์๋น์ค๋ฅผ ๊ตฌํํด๋ณด์
https://dev-seunghee.tistory.com/12 [SpringBoot] ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ฅผ ์์๋ณด๊ณ ์คํ๋ง๋ถํธ์ ์ด๋ฒคํธ๋ฅผ ๊ตฌํํด๋ณด์ ๐ ๋ค์ด๊ฐ๋ฉฐ ๊ต๋ด์ ์คํฌ์ธ ๊ฒฝ๊ธฐ ์ํ์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๋ ์๋น์ค 'ํ์น์น'
dev-seunghee.tistory.com
์ถ์ฒ
https://mangkyu.tistory.com/292
[Spring] ์คํ๋ง์์ ์ด๋ฒคํธ์ ๋ฐํ๊ณผ ๊ตฌ๋ ๋ฐฉ๋ฒ๊ณผ ์ฃผ์์ฌํญ, ์ด๋ฒคํธ ์ฌ์ฉ์ ์ฅ/๋จ์ ๊ณผ ์ฌ์ฉ ์์
์ด๋ฒคํธ(Event)๋ ๋งค์ฐ ์ ์ฉํ์ง๋ง ์๋นํ ๊ฐ๊ณผ๋๋ ๊ธฐ๋ฅ ์ค ํ๋์ ๋๋ค. ์๋ ์ ์๋ง์กด CTO๋ ์ด๋ฒคํธ ๋๋ฆฌ๋ธ ์ํคํ ์ฒ๋ก ๊ฐ์ผ ํ๋ค๊ณ ๊ธฐ์กฐ ์ฐ์ค์ ํ๊ธฐ๋ ํ๋๋ฐ, ์ด๋ฒ์๋ ์คํ๋ง ํ๋ ์์ํฌ์์
mangkyu.tistory.com
'SpringBoot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ