반응형
0. 프록시 vs 리버스 프록시 – 개념 & 목적 비교
| 항목 | 프록시(Forward Proxy) | 리버스 프록시(Reverse Proxy) |
|---|---|---|
| 트래픽 방향 | 클라이언트 앞에 위치, 외부로 나가는 요청을 대리 | 서버 앞에 위치, 외부에서 들어오는 요청을 대리 |
| 주요 목적 | 내부 클라이언트 익명화, 접근 제어, 캐싱, DLP | 로드밸런싱, SSL 종료, 캐싱, 보안(WAF), WAS 보호 |
| 외부에서 보이는 대상 | 프록시 뒤의 클라이언트는 숨겨짐 | 백엔드 서버(WAS)는 숨겨짐 |
| 대표 사용 예 | 사내망에서 외부 인터넷 접근 통제 | NGINX/HAProxy로 여러 WAS에 분산 |
| SEO/트래픽 영향 | 주로 내부 통제라 영향 적음 | 정적 자산 캐싱/압축, HTTP/2/3 종단으로 체감 성능↑ |
블로그 유입을 노린다면, “리버스 프록시로 성능·안정성·보안을 한 번에 잡는 방법”을 강조하세요. 🔥
1. NGINX 리버스 프록시 기본 설정(안정 템플릿)
아래 스니펫은 기본기를 담은 안전한 시작점입니다.
# /etc/nginx/nginx.conf (핵심만 발췌)
worker_processes auto;
events { worker_connections 10240; }
http {
sendfile on;
keepalive_timeout 65;
client_max_body_size 16m;
# 헤더가 큰 응답(세션/쿠키/토큰 등) 대비
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
# 업스트림(백엔드) 정의
upstream app_upstream {
server 127.0.0.1:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
}
server {
listen 80;
server_name _;
# 클라이언트 → NGINX → WAS 전달 헤더
location / {
proxy_pass http://app_upstream;
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;
# 타임아웃(504 방지/진단)
proxy_connect_timeout 3s; # TCP 연결
proxy_send_timeout 30s; # 요청 전송
proxy_read_timeout 30s; # 응답 수신
# 재시도 정책(네트워크/일시 오류 완화)
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 2;
}
}
}
포인트: 502가 “Upstream header too big”일 수 있어 버퍼를 넉넉히. 504는 주로 응답 지연 →
proxy_read_timeout/WAS 처리시간 튜닝 병행.
2. 502/504 원인 & 해결 체크리스트
502 Bad Gateway (게이트웨이가 잘못된 응답 수신)
- 주요 원인
- WAS 다운/Crash, 포트/주소 오타
- WAS 응답 헤더가 너무 큼(쿠키 과다, Set-Cookie 다중)
- 프로토콜 미스매치(HTTP/1.1 keep-alive 헤더/커넥션 처리 문제)
- 해결 가이드
- ✅ Upstream 연결 확인:
curl -v http://127.0.0.1:8080/health - ✅ 버퍼 상향:
proxy_buffer_size,proxy_buffers,proxy_busy_buffers_size - ✅ 헤더 정리: 불필요한 쿠키/헤더 축소, 압축/세션 전략 조정
- ✅ keep-alive 정합:
proxy_http_version 1.1,proxy_set_header Connection ""
- ✅ Upstream 연결 확인:
504 Gateway Timeout (백엔드 응답 지연/타임아웃)
- 주요 원인
- WAS 처리 지연(쿼리 느림, 락, GC), 네트워크 지연
- 타임아웃 값이 업무 특성에 비해 너무 짧음
- 해결 가이드
- ✅
proxy_read_timeout/proxy_send_timeout합리화 - ✅ WAS 스레드/DB 커넥션 풀 최적화(큐 길이·타임아웃 점검)
- ✅ 장기 요청은 비동기/배치로 분리, API는 빠르게 ACK + 폴링/웹훅
- ✅
3-1. NGINX에서 502/504를 HTTP 200 + JSON으로 커스터마이징
⚠️ 주의: 게이트웨이 레벨에서 상태코드를 200으로 바꾸면 모니터링/알람이 무뎌질 수 있습니다.
비즈니스 정책상 꼭 필요한 경우에만 사용하세요.
또한 WAS 자체가 다운이면 WAS에서 바꿀 수 없고 NGINX에서 가공해야 합니다.
방법 A) NGINX에서 직접 JSON 응답(정적 반환)
server {
listen 80;
server_name _;
location / {
proxy_pass http://app_upstream;
proxy_intercept_errors on; # NGINX가 에러 응답을 가로챔
}
# 502 → 200으로 바꾸고 JSON 바디 반환
error_page 502 =200 @err502;
location @err502 {
default_type application/json;
return 200 '{"errorCode":502,"message":"Bad Gateway","success":false}';
}
# 504 → 200으로 바꾸고 JSON 바디 반환
error_page 504 =200 @err504;
location @err504 {
default_type application/json;
return 200 '{"errorCode":504,"message":"Gateway Timeout","success":false}';
}
}
- 장점: WAS 다운이어도 동작, 구현 간단
- 단점: 고정 메시지·현황 반영 한계(동적 컨텍스트 부족)
방법 B) 에러 시 내부 라우팅으로 동적 엔드포인트(백업 서비스/헬스) 호출
upstream app_upstream { server 127.0.0.1:8080; }
upstream fallback_api { server 127.0.0.1:9090; } # 경량 백업/오류 핸들러
server {
listen 80;
proxy_intercept_errors on;
location / {
proxy_pass http://app_upstream;
}
# 502/504 발생 시 fallback API로 내부 재요청하되, 최종 상태코드는 200
error_page 502 = @fallback_502;
error_page 504 = @fallback_504;
location @fallback_502 {
proxy_pass http://fallback_api/error/502;
# fallback 응답이 502라도 최종 200으로 덮어쓰기
header_filter_by_lua_block { ngx.status = 200 }
}
location @fallback_504 {
proxy_pass http://fallback_api/error/504;
header_filter_by_lua_block { ngx.status = 200 }
}
}
- 장점: 동적 메시지/추적ID/시간 등 추가 가능
- 단점: OpenResty(ngx_lua) 등 추가 모듈 필요, 복잡도 상승
3-2. WAS(Spring Boot)에서 “에러를 200 + JSON”으로 응답하기
이 방식은 WAS가 살아 있을 때 적용됩니다.
프록시에서 오는 502/504 자체는 WAS까지 도달하지 않으므로 NGINX 쪽 가공(위 A/B)이 필요합니다.
그래도 내부 예외(Timeout/DB 오류 등)를 200으로 래핑해야 하는 정책이 있다면 아래처럼 처리합니다.
예) @ControllerAdvice로 공통 예외를 200 + JSON으로 래핑
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(TimeoutException.class)
public ResponseEntity<Map<String, Object>> handleTimeout(TimeoutException e) {
Map<String, Object> body = new HashMap<>();
body.put("errorCode", 504);
body.put("message", "Gateway Timeout (wrapped)");
body.put("success", false);
return ResponseEntity.ok(body); // 항상 200 OK
}
@ExceptionHandler(UpstreamBadGatewayException.class)
public ResponseEntity<Map<String, Object>> handleBadGateway(RuntimeException e) {
Map<String, Object> body = new HashMap<>();
body.put("errorCode", 502);
body.put("message", "Bad Gateway (wrapped)");
body.put("success", false);
return ResponseEntity.ok(body);
}
}
- 장점: 응답 형식 통일, 클라이언트는 항상 200 처리 규약
- 단점: 표준 HTTP와 어긋남(모니터링·중간 캐시·CDN 판단에 불리)
4. 운영 체크리스트
proxy_read_timeout은 WAS 평균+피크 처리시간에 맞춤 (너무 짧으면 504, 너무 길면 연결 잠금)- 버퍼/헤더: 대형 쿠키·헤더가 있다면
proxy_buffer_size/proxy_buffers상향 - 재시도 정책:
proxy_next_upstream을 과하게 키우면 중복 처리 위험 → 멱등(GET/HEAD) 위주 적용 - 헬스체크: 간단한
/health엔드포인트, WAS/DB 의존성 최소화 - 관측성:
$upstream_status,$request_time,$upstream_response_time를 접근로그에 포함 - 에러 200 래핑 시: 별도 헤더(
X-Error-Code)와 본문 필드(errorCode)를 일관 표준으로 운영
접근로그 예시 포맷(요약):
log_format main '$remote_addr "$request" $status $request_time $upstream_status $upstream_response_time';
5. 결론 & SEO 포인트
- 핵심: NGINX 리버스 프록시는 성능·보안·안정성의 초석. 502/504는 연결/버퍼/타임아웃 3축으로 빠르게 진단.
- 커스터마이징: 비즈니스 요구 시 502/504를 HTTP 200 + JSON으로 변환 가능(NGINX 또는 WAS).
- SEO 팁: 본문에 자연스럽게 “Nginx reverse proxy 설정, 502 bad gateway 해결, 504 gateway timeout 해결” 키워드를 배치하고, 코드블록·표·체크리스트로 체류시간↑를 노리세요. ✅
6. 예제
1) 리버스 프록시 표준 템플릿(압축)
upstream app_upstream { server 127.0.0.1:8080 max_fails=3 fail_timeout=10s; keepalive 64; }
server {
listen 80;
location / {
proxy_pass http://app_upstream;
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_connect_timeout 3s; proxy_send_timeout 30s; proxy_read_timeout 30s;
proxy_buffer_size 16k; proxy_buffers 8 16k; proxy_busy_buffers_size 32k;
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_intercept_errors on;
}
error_page 502 =200 @err502;
location @err502 { default_type application/json; return 200 '{"errorCode":502,"message":"Bad Gateway","success":false}'; }
error_page 504 =200 @err504;
location @err504 { default_type application/json; return 200 '{"errorCode":504,"message":"Gateway Timeout","success":false}'; }
}
2) Spring Boot 예외 래핑(간단 버전)
@RestControllerAdvice
class GlobalHandler {
@ExceptionHandler({TimeoutException.class})
ResponseEntity<Map<String,Object>> t(TimeoutException e){
return ResponseEntity.ok(Map.of("errorCode",504,"message","Gateway Timeout","success",false));
}
@ExceptionHandler({UpstreamBadGatewayException.class})
ResponseEntity<Map<String,Object>> b(RuntimeException e){
return ResponseEntity.ok(Map.of("errorCode",502,"message","Bad Gateway","success",false));
}
}
반응형
'Backend > Study' 카테고리의 다른 글
| [Study] JWT 토큰 인증 방식과 보안 고려사항 (0) | 2025.10.01 |
|---|---|
| [Study] 서버 다운 없이 배포하는 방법 (무중단 배포) (0) | 2025.09.30 |
| [Study] Redis 캐시 적용 방법 (Spring Boot 실전 예제) (0) | 2025.09.25 |
| [Study] JPA vs MyBatis 비교 – 언제 어떤 걸 써야 할까 (0) | 2025.09.24 |
| [Study] REST API 설계 원칙과 Request/Response 이해하기 (0) | 2025.09.22 |