[SSA] 수강 기능, 알림 기능을 이벤트 기반으로 분리

2025. 9. 9. 03:03·SSA/Back

 

 

# 이벤트 기반 아키텍처 적용 이유

(수강신청 시 등록/거절, 알림 전송 등의 프로세스를 설계하는 과정에서 도출되었습니다.)

 

 

## 왜 분리하게 되었는지 ?

수강신청 수락/거절 시에는 해당 요청이 발생했다는 알림을 전송해야 합니다.

  • 메서드 내에서 수강신청 관련한 요청이 발생할 시 이어서 알림을 전송할 수도 있습니다.
  • 수강 서비스에서 알림 서비스를 의존하여 알림을 전송할 수도 있습니다.
  • 비동기 처리로 해도 가능합니다.
  • Spring Event Publisher를 통해서도 가능할 것 같아요.

초기 구조에서는 단일 서버에서 동작하는 것을 가정하고 기능을 만들고 있습니다만,

추후 수강 기능이 커지거나 사용자가 많아져서 서버를 분리해야 할 경우가 생길 수도 있습니다.

 

이러한 경우에는 위에서 말한 4가지 방법 모두 불가능합니다.

또한, 수강 기능과 알림 기능 간 도메인 결합도가 크다면 쉽게 분리하기 어려운 상황이 생기는 것을 가정하겠습니다.

 

수강(Lecture) 도메인에서 알림(Notification) 도메인을 의존하게 되면 다음과 같은 단점이 생길 수 있습니다.

  • 각 기능에 필요한 자원만을 독립적으로 사용하기 어렵습니다.
    • 예를 들어 Lecture 도메인에서는 Redis를 쓰지 않지만, Notification 도메인에서는 Redis를 사용하는 경우가 있습니다.
  • 하나의 기능에서 에러가 발생하면 다른 기능으로 에러를 전파하게 되고 전체 시스템이 다운될 수 있습니다.
    • 에러를 조기에 차단하기 쉽지 않습니다.
  • 특정 기능의 사용량이 증가할 경우 해당 기능 외의 다른 기능도 같이 스케일 아웃 하게 되는 상황이 발생합니다.
    • 사용자 요청에 따라 자원을 더 많이 쓴다든가, 너무 많은 요청이 들어오는 등의 경우입니다.

 

따라서, 이런 상황을 방지하고 유연하게 대처할 수 있도록 하기 위해서 초기 단계에 수강 기능과 알림 기능을 분리하여 결합도를 낮추면 이점이 있을 거라고 판단했습니다.

 

 

## 어떤 기준으로 도메인을 분리할 것인가 ?

1. 변경의 주기와 이유가 다른가 ?

  • 수강 도메인: 학사 규정이 바뀌거나, 수강 인원 제한 로직이 바뀔 때 수정됩니다.
  • 알림 도메인: FCM에서 알림톡으로 바꿀 수도 있고, 이메일 템플릿을 바꿀 수도 있습니다.
  • 결론: 변경의 원인과 주기가 다르므로, 같이 묶여 있으면 불필요한 배포가 잦아질 수 있습니다.

2. 데이터베이스의 테이블이 분리되는가 ?

  • 수강 도메인: Lecture, LectureEnrollment, Student 테이블이 핵심입니다.
  • 알림 도메인: NotificationHistory, FcmToken 테이블이 핵심입니다.
  • 결론: 서로 참조하는 데이터가 다릅니다.

3. 기능의 장애 여부

  • 알림 기능에 문제가 생겨도 마비되어도 수강 기능은 정상적으로 작동해야 합니다.
  • 수강 신청은 핵심 비즈니스 로직이고, 알림은 부가적인 기능의 성격이 강합니다.

 

이러한 기준을 통해 두 기능은 서로 다른 '도메인'이라고 판단, 이를 느슨하게 연결하기 위해 이벤트 기반 아키텍처를 도입했습니다.

 

 

## 분리 방법

# 알림 기능을 여러 서비스에서 공통으로 사용할 수 있도록 별도의 모듈(멀티 모듈)로 분리했습니다.

  • 수강 신청 모듈 등 다양한 곳에서 알림 기능을 쉽게 재사용할 수 있게 됩니다.

