이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- DNS, TCP, TLS, HTTP, Proxy, Application 계층마다 어떤 증거가 남는가?
- App log에 요청이 없을 때 어디부터 확인해야 하는가?
- 로그, 메트릭, 트레이스, CLI 결과를 어떻게 한 요청 흐름으로 연결하는가?
개요
장애 대응에서 가장 먼저 필요한 것은 “추측”이 아니라 “관찰 지점”이다. 같은 502라도 CDN이 만든 502, Load Balancer가 만든 502, Reverse Proxy가 만든 502, 애플리케이션이 의도적으로 만든 502는 확인할 로그와 조치가 다르다.
계층별 관찰 지점은 요청이 지나가는 길마다 어떤 증거가 남는지 정리한 지도다. 이 지도가 있으면 App log만 뒤지다가 시간을 쓰지 않고, 요청이 마지막으로 관찰된 위치를 기준으로 다음 확인 지점을 좁힐 수 있다.
왜 백엔드 개발자가 알아야 하는가
백엔드 개발자가 보는 애플리케이션 로그는 전체 요청 경로의 일부다. 앱 로그에 요청이 없다는 사실은 “문제가 없다”가 아니라 “요청이 앱까지 오지 못했거나 다른 계층에서 처리되었다”는 증거일 수 있다.
예를 들어 WAF가 403을 반환하면 애플리케이션 로그에는 아무것도 없을 수 있다. CDN cache HIT이면 origin 애플리케이션에는 요청이 오지 않는다. Load Balancer가 target을 unhealthy로 보고 503을 반환하면 컨트롤러 코드는 실행되지 않는다.
요청 흐름에서의 위치
이 문서는 01. 전체 요청 경로 상세.md의 흐름을 실제 운영 증거로 바꾸는 문서다.
DNS
→ TCP
→ TLS
→ HTTP
→ CDN/WAF
→ Load Balancer
→ Reverse Proxy
→ Application
→ DB / Cache / External API각 단계에는 “여기까지는 왔다”는 증거와 “여기서 실패했다”는 증거가 따로 있다.
원리
관찰 지점은 크게 네 종류로 나눈다.
| 관찰 종류 | 대표 도구 | 알 수 있는 것 | 놓치기 쉬운 것 |
|---|---|---|---|
| CLI 확인 | dig, nc, openssl, curl | 외부에서 보이는 도달성, TLS, HTTP 응답 | 내부 backend health, private DNS, app 예외 |
| Access log | CDN/LB/NGINX/App access log | 요청 도달 여부, status, latency, upstream | body 내용, 내부 exception 상세 |
| Error log | NGINX error log, app error log | upstream timeout, connection refused, stack trace | 성공 요청의 성능 문제 |
| Metric | latency, error rate, saturation | 추세, 영향 범위, p95/p99, pool 고갈 | 개별 요청의 정확한 payload |
| Trace | OpenTelemetry, APM | 한 요청의 app, DB, external API span | 브라우저 보안 정책, CDN cache HIT |
하나의 도구로 모든 것을 알 수 없다. CLI는 외부 관찰이고, 로그는 계층별 기록이며, metric은 추세이고, trace는 요청 내부 경로다.
계층별 관찰 지점
| 계층 | 확인할 증거 | 정상 신호 | 위험 신호 |
|---|---|---|---|
| DNS | dig, resolver log, DNS provider | 기대한 A/AAAA/CNAME 반환 | NXDOMAIN, SERVFAIL, 이전 IP, 잘못된 AAAA |
| TCP | nc, ss, firewall log | port 연결 성공 | timeout, refused, SYN 재전송 |
| TLS | openssl s_client, cert monitor | chain 검증, SAN 일치, 만료 여유 | SNI 누락, chain 누락, 만료 임박 |
| HTTP | curl -vI, access log | status/header/body 형식 확인 | redirect loop, 4xx/5xx, header 누락 |
| CDN/WAF | edge log, X-Cache, Age | HIT/MISS 의도와 일치 | WAF deny, cache stale, origin 미도달 |
| LB/Gateway | access log, target health | healthy target, target status code | no healthy target, 502/503/504 |
| Reverse Proxy | access/error log | upstream status와 request time 기록 | upstream timed out, rewrite 오류 |
| Application | app access/error log, trace | request id와 handler 확인 | exception, validation, auth 실패 |
| DB/Cache | slow query, lock, pool metric | latency와 pool 정상 | lock wait, pool pending, eviction spike |
| External API | dependency trace, client metric | latency/error rate 정상 | read timeout, quota, retry storm |
실무에서 자주 만나는 문제
- App log에 요청이 없는데 애플리케이션 버그부터 찾는다.
- CDN/WAF/LB/proxy 계층에서 응답이 끝났을 수 있다.
- 먼저 edge, gateway, proxy access log에 request id가 있는지 확인한다.
- CDN cache HIT을 origin 정상 처리로 착각한다.
- cache HIT이면 origin 애플리케이션은 실행되지 않는다.
Age,X-Cache, CDN log와 origin access log를 나누어 본다.
- WAF 403을 애플리케이션 권한 문제로 본다.
- 앱 권한 실패는 app log나 auth log에 남을 가능성이 크다.
- WAF 차단은 rule id, source IP, request path가 필요하다.
- 로그 시간이 서로 맞지 않는다.
- timezone, clock skew, log ingestion delay 때문에 timeline이 어긋난다.
- 운영에서는 모든 로그를 UTC나 명확한 timezone으로 맞추는 편이 좋다.
디버깅 방법
요청이 어디까지 도달했는지 확인하는 최소 순서는 아래와 같다.
# DNS 결과와 IPv6 여부 확인
dig example.com A
dig example.com AAAA
dig example.com CNAME
# TCP 연결 확인
nc -vz example.com 443
# TLS 인증서와 SNI 확인
openssl s_client -connect example.com:443 -servername example.com </dev/null
# HTTP 응답 주체 확인
curl -vI https://example.com/api/orders
# 시간 구간 확인
curl -sS -o /dev/null \
-w "code=%{http_code}\ndns=%{time_namelookup}\nconnect=%{time_connect}\ntls=%{time_appconnect}\nfirst_byte=%{time_starttransfer}\ntotal=%{time_total}\n" \
https://example.com/api/orders외부 확인 후 내부 로그를 볼 때는 이 순서가 유용하다.
CDN/WAF log
→ Load Balancer access log
→ Reverse Proxy access/error log
→ Application access log
→ Application error log
→ APM trace
→ DB slow query / pool metric
→ External API dependency metric코드로 확인하기
access log에 request id, upstream status, request time이 없다면 관찰 지점이 끊긴다. NGINX에서는 최소한 다음처럼 남긴다.
log_format upstream_timing
'$remote_addr $host "$request" status=$status '
'request_id=$request_id upstream_status=$upstream_status '
'request_time=$request_time upstream_time=$upstream_response_time '
'xff="$http_x_forwarded_for"';
access_log /var/log/nginx/access.log upstream_timing;이 로그에서 볼 것은 다음이다.
status: 사용자에게 반환한 최종 상태 코드upstream_status: 애플리케이션 upstream이 반환한 상태 코드request_time: NGINX가 본 전체 처리 시간upstream_response_time: upstream 응답 대기 시간request_id: 앱 로그와 연결할 키
status=504이고 upstream_status가 비어 있거나 upstream_response_time이 길다면 proxy가 upstream 응답을 기다리다 실패했을 가능성이 있다.
주니어가 자주 하는 오해
- 로그가 없으면 아무 일도 없었다고 생각한다.
- 로그가 없는 계층은 요청이 도달하지 않았거나 로그 설정이 빠진 것이다.
Server헤더를 원인으로 확정한다.Server는 응답을 만든 계층의 단서일 뿐 실제 원인은 뒤쪽 upstream일 수 있다.
- 평균 latency만 보고 장애가 없다고 판단한다.
- p95, p99, 특정 endpoint, 특정 지역, 특정 계정의 지연을 봐야 한다.
- trace가 있으면 네트워크 앞단도 다 보인다고 생각한다.
- trace는 보통 애플리케이션 내부 이후를 잘 보여 주며, DNS/TLS/CDN/WAF는 별도 관찰이 필요하다.
시니어의 설계 판단 기준
- 모든 외부 진입점에 request id를 만들고 downstream으로 전달한다.
- LB/proxy/app access log에서 같은 request id를 검색할 수 있게 한다.
- 상태 코드만이 아니라 upstream status, upstream latency, target ip, cache status를 남긴다.
- 로그는 장애 때 찾기 쉬운 필드 이름과 시간 기준을 가져야 한다.
- metric은 RED 또는 USE 관점으로 latency, traffic, error, saturation을 본다.
인프라 협업 포인트
- 인프라 팀에는 “앱 로그에 없습니다”보다 “CDN/LB/proxy 중 어디 로그에 있는지 확인이 필요합니다”라고 요청한다.
- WAF 차단 분석에는 source IP, path, method, request id, 발생 시각이 필요하다.
- LB 분석에는 target group, target status code, target health reason, listener rule이 필요하다.
- Proxy 분석에는 upstream host, upstream status, upstream response time, rewrite 후 path가 필요하다.
실전 팁
- 장애 보고 템플릿에는
응답 주체,request id,어느 로그에 남았는지를 반드시 넣는다. - 개인 프로젝트라도 NGINX access log와 Spring access log를 연결할 request id를 남겨 두면 품질이 달라진다.
- CDN을 쓰는 서비스는 “origin에 요청이 안 왔다”가 정상 cache 동작일 수도 있음을 팀이 이해해야 한다.
- 로그 수집기가 지연되면 실시간 장애 대응에서는 원본 로그 파일이나 cloud native log도 확인한다.
위험 신호!
- LB access log가 꺼져 있다.
- app log에는 request id가 있지만 proxy log에는 없다.
- CDN cache status를 볼 수 없다.
- error log만 보고 access log를 보지 않는다.
- 각 로그의 timezone이 달라 timeline 작성이 어렵다.
확인 질문
확인 질문
- App log에 요청이 없으면 바로 앱 문제가 아니라고 결론 내릴 수 있는가?
- 아니다. CDN, WAF, LB, proxy에서 요청이 끝났을 수 있으므로 앞단 로그를 확인해야 한다.
status와upstream_status를 나누어 보는 이유는 무엇인가?
- 사용자에게 반환한 상태와 upstream 애플리케이션이 반환한 상태가 다를 수 있기 때문이다.
- 관찰 지점을 설계할 때 가장 중요한 연결 키는 무엇인가?
- request id 또는 trace id다. 계층별 로그를 같은 요청으로 묶는 기준이 된다.