처음 시스템을 만들 때는 대개 command 중심으로 출발는 것이 자연스럽다.
사용자는 어떤 동작을 요청하고, 시스템은 그 요청을 처리하면 된다.
그런데 시스템이 조금만 커지면 command만으로는 복잡해지는 순간이 온다.
어떤 동작의 결과를 여러 곳에서 알고 싶어지고, 후속 처리가 점점 늘어나고, 한 서비스가 다른 서비스의 내부 후처리까지 알아야 하는 상황이 생긴다.
그러면 command에서 event로 전환을 고민해볼 필요가 있다.
Command와 Event는 무엇이 다른가
Command는 의도를 담는다.
“이 일을 해라”라는 요청이다.
누군가 특정 수신자에게 어떤 행위를 기대하면서 보낸다.
예를 들어 다음과 같은 이름은 command에 가깝다.
CreateOrderApprovePaymentSendNotification
반면 Event는 이미 일어난 사실을 담는다.
“이 일이 일어났다”라는 기록이다.
누군가에게 뭔가를 시키는 것이 아니라, 시스템 안에서 발생한 사실을 알린다.
예를 들면 이런 식이다.
OrderCreatedPaymentApprovedNotificationSent
command는 행위의 책임을 특정 대상에게 전달하고, event는 발생한 사실의 전파를 시스템 전체에 열어둔다.
즉, command는 본질적으로 더 직접적이고, event는 더 느슨하게 연결된다.
처음에는 왜 Command로 시작하는가
많은 경우 command로 시작하는 것이 맞다.
사용자 요청은 대부분 명령의 형태로 들어온다.
“주문해줘”, “결제해줘”, “취소해줘”처럼 시스템에 기대하는 행동이 분명하다.
이때 command는 흐름을 이해하기 쉽고, 책임 주체도 명확하다.
예를 들어 주문 생성 유스케이스를 생각해보자.
- 사용자가 주문 생성 요청을 보낸다.
- 주문 서비스는 주문을 생성한다.
- 결과를 반환한다.
이 정도 규모에서는 command만으로도 충분하다.
오히려 event를 너무 일찍 도입하면 구조만 복잡해지고, 실제 이점은 크지 않을 수 있다.
문제는 시스템이 성장하면서 시작된다.
Event로 전환해야 하는 순간은 언제인가
핵심은 이 질문이다.
지금 이 메시지는 특정 행동을 시키기 위한 것인가, 아니면 이미 발생한 사실을 다른 주체들이 활용하도록 알리기 위한 것인가?
처음에는 주문 서비스가 주문을 만들고 끝난다.
그런데 어느 순간부터 주문 생성 이후 해야 할 일이 늘어난다.
- 포인트 적립
- 쿠폰 사용 처리
- 재고 차감
- 포인트 적립
- 알림 발송
이걸 전부 주문 서비스가 직접 command로 호출하기 시작하면 어떤 일이 생길까?
주문 서비스는 점점 더 많은 후속 처리의 존재를 알아야 한다.
어떤 서비스가 있고, 어떤 순서로 불러야 하고, 실패하면 어떻게 해야 하는지도 고민해야 한다.
결국 “주문 생성”이라는 본래 책임보다 “주문 이후 벌어지는 모든 일의 오케스트레이션”에 가까운 역할을 하게 된다.
이때 필요한 것은 후속 처리를 향한 더 많은 command가 아니라,OrderCreated 혹은 OrderCompleted 같은 event다.
주문 서비스는 “주문이 생성되었다”는 사실만 발행하고,
포인트 서비스는 그 사실을 구독해서 적립하고,
알림 서비스는 그 사실을 구독해서 메시지를 보내고,
통계 서비스는 그 사실을 구독해서 집계하면 된다.
하나의 command 결과가 시스템 안의 여러 반응으로 확산되기 시작할 때, event 전환이 필요해진다.
예시: 주문 완료 이후
처음에는 이런 구조였다고 가정하면
CompleteOrdercommand를 받는다.- 주문 서비스가 주문 상태를 완료로 변경한다.
- 주문 서비스가 포인트 서비스에 적립 command를 보낸다.
- 주문 서비스가 알림 서비스에 발송 command를 보낸다.
- 주문 서비스가 통계 서비스에 집계 command를 보낸다.
처음에는 단순해 보인다.
하지만 시간이 지나면 이런 문제가 생긴다.
- 주문 서비스가 너무 많은 후속 책임을 안는다.
- 새 기능이 추가될 때마다 주문 서비스가 계속 수정된다.
- 일부 후속 처리 실패가 주문 완료 자체를 흔들 수 있다.
- 장애가 전파되기 쉽다.
- 테스트도 점점 무거워진다.
이 구조를 event 중심으로 바꾸면 이렇게 된다.
CompleteOrdercommand를 받는다.- 주문 서비스가 주문 상태를 완료로 변경한다.
- 주문 서비스는
OrderCompletedevent를 발행한다. - 포인트 서비스가 이를 구독해 적립한다.
- 알림 서비스가 이를 구독해 메시지를 보낸다.
- 통계 서비스가 이를 구독해 집계한다.
이제 주문 서비스는 “주문 완료”라는 핵심 사실만 책임지게 된다.
후속 처리는 각 서비스가 자신의 관심사로 가져간다.
이 변화의 본질은 단순한 비동기화가 아니라 책임의 방향을 다시 정리한 것이다.
그렇다고 항상 Event가 더 좋은 것은 아니다
여기서 흔히 생기는 오해가 있다.
event가 더 유연해 보인다고 해서 모든 것을 event로 만들면 안 된다.
예를 들어 반드시 성공 여부가 즉시 중요하고, 실패 시 사용자에게 바로 알려야 하며, 순서 보장이 핵심인 작업은 여전히 command가 더 적합하다.
대표적으로는 이런 것들이다.
- 결제 승인 요청
- 회원 가입 처리
- 주문 취소 요청
이런 것은 “사실 알림”보다 “의도 전달과 결과 보장”이 더 중요하다.
또 하나 주의할 점은, event를 도입하면 구조가 느슨해지는 대신 추적 난이도가 올라갈 수 있다는 것이다.
동기 호출에서는 한 흐름으로 보이던 것이, 이벤트 기반 구조에서는 여러 소비자의 반응으로 퍼진다.
그래서 idempotency, 재처리, 중복 소비, 순서 보장, 모니터링 같은 운영 관점도 함께 고려해야 한다.
즉, event 전환은 무조건적인 업그레이드가 아니라 다른 종류의 복잡성을 선택하는 일이다.
판단 기준을 한 문장으로 정리하면
내가 이 메시지로 정말 하고 싶은 것이
- 특정 대상에게 어떤 행동을 시키는 것이라면 command이고,
- 이미 발생한 사실을 다른 주체들이 활용하게 하는 것이라면 event다.
그리고 특히, 하나의 도메인 결과가 여러 시스템의 반응을 유발하기 시작하면
그 시점부터는 command를 더 추가하는 대신, 결과를 event로 승격시키는 편이 더 자연스럽다.
내가 경험에서 적용할 수 있는 포인트를 고민해보면
주문을 다루는 실무에서는 하나의 command가 점점 많은 일을 맡게 되는 순간이 있다.
실제로 주문을 생성하는 과정 안에는 임직원 할인 적용, 프로모션 계산, 최종 주문 생성, 포인트 적립처럼 서로 성격이 다른 로직들이 자연스럽게 함께 들어오게 된다.
처음에는 하나의 흐름으로 묶여 있는 것이 단순해 보이지만, 시간이 지날수록 주문 command가 주문 자체를 처리하는 것을 넘어 주문 이후의 반응까지 모두 책임지는 형태로 커지기 쉽다.
이럴 때 중요한 것은 command가 단순히 커졌다는 사실보다, 그 안에 섞인 책임이 정말 같은 성격이냐는 점이다.
예를 들어 임직원 할인이나 프로모션 적용은 주문을 어떤 조건으로 생성할지를 결정하는 핵심 흐름에 가깝다.
반면 포인트 적립은 주문이 정상적으로 생성된 이후에 이어지는 후속 반응으로 볼 여지가 크다.
이런 식으로 주문을 성립시키는 책임과 주문 이후에 반응하는 책임이 한 command 안에 함께 들어오기 시작한다면, 그때는 event로의 분리를 한 번쯤 고민해볼 만하다.
결국 실무에서 event 전환을 고민하게 되는 지점은 로직의 양이 많아졌을 때라기보다, 하나의 command가 여러 관심사를 함께 끌어안기 시작할 때에 더 가깝다.
주문 생성은 command가 책임지고, 그 결과에 대한 포인트 적립이나 다른 후속 처리는 OrderCreated 같은 event를 통해 풀어내는 방식이 구조를 더 자연스럽게 만들어줄 수 있다.
마무리
command에서 event로의 전환은 기술 스택의 문제가 아니다.
Kafka를 쓰느냐, 메시지 브로커를 쓰느냐의 문제도 아니다.
본질은 시스템 안에서 무엇을 “의도”로 보고, 무엇을 “사실”로 볼 것인가에 대한 모델링의 문제다.
처음에는 command로 시작하는 것이 맞다.
하지만 시스템이 커지면서 후속 반응이 많아지고, 여러 관심사가 얽히고, 한 서비스가 다른 서비스의 내부 사정을 너무 많이 알게 된다면, 그때는 질문을 바꿔야 한다.
“다음 command를 어디로 보낼까?”가 아니라 “여기서 정말 중요한 도메인 사실은 무엇인가?”
결국 command에서 event로의 전환은 메시지 종류를 바꾸는 일이 아니라, 시스템 안에서 요청과 사실을 다시 구분하는 과정에 가깝다.
중요한 것은 기술 선택이 아니라, 도메인의 사실을 정확하게 포착하고 그것을 중심으로 경계와 책임을 나누는 일이다.
좋은 설계는 command와 event 중 하나를 택하는 것이 아니라, 둘이 가장 자연스러운 자리를 찾게 만드는 데서 시작된다.
'공부' 카테고리의 다른 글
| Redis Sorted Set (ZSet) 들여다 보기 (1) | 2026.04.10 |
|---|---|
| 대기열 - 시스템 안정성 높이기 (0) | 2026.04.02 |
| 서킷브레이커를 알았더라면 (0) | 2026.03.20 |
| 인덱스 만능이 아니다 (0) | 2026.03.09 |
| 동시성 제어를 위한 비관적 락, 낙관적 락 (0) | 2026.03.02 |