이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 브라우저에서 보낸 요청은 서버 코드에 도착하기 전 어떤 계층을 지나는가?
  • 502, timeout, CORS, TLS 오류가 났을 때 어느 계층부터 의심해야 하는가?
  • 백엔드 개발자는 장애 제보를 어떤 증거로 바꿔야 하는가?

개요

웹 요청은 브라우저에서 컨트롤러 메서드로 바로 들어오지 않는다. 사용자가 버튼을 누른 뒤 서버 코드가 실행되기까지는 DNS, TCP, TLS, HTTP, CDN/WAF, Load Balancer, Reverse Proxy, WAS, 애플리케이션, DB, 외부 API가 차례로 관여할 수 있다.

백엔드 개발자가 네트워크를 공부해야 하는 이유는 이 경로를 외워서 설명하기 위해서가 아니다. 장애가 났을 때 “내 코드 문제인가, 프록시 문제인가, 로드밸런서 문제인가, 브라우저 정책 문제인가, 외부 API 문제인가”를 증거로 좁히기 위해서다.

왜 백엔드 개발자가 알아야 하는가

운영 장애에서 가장 위험한 말은 “네트워크 문제 같습니다”이다. 이 말은 너무 넓어서 아무것도 설명하지 못한다. 좋은 백엔드 개발자는 같은 상황을 더 좁게 말한다.

  • DNS는 정상 해석되지만 443 TCP 연결이 timeout 난다.
  • TLS handshake는 성공하지만 Gateway가 502를 즉시 반환한다.
  • App access log에는 요청이 없고 WAF deny log에만 남는다.
  • App log에는 요청이 있고 DB slow query가 같은 시각에 증가한다.
  • curl은 성공하지만 브라우저는 CORS 정책 때문에 응답을 읽지 못한다.

이 정도로 말할 수 있으면 인프라 담당자와 대화가 빨라진다. 반대로 서버 로그만 보고 “우리 앱에는 요청이 안 왔으니 우리 문제는 아니다”라고 끝내면, CDN/WAF/LB/proxy 구간에서 멈춘 문제를 놓치기 쉽다.

요청 흐름에서의 위치

이 문서는 Web-Network 전체의 지도다. 이후 DNS, TCP, TLS, HTTP, Cache, Proxy, Load Balancer, Timeout, 장애 대응 문서는 모두 아래 흐름의 특정 구간을 자세히 설명한다.

Browser / Mobile App / API Client
→ DNS Resolver
→ TCP Connection
→ TLS Handshake
→ HTTP Request
→ CDN / WAF
→ Load Balancer / Gateway
→ Reverse Proxy
→ WAS / Application Server
→ Application Code
→ DB / Cache / Queue / External API
→ HTTP Response
→ Browser Security Policy / Cache

중요한 점은 응답이 돌아왔다는 사실이 “애플리케이션까지 정상 도달했다”는 뜻은 아니라는 것이다. CDN, WAF, Gateway, Reverse Proxy도 HTTP 응답을 만들 수 있다.

원리

요청 흐름을 계층으로 나누는 이유는 각 계층이 실패할 때 보이는 증상과 확인 도구가 다르기 때문이다.

계층성공했다는 증거실패할 때 보이는 증상먼저 확인할 것
DNS도메인이 IP나 CNAME으로 해석된다.NXDOMAIN, SERVFAIL, DNS timeoutdig, nslookup, TTL, A/AAAA/CNAME
TCP대상 IP와 port에 연결된다.connection refused, connect timeoutnc, ss, firewall, security group
TLS인증서 검증과 handshake가 끝난다.인증서 만료, hostname mismatch, handshake failureopenssl s_client, SNI, chain
HTTPstatus code와 header가 돌아온다.4xx, 5xx, redirect loop, header 누락curl -vI, Server, Via, Location
CDN/WAFedge log에 요청이 남는다.403, 429, cache stale, origin 미도달cache status, WAF rule, Age, X-Cache
LB/Gatewaytarget으로 전달하거나 자체 응답한다.502, 503, 504, no healthy targettarget health, listener, rule, idle timeout
Reverse Proxyupstream으로 전달한다.upstream timed out, bad gateway, path rewrite 오류NGINX/Envoy access/error log
Applicationapp access log와 trace가 남는다.400, 401, 403, 422, 500, 느린 처리app log, trace id, exception
DB/APIdependency trace가 남는다.first byte 지연, 500/503/504, pool 고갈slow query, pool, lock, external API latency
Browser응답을 JS에 노출한다.CORS, cookie rejected, mixed contentDevTools Network/Console

