infra
Platform

모듈 맵

[Network] Connection Timeout과 Read Timeout 장애 실전 디버깅

0 / 35 완료

펼치기
0 / 35 완료0%

Networking · 17 / 35

[Network] Connection Timeout과 Read Timeout 장애 실전 디버깅

connection timeout vs read timeout vs gateway timeout — curl 타이밍 분석과 nc TCP 점검으로 실무 API 장애를 계층별로 추적합니다

🚨INCIDENT ALERT
HIGH

외부 API 호출이 가끔 30초씩 멈추며 워커가 쌓입니다. HTTP 500도 아니고 로그에는 timeout만 남아 있어 서버 문제인지 네트워크 문제인지 헷갈립니다.

connect timeout과 read timeout을 구분해야 원인 계층이 보입니다. curl 타이밍을 보면 어디서 시간이 사라졌는지 알 수 있습니다.

HTTP 연결·읽기 타임아웃 실전 디버깅

새벽 2시, 슬랙 알림이 연달아 옵니다. "결제 API 호출이 30초 후에 실패한다." 고객은 체크아웃 버튼을 눌렀지만 화면이 멈췄고, 30초 뒤 에러 페이지로 떨어집니다. 로그에는 curl: (28) Operation timed out after 30000 milliseconds가 찍혀 있습니다. 네트워크 문제인지, 서버 문제인지, 아니면 코드 문제인지 — 타임아웃 에러 하나로는 원인을 알 수 없습니다. 이 챕터에서는 타임아웃을 종류별로 정의하고, curl 타이밍 분석과 nc TCP 점검으로 문제의 계층을 정확히 특정하는 방법을 배웁니다.


이번 챕터에서 배울 것
  • 1Connection Timeout, Read Timeout, Gateway Timeout의 정확한 구분
  • 2curl --connect-timeout / --max-time 옵션 차이와 실험
  • 3curl -w '@curl-format.txt'로 time_connect / time_starttransfer / time_total 분석
  • 4nc -zv로 TCP 연결만 분리해서 점검하는 방법
  • 5502 / 503 / 504 에러가 각각 어느 계층 문제인지 판별하는 플로우
  • 6백엔드 개발자가 외부 API 연동 시 타임아웃을 설정하는 실무 기준
실습 환경 준비
curl 설치 확인
curl --version
nc(netcat) 설치 확인
nc -h 2>&1 | head -3 || echo 'nc not found: sudo apt-get install -y netcat-openbsd'
타이밍 분석용 포맷 파일 생성
printf 'time_namelookup: %%{time_namelookup}\ntime_connect: %%{time_connect}\ntime_starttransfer: %%{time_starttransfer}\ntime_total: %%{time_total}\n' > /tmp/curl-format.txt && cat /tmp/curl-format.txt
테스트 서버 접근 확인 (httpbin.org)
curl -s -o /dev/null -w '%{http_code}' https://httpbin.org/get
느린 응답 시뮬레이션 엔드포인트 확인

httpbin.org/delay/{seconds}를 사용하면 지정한 초만큼 응답을 지연시킵니다. 예: https://httpbin.org/delay/5 는 5초 후 응답합니다.


💡개념

Connection Timeout vs Read Timeout vs Gateway Timeout

타임아웃 에러를 마주했을 때 가장 먼저 할 일은 "어느 단계에서 멈췄는가"를 판별하는 것입니다. 타임아웃에는 종류가 있고, 종류마다 원인이 다른 계층에 있습니다.

Connection Timeout vs Read Timeout vs Gateway Timeout

HTTP 요청의 3단계

클라이언트                    서버
    |                          |
    |--- TCP SYN ----------->  |  ← 1단계: TCP 연결 (Connection)
    |<-- SYN-ACK -----------  |
    |--- ACK --------------->  |
    |                          |
    |--- HTTP GET /api ---->   |  ← 2단계: 요청 전송
    |                          |
    |          [서버 처리 중]   |  ← 여기서 막히면 Read Timeout
    |                          |
    |<-- HTTP 200 OK --------  |  ← 3단계: 응답 수신

타임아웃 3종 비교

