Skip to content

Latest commit

ย 

History

History
432 lines (299 loc) ยท 15.2 KB

File metadata and controls

432 lines (299 loc) ยท 15.2 KB

๋ชฉ์ฐจ



๋“ค์–ด๊ฐ€๋ฉฐ

์ผ์ • ๊ทœ๋ชจ์ด์ƒ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์ด๋ผํ•˜๋ฉด ๋ณดํ†ต์€ ์ด๋ฒคํŠธ ์†Œ์‹ฑ๊ณผ CQRS์— ๋Œ€ํ•ด์„œ ๋งŽ์ด ์–˜๊ธฐํ•œ๋‹ค.

์ด๋ฒˆ ๊ธ€์€ ๊ตฌ์ฒด์ ์œผ๋กœ ์ด๋ฒคํŠธ ์†Œ์‹ฑ๊ณผ CQRS์— ๋Œ€ํ•ด์„œ ๋‹ค๋ฃจ๊ธฐ ์ „์—, ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” Hello World ์ˆ˜์ค€์˜ ๊ธ€์„ ์ž‘์„ฑํ•ด๋ณด๊ณ ์žํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์™œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ๋„ ๋‹ค๋ฃฌ๋‹ค.

๋ณธ ๊ธ€์€ ์•„๋ž˜ ํ™˜๊ฒฝ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • Spring Boot 2.6.4
  • Lombok 1.18.22

์ „์ฒด ์†Œ์Šค ์ฝ”๋“œ๋Š” Github์„ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.


๊ฐœ์š”

์Šคํ”„๋ง์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ชจ๋ฅผ ์ˆ˜ ์—†๋Š” ๊ฐ์ฒด๊ฐ€ ํ•˜๋‚˜ ์žˆ๋‹ค.

๋ฐ”๋กœ ApplicationContext์ด๋‹ค! ๊ทธ๋ฆฌ๊ณ  ์ด ApplicationContext ์ธํ„ฐํŽ˜์ด์Šค๋Š” ApplicationEventPublisher๋ฅผ ์ƒ์†ํ•œ๋‹ค. ์ด ๋ง์€ ApplicationContext์˜ ๊ตฌํ˜„์ฒด๋Š” ๋ชจ๋‘ ์ด๋ฒคํŠธ ๋ฐœํ–‰ (Event Publishing)์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.


์Šคํ”„๋ง์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฃฌ๋‹ค๋ฉด ์•„๋ž˜ ์„ธ ๊ฐ€์ง€ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋”ฐ๋ฅด๋ฉด ๋œ๋‹ค.

  • Event (์ด๋ฒคํŠธ)
    • ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ 4.2 ์ด์ „ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ApplicationEvent๋ฅผ ์ƒ์†ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.
    • 4.2 ๋ฒ„์ „ ์ดํ›„๋ผ๋ฉด POJO ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋กœ์จ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • EventPublisher
    • ApplicationEventPublisher๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • EventListener
    • ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ 4.2 ์ด์ „ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ApplicationListener๋ฅผ ์ƒ์† ๋ฐ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค.
    • 4.2 ๋ฒ„์ „ ์ดํ›„๋กœ๋Š” @EventListener ์• ๋…ธํ…Œ์ด์…˜๋งŒ ๋ถ™์—ฌ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด๋ฒˆ ๊ธ€์€ ๊ฐ€๋Šฅํ•œ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์ฝ”๋“œ๋งŒ์„ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.


์™œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

์ด ๊ธ€์€ ์ฒ˜์Œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๊ธฐ์—, ์ด๋ฒคํŠธ ์†Œ์‹ฑ, CQRS๋“ฑ์˜ ๋‚ด์šฉ์€ ์ „ํ˜€ ์–ธ๊ธ‰ํ•˜์ง€ ์•Š๊ณ  ์ตœ๋Œ€ํ•œ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š”๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์•Œ์•˜๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ• ๊นŒ??

๊ฐ„๋‹จํ•œ ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์™œ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณธ๋‹ค.


์š”๊ตฌ์‚ฌํ•ญ

์š”๊ตฌ์‚ฌํ•ญ์€ ๊ฐ„๋‹จํ•˜๋‹ค. SNS ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ํšŒ์›๊ฐ€์ž…ํ•˜๋ฉด ์ด๋ฉ”์ผ๊ณผ SMS๋กœ ๊ฐ€์ž… ์ถ•ํ•˜ ์•ˆ๋‚ด๋ฅผ ํ•œ๋‹ค.