이 표는 장애 때 “어디까지 정상인가”를 체크하는 기준이다. 한 계층이 정상이라고 해서 다음 계층도 정상이라고 보면 안 된다.

HTTP 응답 주체를 먼저 찾기

상태 코드보다 중요한 질문은 “누가 이 응답을 만들었는가”다.

단서의미다음 행동
Server: cloudflareCDN/WAF가 응답했을 가능성이 있다.edge log, WAF rule, cache status 확인
Server: nginxreverse proxy 또는 web server가 응답했을 수 있다.NGINX access/error log 확인
Server: Microsoft-Azure-Application-Gateway/v2Azure Application Gateway가 응답했을 수 있다.backend health, probe, target 설정 확인
Server: envoyEnvoy/Istio/API Gateway 계층일 수 있다.route, cluster, retry, timeout log 확인
App 고유 error body애플리케이션까지 도달했을 가능성이 높다.app log, exception, trace 확인

Server 헤더만으로 원인을 확정하면 안 된다. 하지만 어느 로그부터 볼지 정하는 데는 매우 좋은 단서다.

실무에서 자주 만나는 문제

  • App log에 요청이 없는데 애플리케이션 코드를 먼저 뒤진다.
    • CDN/WAF/LB/proxy에서 응답을 만들었을 수 있다.
    • Gateway access log와 WAF deny log를 먼저 확인한다.
  • curl은 성공하지만 브라우저만 실패한다.
    • 서버 간 HTTP 요청과 브라우저 요청은 다르다.
    • CORS, SameSite, Secure cookie, mixed content, browser cache를 본다.
  • 502가 보이면 backend 서버가 죽었다고 단정한다.
    • backend process down일 수도 있지만 protocol mismatch, Host Header, backend TLS, health probe 실패일 수도 있다.
    • “Gateway가 왜 유효한 upstream 응답을 못 받았는가”로 질문을 바꾼다.
  • 응답이 느리면 네트워크가 느리다고 말한다.
    • DNS/connect/TLS가 느린지, first byte가 느린지, body download가 느린지 나누어 봐야 한다.
    • first byte만 느리면 앱 처리, DB, 외부 API, lock, pool을 의심한다.

디버깅 방법

가장 기본적인 순서는 아래처럼 아래 계층에서 위 계층으로 올라가는 것이다.

# 1. DNS
dig +short example.com
dig example.com A
dig example.com AAAA
 
# 2. TCP
nc -vz example.com 443
 
# 3. TLS
openssl s_client -connect example.com:443 -servername example.com </dev/null
 
# 4. HTTP header
curl -vI https://example.com/api/health
 
# 5. 구간별 시간
curl -sS -o /dev/null \
  -w "code=%{http_code}\nremote_ip=%{remote_ip}\ndns=%{time_namelookup}\nconnect=%{time_connect}\ntls=%{time_appconnect}\nfirst_byte=%{time_starttransfer}\ntotal=%{time_total}\n" \
  https://example.com/api/health

이 명령의 목표는 원인을 한 번에 맞히는 것이 아니다. 가능성이 낮은 계층을 제거하고, 내부 권한이 필요한 확인 항목을 명확히 만드는 것이다.

코드로 확인하기

장애 제보를 받을 때는 다음 형식으로 증거를 모은다.

발생 시각:
영향 범위:
요청 URL:
HTTP Method:
Status Code:
Response Header:
Server / Via / X-Cache:
Request ID / Trace ID:
curl timing:
브라우저 Console 오류:
다른 네트워크 재현 여부:
최근 배포 / 설정 변경:

예를 들어 502 장애라면 아래처럼 말하는 편이 좋다.

example.com에서 502가 보입니다.
DNS는 기대한 Gateway IP로 해석됩니다.
TCP 443 연결과 공개 TLS 인증서 검증은 성공합니다.
curl -vI 결과 Server는 Microsoft-Azure-Application-Gateway/v2입니다.
App access log에는 같은 시각의 request id가 없습니다.
Gateway backend health, probe path, backend port/protocol, backend TLS를 확인해야 합니다.

