Nginx 리버스 프록시로 Node.js 서비스 앞단 구성하기 | SSL·upstream·로그
이 글의 핵심
Nginx 리버스 프록시 Node.js 설정: nginx.conf에서 upstream·프록시 헤더·SSL/TLS·액세스 로그를 실무에 맞게 구성하는 방법을 단계별로 정리합니다.
들어가며
Nginx는 고성능 웹 서버이자 리버스 프록시로, Node.js 앱 앞에서 TLS 종료, 정적 파일 서빙, 로드 밸런싱, 압축, 레이트 리밋을 담당하는 구성이 매우 흔합니다. Node는 비즈니스 로직에 집중하고, 엣지에서 연결·암호화·라우팅을 처리하면 운영이 단순해집니다.
이 글은 단일 Node 인스턴스 뒤에 Nginx를 두는 기본 패턴과, 확장 시 upstream으로 여러 Node 프로세스를 묶는 방법을 다룹니다. 또한 Nginx가 어떤 내부 구조로 동시 연결을 처리하는지(이벤트 루프·워커·버퍼)와 로드 밸런싱 알고리즘, 프로덕션에서 반복되는 패턴을 함께 정리합니다. 컨테이너 기반 스택은 Docker Compose 글과 함께 보시면 연결되고, 오케스트레이션 단계는 Kubernetes(minikube)로 이어집니다. 백엔드가 붙는 DB는 Node.js 데이터베이스 연동·C++ DB 연동(libpq 등)·PostgreSQL vs MySQL과, 캐시는 Redis 캐싱 패턴과 묶어서 보시면 엣지 → 앱 → 캐시 → DB가 한 흐름으로 잡힙니다. 서버 디스크·inode 이슈는 Linux 트러블슈팅과 겹칠 수 있습니다.
요청 흐름은 클라이언트 → (TLS·HTTP/2) → Nginx → (HTTP·keepalive) → Node upstream 순으로 이해하시면 됩니다. Node는 비즈니스 로직, Nginx는 연결·암호화·분배를 맡는 앞단 관문에 가깝습니다.
이 글을 읽으면
- 프록시 헤더와 upstream 블록을 이해하고 실제
nginx.conf에 적용하실 수 있습니다 - Nginx의 이벤트 구동·워커 모델·버퍼링을 개략적으로 파악해 튜닝·장애 분석에 연결하실 수 있습니다
- Let’s Encrypt 인증서와 함께 쓰는 SSL 서버 블록 패턴을 살펴보실 수 있습니다
- 액세스·에러 로그와 WebSocket 업그레이드 설정을 맞추실 수 있습니다
개념: 리버스 프록시와 Nginx 역할
기본 개념
- 리버스 프록시: 클라이언트는 Nginx에만 붙고, Nginx가 백엔드(Node) 로 요청을 전달합니다.
- TLS 종료: 브라우저 ↔ Nginx는 HTTPS, Nginx ↔ Node는 HTTP(내부 네트워크)인 구성이 일반적입니다.
- upstream: 여러 백엔드 주소를 묶고 라운드 로빈, least_conn, ip_hash(세션 고정) 등을 선택합니다.
왜 필요한가
Node 단일 프로세스는 CPU 코어 하나를 주로 쓰므로, PM2 클러스터나 여러 컨테이너 + Nginx 로드 밸런싱으로 수평 확장합니다. 또한 정적 자산은 Nginx가 직접 서빙해 Node 이벤트 루프 부하를 줄입니다.
Nginx 내부: 이벤트 루프·epoll·kqueue·워커
Nginx는 프로세스당 단일 스레드로 이벤트를 처리하는 워커들이 동시에 많은 연결을 감당하도록 설계되어 있습니다. 이는 Node의 이벤트 루프와 철학은 비슷하지만, Nginx는 C로 작성된 전용 웹·프록시 엔진으로 TLS·HTTP 파싱·정적 파일·upstream까지 한 프로세스 안에서 처리합니다.
이벤트 구동과 커널 인터페이스
워커는 수천·수만 개의 소켓을 블로킹 I/O 없이 다루기 위해 OS가 제공하는 이벤트 알림 API에 의존합니다.
- Linux:
epoll이 일반적입니다. 관심 있는 소켓 집합에 대해 준비됨(read/write 가능) 상태를 커널이 통지하고, 워커는 루프 안에서 해당 소켓만 처리합니다. - FreeBSD·macOS 등 BSD 계열:
kqueue가 이 역할을 합니다. 인터페이스는 다르지만 “많은 fd를 한 워커가 효율적으로 폴링” 한다는 목적은 동일합니다. - 구형·제한 환경:
select/poll계열로 빌드·동작할 수 있으나, 대규모 동시 연결에서는epoll/kqueue쪽이 유리합니다.
즉 Nginx는 “연결 하나당 스레드 하나” 가 아니라, 한 워커 스레드가 이벤트 루프로 수많은 연결을 번갈아 처리합니다. 이 구조가 리버스 프록시로서 클라이언트 ↔ Nginx ↔ upstream 사이에 대량의 유휴·저속 연결이 존재하는 환경에 잘 맞습니다.
마스터 프로세스와 워커 모델
- 마스터(master): 설정 읽기, 워커(worker) 프로세스 생성·감시, 시그널 처리, 일부 버전·모듈에서 업그레이드·리로드 조정 등을 담당합니다.
- 워커(worker): 실제로 accept(또는 이에 준하는 연결 수락)·HTTP 처리·프록시를 수행합니다. 워커 수는 보통
worker_processes auto처럼 CPU 코어 수에 맞추는 설정이 흔합니다.
워커마다 자기 이벤트 루프가 있으므로, 코어 수만큼 워커를 두면 멀티코어를 활용합니다. Node 클러스터와 마찬가지로 프로세스 수를 무작정 늘리면 컨텍스트 스위칭 비용이 커질 수 있어, 부하 특성과 측정으로 맞추는 것이 좋습니다.
worker_connections는 워커 하나가 동시에 처리하려는 연결 수 상한에 가깝게 이해할 수 있습니다. 앞단 Nginx + 뒤단 Node 모두 연결당 메모리·파일 디스크립터를 쓰므로, OS의 ulimit -n과 커널 튜닝과 함께 봐야 합니다.
Node와의 역할 분담
Nginx 워커는 엣지에서의 동시 연결·TLS·정적 자산·프록시 버퍼에 최적화되어 있고, Node는 애플리케이션 로직·I/O 바운드 작업에 집중합니다. 둘 다 이벤트 기반이라는 점에서 튜닝 시 “어디서 연결을 받고 어디서 버퍼링할지” 를 명확히 하는 것이 운영에 도움이 됩니다.
연결·버퍼링: 프록시 경로에서의 데이터 흐름
proxy_pass로 upstream(Node)에 붙을 때, Nginx는 기본적으로 응답 본문을 버퍼에 모았다가 클라이언트로 보내는 프록시 버퍼링을 사용합니다. 이는 느린 클라이언트가 있어도 upstream 연결을 빨리 비우고 워커를 다른 요청에 쓰게 하려는 설계입니다.
주요 지시어(개념)
proxy_buffering:on(기본)이면 응답을 버퍼에 쌓는 동작이 활성화됩니다. SSE(Server-Sent Events)·스트리밍·청크 전송을 즉시 흘려보내야 하는 API에서는off를 검토합니다. 끄면 upstream이 느린 클라이언트에 맞춰 더 오래 점유될 수 있으므로, 동시 연결·타임아웃과 함께 봐야 합니다.proxy_buffer_size: 응답 헤더를 읽을 때 쓰는 버퍼 크기입니다. 큰 헤더·쿠키가 많으면 조정 이슈가 됩니다.proxy_buffers/proxy_busy_buffers_size: 본문 조각을 담는 버퍼 개수·크기, 아직 클라이언트로 못 보낸 바쁜 버퍼 상한 등으로, 메모리 사용과 흐름 제어에 영향을 줍니다.proxy_max_temp_file_size: 버퍼를 넘는 큰 응답은 디스크 임시 파일로 넘길 수 있는데, 이 한도와 디스크 I/O가 병목이 될 수 있습니다.
클라이언트가 업로드하는 요청 쪽은 다른 축입니다.
client_body_buffer_size: 작은 본문은 메모리에, 그 이상은 임시 파일로 받을 수 있습니다.client_max_body_size: 업로드 한도(아래 설정 예시와 연결).
프로덕션에서는 대용량 업로드·다운로드·스트리밍 경로만 별도 location으로 나누어 버퍼·타임아웃·proxy_buffering을 다르게 두는 패턴이 자주 쓰입니다.
버퍼·바디 관련 예시
# API 기본: 버퍼링 유지, 업로드 한도만 명시
location /api/ {
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_pass http://node_app;
}
# SSE/스트리밍: 응답을 즉시 전달해야 할 때만 검토
location /events {
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://node_app;
}
실전: nginx.conf 설정
최소 예시: HTTP → Node (개발·내부망)
# /etc/nginx/conf.d/app.conf
upstream node_app {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_http_version 1.1;
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_set_header X-Forwarded-Proto $scheme;
proxy_pass http://node_app;
}
}
upstream+keepalive: Nginx와 Node 사이 TCP 연결을 재사용해 요청마다 핸드셰이크 비용을 줄입니다(HTTP/1.1Connection처리와 맞물립니다).proxy_http_version 1.1: 업스트림에 HTTP/1.1을 씁니다(keepalive·청크 전송 등과 궁합이 좋습니다).Host: Node가 가상 호스트·절대 URL 생성을 할 때 원래 요청 호스트를 알 수 있게 합니다.X-Forwarded-For/X-Forwarded-Proto: Node가 클라이언트 IP·원래 스킴(https) 을 알아 리다이렉트·쿠키·레이트 리밋에 씁니다.
HTTPS + Let’s Encrypt (인증서 경로는 certbot 기본 가정)
upstream node_app {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1.2 TLSv1.3;
access_log /var/log/nginx/api.access.log combined;
error_log /var/log/nginx/api.error.log warn;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
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_set_header X-Forwarded-Proto $scheme;
proxy_pass http://node_app;
}
# WebSocket (Socket.io 등)
location /socket.io/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_pass http://node_app;
}
}
listen 443 ssl http2: TLS 종료와 HTTP/2를 켭니다(클라이언트 ↔ Nginx 구간).ssl_protocols TLSv1.2 TLSv1.3: 구형 TLS 비활성으로 보안·호환을 맞춥니다.- 일반
location에서proxy_set_header Connection ""는 upstream keepalive와 함께 쓸 때 흔한 패턴입니다(모듈·버전에 따라 기본값이 달라 명시하기도 합니다). - WebSocket
location에서는Upgrade·Connection: upgrade를 넘겨 HTTP 업그레이드가 끝까지 전달되게 합니다.
Node(Express)에서 신뢰 프록시 설정
// Express: X-Forwarded-* 반영 (프록시 뒤에 있을 때)
import express from 'express';
const app = express();
app.set('trust proxy', 1); // Nginx 한 홉
app.get('/health', (_req, res) => res.send('ok'));
Docker Compose와 함께
Nginx 컨테이너와 API 컨테이너가 같은 네트워크에 있으면 upstream은 server api:3000; 형태가 됩니다. 자세한 스택 구성은 Docker Compose로 Node API·DB·Redis 한 번에 띄우기를 참고하세요.
고급: 로드 밸런싱 알고리즘과 upstream
upstream 블록에서 백엔드 선택 규칙을 고릅니다. 오픈 소스 Nginx에서 자주 쓰는 방식은 아래와 같습니다.
round-robin (기본)
순서대로 돌아가며 요청을 분배합니다. weight를 주면 가중 라운드 로빈이 되어, 성능이 좋은 인스턴스에 더 많은 비중을 둘 수 있습니다.
upstream node_app {
server 127.0.0.1:3000 weight=3;
server 127.0.0.1:3001 weight=1;
keepalive 32;
}
least_conn
활성 연결이 가장 적은 서버로 보냅니다. 요청 처리 시간 편차가 크거나, 긴 폴링·WebSocket처럼 연결이 오래 붙어 있는 경우 단순 라운드 로빈보다 균형이 나을 수 있습니다. 반대로 연결 수만으로는 CPU·디스크 부하를 완전히 반영하지 못하므로, 지표와 함께 검증하는 것이 좋습니다.
ip_hash
클라이언트 IP(IPv4의 경우 전체, IPv6는 일부 구현에 따라 다름)를 해시해 항상 같은 upstream으로 보냅니다. 세션 스티키가 애플리케이션 외부에서 필요할 때 쓰지만, 캐리어급 NAT나 기업 프록시 뒤에서는 수많은 사용자가 같은 IP로 보여 한 인스턴스에 몰릴 수 있습니다. 이 경우 hash $request_uri consistent 등 다른 키나, 세션 저장소를 Redis 등으로 외부화하는 편이 낫습니다.
장애·재시도와 운영
max_fails·fail_timeout으로 일시적으로 죽은 upstream을 건너뛰는 패턴을 쓰기도 합니다. 헬스체크를 Nginx 외부(오케스트레이터·별도 로드 밸런서)에서 할지, 업스트림 그룹만 교체할지는 배포 방식에 따라 갈립니다.
클라이언트 본문·타임아웃
client_max_body_size 10m;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
레이트 리밋(간단)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location / {
limit_req zone=api_limit burst=20 nodelay;
# ....proxy 설정
}
프로덕션 패턴
TLS·인증서·HSTS
- 인증서 자동 갱신(certbot·ACME)과 배포 파이프라인에서
fullchain.pem경로가 일치하는지 주기적으로 확인합니다. - HSTS, 적절한 cipher suite, OCSP stapling 여부는 보안 요구에 맞춰 검토합니다.
무중단 배포·트래픽 전환
nginx -s reload: 마스터가 설정을 다시 읽고 워커를 점진적으로 교체하는 방식으로, 연결을 최대한 끊지 않으려는 운영이 일반적입니다. 배포 스크립트에서 설정 검증(nginx -t) 후 리로드하는 순서를 고정해 두면 좋습니다.- 블루그린·카나리: upstream 서버 목록을 바꾸거나, 새 인스턴스만 다른 포트로 붙였다가 검증 후 전환합니다. Kubernetes 등에서는 Service·Ingress가 이 역할을 대신하기도 합니다.
관측 가능성·실제 클라이언트 IP
- 액세스 로그에
$http_x_forwarded_for를 남기거나,real_ip모듈로 신뢰할 프록시 대역을 지정해 클라이언트 IP를 복원합니다. - Node 쪽
trust proxy와 로그·레이트 리밋 키가 동일한 IP 기준을 쓰는지 점검합니다.
보안·엣지 정책
- 레이트 리밋, 연결 제한, WAF(별도 제품 또는 클라우드 LB)는 요구에 따라 Nginx 앞·뒤 어디에 둘지 정합니다.
- 보안 헤더(예:
X-Content-Type-Options,X-Frame-Options등)는 Nginx 또는 애플리케이션 중 한 곳에서 일관되게 적용합니다.
리소스·한계
- 파일 디스크립터·워커 연결 수·업로드 임시 디스크는 트래픽이 커질수록 한계에 닿습니다. 부하 테스트로
worker_connections·OSulimit·디스크 여유를 함께 확인합니다.
성능 비교 관점
Nginx는 이벤트 기반(epoll/kqueue) 으로 수만 동시 연결에 적합하고, Node는 I/O에 강점이 있습니다. 정적 파일은 Nginx가 직접 서빙할 때 디스크 I/O와 Node 이벤트 루프를 동시에 아낄 수 있습니다.
| 구성 | 장점 | 주의 |
|---|---|---|
| TLS @ Nginx | Node에서 암호화 부담 감소 | 인증서 갱신 자동화(certbot) |
| keepalive upstream | 연결 재사용으로 지연 감소 | 백엔드 수·타임아웃 맞춤 |
| gzip / brotli | 전송량 감소 | CPU 사용 증가 |
| 프록시 버퍼링(기본 on) | 느린 클라이언트로부터 upstream 보호 | 스트리밍·SSE는 별도 튜닝 |
실무 사례
- 블루그린/무중단: upstream 서버 그룹을 바꾸거나, 새 컨테이너 기동 후 헬스체크 통과 시만 트래픽 전환.
- 스테이징:
server_name만 분리해 동일 Node 이미지에 다른DATABASE_URL주입. - 배포 파이프라인: GitHub Actions로 Node.js CI/CD에서 이미지 배포 후 Nginx는 설정 리로드(
nginx -s reload)만 수행. - 장시간 작업: 업로드·다운로드·SSE 전용
location에 타임아웃·proxy_buffering·client_max_body_size를 API 기본값과 분리.
트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
| 502 Bad Gateway | Node 미기동·포트 불일치 | ss -tlnp, 컨테이너 로그 확인 |
| 리다이렉트가 http로 감 | X-Forwarded-Proto 누락 | proxy_set_header X-Forwarded-Proto $scheme |
| WebSocket 끊김 | Upgrade 헤더 미전달 | 위 socket.io 예시처럼 설정 |
| 실제 IP가 모두 Nginx | trust proxy 미설정 | Express trust proxy, 로그는 $http_x_forwarded_for 참고 |
| 업로드 실패 413 | client_max_body_size 기본 1m | 값 상향 |
| SSE/스트리밍이 버벅임 | proxy_buffering on | 해당 location에서 proxy_buffering off 검토 |
| 디스크 I/O 급증 | 큰 응답·업로드가 임시 파일로 spill | proxy_max_temp_file_size·client_body_buffer_size·디스크 여유 확인 |
디버깅 팁: curl -v https://api.example.com로 TLS·헤더를 보고, Nginx error_log 레벨을 info로 잠시 올려 upstream 에러를 확인합니다.
마무리
- Nginx upstream과 프록시 헤더는 Node를 프록시 뒤에 둘 때의 기본기입니다.
- 이벤트 구동·워커·버퍼링을 개략적으로 이해하면 지연·502·디스크 이슈를 원인 후보에 넣기 쉽습니다.
- TLS·로그·WebSocket·로드 밸런싱까지 한 번에 정의해 두시면 스테이징과 프로덕션 차이를 줄일 수 있습니다.
- Node 배포 전체 맥락은 Node.js 배포 가이드와 함께 보시면 좋습니다.
프로덕션 체크리스트
- 인증서 만료 전에 갱신(cron·certbot)이 도는지,
fullchain경로가 배포와 일치하는지 확인합니다. client_max_body_size·proxy_read_timeout을 업로드·장시간 API에 맞춥니다.- Nginx·Node 액세스 로그에 실제 클라이언트 IP가 남는지(
X-Forwarded-For·real_ip) 점검합니다. - 스트리밍/SSE 경로는
proxy_buffering·타임아웃을 일반 API와 구분했는지 확인합니다.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「Nginx 리버스 프록시로 Node.js 서비스 앞단 구성하기 | SSL·upstream·로그」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「Nginx 리버스 프록시로 Node.js 서비스 앞단 구성하기 | SSL·upstream·로그」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Nginx 리버스 프록시 Node.js 설정: nginx.conf에서 upstream·프록시 헤더·SSL/TLS·액세스 로그를 실무에 맞게 구성하는 방법을 단계별로 정리합니다. Node.js·Nginx·리버스 프록시… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- Nginx 완벽 가이드 | 리버스 프록시·로드 밸런싱·SSL·캐싱·성능 최적화
- [2026] Node.js 백엔드 완전 정리 — libuv·워커·스트림·우아한 종료·프로덕션
- Docker Compose로 Node API·PostgreSQL·Redis 한 번에 띄우기
이 글에서 다루는 키워드 (관련 검색어)
Node.js, Nginx, 리버스 프록시, SSL, 로드 밸런싱, upstream 등으로 검색하시면 이 글이 도움이 됩니다.