타임아웃 종류어느 단계에서 발생curl 옵션원인
Connection TimeoutTCP 3-way handshake 단계--connect-timeout서버 다운, 방화벽 차단, 잘못된 IP/포트
Read Timeout (Max-Time)HTTP 요청 전송 후 응답 대기 단계--max-time앱 서버 행(hang), DB 쿼리 지연, 스레드 풀 고갈
Gateway Timeout프록시/게이트웨이가 업스트림 대기서버(Nginx) 설정클라이언트가 아닌 서버 계층의 타임아웃, 504 응답으로 나타남

왜 두 가지를 분리해서 설정해야 하는가

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part3/exam_17 && cd /tmp/networking/part3/exam_17

# 잘못된 설정 — max-time만 설정
curl --max-time 30 https://api.example.com

# 올바른 설정 — 두 단계를 각각 제어
curl --connect-timeout 5 --max-time 30 https://api.example.com
🔍실행 후 확인할 것
  • 핵심 출력명령 결과에서 성공/실패를 가르는 값을 먼저 확인합니다
  • 대상 식별IP, 포트, 인터페이스, 프로세스명처럼 다음 조치를 결정하는 필드를 봅니다
  • 다음 분기결과가 기대와 다르면 어느 계층을 이어서 점검할지 정합니다

--connect-timeout만 없으면 서버가 다운됐을 때 시스템 기본값(보통 수 분)만큼 TCP 연결 시도를 기다립니다. 두 옵션을 분리하면 "TCP 연결 자체가 안 되는 경우(인프라 문제)"와 "연결은 됐지만 응답이 느린 경우(앱 문제)"를 구분해서 빠르게 원인을 좁힐 수 있습니다.


실습 1: --connect-timeout vs --max-time 차이 실험

실제로 두 옵션이 어떻게 다르게 동작하는지 확인합니다. httpbin.org/delay/{n}n초 후에 응답하는 테스트 엔드포인트입니다.

1connect-timeout 동작 확인 — 존재하지 않는 IP 연결 시도

응답 없는 IP로 TCP 연결을 시도해 connect-timeout이 작동하는 방식을 직접 확인합니다. 3초 후 에러가 발생해야 정상입니다.

로컬 또는 서버
# 존재하지 않는 IP로 TCP 연결 시도 — connect-timeout이 작동
curl --connect-timeout 3 http://10.255.255.1/test
# 결과: curl: (28) Connection timed out after 3001 milliseconds
# → TCP handshake가 3초 내에 완료되지 않아 중단

# httpbin.org는 TCP 연결이 빠르므로 connect-timeout 3초 내에 성공
curl --connect-timeout 3 https://httpbin.org/delay/5
# → TCP 연결은 성공, 이후 5초 응답 대기 (connect-timeout은 이미 통과)
# → max-time 없으면 5초 후 정상 응답 수신
curl --connect-timeout 3 http://10.255.255.1/test
🔍실행 후 확인할 것
  • 10.255.255.1 요청: curl: (28) Connection timed out after 3001 milliseconds
  • httpbin.org/delay/5 요청: TCP 연결 후 5초 기다렸다가 정상 응답 수신 (connect-timeout은 통과)
  • 두 결과 차이: connect-timeout은 TCP 연결 단계만 제어함을 확인
2max-time 동작 확인 — 전체 응답 시간 제한

응답이 5초 걸리는 엔드포인트에 max-time 3초를 설정합니다. TCP 연결이 성공해도 전체 시간이 초과되면 중단됩니다.

로컬 또는 서버
# 응답이 5초 걸리는데, max-time을 3초로 설정
curl --connect-timeout 5 --max-time 3 https://httpbin.org/delay/5
# 결과: curl: (28) Operation timed out after 3001 milliseconds with 0 bytes received
# → TCP 연결은 성공했지만, 전체 시간(연결+응답) 3초 초과로 중단

# max-time을 충분히 주면 정상 응답
curl --connect-timeout 5 --max-time 10 https://httpbin.org/delay/5
# 결과: 5초 후 JSON 응답 수신
curl --connect-timeout 5 --max-time 3 https://httpbin.org/delay/5
🔍실행 후 확인할 것
  • max-time 3: curl: (28) Operation timed out after 3001 milliseconds with 0 bytes received
  • max-time 10: 5초 대기 후 JSON 응답 수신 성공
  • 두 실험으로 connect-timeout(TCP 연결 단계)과 max-time(전체 단계) 차이 명확히 구분
