[SSA] 수강 기능 및 알림 기능을 한 곳에서 처리하면 발생할 수 있는 문제

2025. 7. 19. 11:51·SSA/Back

수강 기능 : 학생이나 강사가 수강 신청 요청을 보내거나 수강 신청 요청에 대해 수락 또는 거절 등

알림 기능 : 학생이나 강사가 수강 신청 요청을 보내거나 수강 신청 요청에 대해 수락 또는 거절 시 대상 사용자에게 알림 전송

 

 

기존의 구현은 수강신청 또는 수강신청 수락/거절 시에 알림 기능 또한 하나의 메서드 안에서 동작하는 모놀리틱 구조입니다.

하나의 메서드가 수강 신청, 수강 신청 수락/거절 이외에

  • 멤버 레포지토리로부터 FCM 토큰 값을 찾고, 접속 여부에 따라 각각 다른 알림 서비스를 호출합니다.

위와 같은 상황이라면,

수강 서비스는 강의 도메인 외에

  1. FCM 토큰이 저장되어 있는 멤버 레포지토리
  2. 실시간 접속 여부 확인을 위한 Redis
  3. 알림 서비스

등의 서비스를 한꺼번에 의존하게 되고 다음과 같은 문제점이 발생할 수 있습니다.

 

  • 수강 기능과 알림 기능이 강하게 결합되어 기능 변경 시 영향 범위가 넓어짐
    • 일단 첫 번째로 수강 기능을 담당하는 서비스가 SSE로 알림을 보낼지, FCM으로 알림을 보낼지 모르는 상황에서 항상 FCM 토큰을 찾으려고 시도합니다. 그리고 만약 접속 중이라면 해당 조회한 FCM 토큰은 사용하지 않게 되고, 이후 SSE 알림이 전송됩니다. 불필요하게 FCM 토큰을 조회하기도 하고 수강을 담당하는 기능이 알림까지 담당하니 책임이 과중해집니다. 또한, 수강 기능과 알림 기능에 변경점이 발생할 경우에 수정 범위가 넓어집니다.
  • 수강신청 트래픽과 알림 트래픽이 증가할수록 자원 소모 증가로 병목 현상 발생할 수 있음
    • 수강 서비스는 해당 기능뿐만 아니라 알림 기능까지 담당하기 때문에 여러 명의 사용자가 수강신청 시에 처리해야 할 내용이 많아집니다. 수강 기능은 처리되었다고 할지라도 FCM 기능과 같이 외부 API를 사용하는 경우 네트워크 문제가 발생하면 이후에 들어오는 각 요청의 처리 속도가 느려질 가능성이 높습니다.
  • 수강 서비스나 알림 서비스, 그외에 Redis 등에서 장애가 발생하면 관련한 모든 기능이 같이 실패함
    • 여러 기능이 하나의 메서드에 집중되어 있는 상태라면 해당 기능 중 하나라도 문제가 생길 경우 나머지 기능이 전부 문제 상태에 빠지기 때문에 모든 서비스에 장애가 전파될 수 있습니다. 수강 기능과 알림 기능이 분리되어 있을 때, 알림 기능에 문제가 생기더라도 수강 기능이 정상적으로 작동한다면 서비스는 중단될 필요가 없게 되고 알림 기능은 수정 후 재가동 하면 전체 서비스가 다시 원할하게 작동합니다.

 

따라서, 전송을 담당할 서비스를 만들고 수강 기능을 담당하는 서비스에서는 알림을 요청하기만 하도록 책임을 분리합니다.

 

수강 서비스는 신청, 수락/거절 처리만 하고

알림 메시지는 알림 서비스로 요청 처리를 넘겨 주기만 합니다.

 

수강 서비스 --> 알림 서비스(접속 중이라면 sse 서비스 호출, 미접속 중이라면 fcm 서비스 호출)

그림으로 보면 다음과 같습니다.

 

최초 구조

 

 

 

 

 

