요청 경로의 계층마다 타임아웃이 있고, 값이 어긋나면 사고가 난다.
- 클라이언트 타임아웃 — 3초
- CDN / LB 유휴 타임아웃 — 60초
- nginx proxy_read_timeout 60초
- WAS connectionTimeout — 20초
- DB 쿼리 타임아웃 — 없음 ← 여기가 구멍
어긋나면 무슨 일이 생기나
바깥이 안쪽보다 짧으면
- 클라이언트는 이미 끊었는데 서버는 계속 처리한다
- 자원 낭비. nginx 로그에 499 가 쌓인다
바깥이 안쪽보다 훨씬 길면
- 죽은 요청을 오래 붙들고 있다
- 스레드·커넥션 고갈로 이어진다
원칙 — 안쪽으로 갈수록 타임아웃이 짧아지게 잡는다
- 그리고 '없는' 타임아웃이 가장 위험하다
nginx 쪽 지시자
location /api/ {
proxy_pass http://backend;
proxy_connect_timeout 3s; # 업스트림에 연결하는 데까지
proxy_send_timeout 10s; # 요청을 보내는 중 무응답 허용 시간
proxy_read_timeout 30s; # 응답을 읽는 중 무응답 허용 시간
proxy_next_upstream error timeout http_502 http_503;
}
proxy_connect_timeout 은 짧게 (수 초)
- 연결조차 안 되는 서버를 오래 기다릴 이유가 없다
proxy_read_timeout 은 '무응답' 기준이지 전체 시간이 아니다
- 데이터가 계속 오면 갱신된다 → 스트리밍은 이 값에 안 걸린다
재시도를 함부로 켜지 않는다
proxy_next_upstream 에 non_idempotent 를 넣지 않는 한 POST 같은 비멱등 요청은 재시도하지 않는다
재시도가 중복 처리를 만들 수 있기 때문이다
- 결제가 두 번 되는 사고가 여기서 나온다
이 기본값을 함부로 바꾸지 않는다 재시도가 필요하면 애플리케이션이 멱등 키로 보호한 뒤에 켠다
유휴 타임아웃도 계층마다 있다
-
keepalive_timeout — nginx ↔ 클라이언트
-
keepalive (upstream) — nginx ↔ 업스트림
-
keepAliveTimeout — WAS
-
LB 유휴 타임아웃 — 클라우드 LB (AWS ALB 기본 60초)
-
앞단의 유휴 타임아웃이 뒤보다 길면
- 뒤가 먼저 끊은 연결을 앞단이 재사용하려다 502 가 난다
-
앞단 값을 뒤보다 '짧게' 잡는 것이 안전하다
점검 방법
계층별 타임아웃을 한 표에 적어 두는 것만으로 절반이 해결된다
확인
- curl -w 'conn=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
- -o /dev/null -s https://example.com
- 어느 구간에서 시간이 가는지 분해해 보여 준다
표로 적어 두는 것이 절반이다
계층 연결 읽기 유휴
──────────────────────────────────────────────
클라이언트(앱) 3s 10s -
CDN - 30s -
클라우드 LB - - 60s
nginx 3s 30s 65s
WAS - 20s 20s
DB 쿼리 1s 5s -
이 표가 없으면 어긋난 것을 발견할 방법이 없다
장애 회고에서 가장 자주 나오는 산출물이기도 하다
타임아웃이 없는 곳을 찾는다
가장 위험한 것은 '설정하지 않은' 타임아웃이다
흔히 빠지는 곳
-
DB 쿼리 타임아웃 (드라이버 기본이 무한인 경우가 있다)
-
외부 API 호출 (HTTP 클라이언트 기본값 확인)
-
커넥션 풀 획득 대기
-
분산 락 획득 대기
-
하나라도 무한이면 그 지점에서 스레드가 영원히 묶인다
-
코드 리뷰 체크리스트에 "이 호출의 타임아웃은?" 을 넣는다
면접 함정
- ❌ "타임아웃은 길게 잡는 게 안전하다" → 죽은 요청이 자원을 붙든다.
- ❌ "타임아웃이 걸리면 재시도하면 된다" → 비멱등 요청은 중복 처리가 된다.