3두 옵션 조합 — 결제 API 현장 재현

실무 설정인 "연결은 빠르게, 총 시간은 넉넉하게"를 직접 실험합니다. TCP 연결이 성공해도 8초 응답은 10초 max-time 안에 들어오지 못합니다.

로컬 또는 서버
# 현장 설정: "연결은 빠르게, 총 시간은 넉넉하게"
curl -v \
  --connect-timeout 3 \
  --max-time 10 \
  https://httpbin.org/delay/8

# 출력 예시:
# * Connected to httpbin.org (...) port 443 (#0)  ← connect-timeout 통과
# ...
# curl: (28) Operation timed out after 10001 milliseconds  ← max-time 초과
curl -v --connect-timeout 3 --max-time 10 https://httpbin.org/delay/8
🔍실행 후 확인할 것
  • Connected to httpbin.org 메시지: TCP 연결 성공 (connect-timeout 통과)
  • curl: (28) Operation timed out after 10001 milliseconds: max-time 초과
  • --connect-timeout 3 --max-time 10은 '3초 내 연결 + 나머지 7초 내 응답'을 의미

핵심 관찰: TCP 연결이 성공해도 max-time이 짧으면 응답 수신 중에 중단됩니다. --connect-timeout 3 --max-time 10은 "3초 내에 연결되고, 연결 후 남은 7초 내에 응답이 와야 한다"는 의미입니다.


💡개념

curl -w '@curl-format.txt'로 타이밍 분석하기

curl은 -w 옵션으로 요청의 각 단계별 소요 시간을 밀리초 단위로 측정할 수 있습니다. 이 수치를 보면 "DNS가 느린지, TCP가 느린지, 서버 처리가 느린지"를 정확히 파악할 수 있습니다.

curl-format.txt 내용

time_namelookup:    %{time_namelookup}
time_connect:       %{time_connect}
time_starttransfer: %{time_starttransfer}
time_total:         %{time_total}

파일로 저장해두고 -w "@파일명" 형태로 사용합니다.

각 변수의 의미

요청 시작
    │
    ├─ time_namelookup    ← DNS 조회 완료까지 경과 시간
    │
    ├─ time_connect       ← TCP 3-way handshake 완료까지 경과 시간
    │                        (HTTPS라면 TLS handshake 직전까지)
    │
    ├─ time_starttransfer ← 첫 번째 바이트(응답 헤더의 첫 줄)를 받기까지 경과 시간
    │                        = DNS + TCP + TLS + 서버 처리 시간의 합
    │
    └─ time_total         ← 응답 바디 수신 완료까지 전체 경과 시간

타이밍으로 병목 계층 판별

패턴의미원인
time_namelookup이 1초 이상DNS 조회 지연DNS 서버 느림, /etc/resolv.conf 문제
time_connect - time_namelookup이 큼TCP 연결 지연방화벽, 네트워크 경로 문제
time_starttransfer - time_connect이 큼서버 처리 지연앱 로직 느림, DB 쿼리 지연, TLS 협상 포함
time_total - time_starttransfer이 큼응답 바디 전송 지연큰 페이로드, 느린 네트워크

실습 2: curl 타이밍 분석으로 병목 구간 찾기

4curl-format.txt 생성 및 정상 응답 기준선 측정

타이밍 분석용 포맷 파일을 만들고 정상 상태의 각 단계별 소요 시간을 측정합니다. 이 수치가 이후 비교의 기준선이 됩니다.

로컬 터미널
cat > /tmp/curl-format.txt << 'EOF'
time_namelookup:    %{time_namelookup}
time_connect:       %{time_connect}
time_starttransfer: %{time_starttransfer}
time_total:         %{time_total}
EOF

curl -s -o /dev/null \
  -w "@/tmp/curl-format.txt" \
  https://httpbin.org/get

# 예시 출력:
# time_namelookup:    0.004521
# time_connect:       0.089234
# time_starttransfer: 0.312456
# time_total:         0.312901
curl -s -o /dev/null -w '@/tmp/curl-format.txt' https://httpbin.org/get
🔍실행 후 확인할 것
  • time_namelookup: DNS 조회 시간 — 정상 범위 1~10ms
  • time_connect: TCP 연결까지 누적 시간 — 국내 서버는 보통 10ms 미만
  • time_starttransfer: 첫 바이트 수신까지 시간 — 서버 처리 시간의 핵심 지표
  • time_total: 전체 완료 시간