# 이로 인해 유연한 확장이 가능합니다.

  • 특정 이벤트로 인해 알림 기능의 사용량이 급증하더라도, 시스템 전체에 부담을 주지 않고 해당 모듈(알림 모듈)만 독립적으로 확장(Scale-out)하여 효율적으로 트래픽에 대응할 수 있습니다.

 

 

 

 

 

기능을 분리하는 경우, 강의 신청이 왔을 때 해당 정보를 알림 서버로 어떻게 전달할지에 대한 방법은 여러 가지가 있습니다.

  • API 호출
  • gRPC
  • Redis
  • RabbitMQ
  • Kafka

그중에서 Kafka를 활용하기로 정했습니다.

 

 

## Kafka를 활용한 이벤트 기반 아키텍처 (Event-Driven Architecture)

이벤트 기반 아키텍처(EDA)는 서비스들이 이벤트(어떤 상태 변화가 일어났다는 사실)를 발행(publish)하고 구독(subscribe)함으로써 서로 통신하는 방식입니다.

서비스들은 직접 서로를 호출하는 대신, 이벤트 브로커(메시지 큐: Kafka, RabbitMQ 등)를 통해 이벤트를 주고받습니다.

  • 만약 강의 신청 서버에서 강의 신청이 완료되면, "강의 신청 완료"라는 이벤트를 생성하고 이벤트 브로커에 발행합니다.
  • 알림 서버는 이벤트 브로커로부터 "강의 신청 완료" 이벤트를 구독하고 있다가, 해당 이벤트가 도착하면 이를 수신하여 알림 생성 로직을 수행합니다.
  • Spring 이벤트(Application Events)를 사용할 수도 있으나, 서버 간 통신에는 메시지 큐와 같은 외부 시스템을 사용하는 것이 일반적입니다.
  • 장점:
    • 여러 모듈이 이벤트 브로커를 통해 간접적으로 통신할 수 있게 됩니다. 이는 서비스 간의 의존성을 크게 줄여 줍니다.
    • 이벤트 브로커를 통해 메시지를 비동기적으로 처리하므로, 특정 서비스에 부하가 집중되어도 시스템 전체의 성능에 미치는 영향이 적습니다. 각 서비스는 필요에 따라 독립적으로 확장될 수 있습니다.
    • 이벤트 브로커가 메시지를 보관하므로, 알림 서버가 일시적으로 다운되더라도 이벤트가 유실되지 않고 서버 복구 후에 다시 처리될 수 있습니다. 이는 레코드 오프셋 등의 세부적인 설정이 필요합니다.
    • 강의 신청 서버는 이벤트를 발행한 후 알림 서버의 응답을 기다릴 필요 없이 추가적인 작업을 수행할 수 있습니다.
  • 단점:
    • 이벤트 브로커 시스템(Kafka, RabbitMQ 등)을 설정해야 하고, 이벤트 발행 및 구독 로직을 구현해야 하므로 초기 설정 및 구현이 복잡할 수 있습니다.
    • 이벤트의 consume을 추적하고 문제가 발생했을 때 디버깅하는 것이 다소 어려울 수 있습니다.
    • 동일한 이벤트가 여러 번 전달될 수 있으므로, 같은 이벤트가 중복 생성되지 않도록 멱등성을 고려하여 설계해야 합니다.
    • 이벤트 발행과 소비의 순서가 보장되지 않을 수 있으며, 순서가 중요한 경우 추가적인 처리가 필요합니다.

 

 

 

## 발행하는 이벤트(토픽)

어떤 이벤트를 발행하는 것만으로는 위에서 해결하고자 한 강결합 문제를 해결할 수 없습니다.

 

이유는 수강신청 수락/거절 이후 '수강신청 수락 알림 전송 이벤트', '수강신청 거절 알림 전송 이벤트'를 발행하는 경우,

각각 수강신청 수락 알림 전송과 수강신청 거절 알림 전송을 기대하는 내용을 담은 이벤트를 발행하였기 때문에,

논리적인 의존관계가 남아있게 됩니다.

 

발행하는 이벤트에는 대상 도메인에게 구체적으로 기대하는 내용(명령)을 담아서는 안 됩니다.

단순히 '수강신청이 수락되었'음을 알리는 이벤트를 발행해야 그 이후 누(어떤 서비스)가 무엇을 할지에 대해서는 전혀 관여하지 않게 됩니다.