# 정리

결과적으로 생각해 보면 수강신청 기능과 알림전송 기능을 분리하는 데에는 여러 가지 방법이 있는데
해당 내용은 다음과 같습니다.

 

  1. 트랜잭션 새로 전파 (REQUIRES_NEW)
    트랜잭션을 새로 전파하면 하나의 트랜잭션에서 전부 롤백되는 상황 자체는 막을 수 있습니다.
    그러나 수강 트랜잭션이 알림 트랜잭션을 요청하고 알림이 전송되었다는 완료 커밋을 받기까지 블로킹됩니다.
    그러면 최종적으로 서버의 응답성을 떨어트리게 됩니다.
    또한, 수강 트랜잭션으로 DB에 저장, 알림 트랜잭션으로 DB에 알림 전송 이력 저장 이렇게 2가지 커넥션이 필요하게 되고 요청이 순간적으로 몰리게 되면 리소스를 2배로 써야 하기 때문에 서버에 부담이 될 것이기 때문에 "REQUIRES_NEW"는 사용하지 않았습니다.

  2. HTTP 호출 (API)
    1번의 경우와 마찬가지로 API를 호출하게 되면 수강 기능은 알림 기능의 응답을 기다리면서 블로킹됩니다.
    네트워크상의 지연이 발생할 가능성이 매우 높고, 데이터가 유실될 수 있습니다. 또한 HTTP 호출 시 알림 서버가 다운되어 있었거나 500 에러를 반환하게 되면 재시도 하기가 까다로울 수 있고 알림 이벤트는 유실될 수 있습니다.

  3. 스프링 이벤트 (@Event)
    자체적으로 스프링 이벤트를 발행하는 것도 가능할 수 있으나, 서버가 여러 대로 분산될 경우 스프링이 발행한 이벤트를 다른 JVM 환경의 서버에서 수신할 수 있는 방법이 없습니다.
    물론 Spring Cloud Bus 등을 쓰면 가능은 하겠지만, 그건 어차피 이벤트 발행하는 것과 동일하다고 생각합니다.
    그리고 만약 수강 신청 트랜잭션은 성공했는데, 알림 이벤트를 발행하고 처리하는 도중에 서버가 갑자기 재시작되거나, 다른 어떤 이유으로 셧다운 된다면 해당 이벤트는 완전히 사라집니다. 학생은 수강 신청이 됐는데도 알림을 못 받는 상황이 발생할 수 있습니다.
    또, 수강 신청이 폭주해서 이벤트가 순식간에 10만 개가 쌓이면 이벤트 처리를 위한 큐가 꽉 차거나 힙 메모리가 부족해져서
    정작 중요한 '수강 신청' 기능까지 같이 느려지거나 뻗어버릴 수 있다고 생각했습니다.
    알림 로직만 수정해서 배포하고 싶어도 수강 서버 전체를 재배포해야 합니다.

  4. Redis Pub/Sub
    Redis의 경우 메시지의 영속과 전달을 보장하지 않습니다. 특정 이벤트를 발행했는데 해당 이벤트를 구독하고 있는 알림 서버가 다운되어 있다면 해당 이벤트는 사라지게 됩니다.
    또한, Redis는 인메모리 기반이기 때문에 Redis 서버가 재시작되면 모든 이벤트가 유실될 위험이 높습니다. Redis로 이벤트를 발행하기 전후로 DB에 저장할 수 있겠으나 그러면 그냥 이미 해당 기능을 제공하는 Kafka를 쓰는 것이 낫습니다.

  5. RabbitMQ (vs Kafka)
    RabbitMQ는 이벤트 스트림보다는 작업 큐에 더 가깝다고 생각했습니다. 알림이라는 작업을 큐에 넣고 작업 성공/재시도/실패 등등 여러 처리 기능을 제공합니다. 그러나 작업을 처리하고 나면 작업 메시지는 소비가 되어 사라집니다. 여기서도 똑같이 RabbitMQ의 작업 전후로 DB에 저장할 수 있겠으나 그러면 그냥 이미 해당 기능을 제공하는 Kafka를 쓰는 것이 낫다고 생각합니다.

  6. Kafka
    수강 기능은 Kafka에 수강신청 이벤트를 발행하고 사용자에게 즉시 응답합니다.
    또한, Kafka는 모든 이벤트를 디스크에 저장하고 알림 서버가 다운되었다고 하더라도 서버가 다시 켜지는 순간 Kafka에 기록된 로그를 Offset 기준으로 다시 읽어 와 알림 이벤트의 유실 없이 동작할 수 있습니다.
    이건 레디스나 래빗엠큐를 쓰더라도 같은 장점인데 향후 MSA 전환 시 배포의 독립성을 보장해 줍니다. 서비스 간 강결합을 끊어내므로, 알림 로직이 변경되더라도 수강 서버를 재배포할 필요가 없어집니다.
    물론, Kafka도 단점이 있습니다. 1, 2, 3번에 비해 구현이 다소 복잡하고 새롭게 기술을 사용해야 하기에 러닝 커브도 있을 수 있습니다. 새로운 컴포넌트를 도입한다는 점, 세부적인 Kafka 설정을 조작해야 한다는 점, 구체적인 구현 또한 사용자가 직접 해야 한다는 점 등이 단점일 수 있습니다. RabbitMQ에 비해서 1:1 처리 속도는 느릴 것입니다.

 

 

 

