Post

애플리케이션 이벤트: 관심사 분리와 트랜잭션 최적화를 위한 아키텍처 전략

애플리케이션 이벤트: 관심사 분리와 트랜잭션 최적화를 위한 아키텍처 전략

애플리케이션 이벤트(Application Event)는 소프트웨어 아키텍처에서 관심사 및 트랜잭션을 분리하고 코드의 결합도를 낮추기 위해 활용되는 중요한 기술입니다. 제공된 소스에 근거하여 애플리케이션 이벤트에 대해 자세히 설명드립니다.

1. 애플리케이션 이벤트의 정의 및 목적

애플리케이션 이벤트는 비즈니스 로직을 적절하게 처리하고 선후관계를 파악하여, 비즈니스 로직 간의 의존을 느슨하게 만들고 관심사를 분리하도록 시스템을 개선하는 데 사용됩니다.

핵심 역할

  • 관심사 분리: 이벤트를 발행 및 구독하는 모델링을 통해 코드의 강한 결합을 분리합니다. 이로써 각 로직은 이벤트에 의해 본인의 관심사만 수행하도록 합니다.
  • 트랜잭션 분리 및 축소: 비대해진 트랜잭션 내의 각 작업을 작은 단위의 트랜잭션으로 분리할 수 있습니다.
  • 시스템 유연성 확보: 이벤트는 단지 비동기 처리 도구에 그치지 않고, 서비스 간 약한 결합을 통해 유연한 시스템을 설계하는 아키텍처 전략입니다.

2. 애플리케이션 이벤트 도입의 필요성 (해결하려는 문제)

과거의 코드 구현 방식에서는 핵심 로직과 부가 로직이 하나의 큰 트랜잭션 범위 내에 묶여 있을 때 여러 문제가 발생했습니다.

  1. 느린 처리 및 자원 대기 유발 (문제 상황 1): 하나의 트랜잭션이 너무 많은 작업(혹은 느린 조회)을 처리하면 요청 처리에 영향을 줄 수 있으며, Lock을 사용 중일 경우 다른 요청의 대기나 데드락을 유발할 수 있습니다.
  2. DB 무관 작업의 트랜잭션 전파 (문제 상황 2): 트랜잭션 범위 내에서 DB와 무관한 작업 (예: 외부 API 호출)을 수행할 경우, 해당 작업이 오래 걸려 트랜잭션이 길어지거나, 외부 작업의 실패가 전체 비즈니스 로직을 롤백(Rollback)시키는 문제를 야기합니다.
  3. 무결성 상실 가능성: 외부 API가 타임아웃으로 트랜잭션을 롤백시켰으나 외부 서비스에서는 사실 정상 처리되었을 경우 무결성을 잃을 수 있습니다.
  4. 낮은 가독성 및 복잡도: 핵심 로직에 부가 로직(예: 비동기 처리, 예외 처리)을 직접 구현할 경우, 코드가 원하는 대로 정확하게 동작하더라도 핵심 로직을 이해하기 어려워 가독성이 떨어집니다.

애플리케이션 이벤트를 활용하면, 부가 로직이 핵심 로직에 영향을 끼치지 않도록 관심사를 분리할 수 있으며, 코드의 가독성을 향상시킬 수 있습니다.

3. 애플리케이션 이벤트의 구현 및 활용 방안

애플리케이션 이벤트를 사용하면 특정 작업이 완료되었을 때 후속 작업이 이벤트에 의해 트리거(trigger) 되도록 구성함으로써, 주 비즈니스 로직이 과도하게 많은 로직을 알고 있을 필요가 없습니다.

핵심 로직과 부가 로직 분리 (개선된 코드 예시)

개선된 코드에서는 OrderPaymentService 내의 핵심 로직(포인트 차감, 결제 정보 저장, 주문 상태 변경)이 완료된 후 결제_완료_이벤트_발행만 수행합니다.

  • 이벤트 리스너 (부가 로직): 별도의 OrderPaymentEventListener에서 @TransactionalEventListener를 통해 이 이벤트를 구독합니다.
    • 부가 로직 예시: 주문 정보 전파, 결제 완료 알림톡 발송 등

이때, 부가 로직은 비동기(Async)로 처리되며, 주로 이벤트 발행 주체의 트랜잭션이 커밋된 후에 수행하도록 설정합니다 (TransactionalEventListener(AFTER_COMMIT)).

주요 활용 기술 (Spring 기준)

기술/키워드설명
ApplicationEventPublisher이벤트를 발행하는 데 사용됩니다.
@TransactionalEventListener트랜잭션 완료 시점(phase)에 연관하여 이벤트 리스너가 동작하도록 합니다.
TransactionPhase.AFTER_COMMIT발행 주체의 트랜잭션이 성공적으로 커밋된 후에만 이벤트 리스너의 로직이 수행되도록 보장합니다.
@Async이벤트 리스너를 비동기적으로 처리하여 핵심 트랜잭션의 완료를 기다리지 않도록 합니다.

4. 아키텍처적 고려사항

  • MSA에서의 활용: MSA 아키텍처에서는 이벤트 기반 통신(Event-driven architecture)이 서비스 간 강한 결합 없이 협력하는 해답이 됩니다. 예를 들어, OrderService에서 “주문 완료” 이벤트를 발행하면 InventoryService가 이를 구독하여 재고를 차감합니다.
  • 이벤트 발행의 원칙: 이벤트는 특정 트랜잭션의 완료에 대한 이벤트를 발행하는 것이 일반적입니다. 특정 로직에서 다수의 이벤트를 발행하는 방식은 이벤트 리스너에 의존되는 구조로 발전되기 쉬우므로, 피해야 하는 안티패턴입니다.
  • 실패 처리: 이벤트에 의해 파생된 작업이 실패했을 경우, 원본 작업 또한 실패 처리를 해야 한다면 보상 트랜잭션(SAGA 패턴)과 같은 처리가 필요합니다.

애플리케이션 이벤트는 마치 핵심 비즈니스 로직을 지휘하는 오케스트라의 지휘자와 같습니다. 지휘자가 연주(핵심 로직)를 마치면, 그 신호(이벤트)를 받은 악기 연주자들(이벤트 리스너)이 각자의 역할(부가 로직)을 독립적으로 수행하여, 전체 연주가 매끄럽고 효율적으로 진행되도록 하는 원리입니다.

This post is licensed under CC BY 4.0 by the author.