์ด๋•Œ ์ด๋ฉ”์ผ๋กœ๋Š” ๊ฐ€์ž… ์ถ•ํ•˜ ์ฟ ํฐ์„ ๋ณด๋‚ด์ค€๋‹ค.


์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

RegisterUserService.java

@Service
public class RegisterUserService {

    // private final EmailSender emailSender;
    // private final SMSSender smsSender;

    public void register(RegisterUserCommand registerUserCommand) {
        // ํšŒ์› ๊ฐ€์ž…
        System.out.println(registerUserCommand.getName() + "๋‹˜ ํšŒ์› ๊ฐ€์ž… ์™„๋ฃŒ - DB์— ์˜์†ํ™”");

        // ๊ฐ€์ž… ์ถ•ํ•˜ ์ด๋ฉ”์ผ ์ „์†ก (์ฟ ํฐ ํฌํ•จ)
        System.out.println(registerUserCommand.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋ฒˆํ˜ธ: xxxxx");

        // ๊ฐ€์ž… ์ถ•ํ•˜ SMS ์ „์†ก
        System.out.println(registerUserCommand.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž");
    }
}

์šฐ์„ ์€ ๋น ๋ฅธ ๊ตฌํ˜„์„ ์œ„ํ•ด System.out์œผ๋กœ ๋Œ€์ฒดํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋ก  EmailSender, SMSSender๋“ฑ์˜ ์˜์กด์„ฑ์„ ๋„ฃ์–ด์„œ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

์ค‘์š”ํ•œ ๊ฒƒ์€ ์˜์กด์„ฑ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ!


RegisterUserController.java

@AllArgsConstructor
@RestController
public class RegisterUserController {

    private final RegisterUserService registerUserService;

    @GetMapping("/register/{name}")
    public ResponseEntity<String> register(@PathVariable String name) {
        registerUserService.register(new RegisterUserCommand(name));
        String successMessage = "๊ฐ€์ž… ์š”์ฒญ 200";
        System.out.println(successMessage);
        return ResponseEntity.ok(successMessage);
    }
}

register/binghe๋กœ ๊ฐ€์ž… ์š”์ฒญ์„ ๋‚ ๋ฆฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋œ๋‹ค.


๐Ÿค” ์œ„ ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์€ ๋ฌด์—‡์ผ๊นŒ?


1๏ธโƒฃ ์˜์กด์„ฑ

  • ๋ฌธ์ œ
    • ํ˜„์žฌ ํšŒ์›๊ฐ€์ž… ์„œ๋น„์Šค๋Š” 3๊ฐ€์ง€์˜ ๋กœ์ง์ด ์„œ๋กœ ๊ฐ•ํ•˜๊ฒŒ ์˜์กด์ ์ด๋‹ค. (ํšŒ์›๊ฐ€์ž…, ์ด๋ฉ”์ผ, SMS)
    • ์ฆ‰, ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์ด ๋ฐœ์ƒํ•˜๋ฉฐ, ์ด๋ฉ”์ผ๊ณผ SMS์˜ ์ฝ”๋“œ ์ˆ˜์ •๋งŒ์œผ๋กœ๋„ ํšŒ์› ๊ฐ€์ž… ๋กœ์ง์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ด๊ฒฐ๋ฒ•
    • ํšŒ์›๊ฐ€์ž…, ์ด๋ฉ”์ผ, SMS ๊ธฐ๋Šฅ์„ ์„œ๋กœ ๋ถ„๋ฆฌ์‹œํ‚ค๋ฉด ๋œ๋‹ค.

2๏ธโƒฃ ์„ฑ๋Šฅ

  • ํšŒ์›๊ฐ€์ž…ํ•˜๋Š”๋ฐ 0.1์ดˆ๋ฉด ๋˜์ง€๋งŒ, ์ด๋ฉ”์ผ ๋ณด๋‚ด๋Š”๋ฐ 5๋ถ„, SMS ๋ณด๋‚ด๋Š”๋ฐ 5๋ถ„์ด ๊ฑธ๋ฆฐ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ํšŒ์›๊ฐ€์ž…์ด๋ผ๋Š” ์ „์ฒด ๋กœ์ง์€ 10๋ถ„๋„˜๊ฒŒ ๊ฑธ๋ฆฐ๋‹ค.
    • ํšŒ์›๊ฐ€์ž…์ด๋ผ๋Š” ํ•ต์‹ฌ ๋กœ์ง์„ ์ œ์™ธํ•œ ์ด๋ฉ”์ผ๊ณผ SMS ๋ณด๋‚ด๋Š” ๋กœ์ง์œผ๋กœ์ธํ•ด ํšŒ์›๊ฐ€์ž…์— ๋Œ€ํ•œ ์š”์ฒญ์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.
  • ํ•ด๊ฒฐ๋ฒ•
    • ํ˜„์žฌ ํšŒ์›๊ฐ€์ž… ์š”์ฒญ-> ์ด๋ฉ”์ผ ์ „์†ก -> SMS ์ „์†ก -> ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต์˜ ํ”Œ๋กœ์šฐ๋ฅผ ํšŒ์›๊ฐ€์ž… ์š”์ฒญ -> ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต + ๋น„๋™๊ธฐ ์ด๋ฉ”์ผ ์ „์†ก + ๋น„๋™๊ธฐ SMS ์ „์†ก๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค.

3๏ธโƒฃ ํŠธ๋žœ์žญ์…˜

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

์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•œ ๊ตฌํ˜„

์ด์ œ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์˜ ๋ฆฌํŒฉํ† ๋ง์„ ํ†ตํ•ด ์œ„ ๋ฌธ์ œ 3๊ฐ€์ง€๋ฅผ ํ•ด๊ฒฐํ•ด๋ณธ๋‹ค.

์ด๋ฒคํŠธ์˜ ์‹คํ–‰ ๋‹จ๊ณ„๋Š” ํฌ๊ฒŒ 3๊ฐ€์ง€๋กœ ๋‚˜๋‰œ๋‹ค.

  1. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋„๋ฉ”์ธ๊ฐ„์˜ ์ˆœ์„œ์™€ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ์—ญํ• ์„ ํ•˜๋Š” ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์ด๋ฒคํŠธ ๋””์ŠคํŒจ์ฒ˜์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.
  2. ์ด๋ฒคํŠธ ๋””์ŠคํŒจ์ฒ˜๋Š” ์ด๋ฒคํŠธ์— ๋งž๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.
  3. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ด๋ฒคํŠธ์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์ฃผ์–ด์ง„ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค.

์˜์กด์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ -> ์ด๋ฒคํŠธ ๊ตฌํ˜„

์˜์กด์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„  ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋ถ„๋ฆฌ๊ฐ€๋Šฅํ•˜๋‹ค.

1. ์šฐ์„  ์ด๋ฒคํŠธ๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค

RegisterUserEvent.java

@NoArgsConstructor
@AllArgsConstructor
@Getter
public class RegisteredUserEvent {

    private String name;
}

์ด๋ฒคํŠธ ํด๋ž˜์Šค์—๋Š” ์ด๋ฒคํŠธ ๋ฐœ์ƒํ•˜๊ณ ๋‚˜์„œ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•ด์ฃผ๋ฉด ๋œ๋‹ค.


2. ์„œ๋น„์Šค ๋ ˆ์ด์–ด ๋ฆฌํŒฉํ„ฐ๋ง

RegisterUserService.java

@AllArgsConstructor
@Service
public class RegisterUserService {

    @Autowired
    private final ApplicationEventPublisher publisher;

    // ๊ธฐํƒ€ ์˜์กด์„ฑ ์ œ๊ฑฐ๋จ. (ex. EmailSender, SMSSender)

    public void register(RegisterUserCommand registerUserCommand) {
        // ํšŒ์› ๊ฐ€์ž…
        System.out.println(registerUserCommand.getName() + "๋‹˜ ํšŒ์› ๊ฐ€์ž… ์™„๋ฃŒ - DB์— ์˜์†ํ™”");

        // ๊ฐ€์ž… ์™„๋ฃŒ ์ด๋ฒคํŠธ ๋ฐœํ–‰
        publisher.publishEvent(new RegisteredUserEvent(registerUserCommand.getName()));
    }
}

๊ธฐ์กด์—” Email๊ณผ SMS๋ฅผ ๋ณด๋‚ด๋Š” ๋กœ์ง๊ณผ ์˜์กด์„ฑ์ด Service์— ์กด์žฌํ–ˆ๋‹ค๋ฉด ์ด์   ApplicationEventPublisher๋งŒ ์กด์žฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ๊ฐ•ํ•œ ์˜์กด์„ฑ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.


3. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

EventHandler.java

@Component
public class EventHandler {

    @EventListener
    public void sendEmail(RegisteredUserEvent event) {
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋ฒˆํ˜ธ: xxxxx");
    }

    @EventListener
    public void sendSMS(RegisteredUserEvent event) {
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž");
    }
}

๋งˆ์ง€๋ง‰์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

์ด์ œ ์‹คํ–‰ํ•˜์—ฌ ๋™์ผํ•˜๊ฒŒ ์š”์ฒญ(GET http://localhost:8080/register/binghe)์„ ๋ณด๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ฒŒ๋œ๋‹ค.


์„ฑ๋Šฅ ๋ฌธ์ œ ํ•ด๊ฒฐ -> ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

์ด๋ฒคํŠธ๋ฅผ ์ ์šฉ์‹œํ‚ด์œผ๋กœ์จ ์˜์กด์„ฑ์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์˜€์ง€๋งŒ, ์—ฌ์ „ํžˆ ๋‚จ์•„์žˆ๋Š” ๋ฌธ์ œ๋Š” ์„ฑ๋Šฅ์ด๋‹ค.

ํ˜„์žฌ ๋”ฐ๋กœ ์„ค์ •์„ ํ•ด์ฃผ์ง€ ์•Š์•„์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ด๋ฉ”์ผ๊ณผ SMS ๋ณด๋‚ด๋Š” ๋กœ์ง์ด ๊ฐ๊ฐ 3๋ถ„์”ฉ ๊ฑธ๋ฆฐ๋‹ค๋ฉด, ํšŒ์›๊ฐ€์ž…์„ ์š”์ฒญํ•œ ํด๋ผ์ด์–ธํŠธ๋Š” ์ ์–ด๋„ 6๋ถ„์ด์ƒ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ ค์•ผํ•œ๋‹ค.

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

์ผ๋ถ€๋Ÿฌ ๋งŒ๋“  ์ด๋ฉ”์ผ๊ณผ SMS์„ ๋ณด๋‚ด๋Š” 3๋ถ„์”ฉ ๊ฑธ๋ฆฌ๋Š” ์ฝ”๋“œ.

@Component
public class EventHandler {

    @EventListener
    public void sendEmail(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000); // 3์ดˆ๊ฐ„ sleep
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋ฒˆํ˜ธ: xxxxx");
    }

    @EventListener
    public void sendSMS(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000); // 3์ดˆ๊ฐ„ sleep
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž");
    }
}

์ด๋Š” ํ˜„์žฌ ๋™๊ธฐ์ ์œผ๋กœ ํšŒ์›๊ฐ€์ž… ์š”์ฒญ-> ์ด๋ฉ”์ผ ์ „์†ก -> SMS ์ „์†ก -> ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

ํšŒ์›๊ฐ€์ž… ์š”์ฒญ -> ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต + ๋น„๋™๊ธฐ ์ด๋ฉ”์ผ ์ „์†ก + ๋น„๋™๊ธฐ SMS ์ „์†ก๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ์„ฑ๋Šฅ์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.


1. @Async ์ถ”๊ฐ€

EventHandler.java

@Component
public class EventHandler {