5느린 응답에서 병목 구간 특정

응답이 3초 걸리는 엔드포인트에서 타이밍을 측정합니다. time_starttransfer - time_connect 값이 크면 서버 처리 단계가 병목입니다.

로컬 또는 서버
curl -s -o /dev/null \
  --connect-timeout 5 \
  --max-time 15 \
  -w "@/tmp/curl-format.txt" \
  https://httpbin.org/delay/3

# 예시 출력:
# time_namelookup:    0.004210
# time_connect:       0.088901
# time_starttransfer: 3.091234  ← 서버 처리에 3초 소요
# time_total:         3.091678
curl -s -o /dev/null --connect-timeout 5 --max-time 15 -w '@/tmp/curl-format.txt' https://httpbin.org/delay/3
🔍실행 후 확인할 것
  • time_starttransfer - time_connect ≈ 3.00초: DNS도 TCP도 정상, 서버 처리만 느림
  • 이 패턴은 백엔드 앱 지연 또는 DB 쿼리 지연을 의심해야 하는 신호
  • time_namelookup이 1초 이상이면 DNS 문제, time_connect가 크면 네트워크/방화벽 문제
6반복 측정으로 간헐적 지연 패턴 확인

5회 반복 측정으로 간헐적 지연(특정 요청만 느린 패턴)을 찾아냅니다. 한 번만 측정하면 놓치는 이상값을 발견할 수 있습니다.

로컬 터미널
for i in {1..5}; do
  curl -s -o /dev/null \
    -w "run$i: connect=%.3f starttransfer=%.3f total=%.3f\n" \
    --connect-timeout 5 --max-time 10 \
    https://httpbin.org/get \
    2>/dev/null
done
for i in {1..5}; do curl -s -o /dev/null -w 'run$i: connect=%.3f starttransfer=%.3f total=%.3f\n' --connect-timeout 5 --max-time 10 https://httpbin.org/get 2>/dev/null; done
🔍실행 후 확인할 것
  • 5회 중 특정 회차만 starttransfer가 크면 간헐적 앱 서버 지연
  • 모든 회차에서 connect가 크면 네트워크 경로 문제
  • 전체가 일정하면 서버 부하는 안정적인 상태

실습 3: nc -zv로 TCP 연결만 분리해서 점검

curl이 타임아웃된 상황에서 가장 먼저 해야 할 질문은 "TCP 연결 자체가 되는가?"입니다. nc(netcat)는 HTTP 없이 순수 TCP 연결만 시도하므로 계층을 분리할 수 있습니다.

7nc -zv로 TCP 연결 점검

nc는 TCP 3-way handshake만 시도하고 HTTP는 보내지 않습니다. curl이 타임아웃됐을 때 TCP 계층 문제인지 앱 계층 문제인지 구분하는 첫 번째 단계입니다.

로컬 터미널
# nc -zv <호스트> <포트>
# -z: 연결만 하고 데이터는 보내지 않음 (zero I/O mode)
# -v: verbose — 결과를 터미널에 출력
# -w 3: 3초 내에 연결 안 되면 포기

nc -zv -w 3 httpbin.org 443
# 성공: Connection to httpbin.org 443 port [tcp/https] succeeded!
# 실패: nc: connect to httpbin.org port 443 (tcp) failed: Connection refused
#       nc: connect to httpbin.org port 443 (tcp) timed out
nc -zv -w 3 httpbin.org 443
🔍실행 후 확인할 것
  • succeeded: TCP 연결 정상 — curl 타임아웃이라면 앱 계층(HTTP/앱서버) 문제
  • Connection refused: 서버 도달했으나 포트가 닫힘 — 앱 프로세스 미실행 확인
  • timed out: 패킷이 사라짐 — 방화벽 DROP 또는 서버 다운
8포트별 일괄 점검 스크립트

여러 포트를 순서대로 점검해 어떤 포트가 열려 있는지 한눈에 파악합니다. 장애 초기 대응에서 서비스 포트 상태를 빠르게 확인할 때 사용합니다.

