이벤트는 '이미 발생한 사실'… 지시 사항은 안 돼
Jump to 3:31푸르니마 나야르에 따르면 이벤트는 이미 발생한 사실을 알리는 불변의 메시지이며, 결코 지시 사항이 되어서는 안 된다. 이벤트의 핵심은 '세상에 알리는 것'에 있으며, 이는 변경될 수 없는 객관적 사실의 전달이라는 점이 강조됐다. 이러한 정의는 메시징 시스템 설계의 기본 원칙을 제시한다.
푸르니마 나야르, 분산 시스템의 데이터 손실, 워크플로 문제, 부하 처리 등 실질적 난관 해결을 위한 고급 메시징 패턴 제시
Want the next one from this channel too?
푸르니마 나야르에 따르면 이벤트는 이미 발생한 사실을 알리는 불변의 메시지이며, 결코 지시 사항이 되어서는 안 된다. 이벤트의 핵심은 '세상에 알리는 것'에 있으며, 이는 변경될 수 없는 객관적 사실의 전달이라는 점이 강조됐다. 이러한 정의는 메시징 시스템 설계의 기본 원칙을 제시한다.
나야르는 이벤트 메시지에 지시 사항이 숨겨져 있거나 위장된 경우를 '수동 공격적인 이벤트'이자 명백한 안티패턴으로 규정했다. 이벤트는 '무엇이 일어났는지'를 알려야 하며, '무엇을 해야 하는지'를 지시하는 것은 이벤트의 본질을 왜곡한다고 지적했다. 이러한 설계는 시스템 간의 결합도를 높여 유연성을 저해하고, 문제 발생 시 디버깅을 어렵게 만드는 원인이 된다는 설명이다. 이벤트 기반 아키텍처의 목적은 서비스 간의 느슨한 결합을 통해 독립적인 확장을 가능하게 하는 것인데, 지시 사항을 포함하는 이벤트는 이 목적에 부합하지 않는다. 이는 이벤트의 역할과 책임에 대한 명확한 이해가 필요함을 강조한다. 이벤트는 단순히 정보 전달자의 역할을 해야 하며, 그 정보를 바탕으로 각 서비스가 자율적으로 판단하고 행동하도록 설계되어야 한다. 이러한 원칙을 준수할 때 비로소 견고하고 확장 가능한 분산 시스템을 구축할 수 있다.
분산 컴퓨팅 환경에서 '네트워크가 신뢰할 수 있다'는 가정은 매우 위험하다고 지적됐다. 이러한 가정이 실제 시스템 운영 시 다양한 문제로 이어질 수 있다는 것이다. 네트워크는 언제든지 지연되거나 실패할 수 있는 불확실한 요소이기 때문에, 시스템 설계 시 이를 항상 염두에 두어야 한다.
나야르는 '모든 구독자가 '주문 접수됨' 이벤트를 받을 것'이라는 가정도 신뢰할 수 없다고 말했다. 주문 접수 이벤트가 브로커에 발행되더라도, 사기 서비스나 결제 서비스가 다운되면 해당 서비스들은 이벤트를 받지 못할 수 있기 때문이다. 이는 분산 시스템에서 메시지 전달의 신뢰성을 보장하기 어렵다는 점을 보여준다.
이벤트가 순서대로 도착하는 것을 보장하는 것은 사실상 불가능하다고 강조됐다. 나야르는 이를 '중력과 같아서 거스를 수 없다'고 비유하며, 순서 보장이 되는 경우는 운이 좋은 날일 뿐이며, 이러한 가정이 잘못되면 시스템에 큰 문제가 발생할 수 있다고 경고했다. 분산 시스템에서 네트워크 지연이나 서비스 장애 등으로 인해 이벤트가 발행된 순서와 다르게 도착할 수 있음을 인정하고 설계해야 한다는 의미다.
분산된 워크플로에서는 주문 상태를 파악하기 위해 여러 서비스에 일일이 질의해야 하는 비효율이 발생한다고 지적됐다. 워크플로가 다양한 구독자 서비스에 걸쳐 분산되어 있기 때문에, 특정 주문의 현재 상태를 확인하려면 마치 탐정처럼 여러 시스템을 조사해야 하는 복잡성이 생긴다는 것이다. 이는 전체 비즈니스 프로세스의 가시성을 떨어뜨리고 문제 해결 시간을 지연시킬 수 있다.
독립적으로 반응하는 다수의 구독자가 있을 때 시스템 설계자는 여러 가지 질문에 직면하게 된다. 나야르는 데이터 저장 방식, 소비자 실패 처리, 이벤트 순서 미보장 시 대응, 일관성 보장, 느리거나 과부하된 소비자 처리, 그리고 비즈니스 보상 조치 등에 대한 질문들을 던졌다. 이러한 질문들은 분산 시스템 설계 시 고려해야 할 복잡한 문제들을 요약하고 있다.
가장 흔한 과제 중 하나로 '이중 쓰기(dual-write) 문제'가 제시됐다. 이는 비즈니스 데이터와 메시지 발행 간의 일관성을 유지해야 할 때 발생한다. 시스템이 데이터베이스와 메시지 브로커라는 두 개의 다른 경계에 동시에 쓰려고 할 때 불일치가 발생할 수 있기 때문이다. 즉, 비즈니스 로직이 성공적으로 처리되었음에도 불구하고 해당 이벤트 메시지가 브로커에 발행되지 않거나, 그 반대의 경우가 발생할 수 있다는 것이다.
나야르는 모든 작업에 대해 정확히 하나의 메시지가 브로커로 발행되고, 정확히 하나의 부수 효과가 발생하도록 보장하는 방법을 모색하는 것이 중요하다고 강조했다. 이러한 불일치는 데이터의 정합성을 해치고, 다운스트림 서비스의 오작동을 유발할 수 있어 분산 시스템에서는 치명적일 수 있다.
따라서 이중 쓰기 문제를 해결하는 것은 분산 시스템의 신뢰성과 안정성을 확보하는 데 필수적인 요소이다. 이는 단순히 기술적인 문제를 넘어, 비즈니스 프로세스의 완전성과도 직결되는 문제로 이해해야 한다.
이중 쓰기 문제를 해결하기 위한 방안으로 '트랜잭션 아웃박스(Transaction Outbox) 패턴'이 제시됐다. 이 패턴은 비즈니스 데이터와 메시지 발행 간의 일관성을 보장하는 인프라 역할을 한다. 나야르는 이 패턴을 통해 비즈니스 데이터와 메시지가 단일 항목으로서 일관성을 유지할 수 있다고 설명했다.
트랜잭션 아웃박스 패턴은 비즈니스 데이터와 발행할 메시지를 단일 데이터베이스 트랜잭션으로 데이터베이스에 함께 저장한다. 메시지 브로커로의 발행은 나중에 별도의 프로세스가 처리하도록 하여, 두 가지 작업을 원자적으로 처리하지 않고도 일관성을 확보하는 방식이다. 이로써 데이터베이스에 비즈니스 데이터와 아웃박스 메시지가 함께 저장되거나, 둘 다 저장되지 않도록 보장한다.
이후 메시지 릴레이(message relay) 컴포넌트가 아웃박스 테이블을 주기적으로 스캔하여 아직 발행되지 않은 메시지를 브로커로 발행한다. 이 과정에서 브로커 발행에 실패하더라도, 메시지는 여전히 아웃박스 테이블에 남아 있어 재시도를 통해 결국 발행될 수 있다. 이 패턴은 분산 시스템에서 흔히 발생하는 일관성 문제를 효과적으로 해결하며, 서비스 간의 강력한 결합 없이도 안정적인 메시지 기반 통신을 가능하게 한다.
나야르는 트랜잭션 아웃박스 패턴에 멱등성(idempotency)을 더하면 '정확히 한 번 처리(exactly once processing)'가 된다는 주장은 '환상'이라고 단언했다. 그녀는 이 패턴이 실제로 보장하는 것은 브로커에서 메시지를 '최소 한 번' 전달하는 것에 멱등성을 더한 것에 불과하다고 설명했다. 즉, 메시지는 여러 번 전달될 수 있지만, 수신자 측에서는 중복 메시지를 처리해도 결과가 동일하도록 설계해야 한다는 의미다.
멱등성은 동일한 작업을 여러 번 수행해도 시스템의 상태가 변하지 않도록 보장하는 속성을 말한다. 예를 들어, '주문 생성' 메시지가 두 번 도착하더라도 주문이 한 번만 생성되도록 처리하는 식이다. 이는 '정확히 한 번 처리'와는 다르다. '정확히 한 번 처리'는 메시지가 단 한 번만 전달되고 처리되는 것을 의미하는데, 분산 시스템의 복잡성과 네트워크 불확실성 때문에 이를 완벽히 보장하는 것은 극히 어렵거나 불가능에 가깝다.
따라서 개발자는 '정확히 한 번 처리'에 대한 환상을 버리고, '최소 한 번 전달'과 멱등성을 통해 실제 비즈니스 요구사항을 충족시키는 방향으로 시스템을 설계해야 한다. 메시지 중복 처리 시 발생할 수 있는 부작용을 방지하는 것이 핵심이라는 점이 강조됐다.
푸르니마 나야르는 '분산 트랜잭션'이 분산 시스템의 해결책이 될 수 없다고 강하게 비판했다. 그녀는 분산 트랜잭션이 여러 데이터베이스에서 일관성을 처리하는 데 중점을 두지만, 항상 긴밀한 결합을 가정하고 있다고 지적했다. 이는 빠르고 균일하며 단일 팀 통제하에 있는 모놀리식 시스템의 특징이지, 분산 시스템의 현실과는 거리가 멀다는 설명이다. 분산 트랜잭션은 모든 서비스에 걸쳐 원자성(atomicity), 즉 모두 아니면 아무것도 아닌 결과를 보장하지만, 이 방식은 유연성이 떨어지고 확장성이 제한된다.
나야르는 또한 분산 트랜잭션이 비즈니스 현실과 동떨어져 있다고 강조했다. 비즈니스는 '롤백'이라는 개념보다는 '환불'이나 '백오더'와 같은 보상 조치를 통해 문제를 해결한다. '주문 환불'은 비즈니스 결정이지 기술적인 롤백이 아니라는 것이다. 재고를 백오더하는 것 역시 비즈니스 결정이며, 이러한 비즈니스 흐름은 분산 트랜잭션의 강제적인 '모두 아니면 아무것도 아닌' 모델과 맞지 않는다고 설명했다.
더불어 분산 트랜잭션에는 조정자(coordinator)와 시간이라는 두 가지 단일 실패 지점(single point of failures)이 존재한다고 지적했다. 조정자가 괜찮고, 모든 참가자가 제시간에 응답할 것이라는 가정 자체가 분산 시스템의 불확실한 현실을 무시하는 것이라는 비판이다. 따라서 분산 트랜잭션의 가정을 버리고, 워크플로의 비즈니스 현실에 맞춰 시스템을 설계해야 한다고 결론지었다.
나야르는 사가(Saga) 패턴이 비즈니스 로직을 직접 수행하는 것이 아니라, 결정에 따라 다른 명령을 보내거나 이벤트를 발행하는 일종의 인프라스트럭처라고 설명했다. 사가는 메시징 라이브러리가 제공하는 코드 인프라와 유사하며, 개발자는 미리 정의된 비즈니스 로직을 활용해 메시지 처리를 다룬다. 즉, 사가는 복잡한 분산 트랜잭션 대신 일련의 로컬 트랜잭션을 조정하며, 각 로컬 트랜잭션이 성공하면 다음 단계를 진행하고, 실패하면 보상 트랜잭션을 통해 이전 단계를 취소하는 방식으로 동작한다. 이는 비즈니스 프로세스의 흐름을 관리하고, 분산 환경에서의 일관성을 확보하는 데 중요한 역할을 한다.
나야르는 분산 워크플로를 설계할 때 오케스트레이션(orchestration)과 코레오그래피(choreography) 중 어떤 방식을 선택할지 결정하는 중요한 기준으로 '보상 동작(compensating action)'의 복잡성을 제시했다. 보상 동작은 분산 시스템에서 트랜잭션의 일부가 실패했을 때 이미 완료된 작업을 되돌려 일관성을 유지하는 메커니즘을 의미한다.
만약 특정 비즈니스 결정이 많거나, 워크플로 다이어그램에서 각 단계 간에 화살표가 복잡하게 얽혀 있다면, 이는 많은 비즈니스 결정이 필요하다는 신호이며, 오케스트레이터 방식이 더 적합할 수 있다고 설명했다. 오케스트레이션은 중앙 집중식 코디네이터(orchestrator)가 워크플로의 모든 단계를 관리하고 각 서비스에 명령을 내리는 방식이다. 이는 워크플로의 전체적인 흐름을 파악하기 쉽고, 복잡한 보상 로직을 한 곳에서 관리할 수 있다는 장점이 있다.
반면, 코레오그래피는 각 서비스가 서로의 이벤트를 구독하여 자율적으로 다음 작업을 수행하는 분산 방식이다. 이는 느슨한 결합을 제공하고 확장성이 뛰어나지만, 워크플로의 전체 흐름을 파악하기 어렵고 보상 동작이 복잡해질 경우 관리가 어려울 수 있다. 따라서 보상 동작의 복잡성을 분석하여 오케스트레이션과 코레오그래피 중 비즈니스 요구사항에 더 적합한 방식을 선택해야 한다는 점이 강조됐다.
나야르는 발행/구독(Pub/Sub) 패턴과 경쟁 소비자(Competing Consumers) 패턴의 역할이 다르며, 해결하는 문제도 다르기 때문에 함께 사용되어야 한다고 설명했다. Pub/Sub은 기본적으로 '커뮤니케이션 스타일'로서, 모든 구독자에게 메시지 사본을 전달하여 확장성과 브로드캐스트 시나리오에 최적화되어 있다. 이는 정보의 확산과 관심 있는 여러 서비스에 동일한 데이터를 제공하는 데 효과적이다.
반면, 경쟁 소비자 패턴은 '스케일링 패턴'으로, 백프레셔(backpressure) 및 플로우 제어(flow control)라는 더 큰 그림의 일부다. 이 패턴에서는 여러 소비자가 하나의 메시지를 두고 경쟁하며, 각 메시지는 단 하나의 소비자만 처리한다. 이는 메시지 처리량을 높이고 시스템의 부하를 분산하는 데 최적화되어 있다. 예를 들어, 대량의 작업을 처리해야 할 때 여러 워커(worker)들이 큐에서 작업을 가져가 처리하는 방식이다.
따라서 Pub/Sub은 이벤트 발생을 알리는 데 사용하고, 경쟁 소비자 패턴은 그 이벤트를 실제 작업으로 변환하여 처리할 때 사용함으로써 두 패턴의 장점을 모두 활용할 수 있다. 두 패턴은 상호 보완적인 관계에 있으며, 분산 시스템의 다양한 요구사항을 충족시키기 위해 적절히 조합하여 사용하는 것이 중요하다고 강조했다.
경쟁 소비자 패턴을 사용할 때는 메시지 순서가 전혀 보장되지 않는다는 점을 명확히 이해해야 한다고 나야르가 강조했다. 이 패턴은 여러 소비자가 동시에 메시지를 처리하기 위해 경쟁하기 때문에, 메시지가 발행된 순서와 다르게 처리될 수 있다. 따라서 작업 간에 엄격한 종속성이 있는 시나리오에서는 경쟁 소비자 패턴을 사용하지 않는 것이 좋다고 조언했다. 이는 '한 번 작업 실행'과 같이 개별 작업이 서로 독립적인 시나리오에 가장 적합하다는 설명이다. 작업 순서가 중요한 비즈니스 로직에는 다른 패턴이나 추가적인 조정 메커니즘을 고려해야 한다.
나야르는 시스템 과부하를 방지하기 위한 '백프레셔(Backpressure) 패턴'의 여러 기법을 소개했다. 첫째, 소비자 측 스로틀링(consumer-side throttling)은 엔드포인트가 동시에 처리할 수 있는 메시지 수를 제한하여, 엔드포인트가 데이터베이스와 같은 백엔드 시스템에 과부하를 주는 것을 방지한다. 둘째, 자동화된 속도 제한(automated rate limiting)은 타사 서비스 호출과 같이 외부 종속성에 대한 호출 빈도를 제한하여, 외부 서비스의 과부하를 막는다. 마지막으로, NServiceBus와 같은 시스템에서는 오류 큐에 메시지가 많이 쌓일 경우 엔드포인트 자체에 서킷 브레이커(circuit breaker)를 작동시켜 메시지 처리를 일시적으로 중단하는 기술을 사용한다. 이 세 가지 기법은 시스템의 안정성을 확보하고 예상치 못한 부하에 대응하는 데 효과적이다.
벌크헤드(Bulkhead) 패턴은 애플리케이션의 요소를 격리된 풀(pool)로 분리하여 설계함으로써, 한 요소의 실패가 전체 시스템으로 확산되는 것을 방지한다. 이는 선박의 방화벽(벌크헤드)처럼 한 구획이 손상되더라도 다른 구획은 정상적으로 기능하도록 하는 원리다. 예를 들어, 특정 서비스가 과도한 요청으로 인해 장애가 발생하더라도, 다른 서비스들은 영향을 받지 않고 정상적으로 작동할 수 있도록 자원(스레드 풀, 연결 풀 등)을 분리하는 방식이다. 이 패턴은 엔드포인트를 설계할 때 매우 중요하며, 시스템의 복원력과 안정성을 크게 향상시키는 데 기여한다.
푸르니마 나야르는 다양한 고급 메시징 패턴을 배웠다고 해서 모든 패턴을 한 번에 적용하려 하지 말라고 조언했다. 대신, 어떤 패턴이 현재 시스템의 사용 사례에 가장 필요한지 확인하고, 점진적으로 도입하는 것이 중요하다고 강조했다. 시스템의 복잡성을 한 번에 늘리는 대신, 핵심적인 문제 해결에 집중하여 가장 시급한 패턴부터 적용해야 한다는 것이다.
특히, 아웃박스 패턴, 복구 가능성(recoverability), 그리고 에러 큐(error queue) 관리는 분산 시스템의 기본적인 안정성과 신뢰성을 확보하는 데 필수적이므로, 이들을 우선적인 시작점으로 삼아야 한다고 제안했다. 다른 고급 패턴들은 그 이후에 필요에 따라 점진적으로 도입해도 늦지 않다는 설명이다. 이러한 접근 방식은 개발 팀이 시스템 변경의 위험을 최소화하고, 안정적인 방식으로 아키텍처를 발전시켜 나갈 수 있도록 돕는다.
Answers come from the transcript, with the exact spot cited.
Want the next one from this channel too?
When NDC Conferences publishes, we'll write it up like the one you just read and email it to you.
No new videos from this channel in the last 7 days.
We skip Shorts. You can unfollow any time.
둠 세계에서 AI 에이전트 시스템 탐색: 제약 R&D 적용 가능성NDC Conferences2 weeks ago · 52:42 · 32 views · Created 2 weeks ago
정확한 ML 모델도 현장에서는 실패… 9년간의 배포 실패 교훈 공개NDC Conferences2 weeks ago · 59:13 · 39 views · Created 2 weeks ago
기술 역량 유지하며 엔지니어링 관리자 되기: 폴 윌리엄스의 여정NDC Conferences2 weeks ago · 1:00:58 · 71 views · Created 2 weeks ago