긴 작업 비동기 처리와 진행률 — 202 Accepted, 폴링, SSE
대용량 Excel 다운로드나 월 정산처럼 오래 걸리는 작업을 요청 스레드에서 바로 처리하지 않고, 즉시 202 로 응답한 뒤 상태 조회 API 로 진행률을 알려주는 패턴을 JDK 만으로 만들어 봅니다.
1. 왜 배우는가
사내 시스템에는 "엑셀로 다운로드", "월말 정산 실행", "전 직원에게 메일 발송" 같은 버튼이 흔합니다. 이런 작업은 몇 초에서 몇 분까지 걸립니다. 이 처리를 HTTP 요청 스레드 안에서 그대로 실행하면 여러 문제가 한꺼번에 터집니다.
Tomcat 스레드 풀은 개수가 제한돼 있습니다. 긴 작업이 스레드 하나를 몇 분간 붙잡으면, 그만큼 다른 요청을 받을 여유가 줄어듭니다. 동시에 여러 명이 정산 버튼을 누르면 스레드 풀이 금방 바닥납니다.
nginx 는 기본 proxy_read_timeout 이 지나면 504 를 반환합니다. 브라우저는 504 를 받으면 사용자가 새로고침하거나, 프런트 코드가 재시도 로직으로 같은 요청을 다시 보내기도 합니다. 서버 입장에서는 이미 실행 중인 정산 작업이 중복으로 한 번 더 시작되는 최악의 상황입니다.
해결책은 요청과 실행을 분리하는 것입니다. 제출 요청은 "접수했다"는 응답만 즉시 돌려주고, 실제 작업은 별도 스레드가 백그라운드에서 처리합니다. 클라이언트는 상태 조회 API 로 진행률을 확인하다가, 끝나면 결과를 내려받습니다.
이 레슨은 이 패턴을 JDK 의 HttpServer 와 ExecutorService 만으로 구현하고, 폴링과 SSE(Server-Sent Events) 두 가지 진행률 전달 방식을 비교합니다. 코드는 java-src/extension/16_async_job 에 있고 Main·JobManager 두 파일로 구성됩니다.