로컬 터미널
# 여러 포트를 한 번에 점검
for port in 80 443 8080 8443; do
  result=$(nc -zv -w 2 api.example.com $port 2>&1)
  if echo "$result" | grep -q "succeeded"; then
    echo "PORT $port: OPEN"
  else
    echo "PORT $port: CLOSED/FILTERED"
  fi
done
for port in 80 443 8080 8443; do result=$(nc -zv -w 2 api.example.com $port 2>&1); if echo "$result" | grep -q 'succeeded'; then echo "PORT $port: OPEN"; else echo "PORT $port: CLOSED/FILTERED"; fi; done
🔍실행 후 확인할 것
  • OPEN 포트와 CLOSED/FILTERED 포트 목록 확인
  • 예상 포트가 CLOSED이면 방화벽/Security Group 또는 앱 미실행 확인
  • nc + curl 조합 진단: nc 성공 + curl 타임아웃 = 앱 계층 문제로 범위 좁힘

💡개념

502 / 503 / 504 — 어느 계층 문제인지 판별 플로우

게이트웨이(Nginx, API Gateway, 로드밸런서)가 있는 환경에서 5xx 에러는 "게이트웨이가 업스트림 서버와의 통신에서 겪은 문제"를 나타냅니다. 에러 코드별로 문제의 계층이 다릅니다.

5xx 에러 계층 구분

[클라이언트] → [게이트웨이/Nginx] → [업스트림 앱 서버] → [DB]

500 Internal Server Error:
  └─ 업스트림 앱 서버 내부에서 예외 발생 (코드 버그, DB 연결 실패 등)
     게이트웨이는 앱의 오류 응답을 그대로 클라이언트에 전달

502 Bad Gateway:
  └─ 게이트웨이가 업스트림에 연결했지만 유효하지 않은 응답 수신
     (업스트림이 HTTP가 아닌 응답 반환, 프로토콜 오류, 앱 크래시로 인한 연결 끊김)

503 Service Unavailable:
  └─ 게이트웨이가 업스트림에 연결 자체를 거부당함
     (업스트림 서버가 다운, 포트를 열지 않음, 과부하로 연결 거부)

504 Gateway Timeout:
  └─ 게이트웨이가 업스트림에 연결했지만 설정 시간 내 응답 없음
     (업스트림 앱이 느린 DB 쿼리, 외부 API 대기, 데드락 등으로 지연)

판별 플로우차트

에러 코드 확인
    │
    ├─ 502 → 업스트림 앱 로그 확인 (크래시? 프로토콜 오류?)
    │         nc로 앱 포트 직접 연결 테스트
    │
    ├─ 503 → 업스트림 앱 프로세스 살아있는가? (ps aux, systemctl status)
    │         포트 리스닝 중인가? (ss -tlnp | grep <port>)
    │
    ├─ 504 → 타임아웃 계층 특정
    │         curl -w "@curl-format.txt"로 time_starttransfer 측정
    │         → DB 쿼리 시간, 외부 API 호출 시간 확인
    │
    └─ 500 → 앱 서버 에러 로그 확인 (코드 예외, 스택트레이스)

Nginx proxy_read_timeout과 504의 관계

Nginx 기본값은 proxy_read_timeout 60s입니다. 업스트림이 60초 내에 응답하지 않으면 클라이언트에게 504를 반환합니다. 이 값을 늘려도 업스트림의 실제 처리 시간이 줄지는 않습니다 — 근본 원인(느린 쿼리, 외부 API 지연)을 해결해야 합니다.

Nginx
# Nginx 설정 예시 (근본 해결 전 임시 조치)
proxy_connect_timeout 5s;   # 업스트림 TCP 연결 제한
proxy_read_timeout    60s;  # 업스트림 응답 대기 제한
proxy_send_timeout    60s;  # 업스트림 요청 전송 제한

상황

결제 서비스가 외부 PG사 API를 호출하는 코드에서 curl: (28) Operation timed out after 10000 milliseconds with 0 bytes received 에러가 발생하고 있습니다. 0 bytes received는 응답 바디가 전혀 오지 않았음을 뜻합니다.

단계별 진단

1단계: TCP 연결 분리 점검