다음 예시와 같이 동작해야 두 서비스 간 강결합을 해소할 수 있습니다.

 

수강신청 수락 --> 수강신청 수락 이벤트 --> 수강신청 수락 알림 전송

수강신청 거절 --> 수강신청 거절 이벤트 --> 수강신청 거절 알림 전송

 

이러한 설계에서 얻을 수 있는 이점은 다음과 같습니다.

  • 이런 식으로 단순 사실만을 알리는 이벤트를 발행하면 수강 서비스는 알림 서비스에 의존하지 않아도 됩니다.
  • 수강 서비스는 자신의 도메인에서 발생한 이벤트를 외부에 알리기만 하면 됩니다.
  • 추후 다른 서비스 도입 시도 기존 서비스의 수정 없이 새로운 구독자를 추가하면 되므로, 확장에 유연한 설계를 만들 수 있습니다.
  • 수강 서비스는 '수강'에만, 알림 서비스는 '알림'에만 집중할 수 있게 되어 시스템 전체의 구조가 명확히 파악될 수 있습니다.

 

 

 

 

 

'SSA > Back' 카테고리의 다른 글

[SSA] 락의 종류와 활용 가능한 상황  (0) 2025.09.09
[SSA] Kafka Consumer가 멱등성을 가질 수 있도록 설계  (0) 2025.09.09
[SSA] 이벤트 발행에 트랜잭셔널 아웃박스 패턴을 적용  (1) 2025.09.08
[SSA] 비동기 이벤트 발행기 테스트  (1) 2025.09.08
[SSA] 알림 모듈 구조 개선 및 간단한 동시성 제어 ..  (2) 2025.08.30
'SSA/Back' 카테고리의 다른 글
  • [SSA] 락의 종류와 활용 가능한 상황
  • [SSA] Kafka Consumer가 멱등성을 가질 수 있도록 설계
  • [SSA] 이벤트 발행에 트랜잭셔널 아웃박스 패턴을 적용
  • [SSA] 비동기 이벤트 발행기 테스트
하가네
하가네
  • 하가네
    하 렌
    하가네
  • 전체
    오늘
    어제
    • 분류 전체보기 (128) N
      • Computer Science (23)
        • 운영체제 (7)
        • 데이터통신 (6)
        • 자료구조 (4)
        • 논리회로 (0)
        • 확률 및 통계 (0)
        • 데이터베이스 (2)
        • AI소프트웨어 (3)
        • 컴퓨터네트워크 (1)
      • Design (5) N
        • OOP - 객체 지향 프로그래밍 (3) N
        • DDD - 도메인 주도 개발 (데이터베이스 주도 .. (0)
        • EDA - 이벤트 기반 아키텍처 (1)
        • MSA - 마이크로서비스 아키텍처 (0)
        • ADD - AI 주도 개발 (1)
      • Language (2)
        • Java (0)
        • TypeScript (2)
      • Framework (12)
        • Spring (9)
        • NestJS (3)
      • Engine (3)
        • Elasticsearch (1)
        • GraphQL + Apollo Federation (2)
      • Plugin - Extension (1)
        • VS Code (1)
        • IntelliJ (0)
      • Tips (2)
        • 터미널 명령어 (1)
        • 우분투 명령어 에러 (1)
      • SSA (26)
        • Front (1)
        • Back (23)
        • DB (1)
        • 기획 (1)
      • CNU SW 아카데미 (42)
        • 1주차 (5)
        • 2주차 (5)
        • 3주차 (2)
        • 4주차 (1)
        • 5주차 (3)
        • 6주차 (2)
        • 7주차 (0)
        • 8주차 (1)
        • 9주차 (14)
        • 10주차 (0)
        • 11주차 (1)
        • 12주차 (0)
        • 13주차 (2)
        • 14주차 (2)
        • 15주차(최종 프로젝트) (3)
        • 최종 프로젝트 이후 (1)
      • 모각코 (6)
        • 2023 동계 (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ci/cd
    개발자경험(DX)
    릴리스엔지니어링
    lint-staged
    Typescript
    ESLint
    프론트엔드/백엔드
    Husky
    아키텍처
    생산성
    DX(DeveloperExperience)
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.
하가네
[SSA] 수강 기능, 알림 기능을 이벤트 기반으로 분리
상단으로

티스토리툴바