    @Async // ๋น„๋™๊ธฐ
    @EventListener
    public void sendEmail(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000);
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋ฒˆํ˜ธ: xxxxx");
    }

    @Async // ๋น„๋™๊ธฐ
    @EventListener
    public void sendSMS(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000);
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž");
    }
}

๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•  ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์— @Async๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.


2. @EnableAsync ์ถ”๊ฐ€

SpringEventApplication.java

@EnableAsync
@SpringBootApplication
public class SpringEventApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringEventApplication.class, args);
    }

}

ConfigurationํŒŒ์ผ ํ˜น์€ ์Šคํ”„๋ง ์‹คํ–‰ Application์— @EnableAsync๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.


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

์ด๋ฅผ ํ†ตํ•ด ํšŒ์›๊ฐ€์ž…์˜ ํ•ต์‹ฌ ๋กœ์ง์€ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค!


ํŠธ๋žœ์žญ์…˜ -> TransactionalEventListener ์‚ฌ์šฉ

์ง€๊ธˆ๊นŒ์ง€ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋Š” ํšŒ์›๊ฐ€์ž… ๋„์ค‘์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์ด๋ฉ”์ผ๊ณผ SMS์ด ๋ฐœ์†ก๋˜๊ฒŒ๋œ๋‹ค.