이런 보고는 “Azure 문제 같습니다”보다 훨씬 낫다. 응답 주체와 남은 확인 범위를 분리했기 때문이다.

주니어가 자주 하는 오해

  • ping이 되면 웹 서비스도 정상이라고 생각한다.
    • ping은 ICMP 도달성만 본다. HTTP, TLS, proxy, application 상태를 보지 못한다.
  • App log에 없으면 애플리케이션과 무관하다고 생각한다.
    • 앱 앞단 설정이 잘못되어 요청이 도달하지 못한 것도 백엔드 운영 책임과 연결된다.
  • 4xx는 무조건 클라이언트 잘못, 5xx는 무조건 서버 코드 잘못이라고 본다.
    • WAF가 403을 만들 수 있고 Gateway가 502를 만들 수 있다.
  • 브라우저 실패와 서버 실패를 같은 것으로 본다.
    • 브라우저는 CORS, cookie, cache, mixed content 같은 추가 정책을 적용한다.

시니어의 설계 판단 기준

  • 모든 요청에 request id가 있어야 하고, CDN/Gateway/proxy/app log에서 추적 가능해야 한다.
  • 앱은 자신이 받은 Host, scheme, client IP, request id를 신뢰 가능한 proxy 경계 안에서만 해석해야 한다.
  • health check, readiness, graceful shutdown은 배포와 로드밸런싱 설계의 일부다.
  • timeout, retry, connection pool, cache 정책은 개별 설정값이 아니라 요청 경로 전체의 계약으로 관리한다.
  • 장애 대응 문서에는 “확인 명령”, “정상 예시”, “실패 예시”, “다음 확인 위치”가 있어야 한다.

인프라 협업 포인트

인프라 담당자에게는 원인 추측보다 관찰 사실을 전달해야 한다.

  • DNS 변경 요청: 도메인, record type, 현재 값, 목표 값, TTL, rollback 값을 함께 준다.
  • 인증서 문제: 접속 도메인, 인증서 SAN, 만료일, chain, SNI 확인 결과를 함께 준다.
  • LB 문제: listener, rule, target group, health check path, backend port, protocol을 함께 확인한다.
  • WAF 문제: source IP, request id, path, method, 차단 시각, rule id를 요청한다.
  • Proxy 문제: 외부 path와 upstream path, Host Header, body size, timeout 값을 맞춘다.

실전 팁

  • 첫 5분은 원인 찾기보다 요청이 도달한 마지막 계층을 찾는 데 쓴다.
  • curl -w 결과의 first_byte가 길면 서버 처리 이후를, connect가 길면 TCP/방화벽/라우팅을 먼저 본다.
  • CDN을 쓰면 origin log가 없다는 사실이 정상일 수도 있다. X-Cache, Age, cache key를 같이 본다.
  • 장애는 “전체 장애인지 일부 사용자 장애인지”를 먼저 나누면 절반은 좁혀진다.
  • 모바일 앱 장애는 네트워크 전환, DNS cache, retry, 중복 요청 가능성을 브라우저보다 더 크게 본다.

위험 신호!

  • 장애 보고에 screenshot만 있고 URL, 시간, request id가 없다.
  • App log만 있고 Gateway/LB/proxy log를 볼 방법이 없다.
  • CORS 오류를 서버 간 API 연결 문제로 설명한다.
  • 모든 5xx를 같은 알림으로 묶어 502, 503, 504의 대응이 구분되지 않는다.
  • 배포 후 smoke test가 브라우저 접속 한 번으로 끝난다.

확인 질문

확인 질문

  • HTTP 응답이 왔다는 사실만으로 무엇을 알 수 있는가?
    • 응답을 만든 어떤 계층까지 요청이 도달했다는 점이다. 그 계층이 애플리케이션인지 CDN/Gateway/proxy인지는 헤더와 로그로 확인해야 한다.
  • curl -w에서 time_starttransfer만 길다면 무엇을 의심하는가?
    • 서버가 요청을 받은 뒤 첫 바이트를 내기까지 오래 걸린 것이므로 애플리케이션 처리, DB, 외부 API, lock, pool 병목을 의심한다.
  • 브라우저에서만 실패하고 curl은 성공한다면 무엇을 확인하는가?
    • CORS, cookie 전송 조건, SameSite, Secure, mixed content, 브라우저 cache, 확장 프로그램을 확인한다.

참고 문서