OSI 모델 & Web Server
분류: Layer 2 - 인프라 기초 | 작성일: 2026-03-22
1. 한 줄 정의
섹션 제목: “1. 한 줄 정의”OSI 모델은 네트워크 통신을 7개 계층으로 나눈 개념 모델이고, Web Server(Nginx 등)는 HTTP 요청을 받아 정적 파일을 제공하거나 애플리케이션 서버로 요청을 전달하는 소프트웨어다.
2. 왜 중요한가
섹션 제목: “2. 왜 중요한가”네트워크 문제가 생겼을 때 “어느 계층에서 문제인가”를 판단하는 기준이 OSI 모델이다. Web Server는 Nest.js 앞에 Nginx가 붙어 있는 구성에서 요청이 어떻게 흘러들어오는지 이해하는 데 필요하다. “80포트로 들어온 요청이 3000번 포트의 Node.js 서버에 도달하는가”의 답이 여기에 있다.
3. 핵심 개념
섹션 제목: “3. 핵심 개념”비유로 시작 — “국제 택배”
편지(HTTP 요청)를 해외로 보낼 때 여러 단계가 있다: 편지 내용 작성(Application Layer) → 암호화 봉투(Presentation/TLS) → 주소 라벨 붙이기(Network/IP) → 택배 차량 탑재(Transport/TCP) → 실제 도로 이동(Physical). 각 단계는 독립적이라 상위 단계는 하위 단계가 어떻게 동작하는지 몰라도 된다.
OSI 7계층 — 원리와 실무 관점
📖 더 보기: Understanding OSI & TCP/IP with Real-World Examples - Medium — 실제 요청 흐름으로 OSI 계층 비유 설명
실제 네트워크 통신은 OSI보다 단순한 TCP/IP 4계층을 사용하지만, 문제 진단 시 OSI 7계층이 유용하다. “어느 계층에서 문제인가”를 좁히면 원인을 빠르게 찾을 수 있다.
| 계층 | 이름 | 실무 관련 예시 | 문제 증상 |
|---|---|---|---|
| 7 | Application | HTTP, HTTPS, DNS | 4xx/5xx 에러, API 응답 이상 |
| 6 | Presentation | TLS/SSL 암호화 | 인증서 오류, HTTPS 핸드셰이크 실패 |
| 5 | Session | 세션 연결 유지 | 연결이 갑자기 끊김 |
| 4 | Transport | TCP/UDP, 포트 번호 | 포트 미오픈, 방화벽 차단 |
| 3 | Network | IP 주소, 라우팅 | IP 충돌, VPC 라우팅 오류 |
| 2 | Data Link | MAC 주소 | (실무에서 거의 안 만남) |
| 1 | Physical | 케이블, Wi-Fi | (클라우드 환경에서 AWS 책임) |
실무에서 주로 보는 계층: 7(HTTP 에러) → 4(포트/방화벽) → 3(IP/VPC/Security Group) → 6(TLS 인증서)
왜 7계층으로 나누는가 — 설계 원리
각 계층이 독립적으로 설계된 덕분에 상위 계층은 하위 계층이 어떻게 동작하는지 몰라도 된다. 예를 들어, HTTP는 TCP 위에서 동작하지만 HTTP 코드를 작성할 때 TCP 3-way handshake를 신경 쓸 필요가 없다. Nginx도 마찬가지로 TLS 핸드셰이크(6계층)를 처리하면서 내부 Nest.js 서버에는 평문 HTTP(7계층)로 전달한다. 이 분리가 리버스 프록시의 핵심 원리다.
실제 HTTPS 요청의 전체 흐름
브라우저에서 https://api.example.com/users를 호출할 때 내부적으로:
1. DNS 조회: api.example.com → IP 주소 (예: 52.68.1.100) [7계층]2. TCP 3-way Handshake [4계층] 클라이언트 → SYN → 서버 클라이언트 ← SYN-ACK ← 서버 클라이언트 → ACK → 서버 (연결 완료)3. TLS Handshake [6계층] → 인증서 교환, 암호화 키 협상 (약 1~2 RTT 추가 지연)4. HTTP 요청 전송 [7계층] GET /users HTTP/1.1 Host: api.example.com5. 서버 응답 → 브라우저 수신프론트엔드 → 플랫폼 브릿지: fetch() 호출이 OSI 레이어를 어떻게 통과하는가
프론트엔드 코드에서 fetch('https://api.example.com/users')를 호출하면, 브라우저 내부에서 OSI 7계층을 모두 거치게 된다. 개발자는 JavaScript 레벨(7계층)만 보지만, 실제로는 아래처럼 모든 계층이 협력한다.
JavaScript 코드 (개발자가 작성) fetch('https://api.example.com/users') │ ▼[Layer 7 — Application] 브라우저 HTTP 엔진: 요청 URL 파싱, 헤더 조립 GET /users HTTP/1.1 Host: api.example.com Authorization: Bearer token... │ ▼[Layer 6 — Presentation / TLS] TLS 라이브러리(BoringSSL): 요청 본문 암호화 인증서 검증, 세션 키 협상 (이미 연결이 있으면 세션 재사용) │ ▼[Layer 5 — Session] 브라우저 연결 풀: 같은 origin(api.example.com:443)에 대한 기존 TCP 연결이 있으면 재사용 (HTTP Keep-Alive) → 새 연결이면 3-way handshake 후 TLS handshake 수행 │ ▼[Layer 4 — Transport / TCP] TCP 세그먼트화: 큰 요청 데이터를 MSS(1460B) 단위로 분할 시퀀스 번호 부여, ACK 대기 Source Port: 55123 (브라우저가 임의 선택) Dest Port: 443 │ ▼[Layer 3 — Network / IP] IP 패킷: Source IP(클라이언트), Dest IP(52.68.1.100) 라우터가 목적지 IP를 보고 다음 홉 결정 │ ▼[Layer 2 — Data Link] Ethernet 프레임: MAC 주소(게이트웨이/공유기) │ ▼[Layer 1 — Physical] 실제 신호: Wi-Fi 전파 또는 UTP 케이블 전기 신호핵심 포인트: 개발자가 fetch()를 호출하고 await response.json()을 받는 사이에, 브라우저는 보이지 않는 곳에서 TCP 3-way handshake → TLS handshake → HTTP 요청 전송 → TCP 수신 확인 → TLS 복호화의 전체 과정을 수행한다. curl -v로 이 과정을 직접 볼 수 있다.
# fetch()와 동일한 요청을 curl로 시뮬레이션 (OSI 레이어 동작 관찰)curl -v https://api.example.com/users
# 예상 출력 (각 줄이 어느 OSI 계층인지 주석 추가):# * Trying 52.68.1.100:443... ← Layer 3: IP 주소로 연결 시도# * Connected to api.example.com port 443 ← Layer 4: TCP 연결 완료 (3-way handshake)# * TLSv1.3 (OUT), TLS handshake... ← Layer 6: TLS 협상 시작# * SSL certificate verify ok. ← Layer 6: 인증서 검증 성공# > GET /users HTTP/2 ← Layer 7: HTTP 요청 전송# > Host: api.example.com# > Authorization: Bearer ...# < HTTP/2 200 ← Layer 7: 서버 응답 수신# < content-type: application/json📖 더 보기: What Happens When You Type a URL — Medium/HackerNoon — 브라우저 URL 입력부터 응답까지 OSI 레이어별 전체 흐름 설명 (입문)
TCP vs UDP
- TCP: 연결 확인 후 전송, 순서 보장, 신뢰성 높음 → HTTP, DB 연결, SSH
- UDP: 연결 없이 전송, 빠르지만 손실 가능 → DNS 쿼리(초기), 동영상 스트리밍, WebRTC
Web Server (Nginx) — 리버스 프록시 원리
📖 더 보기: NGINX Reverse Proxy 공식 문서 — proxy_pass, 업스트림 설정 공식 가이드
Nginx가 리버스 프록시로 동작할 때의 핵심은 이벤트 기반 비동기 아키텍처다. Apache처럼 요청마다 스레드를 생성하지 않고, 하나의 마스터 프로세스가 여러 워커 프로세스를 관리하며 각 워커가 수천 개의 연결을 동시에 처리한다. 이 설계 덕분에 메모리 효율이 높고 C10K(동시 1만 연결) 문제를 해결할 수 있다.
Node.js도 싱글 스레드 이벤트 루프로 동시 요청을 처리하는 것처럼, Nginx 워커 프로세스도 비슷한 원리로 수천 개의 연결을 비동기 처리한다. 차이는 Nginx는 정적 파일 서빙·SSL 처리·프록시 같은 네트워크 I/O에 특화되어 있고, Node.js는 JavaScript 코드 실행이 추가된다는 점이다. 두 컴포넌트 모두 “스레드를 블로킹하지 않는다”는 원칙 위에서 동작하므로, Nginx(앞단) + Node.js(뒷단) 조합이 고성능을 낼 수 있다.
클라이언트 ──HTTPS(443)──→ Nginx ──HTTP(3000)──→ Nest.js │ ├─ SSL 인증서 처리 (클라이언트 ↔ Nginx 구간만 암호화) ├─ 요청 헤더 추가: X-Real-IP, X-Forwarded-For ├─ 로드밸런싱: upstream 서버 여러 개로 분배 └─ 정적 파일은 직접 서빙 (Node.js 부하 감소)Nginx 설정 핵심 예시:
server { listen 443 ssl; server_name api.example.com;
ssl_certificate /etc/ssl/cert.pem; # TLS 인증서 ssl_certificate_key /etc/ssl/key.pem;
location / { proxy_pass http://localhost:3000; # Nest.js로 전달 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 60s; # 업스트림 응답 대기 시간 }
# 정적 파일은 Nginx가 직접 처리 (Node.js 부하 감소) location /static/ { root /var/www; expires 30d; }}HTTP/2 — 성능 개선과 Nginx 설정
HTTP/1.1에서는 요청마다 새 TCP 연결을 열거나 순차적으로 처리해 헤드-오브-라인 블로킹이 발생한다. HTTP/2는 하나의 TCP 연결로 여러 요청을 동시에 처리(멀티플렉싱)하므로 지연 시간이 크게 줄어든다. Nginx에서 HTTP/2를 활성화하면 클라이언트 ↔ Nginx 구간에서 이 혜택을 받을 수 있다.
server { # ✅ HTTP/2 활성화 — SSL과 함께 사용 필요 listen 443 ssl; http2 on; # Nginx 1.25.1+ 방식 (구버전은 listen 443 ssl http2;)
# TLS 1.3 + HTTP/2 조합으로 최적 성능 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5;
# Keep-alive 설정 — 연결 재사용으로 오버헤드 감소 keepalive_timeout 65; keepalive_requests 100;
location / { proxy_pass http://localhost:3000; # Nginx 1.29.4+ (2025.12)부터 upstream HTTP/2 프록시 지원 # gRPC 또는 소규모 응답이 많은 백엔드에서 멀티플렉싱 효과 있음 # 대부분의 REST API 환경에서는 HTTP/1.1로도 충분 proxy_http_version 1.1; proxy_set_header Connection ""; # Keep-alive upstream }}ALB → ECS Fargate 환경에서는 ALB가 이미 HTTP/2를 지원하므로 별도 Nginx 설정 없이 자동 적용된다. ALB Target Group 프로토콜 버전을 HTTP2로 설정하면 ALB → 컨테이너 구간도 HTTP/2로 동작한다.
ECS Fargate 환경의 실제 흐름
📖 더 보기: AWS ALB 공식 문서 - Target Groups — ECS Fargate 환경에서 ALB 설정 이해
인터넷 ↓ HTTPS (443)ALB (Application Load Balancer) ← Layer 7에서 동작 (HTTP 헤더 기반 라우팅 가능) │ ← SSL 인증서 처리 (ACM 인증서) │ ← Health Check로 정상 컨테이너만 라우팅 ↓ HTTP (3000) — Target GroupNest.js 컨테이너 (Fargate Task)
※ Fargate 환경에서는 ALB가 Nginx 역할을 대신함※ Security Group으로 ALB → 컨테이너 포트만 허용※ ALB는 경로 기반 라우팅 가능: /api/* → Nest.js, /static/* → S34. 실무에서 어디에 쓰이나
섹션 제목: “4. 실무에서 어디에 쓰이나”- 네트워크 장애 발생 시 계층별 원인 찾아내기 (“3계층 문제인가, 7계층 문제인가”)
- Nginx 설정 파일 읽기/수정 (리버스 프록시, 타임아웃 설정)
- HTTPS 인증서 적용 위치 이해
- 로컬 개발 환경에서 포트 충돌 디버깅
- ALB Health Check 실패 원인 분석 및
/health엔드포인트 구현
BackOps 실무 시나리오
- 배포 후 ECS Task가 계속 재시작됨 → Health Check 실패 →
/health엔드포인트 없거나, Security Group이 ALB → 컨테이너 포트 막음 - “HTTPS로 접속하는데 인증서 오류” → 6계층 문제, 인증서 도메인·만료일 확인 (
openssl s_client) - 특정 API만 504 응답 → Nginx
proxy_read_timeout설정 확인, 근본적으로는 무거운 작업을 Queue로 분리 - “로컬에서는 되는데 배포하면 안 됨” → Security Group이 해당 포트 열려있는지 확인 (4계층 문제)
5. 현재 내 업무와 연결점
섹션 제목: “5. 현재 내 업무와 연결점”- 서비스 앞에 Nginx가 있는지, ALB만 있는지 파악
- HTTPS 인증서가 어디서 처리되는지 이해
- 네트워크 에러 발생 시 계층 기반으로 원인 찾아내기
6. 자주 헷갈리는 개념 비교
섹션 제목: “6. 자주 헷갈리는 개념 비교”| 개념 A | 개념 B | 차이점 |
|---|---|---|
| Web Server | WAS(Web Application Server) | Web Server는 정적 파일/프록시, WAS는 비즈니스 로직 실행 (Node.js는 WAS) |
| 리버스 프록시 | 포워드 프록시 | 리버스는 서버 앞에서 요청 분배, 포워드는 클라이언트 대신 요청 |
| Nginx | Apache | 둘 다 Web Server. Nginx는 비동기/논블로킹으로 고성능, Apache는 프로세스 기반 |
| ALB | Nginx | ALB는 AWS 관리형 로드밸런서, Nginx는 직접 운영하는 Web Server |
| HTTP/1.1 | HTTP/2 | HTTP/1.1은 요청 순차 처리, HTTP/2는 하나의 연결로 동시 처리(멀티플렉싱) |
6.5 선택 매트릭스 — Nginx vs ALB vs Envoy
섹션 제목: “6.5 선택 매트릭스 — Nginx vs ALB vs Envoy”세 가지 모두 리버스 프록시/로드밸런서 역할을 할 수 있지만 적합한 상황이 다르다. “다 가능하다”가 아니라 어떤 조건이면 무엇이 최선인가를 판단해야 한다.
| 판단 기준 | Nginx | ALB (AWS) | Envoy |
|---|---|---|---|
| 운영 환경 | 온프레미스 또는 EC2 직접 운영 | AWS 클라우드 (ECS/EKS/Lambda) | 마이크로서비스 메시 (Kubernetes) |
| 관리 복잡도 | 높음 (직접 설치·설정·업그레이드) | 낮음 (AWS 완전 관리) | 매우 높음 (Istio/콘트롤 플레인 필요) |
| 트래픽 규모 기준 | 소~중규모, 고정적 트래픽 | 중~대규모, 급격한 트래픽 변동 | 대규모 다중 서비스 간 내부 트래픽 |
| 핵심 강점 | 정적 파일 서빙, 세밀한 설정 제어 | AWS 네이티브 통합, 자동 스케일링 | 서비스 디스커버리, gRPC, 동적 구성 |
| 비용 구조 | 서버 비용 + 엔지니어 운영 비용 | 트래픽·LCU 기반 종량제 | 인프라 비용 + 높은 운영 인건비 |
| circuit breaker | 없음 (앱 레벨에서 별도 구현) | 없음 (Target Group health check으로 대체) | 내장 (outlier detection, retry) |
| 프로토콜 | HTTP/1.1, HTTP/2, TCP | HTTP/1.1, HTTP/2, WebSocket | HTTP/1.1, HTTP/2, HTTP/3, gRPC, TCP |
실무 의사결정 기준 (BackOps)
팀 규모 소~중, AWS ECS 환경 → ALB 단독 사용이 기본값 → Nginx를 추가하는 이유: 정적 파일 서빙, 세밀한 요청 재작성, ALB 뒤에서 부분 캐싱
트래픽 < 1,000 RPS & EC2 단일 인스턴스 → Nginx 직접 운영이 ALB보다 저렴할 수 있음 (ALB 최소 비용 ~$15/월 + LCU)
마이크로서비스 10개 이상, 서비스 간 gRPC 통신 필수 → Envoy 검토 (Istio 기반 서비스 메시) → 단, 학습곡선과 운영 복잡도를 감당할 팀 역량이 전제📖 참고: AWS ALB 공식 문서 - Features | Envoy 공식 문서 - What is Envoy
6.6 트러블슈팅
섹션 제목: “6.6 트러블슈팅”🔧 502 Bad Gateway — Nginx가 업스트림 서버에 연결 실패
섹션 제목: “🔧 502 Bad Gateway — Nginx가 업스트림 서버에 연결 실패”📖 더 보기: 502 Bad Gateway Nginx Fix - CloudPanel — 502 원인별 진단과 해결 방법 정리
증상: 브라우저에서 502 Bad Gateway 에러 발생. Nginx 에러 로그에 아래 메시지
connect() failed (111: Connection refused) while connecting to upstream원인: Nginx는 정상이지만 업스트림(Nest.js) 서버가 응답을 못하는 상태. 주요 원인:
- Node.js 프로세스가 죽어있음
proxy_pass에 설정한 포트가 틀림 (3000 vs 8080 등)- Node.js가 아직 기동 중 (헬스체크 실패)
해결:
# 1. Node.js 프로세스 실행 여부 확인ps aux | grep node# 예상 출력 (정상):# young 1234 1.2 2.3 node dist/main.js# 결과 없으면 → 프로세스 재시작 필요
# 2. 해당 포트 리스닝 여부 확인ss -tlnp | grep 3000# 예상 출력 (정상):# LISTEN 0 128 0.0.0.0:3000 users:(("node",pid=1234,fd=18))
# 3. Nginx에서 직접 업스트림 연결 테스트curl -v http://localhost:3000/health# → 200 OK이면 Nginx 설정 문제, 연결 실패면 Node.js 문제
# 4. Nginx 에러 로그 실시간 확인tail -f /var/log/nginx/error.log# → 에러 메시지로 원인 파악🔧 504 Gateway Timeout — 업스트림 응답 시간 초과
섹션 제목: “🔧 504 Gateway Timeout — 업스트림 응답 시간 초과”증상: 특정 API에서 504 Gateway Timeout 발생. 오래 걸리는 작업(대용량 CSV 처리, 복잡한 쿼리 등)에서 자주 발생
원인: Nginx의 proxy_read_timeout (기본 60초)보다 업스트림 응답이 늦음
해결:
# nginx.conf — 해당 location 블록에 타임아웃 늘리기location /api/heavy-job { proxy_pass http://localhost:3000; proxy_read_timeout 300s; # 기본 60s → 필요에 따라 조정 proxy_connect_timeout 10s; proxy_send_timeout 60s;}근본 해결책은 타임아웃을 늘리는 게 아니라 무거운 작업을 Queue로 분리하고 즉시 응답하는 것
🔧 502 Bad Gateway — 응답 헤더/바디가 버퍼 크기를 초과
섹션 제목: “🔧 502 Bad Gateway — 응답 헤더/바디가 버퍼 크기를 초과”증상: 특정 API만 502 Bad Gateway 발생. 특히 대용량 쿠키나 긴 Authorization 헤더를 사용하는 요청에서 발생. Nginx 에러 로그:
upstream sent too big header while reading response header from upstream원인: Nginx는 업스트림 응답을 버퍼에 담아 처리하는데, 응답 헤더가 기본 버퍼 크기(4k 또는 8k)를 초과하면 처리하지 못하고 502를 반환한다. JWT 토큰이 길거나 Set-Cookie 헤더가 많은 경우 흔히 발생한다.
해결:
location / { proxy_pass http://localhost:3000; # 응답 헤더 버퍼 크기 증가 (기본값: 4k 또는 8k) proxy_buffer_size 16k; proxy_buffers 4 16k; proxy_busy_buffers_size 16k;}📖 더 보기: Nginx 502 Bad Gateway Debugging Checklist - ZeonEdge — 502 원인별 체계적 진단 체크리스트 (중급)
🔧 HTTPS 접속 시 “인증서 오류” 또는 연결 실패
섹션 제목: “🔧 HTTPS 접속 시 “인증서 오류” 또는 연결 실패”증상: 브라우저에서 ERR_CERT_COMMON_NAME_INVALID 또는 SSL_ERROR_RX_RECORD_TOO_LONG
원인 1: 인증서 도메인과 실제 접속 도메인 불일치 (www. 유무, 와일드카드 여부)
원인 2: HTTP 포트(80)로 HTTPS 요청을 보내는 경우 (ERR_SSL_PROTOCOL_ERROR)
해결:
# 인증서 정보 확인openssl s_client -connect api.example.com:443 -showcerts 2>/dev/null | \ openssl x509 -noout -text | grep "Subject\|DNS"# 예상 출력:# Subject: CN=api.example.com# DNS:api.example.com, DNS:*.example.com
# Nginx가 어느 포트에서 SSL을 처리하는지 확인grep -n "listen\|ssl_certificate" /etc/nginx/conf.d/*.conf# 예상 출력:# /etc/nginx/conf.d/default.conf:2: listen 443 ssl;# /etc/nginx/conf.d/default.conf:5: ssl_certificate /etc/ssl/cert.pem;🔧 ALB Health Check 실패로 ECS Task가 계속 재시작됨
섹션 제목: “🔧 ALB Health Check 실패로 ECS Task가 계속 재시작됨”증상: ECS Fargate에서 태스크가 시작되자마자 Unhealthy 판정을 받고 반복 재시작. ALB Target Group에서 “unhealthy” 상태 지속
원인:
- Health Check 경로(
/health)가 Nest.js에 구현되어 있지 않음 - Health Check 포트/프로토콜 설정이 실제 앱과 다름
- Nest.js 앱이 기동 완료 전에 ALB가 체크를 시작 (Startup 지연)
해결:
// Nest.js에 헬스체크 엔드포인트 추가@Controller()export class HealthController { @Get("/health") health() { return { status: "ok" }; // → ALB가 이 엔드포인트를 호출해 200 OK이면 Healthy 판정 }}# AWS 콘솔 또는 CLI로 Target Group Health Check 설정 확인# 경로: EC2 → Load Balancers → Target Groups → [해당 그룹] → Health checks 탭# 확인 항목:# - Health check path: /health (앱에 있는 경로인지 확인)# - Healthy threshold: 2 (2번 연속 성공 시 Healthy)# - Unhealthy threshold: 3 (3번 연속 실패 시 Unhealthy)# - Timeout: 5s (앱 응답이 이 시간 내에 와야 함)🔧 503 Service Unavailable — ALB에 등록된 Target이 없거나 모두 Unhealthy
섹션 제목: “🔧 503 Service Unavailable — ALB에 등록된 Target이 없거나 모두 Unhealthy”증상: ALB DNS로 요청 시 503 Service Unavailable 반환. ECS 콘솔에서 태스크는 실행 중으로 보임
원인:
- ALB Target Group에 등록된 ECS Task가 없음 (태스크 배포 직후 공백 구간)
- 모든 Target이 Unhealthy 상태 (Health Check 실패 상태)
- Security Group 설정으로 ALB → ECS 컨테이너 포트가 막혀 있음
해결:
# 1. Target Group의 Target 상태 확인aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789:targetgroup/my-tg/abc123# 예상 출력 (정상):# { "TargetHealthDescriptions": [# { "Target": {"Id": "10.0.1.5", "Port": 3000},# "TargetHealth": {"State": "healthy"} }# ] }# 예상 출력 (문제 있을 때):# { "TargetHealth": {"State": "unhealthy", "Reason": "Target.FailedHealthChecks"} }
# 2. Security Group 규칙 확인 — ALB → ECS 컨테이너 포트 허용 여부aws ec2 describe-security-groups --group-ids sg-xxxx \ --query 'SecurityGroups[].IpPermissions'# → Inbound 규칙에 ALB의 Security Group에서 3000 포트 허용이 있어야 함
# 3. ECS 태스크 배포 직후 503이 발생한 경우# → ALB deregistration_delay(기본 300초) 동안 기존 Target이 유지되므로# → 신규 배포 시 "최소 정상 백분율"을 100%로 설정하여 공백 방지🔧 Cascading Failure — L4 timeout이 L7 circuit breaker를 무력화하는 패턴
섹션 제목: “🔧 Cascading Failure — L4 timeout이 L7 circuit breaker를 무력화하는 패턴”시나리오: DB 응답이 느려졌을 때 앱 전체가 멈추는 이유
[정상]클라이언트 → ALB → Nest.js → DB (100ms)
[DB 느려짐 — L4 timeout 유발]클라이언트 → ALB → Nest.js ──────────────────→ DB (30s 이상) ↑ TCP 연결은 살아있음 (L4 레벨) 하지만 응답은 안 옴핵심 문제: ALB Health Check는 HTTP 200을 기준으로 판단한다. DB가 느릴 때 /health 엔드포인트는 DB를 조회하지 않으면 여전히 200을 반환하므로 ALB는 Healthy로 판단하지만, 실제 API는 모두 타임아웃 상태다.
- DB 응답 지연 → Nest.js 워커 스레드/이벤트루프 점유 증가
- ALB Health Check는
/health(DB 미조회)만 보므로 Target = Healthy 유지 - ALB가 계속 새 요청을 전송 → Nest.js 커넥션 풀 포화
- L4 TCP 연결은 유지되지만 L7 HTTP 응답이 없음 → 클라이언트 전체 타임아웃
- 클라이언트가 재시도 → 부하 가중 → 전체 서비스 다운 (cascading failure)
circuit breaker가 없으면 왜 멈추는가: Nginx나 ALB에는 L7 circuit breaker가 내장되어 있지 않다. “업스트림이 n번 연속 실패하면 요청을 차단” 로직을 앱 레벨에서 별도로 구현하지 않으면, 느린 업스트림이 회복될 때까지 모든 요청이 쌓인다.
방어 패턴:
// 1. Health Check에 DB 연결 상태 포함 (shallow health check와 분리)@Get('/health/deep')async healthDeep() { await this.dataSource.query('SELECT 1'); // DB 실제 조회 return { status: 'ok', db: 'connected' }; // ALB Health Check는 /health(빠른 응답), 모니터링은 /health/deep}# 2. Nginx: upstream 응답 실패 시 서버를 임시 제외 (passive health check)upstream backend { server localhost:3000 max_fails=3 fail_timeout=30s; # 30초 내 3번 실패 시 해당 서버를 30초간 제외}
location / { proxy_pass http://backend; proxy_read_timeout 10s; # 너무 길면 연결이 쌓임 — DB timeout보다 짧게 설정 proxy_connect_timeout 3s;}정리: L4(TCP) 연결이 살아있다고 L7(HTTP) 요청이 정상 처리되는 것이 아니다. 느린 업스트림은 빠른 업스트림 다운보다 더 위험하다 — Health Check를 통과한 채로 요청을 계속 받기 때문이다.
7. 체크리스트
섹션 제목: “7. 체크리스트”- OSI 7계층을 외우지 않아도 “이 에러는 몇 계층 문제”인지 판단할 수 있다
- 리버스 프록시가 뭔지, 왜 쓰는지 설명할 수 있다
- 80포트로 들어온 요청이 Node.js 3000포트까지 도달하는 과정을 설명할 수 있다
- 팀 서비스 앞에 Web Server가 있는지, ALB만 있는지 확인했다
- 502와 504 에러의 차이를 설명할 수 있다
8. 추가 학습 키워드
섹션 제목: “8. 추가 학습 키워드”TCP 3-way handshake, TLS handshake, Nginx upstream, upstream timeout, CDN, DNS round-robin, ALB Target Group, Health Check, HTTP/2 multiplexing, keepalive_timeout
8.5 추천 리소스
섹션 제목: “8.5 추천 리소스”- 📖 NGINX Reverse Proxy 공식 문서 — proxy_pass, 업스트림 설정 공식 가이드 (입문)
- 📖 Understanding OSI & TCP/IP with Real-World Examples - Medium — 실제 요청 흐름으로 OSI 계층 설명, 비유 이해 쉬움 (입문)
- 📖 502 Bad Gateway Nginx Fix - CloudPanel — 502 원인별 진단과 해결 방법 정리 (중급)
- 📖 AWS ALB 공식 문서 - Target Groups — ECS Fargate 환경에서 ALB Target Group 설정 이해 (중급)
- 📖 Ultimate Nginx Performance Tuning Checklist - WeHaveServers — HTTP/2, TLS, 캐싱 포함 Nginx 프로덕션 튜닝 가이드 (중급)
9. 내가 직접 확인해볼 것
섹션 제목: “9. 내가 직접 확인해볼 것”- 팀 서비스의 트래픽 진입점 확인 (ALB → Nginx → Node? ALB → Node?)
# ALB DNS를 직접 curl로 호출해서 응답 헤더 확인curl -I https://your-alb-dns.ap-northeast-2.elb.amazonaws.com/health# 예상 출력:# HTTP/1.1 200 OK# Server: nginx/1.24.0 ← Nginx 있으면 표시됨# Server: Cowboy ← Fastify(Node.js) 직접이면 이쪽- Nginx 설정 파일이 있다면 proxy_pass 설정 확인
# Nginx 설정 파일 위치 찾기find /etc/nginx -name "*.conf" | xargs grep -l "proxy_pass"# 설정 확인cat /etc/nginx/conf.d/default.conf# 예상 출력 (핵심 부분):# location / {# proxy_pass http://127.0.0.1:3000;# proxy_set_header Host $host;# }-
curl -v https://서비스주소로 TLS 핸드셰이크 과정 확인
curl -v https://api.example.com/health 2>&1 | head -30# 예상 출력 (주요 부분):# * Trying 52.68.1.100:443...# * Connected to api.example.com (52.68.1.100) port 443# * TLSv1.3 (OUT), TLS handshake, Client hello# * TLSv1.3 (IN), TLS handshake, Server hello# * SSL certificate verify ok. ← 인증서 정상# > GET /health HTTP/2# < HTTP/2 200- ALB Target Group에서 ECS 태스크 Health 상태 확인
# AWS CLI로 Target Group Health 상태 조회aws elbv2 describe-target-health \ --target-group-arn $(aws elbv2 describe-target-groups \ --query 'TargetGroups[0].TargetGroupArn' --output text)# 예상 출력 (정상):# { "TargetHealthDescriptions": [# { "TargetHealth": { "State": "healthy" } }# ] }# 예상 출력 (문제 있을 때):# { "TargetHealth": { "State": "unhealthy", "Reason": "Target.FailedHealthChecks",# "Description": "Health checks failed" } }# → /health 엔드포인트 구현 여부, Security Group 포트 허용 여부 순서로 확인10. 핵심 요약
섹션 제목: “10. 핵심 요약”| 항목 | 핵심 내용 |
|---|---|
| OSI 7계층 | 문제 진단 기준 — 7(HTTP) → 4(포트/방화벽) → 3(IP/VPC) → 6(TLS) 순으로 확인 |
| 리버스 프록시 | 클라이언트 앞에서 SSL 처리 + 내부 서버로 요청 전달 (포트 숨김, 로드밸런싱) |
| Nginx vs ALB | Nginx는 직접 운영, ALB는 AWS 관리형 — ECS Fargate에서는 ALB가 대신함 |
| 502 vs 504 | 502: 업스트림 연결 실패(Node.js 다운), 504: 응답 시간 초과(타임아웃) |
| Health Check | ALB가 /health를 주기적으로 체크 — 실패하면 Unhealthy로 태스크 교체 |
5줄 핵심
- OSI 7계층은 네트워크 문제를 계층별로 나눠서 진단하는 기준이다
- 실무에서 주로 마주치는 계층은 7(HTTP), 4(TCP/포트), 3(IP), 6(TLS)이다
- Web Server(Nginx)는 클라이언트 요청을 가장 먼저 받아 내부 서버로 전달하며 SSL 처리, 로드밸런싱을 담당한다
- 502(업스트림 연결 실패)와 504(응답 시간 초과)는 원인과 해결 방향이 다르다
- ECS Fargate 환경에서는 ALB가 Nginx 역할을 대신하고, Security Group이 포트 접근을 제어한다
11. 다음 학습 경로
섹션 제목: “11. 다음 학습 경로”OSI 모델 & Web Server (지금 여기) ↓TCP/IP 심화 ← TCP 3-way handshake, Keep-Alive, TIME_WAIT 상태 ↓HTTPS / TLS 심화 ← TLS 1.3 핸드셰이크, mTLS, 인증서 갱신 자동화 (ACM) ↓AWS 네트워킹 ← VPC, Subnet, Security Group, Route 53, ALB 라우팅 규칙 ↓서비스 메시 / API Gateway ← Kong, AWS API Gateway, 트래픽 제어 패턴인터뷰 대비 핵심 질문 (실제 자주 출제)
- “HTTP와 HTTPS의 차이는? TLS Handshake는 어느 OSI 계층인가?”
- “502와 504 에러의 차이는? 각각 어떻게 디버깅하는가?”
- “리버스 프록시와 포워드 프록시의 차이는? 리버스 프록시를 쓰는 이유는?”
- “Nginx가 Apache보다 고성능인 이유는? (이벤트 기반 vs 프로세스 기반)”
- “ECS Fargate에서 ALB Health Check가 실패하는 주요 원인은?”
최종 수정: 2026-04-01