1. 비동기 통신의 필요성
우리 프로젝트는 AI 개선안 생성과 원격 보안 점검처럼, 요청 시 즉시 결과가 나오지 않고 백그라운드에서 일정 시간이 소요되는 비동기 작업을 수행합니다.
이런 경우 “언제 끝날지 모르는 작업의 완료 시점”을 클라이언트가 알아차릴 수 있도록 해야 합니다.
즉, 서버에서 처리 중인 상태를 사용자에게 실시간으로 알려주는 통신 방식이 필요합니다.
실서비스에서도 다음 세 가지 패턴 중 하나로 해결합니다.
- 폴링(Polling) — 주기적으로 상태 확인 (짧은 폴링 / 롱 폴링)
- 실시간 푸시(Streaming) — 서버에서 이벤트를 직접 보냄 (SSE / WebSocket)
- 콜백(Webhook) — 서버가 제3의 시스템(대시보드, 외부 서버 등)에 완료 신호를 전송
우리 프로젝트는 이 중 **“사용자 단말(브라우저)에서 AI 결과 완료를 즉시 받는 구조”**가 필요하므로, 웹훅보다는 SSE 또는 롱 폴링 기반 구조가 적합합니다.
2. 주요 통신 방식 정리
| 구분 | 개념 | 특징 | 장점 | 단전 |
| 짧은 폴링(Short Polling) | 클라이언트가 일정 주기로 /status를 조회 | 구현이 매우 간단 | 캐시/프록시 친화, 브라우저 제약 없음 | 요청 수 많음, 서버/네트워크 낭비 |
| 롱 폴링(Long Polling) | 서버가 응답을 지연시켜 결과 생길 때 반환 | SSE 미지원 환경에서 적합 | 네트워크 효율↑, 폴링 대비 지연↓ | 동시 연결 많을 경우 스레드 점유 |
| SSE(Server-Sent Events) | 서버→클라이언트 단방향 실시간 푸시 | 텍스트 스트림 기반, 자동 재연결 | 구현 간단, HTTP 친화, 프록시 통과 용이 | 브라우저→서버 역방향 통신 불가 |
| WebSocket | 양방향 연결로 실시간 상호작용 | 채팅/협업/게임 등에서 활용 | 완전 양방향, 낮은 지연 | 운영 복잡도 높음, LB 스티키 필요 |
| 웹훅(Webhook) | 서버→서버 콜백으로 알림 전송 | 외부 통합용 | 비동기 API 통합에 적합 | 인증/보안 설정 필요 |
3. 우리 프로젝트 요구 분석
| 항목 | 설명 | 비고 |
| 작업 특성 | AI 개선안 생성, 보안 점검 등 → 처리 완료 시점 불명확 | 비동기성 |
| 클라이언트 환경 | React 프론트엔드 (브라우저 기반) | HTTP 친화적 필요 |
| 서버 구조 | Spring Boot 백엔드 | SSE, 롱폴링 둘 다 구현 용이 |
| 요구 기능 | 결과 생성 완료 시 즉시 알림 | 실시간 푸시형 |
| 양방향 통신 필요 여부 | X (요청은 기존 REST, 알림만 단방향) | SSE 적합 |
| 운영 환경 | Nginx Reverse Proxy, HTTPS | HTTP 스트림 안정 통과 필요 |
→ 결론적으로, SSE(Server-Sent Events) 와 롱 폴링(Long Polling) 이 가장 현실적 선택지입니다.
4. 시나리오 A: SSE
원리
서버에서 클라이언트로 단방향 스트림 연결을 유지하며, 이벤트가 생길 때마다 데이터를 푸시합니다.
HTTP 헤더는 Content-Type: text/event-stream 으로 설정하며, 클라이언트는 EventSource 객체를 통해 구독합니다.
구조
Client (EventSource)
↓
Nginx (proxy_buffering off)
↓
Spring Boot (SseEmitter)
↓
DB (상태 저장)
장점
- 자동 재연결, Last-Event-ID 지원
브라우저에서 연결이 끊겨도 자동 복구 가능. - HTTP 기반이라 LB, Nginx, 프록시 환경에서 안전하게 동작.
- 구현 난이도 낮음 (SseEmitter 하나로 충분).
단점
- 단방향(서버→클라이언트) 통신만 가능.
- 대규모 연결 환경에서는 커넥션 유지 비용 증가.
코드 예시 (Spring Boot)
// Publisher
@Component
public class TestEventPublisher {
private final SseEmitterManager manager; // testId -> SseEmitter 보관
public void publishDone(UUID testId, TestDoneEvent payload) {
manager.send(testId, SseEmitter.event()
.name("done")
.id(payload.getSeq())
.data(payload));
}
}
// Controller
@GetMapping(value="/sse/tests/{testId}", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@PathVariable UUID testId, Principal p) {
SseEmitter emitter = new SseEmitter(0L); // 무제한 또는 LB idle 고려
manager.register(testId, emitter, p);
return emitter;
}
5. 시나리오 B: 롱 폴링
원리
클라이언트가 /wait 요청을 보내면,
서버는 결과가 준비될 때까지 응답을 일시 지연시킨 후 결과를 반환합니다.
타임아웃(timeout=30s)이 지나면 204(No Content)를 반환하며,
클라이언트는 다시 요청을 보내 재대기합니다.
구조
Client (Axios GET /wait)
↓
Spring Boot (DeferredResult)
↓
DB 상태 변경 감지 시 complete()
장점
- 모든 브라우저, 모든 프록시 환경에서 안정적으로 작동.
- SSE 미지원 환경에서도 동작.
- 커넥션 유지 시간이 짧아 운영 부담 적음.
단점
- 재요청 오버헤드 존재.
- 응답 사이에 지연이 발생할 수 있음.
코드 예시
@GetMapping("/api/tests/{id}/wait")
public DeferredResult<ResponseEntity<?>> wait(@PathVariable String id) {
DeferredResult<ResponseEntity<?>> result = new DeferredResult<>(30000L);
asyncService.register(id, result);
return result;
}
6. A/B 테스트 설계
두 방식의 장단점을 실제 환경에서 계측해보기 위해,
SSE 시나리오(A) 와 롱 폴링 시나리오(B) 를 동시에 구성합니다.
테스트 플로우
| 구분 | SSE | 롱 폴링 |
| 작업 요청 | POST /api/tests → testId | 동일 |
| 진행 이벤트 | GET /api/tests/{id}/events | GET /api/tests/{id}/wait?timeout=30s |
| 완료 시 동작 | 서버가 즉시 이벤트 전송 | 서버가 즉시 응답 반환 |
| 끊김 복구 | 자동 재연결 | 재요청 필요 |
측정 지표
| 지표 | 설명 |
| Latency(지연) | 작업 완료 시점 → 프런트 수신 시점(ms) |
| Network Cost | 요청 수, 데이터 전송량 |
| Server Load | CPU 사용률, 커넥션 점유율 |
| Error/Timeout Rate | 4xx/5xx, 재연결/재요청 횟수 |
| UX 지표 | 스피너 유지 시간, 사용자 체감 응답성 |
시뮬레이션 환경
- N=100, 1k, 10k 동시 요청 시점별 부하 테스트
- LB/Nginx idle timeout, 재연결 처리 검증
- 서버 롤링 배포 중 스트림 끊김 대응 확인
예상 가설
- SSE는 지연(latency) 면에서 우수하지만,
동시 커넥션 수가 많을 때 부하 증가 가능. - 롱 폴링은 부하 안정성이 높지만,
요청 횟수 증가로 네트워크 오버헤드 발생 가능.
7. 결론
- 기본 구조는 SSE(Server-Sent Events) 로 운영합니다.
→ Spring + Nginx 환경에 가장 자연스럽고, 완료 이벤트를 즉시 전달 가능. - 대안 및 백업 채널로 롱 폴링(Long Polling) 을 구성합니다.
→ SSE가 불가능한 환경(네트워크 차단, 레거시 브라우저 등)에서 폴백으로 사용. - A/B 테스트를 통해 계량적 데이터 확보 후 결정
→ 실제 트래픽 기반으로 SSE 연결 유지율, 롱 폴링 응답 속도, 서버 커넥션 수를 측정합니다.
→ 이후 인프라 스케일링 전략에 반영합니다.
'프로젝트 > 웹 성능 테스트' 카테고리의 다른 글
| 장애 대응을 위한 메트릭 모니터링 단 구축(1. Actuator + Micrometer) (0) | 2025.11.28 |
|---|---|
| 롱폴링 비동기 완료 (0) | 2025.11.27 |
| Nginx 도입 2편: 적용 (0) | 2025.11.09 |
| Nginx 도입 1편: 왜 Nginx (0) | 2025.11.06 |
| Server Sent Events(SSE): 구현 (0) | 2025.11.02 |