로컬 터미널
nc -zv -w 3 pg-api.example.com 443
# 결과: Connection to pg-api.example.com 443 port [tcp/https] succeeded!
# → TCP는 열림. 포트 차단이나 서버 다운은 아님

2단계: curl 타이밍으로 어느 단계에서 멈추는지 확인

로컬 또는 서버
curl -s -o /dev/null \
  --connect-timeout 3 \
  --max-time 10 \
  -w "@/tmp/curl-format.txt" \
  https://pg-api.example.com/v1/charge

# 결과:
# time_namelookup:    0.003210
# time_connect:       0.045678   ← TCP 연결 45ms, 정상
# time_starttransfer: 10.000000  ← 10초 후 강제 종료 (starttransfer 미도달)
# time_total:         10.001234
# curl: (28) Operation timed out

해석: time_starttransfer가 타임아웃값과 일치합니다. 즉, 첫 번째 바이트가 전혀 오지 않았습니다. TCP는 연결됐지만 서버가 HTTP 응답을 시작조차 하지 않고 있습니다.

3단계: 원인 가설과 확인

로컬 또는 서버
# 가능한 원인 1: PG사 서버 측 장애
# → PG사 상태 페이지 확인, 다른 엔드포인트 테스트
curl -s -o /dev/null -w "%{http_code}" https://pg-api.example.com/health

# 가능한 원인 2: 특정 IP에서만 차단 (화이트리스트)
# → 서버에서와 로컬에서 같은 명령 실행 후 비교
# → PG사에 서버 출구 IP 화이트리스트 등록 여부 확인

# 가능한 원인 3: 요청 헤더 누락으로 서버가 응답 보류
curl -v --connect-timeout 3 --max-time 10 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  https://pg-api.example.com/v1/charge

해결

PG사 서버가 특정 API 키 없이 들어오는 요청을 응답 없이 연결만 유지(hang)시키고 있었습니다. 올바른 인증 헤더를 추가하자 즉시 응답이 왔습니다.

예방: 코드 레벨 타임아웃 설정

JS
// Node.js: axios 타임아웃 설정
const response = await axios.post(url, data, {
  timeout: 10000,           // 전체 10초 (max-time 역할)
  // axios는 connect-timeout을 별도로 노출하지 않음
  // → httpAgent로 세밀하게 제어 가능
});

// Python: requests 타임아웃 설정 (튜플로 분리 가능)
response = requests.post(url, json=data, timeout=(3, 10))
# (connect_timeout=3초, read_timeout=10초)

# Java Spring: RestTemplate 타임아웃
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(3000);   // 3초
factory.setReadTimeout(10000);     // 10초

증상

평소에는 잘 되다가 트래픽이 몰리는 시간대(오전 9시, 점심)에만 타임아웃이 납니다. 로그에는 같은 에러가 반복됩니다.

로컬 또는 서버
# 애플리케이션 로그 (Spring Boot)
# 09:03:12 ERROR - HikariPool-1 - Connection is not available, request timed out after 30000ms
# 09:03:15 ERROR - Read timed out
# 09:03:18 ERROR - HikariPool-1 - Connection is not available, request timed out after 30000ms

# curl로 직접 테스트하면 바로 응답됨
curl -v --max-time 5 https://api.external-service.com/v1/data
# 200 OK, 123ms

원인 진단

로컬 또는 서버
# DB 연결 풀 상태 확인 (HikariCP의 경우)
# 애플리케이션 actuator 엔드포인트로 확인
curl http://localhost:8080/actuator/metrics/hikaricp.connections.active
curl http://localhost:8080/actuator/metrics/hikaricp.connections.pending

# 또는 DB에서 직접 확인
# PostgreSQL
SELECT count(*), wait_event_type, wait_event
FROM pg_stat_activity
WHERE datname = 'mydb'
GROUP BY wait_event_type, wait_event;

해결

로컬 터미널
# 연결 풀 크기 조정 (application.yml)
spring:
  datasource:
    hikari:
      maximum-pool-size: 20      # 기본값 10 → 늘림
      connection-timeout: 5000   # 5초 (기본 30초에서 줄임)
      idle-timeout: 300000       # 5분

# 근본 원인 파악 — 느린 쿼리가 연결을 오래 붙잡는 경우
# PostgreSQL slow query log 확인
# log_min_duration_statement = 1000  # 1초 이상 쿼리 로깅