์ด๋Š” ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต๋˜์ง€๋„ ์•Š์•˜๋Š”๋ฐ, ํšŒ์›๊ฐ€์ž… ์ถ•ํ•˜ ์†Œ์‹์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ํฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.


์ผ๋ถ€๋Ÿฌ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ๋ฆฌํŒฉํ„ฐ๋งํ•œ ์ฝ”๋“œ

@AllArgsConstructor
@Transactional
@Service
public class RegisterUserService {

    @Autowired
    private final ApplicationEventPublisher publisher;

    public void register(RegisterUserCommand registerUserCommand) {
        // ํšŒ์› ๊ฐ€์ž…
        System.out.println(registerUserCommand.getName() + "๋‹˜ ํšŒ์› ๊ฐ€์ž… ์š”์ฒญ - DB์— ์˜์†ํ™”");

        // ๊ฐ€์ž… ์™„๋ฃŒ ์ด๋ฒคํŠธ ๋ฐœํ–‰
        publisher.publishEvent(new RegisteredUserEvent(registerUserCommand.getName()));

        throw new RuntimeException("์ผ๋ถ€๋Ÿฌ ์˜ˆ์™ธ ํ„ฐํŠธ๋ฆผ");
    }
}

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋™์ผํ•œ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์˜จ๋‹ค.

