SseEmitter 자원 고갈 방지 전략
- Ingress Gateway 버퍼링 제어와 브라우저 단일 커넥션 관리를 중심으로 -
1. 서론: 분산 환경에서의 실시간 통신과 자원 관리
Kubernetes 기반 마이크로서비스 아키텍처(MSA)에서 실시간 데이터 피드백은 사용자 경험을 결정짓는 핵심 요소이다. 본 팀은 서버 부하, 구현 복잡도, 프로토콜 표준 준수를 종합적으로 판단하여 SSE(Server-Sent Events)를 실시간 알림의 핵심 프로토콜로 채택하였다. 그러나 운영 환경에서 SSE를 도입하는 과정에서 Istio Ingress Gateway를 통과하며 발생하는 데이터 지연 및 연결 유실 문제와 직면하였다. 본 문서에서는 실제 운영 환경의 트래픽 흐름을 분석하여 문제의 발생 지점을 식별하고, Ingress Gateway–애플리케이션–클라이언트 각 계층에서 수립한 최적화 전략을 분석한다.
2. 기술적 의사결정: SSE 선택 근거
알림 서비스는 서버에서 발생한 이벤트를 클라이언트로 푸시하는 단방향(Unidirectional) 모델이 적합하다. WebSocket의 전이중(Full-duplex) 통신은 불필요한 TCP 핸드셰이크와 프로토콜 전환 오버헤드를 야기하며, SSE는 표준 HTTP 프로토콜을 그대로 활용하므로 L7 로드밸런서, 보안 필터, Istio 인프라를 별도 설정 없이 통과할 수 있다. 또한 브라우저 네이티브 자동 재연결(retry)과 Last-Event-ID 헤더를 통한 유실 데이터 복구를 지원하여 유지보수 효율성이 향상된다.

3. 문제 식별: Ingress Gateway의 SSE 스트림 간섭
로컬 개발 환경에서는 정상 동작하던 SSE 스트림이 운영 환경에 배포되자 두 가지 증상이 관측되었다. 첫째, 이벤트가 실시간으로 도달하지 않고 수 초 단위로 뭉쳐서 전달되는 청크 딜레이(Chunk Delay) 현상이 발생하였다. 둘째, 일정 시간 이벤트가 없으면 연결이 자동으로 끊기는 유휴 연결 강제 종료 현상이 반복되었다.
운영 환경의 트래픽 흐름을 분석한 결과, 외부 트래픽이 클러스터에 진입하는 유일한 프록시 지점인 Istio Ingress Gateway(Envoy)가 이 두 가지 문제의 직접적인 원인임을 확인하였다. Ingress Gateway의 Envoy는 네트워크 효율을 위해 응답을 내부 버퍼에 집적한 후 전송하며, 기본 타임아웃 설정에 의해 유휴 커넥션을 자동 종료한다. 일반적인 REST API에서는 유익한 동작이지만, 장시간 연결을 유지하며 소량의 데이터를 실시간으로 흘려보내야 하는 SSE 스트림에는 치명적인 제약이었다.
4. 인프라 최적화: Ingress Gateway Envoy 제어
4.1 응답 버퍼링 우회
Ingress Gateway의 Envoy가 SSE 응답을 버퍼링하는 문제를 해결하기 위해 애플리케이션 응답 헤더에 X-Accel-Buffering: no를 명시적으로 삽입하였다. Envoy는 이 헤더를 감지하는 즉시 해당 요청에 대한 버퍼링 로직을 우회(Bypass)하며, 서버가 데이터를 쓰는 즉시 클라이언트로 패킷을 송출한다. 동시에 Content-Type: text/event-stream을 명확히 설정하여 해당 응답이 실시간 스트리밍 데이터임을 인프라 계층에 선언하였다.
4.2 스트림 타임아웃 해제
Ingress Gateway의 기본 타임아웃 설정은 이벤트 미발생 구간의 유휴(Idle) 커넥션을 강제 종료시키는 병목이 된다. SSE 전용 경로를 분리하고 해당 경로에 대해 스트림 타임아웃을 해제함으로써 Gateway가 세션을 장시간 유지하도록 허용하였다. 이를 통해 클라이언트의 불필요한 재연결 시도로 인한 서버 부하 가중을 원천 차단하였다.

5. 애플리케이션 최적화: Keep-alive 전략
인프라 설정과 별개로, 실제 네트워크 경로상의 중간 장비들은 일정 시간 이상 패킷이 없는 커넥션을 유휴 상태로 간주하여 종료할 수 있다. 이를 방지하기 위해 서버 애플리케이션에서 실제 알림 이벤트 유무와 무관하게 주기적으로 빈 데이터를 송출하는 Keep-alive(Heartbeat) 전략을 도입하였다. 이 방식은 Ingress Gateway의 유휴 타임아웃에 의한 강제 종료를 방지하는 동시에, 서버가 IOException을 통해 클라이언트의 비정상 종료를 즉각 감지하여 SseEmitter 자원을 신속히 회수함으로써 메모리 누수를 방지한다.
6. 클라이언트 최적화: 커넥션 단일화
인프라와 서버의 최적화가 완료되었음에도 사용자가 브라우저 탭을 복수 개 열 때마다 독립적인 SseEmitter 객체가 생성되어 서버 힙 메모리를 점유하는 문제가 존재하였다. 이를 해결하기 위해 sessionStorage를 연결 상태 공유 저장소로 활용하는 타임스탬프 기반 커넥션 점유 로직을 설계하였다. 새로운 탭이 열릴 때 LAST_CONN_TIME을 확인하여 현재 시간과의 차이가 임계치(예: 5초) 이내라면 추가 연결을 차단하고, 메인 연결이 유지되는 동안 타임스탬프를 주기적으로 갱신하여 세션 생존을 증명한다.
7. 결론
본 프로젝트를 통해 단순한 기능 구현을 넘어 전체 시스템 가용성 확보라는 유의미한 성과를 달성하였다. Ingress Gateway의 Envoy 버퍼링을 제어하여 이벤트 지연을 최소화하였고, Gateway 타임아웃 해제와 애플리케이션 Heartbeat 전송이라는 계층적 방어 전략을 통해 스트림 안정성을 극대화하였다. 아울러 sessionStorage 제어 로직으로 서버 커넥션 점유를 세션 단위로 단일화함으로써 힙 메모리 고갈 위험을 사전에 차단하였다.
결국 코드가 실행되는 인프라 환경과 사용자가 머무르는 브라우저 환경 각각에서 문제의 발생 지점을 정확히 식별하고, 계층에 맞는 해결책을 수립하는 것이 실질적인 엔지니어링 최적화의 핵심이었다.
Pancake Maker