메시지 본문(Body) 크기가 성능에 미치는 상관관계는?
엔터프라이즈 환경에서 메시지 브로커(ActiveMQ, RabbitMQ, Kafka 등)를 도입할 때 가장 간과하기 쉬운 아키텍처 설계 요소가 바로 '메시지의 크기(Payload Size)'입니다. 브로커를 무제한 파일 서버나 데이터베이스처럼 여기고 수 메가바이트(MB)에 달하는 JSON 배열이나 이미지, XML 데이터를 통째로 밀어 넣는 경우가 빈번하게 발생합니다.
하지만 메시지 브로커의 내부 아키텍처는 작고 빈번한 이벤트를 초고속으로 라우팅하는 데 최적화되어 있습니다. 메시지 본문(Body)의 크기가 비대해질수록 브로커의 성능은 단순한 선형적 하락을 넘어, 시스템 전체를 마비시키는 기하급수적인 장애 포인트로 돌변합니다. 메시지 크기와 성능 저하 간의 상관관계와 내부 메커니즘을 상세히 해부해 보겠습니다.

1. 메모리 점유율과 가비지 컬렉션(GC)의 역습
메시지가 브로커에 인입되면 처리 및 라우팅을 위해 가장 먼저 브로커 애플리케이션의 JVM 힙(Heap) 메모리나 시스템 메모리에 적재됩니다.
크기가 작은 메시지(수십 바이트 ~ 수 킬로바이트)는 큐에 수만 개가 쌓여도 전체 메모리에 미치는 압박이 적습니다. 하지만 10MB 크기의 메시지가 초당 100건만 유입되어도 브로커는 순식간에 1GB 이상의 메모리를 소모하게 됩니다. 메모리가 빠르게 채워지면 JVM은 여유 공간을 확보하기 위해 가비지 컬렉션(GC)을 쉴 새 없이 수행해야 합니다.
특히, 거대한 객체들을 힙 메모리에서 정리하기 위한 'Stop-the-World' GC 페이즈가 길어지면, 브로커는 그 시간 동안 어떠한 메시지도 송수신하지 못하고 완전히 멈춰버리는 치명적인 지연(Latency Spike)을 유발합니다. 이는 정상적으로 빠르게 처리되어야 할 소형 이벤트들까지 큐에 묶이게 만드는 1차적인 병목 원인입니다.
2. 디스크 I/O 병목과 극심한 페이징(Paging) 현상
대부분의 엔터프라이즈 메시징 시스템은 장애 시 데이터 유실을 막기 위해 메시지를 디스크(KahaDB, 저널 파일 등)에 기록하는 영속성(Persistence)을 기본으로 사용합니다.
메시지 본문이 클수록 디스크에 바이트를 쓰는 시간(Write Time) 자체가 물리적으로 길어집니다. 더 치명적인 문제는 큐에 메시지가 쌓여 메모리 임계치에 도달했을 때 발생합니다. 브로커는 OOM(Out of Memory)으로 스스로가 죽는 것을 방지하기 위해, 메모리에 들고 있던 메시지들을 디스크로 강제로 밀어내는 페이징 아웃(Paging Out) 작업을 시작합니다.
이후 컨슈머(Consumer)가 큐에서 이 메시지를 다시 읽어갈 때는 반대로 디스크에서 메모리로 거대한 페이로드를 끌어올리는 페이징 인(Paging In)이 발생합니다. 결과적으로 거대한 본문 크기는 브로커의 한정된 디스크 I/O 대역폭을 100% 점유하게 만들며, 전체 시스템의 메시지 처리량(Throughput)을 바닥으로 곤두박질치게 합니다.
3. 네트워크 대역폭 및 직렬화(Serialization) 오버헤드
메시지는 네트워크를 통해 송수신되어야 하므로 필연적으로 인코딩, 직렬화(Serialization) 및 역직렬화 과정을 거칩니다.
본문이 거대할수록 생산자(Producer), 브로커, 소비자(Consumer) 양단 모두에서 데이터를 바이트 배열로 변환하고 파싱하는 데 막대한 CPU 연산 자원을 소모합니다. 또한 스위치나 라우터의 한정된 네트워크 대역폭(Bandwidth)을 심하게 점유하게 되어, 크기가 작은 정상적인 핵심 비즈니스 이벤트 메시지들마저 거대한 덩치업 메시지 뒤에 갇혀 네트워크 전송이 지연되는 '선두 차단(Head-of-Line Blocking)' 현상을 초래합니다.
4. 브로커의 대용량 메시지(Large Message) 처리 메커니즘의 한계
물론 ActiveMQ나 Artemis 같은 성숙한 브로커들은 이러한 문제를 완화하기 위해 'Large Message' 지원 기능을 기본적으로 내장하고 있습니다.
특정 임계치(예: 100KB)를 초과하는 메시지가 들어오면, 브로커는 이를 일반 메모리 큐에 올리지 않고 즉시 디스크의 별도 디렉토리에 청크(Chunk) 단위로 쪼개어 파일 형태로 스트리밍 저장 및 전송합니다.
하지만 주의해야 할 점은 이 기능이 '메모리 고갈로 인한 브로커 다운'을 막기 위한 최후의 방어책일 뿐이라는 것입니다. 파일 스트리밍 방식으로 우회하더라도 막대한 디스크 I/O와 네트워크 대역폭 소비 자체를 없애주지는 못하므로 근본적인 성능 저하 문제는 여전히 남아있게 됩니다.
5. 성능 최적화를 위한 아키텍처 설계 패턴 (Best Practices)
거대한 페이로드를 분산 시스템 간에 꼭 전달해야 한다면, 브로커의 파라미터 튜닝에 집착하기보다 아키텍처 수준의 구조적 개선을 단행하는 것이 유일한 해법입니다.
- 클레임 체크 패턴 (Claim-Check Pattern): 대용량 메시지 처리를 위한 가장 강력하고 표준적인 아키텍처입니다. 거대한 본문(이미지, 대용량 JSON, 문서 파일 등)은 Amazon S3, MinIO, 혹은 RDBMS에 먼저 저장합니다. 그리고 메시지 브로커에는 "이 URL(또는 ID)에 처리해야 할 데이터가 저장되었음"을 알리는 수십 바이트 크기의 가벼운 메타데이터 뼈대만 발행합니다. 소비자는 메시지를 수신한 후 해당 식별자를 가지고 외부 저장소에서 실제 데이터를 다운로드하여 비즈니스 로직을 수행합니다. 브로커의 부하를 완벽하게 제거할 수 있습니다.
- 메시지 압축 (Compression): 페이로드가 주로 텍스트(JSON, XML)라면, 발행자(Producer) 단계에서 GZIP이나 Snappy 알고리즘으로 압축하여 전송하는 것이 네트워크 및 디스크 I/O 비용을 크게 줄여줍니다. 단, 양단 서버에서 발생하는 압축/해제 CPU 오버헤드와의 트레이드오프를 반드시 사전 벤치마킹해야 합니다.
- 메시지 분할 (Chunking/Splitting): 논리적으로 쪼갤 수 있는 컬렉션 데이터라면 (예: 1만 건의 사용자 업데이트 배치 데이터), 이를 하나의 메시지로 보내지 말고 100건씩 100개의 개별 메시지로 분할하여 전송합니다. 이는 여러 컨슈머들이 큐에서 병렬로 메시지를 빠르게 가져가 처리할 수 있도록 유도하여 전체 처리 속도를 향상시킵니다.
6. 요약 및 결론
메시지 브로커의 최적 성능은 메시지 크기가 수 킬로바이트(KB) 수준으로 작게 유지될 때 극대화됩니다. 메시지 브로커는 우편물을 배달하는 '집배원'이지, 대형 화물을 나르는 '덤프트럭'이 아닙니다. 메시지의 본문 크기와 시스템의 전체 성능(Throughput, Latency)은 정확히 반비례 관계에 있습니다. 따라서 성공적인 메시징 기반 시스템을 구축하기 위해서는 메시지 내부에 꼭 필요한 데이터만 남기고 무거운 페이로드는 외부 저장소로 분리하는 아키텍처 철학을 처음부터 고수해야 합니다.
'1. 개발 > 1.8. ActiveMQ' 카테고리의 다른 글
| 브로커가 메시지를 버리는 'Discard' 정책의 설정법은? (0) | 2026.03.17 |
|---|---|
| JMS 전용 속성(JMSX로 시작하는 속성)의 종류와 활용법은? (0) | 2026.03.16 |
| 복합 헤더 속성을 이용한 라우팅 필터링 기법은? (0) | 2026.03.16 |
| 미전달 메시지(DLQ)로 보내기 전 '최대 재시도 횟수'의 기본값과 변경법은? (0) | 2026.03.16 |
| 독점 컨슈머(Exclusive Consumer)의 우선순위 결정 방식은? (0) | 2026.03.16 |