ํšŒ์›๊ฐ€์ž…์— ๋Œ€ํ•œ ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์Œ์—๋„ ์ด๋ฉ”์ผ๊ณผ SMS์ด ๋ฐœ์†ก๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


Spring๊ณผ Spring Data JPA

Spring Data JPA์—์„œ ์ œ๊ณตํ•˜๋Š” @TransactionalEventListener๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ‘์ž๊ธฐ ์™œ JPA๋ƒ๊ณ ? ์ด๋ฒˆ ๊ธ€์€ ๊ฐ ๊ธฐ๋Šฅ๋ณ„ Hello World๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•จ์ด์ง€ ๊ฐ ๋ถ€๋ถ„์„ ๊นŠ๊ฒŒ ๋‹ค๋ฃจ๋Š” ๊ธ€์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์œ„ ์ฝ”๋“œ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ „์— Spring Data JPA์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

@TransactionalEventListener๋Š” @EventListener๋ฅผ ์ƒ์†ํ•œ ์• ๋…ธํ…Œ์ด์…˜์ด๋ฉฐ, ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์•„๋ž˜ ์„ค์ •์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  • AFTER_COMMIT (๊ธฐ๋ณธ)
  • AFTER_ROLLBACK
  • AFTER_COMPLETION
  • BEFORE_COMMIT

@TransactionalEventListener ๋ฆฌํŒฉํ„ฐ๋ง

@Component
public class EventHandler {

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void sendEmail(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000);
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋ฒˆํ˜ธ: xxxxx");
    }

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void sendSMS(RegisteredUserEvent event) throws InterruptedException {
        Thread.sleep(3_000);
        System.out.println(event.getName() + "๋‹˜ ๊ฐ€์ž… ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž");
    }
}

์ด์ œ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํšŒ์›๊ฐ€์ž… ๋กœ์ง์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๋กค๋ฐฑ๋˜๋ฉด ์ด๋ฉ”์ผ๊ณผ SMS๋ฅผ ๋ฐœ์†กํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


๋งˆ์น˜๋ฉฐ

์–ด๋–ค๊ฐ€? ์Šคํ”„๋ง์—์„œ ๋‹ค์–‘ํ•œ ์• ๋…ธํ…Œ์ด์…˜์„ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ์กฐ๊ธˆ ๊นŠ๊ฒŒ ๋“ค์–ด๊ฐ€๋ฉด ์‰ฝ์ง€ ์•Š๋‹ค :) ํ•˜์ง€๋งŒ ์ฐจ๊ทผ์ฐจ๊ทผ ์•Œ์•„๋ณด๋ฉด ๋  ๋“ฏ ์‹ถ๋‹ค!

๋‹ค์Œ ๊ธ€์€ ์ด๋ฒคํŠธ ์†Œ์‹ฑ๊ณผ CQRS์— ๋Œ€ํ•ด์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •์ด๋‹ค!


์ฐธ๊ณ