항상, 기술을 선택할 때에는 현재 어떤 기능을 개발해야 하는지, 어떤 비즈니스 문제를 해결해야 하는지 등등 여러 가지 고려사항을 고민해야 한다고 생각합니다.

 

저의 경우 크게는

  1. 학습용 목적
  2. 수강 기능, 알림 기능 외에 사용자 수강 이력 분석 후 추천 기능 등으로의 확장

정도를 고려하여 Kafka를 도입하고자 결정했습니다.

 

 

 

 

 

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

[SSA] 트랜잭션을 새로 만들어서 사용하면 ?  (0) 2025.08.29
[SSA] 500명 알림 발송 6.4초 -> 0.27초, 카프카 컨슈머 병렬 처리 적용기  (3) 2025.08.28
[SSA] 서버 SSE 기능 구현  (0) 2025.05.11
[SSA] 서버 FCM 기능 구현  (1) 2025.05.04
[SSA] 강의 도메인  (0) 2025.05.04
'SSA/Back' 카테고리의 다른 글
  • [SSA] 트랜잭션을 새로 만들어서 사용하면 ?
  • [SSA] 500명 알림 발송 6.4초 -> 0.27초, 카프카 컨슈머 병렬 처리 적용기
  • [SSA] 서버 SSE 기능 구현
  • [SSA] 서버 FCM 기능 구현
하가네
하가네
  • 하가네
    하 렌
    하가네
  • 전체
    오늘
    어제
    • 분류 전체보기 (128) N
      • Computer Science (27)
        • 운영체제 (7)
        • 데이터통신 (10)
        • 자료구조 (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 아카데미 (43)
        • 1주차 (5)
        • 2주차 (5)
        • 3주차 (2)
        • 4주차 (1)
        • 5주차 (3)
        • 6주차 (2)
        • 7주차 (0)
        • 8주차 (2)
        • 9주차 (14)
        • 10주차 (0)
        • 11주차 (1)
        • 12주차 (0)
        • 13주차 (2)
        • 14주차 (2)
        • 15주차(최종 프로젝트) (3)
        • 최종 프로젝트 이후 (1)
      • 모각코 (6)
        • 2023 동계 (6)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

  • hELLO· Designed By정상우.
하가네
[SSA] 수강 기능 및 알림 기능을 한 곳에서 처리하면 발생할 수 있는 문제
상단으로

티스토리툴바