📢 공지합니다
이 게시글은 메인 페이지에 항상 고정되어 표시됩니다.
안녕하세요! 스위프트 앱2기에서 진행하고 있는 실시간 인구 공유 플랫폼 Boombim을 개발하고 있습니다. 초기에는 모놀리식 아키텍처로 시작했지만, 서비스 규모가 커지는걸 고려해 생각해본 결과 알림 기능의 성능 이슈가 심각해졌습니다.
특히 FCM 알림 발송 시 18초라는 말도 안 되는 응답 시간을 기록하면서 뭔가 대책이 필요하다는 걸 깨달았습니다.
그래서 MSA 아키텍처와 RabbitMQ를 도입해서 성능을 개선해보기로 했습니다. 결과적으로 126ms까지 응답 시간을 단축시킬 수 있었는데, 그 과정에서 겪었던 시행착오와 해결 방법들을 공유해보려고 합니다.

먼저 기본부터 정리해보겠습니다.
모놀리식 아키텍처는 하나의 큰 애플리케이션에 모든 기능이 통합되어 있는 구조입니다. 하나의 통합된 단위로 애플리케이션을 구축하는 접근 방식이죠. 마치 모든 기능이 하나의 거대한 건물 안에 들어있는 것과 같습니다.
반면 MSA(Microservices Architecture)는 비즈니스 역량을 중심으로 조직된 느슨하게 결합된, 배포/실행 가능한 컴포넌트들의 집합으로 애플리케이션을 구조화하는 방식입니다. 쉽게 말해서 큰 건물을 여러 개의 작은 건물들로 나누어 각각 독립적으로 운영하는 것과 비슷합니다.
장점:
단점:
그래서 MSA의 핵심은 도메인 간 의존성을 최대한 줄이는 것이 중요합니다. 잘못 설계하면 오히려 독이 될 수 있습니다.

처음에는 붐빔 서비스를 위와 같이 3개로 나눴습니다.
하지만 곧 문제에 직면했습니다. 우리 도메인 구조를 보면:
RabbitMQ 서버에서는 FCM 도메인만으로 알림을 전송하고 싶었는데, 알림이 전송되면 DB에 저장되어야 하고, 이를 위해서는 Alarm 도메인과 Member 도메인이 함께 필요했습니다.
이렇게 되면 API 서버에서 Member와 Alarm 도메인을 업데이트할 때마다 RabbitMQ 서버에서도 같이 업데이트해야 하는 상황이 발생했어요. 오히려 더 비효율적이 되었습니다.
해결책으로 RabbitMQ 서버에 Alarm, FCM 도메인을 묶어서 처리하고, API 서버에서는 알림 내역 조회 시 DTO와 Feign Client를 활용해서 어느 정도 분리를 시도해봤습니다.
하지만 Member 도메인이 문제였습니다. 이 도메인이 거미줄처럼 모든 곳에 얽혀있어서 쉽게 분리할 수 없었습니다. Member 관련해서도 별도 서버를 만들어야 할 것 같았지만, 현재 시간과 비용 제약으로 인해 현실적으로 불가능했습니다.
결국 현실적인 타협안을 선택했습니다. API 서버, 장소 스케줄링 및 분석 서버, RabbitMQ 서버 이렇게 3개로 물리적으로 나누어서 요청 부하를 분산시키는 것으로 결정했습니다.
완벽한 MSA는 아니지만, 적어도 각 서버가 담당하는 역할을 명확히 분리하여 성능 향상을 꾀할 수 있었습니다.

RabbitMQ는 메시지 브로커(Message Broker) 역할을 하는 오픈소스 소프트웨어입니다. 애플리케이션 간에 메시지를 주고받을 수 있게 해주는 중간 매개체 역할을 합니다.
장점:
단점:
이거는 추후 다시 다루겠습니다.
붐빔에는 총 3가지 알림 기능이 있습니다:
기존에는 공지 API가 호출되면 다음과 같은 흐름으로 처리했습니다:
1,000명을 임의로 만들어서 테스트한 결과 이 방식의 문제는 순차적으로 처리하다 보니 응답 시간이 무려 18,000ms(18초)나 걸렸다는 것입니다. 사용자 경험상 말이 안 되는 수준이었습니다.

개별 전송을 병렬 처리로 바꾸고, 500개씩 배치로 묶어서 처리하도록 개선했습니다. 그 결과 4,067ms로 단축되었습니다. 하지만 여전히 만족스럽지 않았고, N+1 문제도 많이 발생했습니다.
사용자 조회 관련 코드와 알림 발송하는 코드를 완전히 분리하고, 이를 호출하는 별도의 시스템이 있으면 좋겠다는 생각이 들었습니다. 그래서 메시큐를 도입했고 그 중 RabbitMQ를 도입하게 되었습니다.
공지 알림 예시:

이렇게 2개로 나누어 처리하니까:
물론 RabbitMQ 자체가 비동기 방식이라서 API 응답시간만 측정한 것이고, 실제 알림 전송 완료 시간은 배치 방식과 크게 다르지 않을 겁니다. 하지만 전체적인 코드 흐름과 관심사 분리 측면에서는 확실히 개선되었습니다.

MSA를 도입하면서 가장 큰 어려움은 도메인 간 의존성이었습니다. 특히 Member 도메인이 모든 곳에 얽혀있어서 완전한 분리가 어려웠어요. 이상적으로는 Member 서비스도 별도로 분리해야 하지만, 현실적인 제약으로 인해 타협할 수밖에 없었습니다.
기존 FCM 구현 방식의 문제점:
Producer (API 서버):
// 알림 엔티티 생성
Alarm alarm = Alarm.builder()
.message(message)
.status(AlarmStatus.QUEUED)
.build();
// RabbitMQ로 메시지 전송
rabbitTemplate.convertAndSend(
"notification.exchange",
"notification.key",
alarm.getId()
);
Consumer (RabbitMQ 서버):
@RabbitListener(queues = "notification.queue")
public void processNotification(Long alarmId) {
// 배치 단위로 FCM 토큰 조회
List<String> tokens = fcmService.getActiveTokensBatch(alarmId);
// 500개씩 나누어 병렬 처리
tokens.parallelStream()
.collect(Collectors.groupingBy(it -> tokens.indexOf(it) / 500))
.values()
.parallelStream()
.forEach(batch -> fcmService.sendBatch(batch, message));
}
| 구분 | 개인별 FCM 전송 | 500건 단위 배치 처리 | MSA 기반 RabbitMQ 비동기 처리 |
| 처리 구조 | 단일 서버 내 개별 요청 순차 처리 | 단일 서버 내 일정 단위로 병렬 전송 | API 서버(Producer) ↔ 알림 서버(Consumer) 분리, 메시지 큐 기반 |
| 처리 방식 | 동기(Synchronous) | 병렬(Partial Parallel) | 완전 비동기(Asynchronous) |
| 응답 지연 시간 | 18,000 ms | 4,067 ms | 126 ms |
| 개선율(이전 단계 대비) | - | 약 77.4 % 단축 | 약 97 % 추가 단축 |
| 서버 부하 | 매우 높음 | 개선됨 (중간 수준) | 최소화됨 |
| 확장성 | 낮음 | 보통 | 높음 (대규모 트래픽 안정 처리 가능) |
| 특징 요약 | 요청 단위로 전송되어 지연 발생 | 일정 단위 병합으로 속도 개선 | MSA 및 큐 분산 처리로 실시간 성능 확보 |
1,000명을 임의로 테스트 한 결과 약 99.3%의 성능 향상을 달성했습니다!
이전에도 말씀드렸듯이 API 응답시간만 측정한 것이고, 실제 알림 전송 완료 시간은 배치 방식과 크게 다르지 않을 겁니다. 하지만 전체적인 코드 흐름과 관심사 분리 측면에서는 확실히 개선되었습니다!
스위프트 앱2기 붐빔 프로젝트에서 MSA와 RabbitMQ를 도입하면서 많은 것을 배웠습니다. 완벽한 구조는 아니지만, 현실적 제약 안에서 최대한의 성능 개선을 이뤄낼 수 있었습니다..
특히 18초에서 126ms로 알림 성능을 개선한 것은 사용자 경험 관점에서 엄청난 발전이었습니다. 앞으로도 지속적으로 아키텍처를 개선해나가면서 더 나은 서비스를 만들어가려고 합니다.
MSA 도입을 고민하고 계신 분들께는 "완벽함보다는 점진적 개선"을 추천드리고 싶습니다. 현실적 제약을 인정하면서도 개선 가능한 부분부터 차근차근 시작하는 것이 중요한 것 같습니다.
| [맞춤형 헤어 컨설팅 예약] 동시성 충돌 연구해보기 (0) | 2025.10.10 |
|---|---|
| [9oormthon 제주 버스 알림콜] 리메이크 및 조회 성능 최적화 하기 (0) | 2025.10.09 |
| [맞춤형 헤어 컨설팅 예약] Fegin Client를 활용한 카카오페이 API 기반 결제 시스템 개발 (0) | 2025.02.24 |
| 웹 서비스 실시간 채팅 구현하기 (관리자 : 사용자, AWS 환경) (8) | 2024.10.10 |
| 인텔리제이에서 파이썬 사용하기 (1) | 2024.08.04 |