핵심: curl이 빠른데 앱이 느리다면 외부 서버 문제가 아니라 앱 내부 자원(DB 연결 풀, 스레드 풀) 고갈입니다.


💼
실무 맥락
현업 패턴

백엔드 개발자가 외부 API 연동할 때 항상 이렇게 설정한다

외부 API(결제, SMS, 지도, 소셜로그인 등)를 연동하는 코드에 타임아웃을 설정하지 않으면, 외부 서비스가 응답하지 않을 때 내 서버의 스레드가 영원히 대기합니다. 스레드가 쌓이면 내 서비스 전체가 응답 불능 상태가 됩니다. 외부 API 하나의 장애가 내 서비스 전체 장애로 번지는 가장 흔한 패턴입니다.

타임아웃 설정 기준

Connection Timeout: 1~5초
  - 정상 서버라면 TCP 연결은 수백ms 내에 완료됩니다
  - 3초를 넘는다면 인프라 문제입니다
  - 기본값: 3초

Read Timeout: 외부 API SLA의 2배 또는 최대 30초
  - 결제 API가 "평균 500ms, 최대 3초"를 보장한다면 → 6~10초 설정
  - 명세에 SLA가 없다면 5~15초 사이에서 보수적으로 설정
  - 절대 무한(0) 설정 금지

재시도 정책과 함께 사용:
  - 타임아웃 후 무조건 재시도하면 멱등하지 않은 API(결제)에서 중복 처리 위험
  - 재시도 전 idempotency key 사용 또는 조회 API로 상태 확인 필요

장애 상황에서 curl로 먼저 확인하는 루틴

외부 API 장애 신고를 받으면 코드를 보기 전에 이것부터 실행합니다:

로컬 터미널
# 1. TCP 연결 점검
nc -zv -w 3 <외부_API_호스트> <포트>

# 2. curl 타이밍 분석
curl -s -o /dev/null \
  --connect-timeout 3 --max-time 15 \
  -w "@/tmp/curl-format.txt" \
  <외부_API_엔드포인트>

# 3. 결과 판독
# nc 실패 → "저희 서버에서 해당 호스트의 포트가 막혀있습니다"
# nc 성공, time_starttransfer ≈ max-time → "API 서버가 응답을 안 하고 있습니다"
# nc 성공, time_starttransfer 정상 → "HTTP 응답은 오는데 내용 문제 — curl -v로 헤더 확인"

이 3단계로 "내 코드 문제인가, 외부 서버 문제인가, 네트워크 문제인가"를 5분 안에 좁힐 수 있습니다. 원인 계층이 명확해야 외부 업체에 정확한 보고서를 보낼 수 있고, 내부에서 코드를 고칠지 인프라 팀을 부를지 결정할 수 있습니다.

다음 모듈에서는 네트워크 장애 전반을 체계적으로 고립시키는 troubleshooting-isolation 방법론을 다루며, L1부터 L7까지 계층별 점검 절차를 완성합니다.

지식 확인

퀴즈 — 3문제

Q1

curl --connect-timeout 3 --max-time 10으로 요청했을 때, 서버와의 TCP 연결은 1초 만에 성공했지만 응답 바디가 20초 후에 도착했다. 어떤 결과가 나오는가?

Q2

502 Bad Gateway와 504 Gateway Timeout의 차이는 무엇인가?

Q3

nc -zv api.example.com 443 명령이 성공('Connection to api.example.com 443 port [tcp/https] succeeded')했는데, curl https://api.example.com/v1/data가 타임아웃된다. 가장 가능성 높은 원인은?

0 / 3 답변

🧪 실습으로 확인하기

포트는 열렸다는데 왜 안 되지? — ss/netstat/telnet으로 TCP 진단

초급

"포트 8080 열었는데요?"와 "왜 안 돼요?" 사이의 간극을 메우는 실습. ss로 바인딩 상태를 확인하고, telnet/nc으로 원격 연결을 테스트하고, iptables 방화벽을 진단하고, 바인딩 주소(0.0.0.0 vs 127.0.0.1)까지 수정하는 4단계 TCP 포트 진단 플로우를 완성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

networking중급 · 30
[Network] ARP 캐시 원리와 네트워크 IP 충돌 추적 및 해결
Networking 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점