1. 문제 정의: 제각각인 에러 화면과 보안 리스크
엔터프라이즈 및 마이크로서비스 웹 아키텍처에서 에러가 발생했을 때, 에러 발생 계층에 따라 화면이 제각각 노출되는 문제가 발생합니다. 최전방에 Nginx 리버스 프록시가 있고 뒤단에 Tomcat 내장 Spring Boot 애플리케이션이 연결된 환경에서는 다음과 같은 분절된 화면이 나타납니다.
-
Spring Boot Whitelabel Error Page: HTTP 상태 코드, 타임스탬프, 에러 메시지가 날것 그대로 브라우저에 출력됩니다.
-
Apache Tomcat 기본 에러 페이지: 서블릿 컨테이너 레벨의 404/500 에러 발생 시 특유의 회색조 톰캣 화면이 나타납니다.
-
Nginx 기본 에러 페이지: 백엔드 애플리케이션이 다운되거나 타임아웃될 때 투박한 502 Bad Gateway / 504 Gateway Time-out 텍스트 화면이 표시됩니다.
이러한 파편화는 사용자 경험(UX)을 해칠 뿐만 아니라, 보안 취약점(CWE-209: 정보 노출)을 유발합니다. 톰캣 버전 정보나 프레임워크 스택 트레이스(Stack Trace)가 노출되는 '서버 핑거프린팅(Server Fingerprinting)'으로 인해 공격자에게 시스템 취약점 분석의 단서를 제공하게 됩니다.

Spring Boot 에러 페이지
Apache Tomcat 에러 페이지
Nginx 에러 페이지
2. 기술적 한계: Spring Boot 내부 처리의 한계
Spring Boot 애플리케이션 내부의 @ExceptionHandler나 ErrorController는 애플리케이션(JVM/Tomcat) 프로세스가 정상적으로 살아있고 응답을 생성할 수 있는 상태에서만 동작합니다.
실제 운영 환경의 치명적인 장애는 애플리케이션 코드가 실행조차 되지 못하는 구간에서 발생합니다.
-
JVM OOM(Out of Memory) 크래시: 힙 메모리 고갈로 프로세스가 강제 종료된 경우 (502 Bad Gateway)
-
Tomcat Thread Pool / DB Connection Pool 고갈: 대규모 트래픽으로 톰캣이 신규 커넥션을 맺지 못하는 경우 (504 Gateway Time-out)
-
무중단 배포 롤링 업데이트 순단: 신규 컨테이너 기동 전 찰나의 순간에 유입된 요청
이러한 인프라성 장애 상황에서는 Spring Boot 코드가 실행될 수 없으므로 Nginx의 날것 화면이 노출됩니다. 진정한 고가용성 에러 제어를 위해서는 트래픽의 최초 관문인 Nginx 계층에서 에러 핸들링을 일원화해야 합니다.
3. 해결 설계: Nginx proxy_intercept_errors 기반 중앙 제어
Nginx를 최전방 에러 컨트롤러로 전환하는 핵심 메커니즘은 proxy_intercept_errors 지시어입니다. 이 지시어를 활성화하면 Nginx는 Upstream(Tomcat/Spring Boot)이 반환하는 HTTP 응답 코드가 300 이상일 때 응답 본문을 가로채어 사전에 정의된 error_page 경로로 내부 재라우팅합니다.
-
보안 격리: 백엔드에서 어떤 예외가 발생하든 응답 바디를 폐기하고 Nginx의 정제된 정적 파일만 서빙하여 내부 정보 노출을 차단합니다.
-
인프라 레벨 fallback: Spring Boot의 다운 여부와 무관하게 일관된 서비스 안내 화면을 제공합니다.
-
직접 접근 차단: internal 지시어를 통해 사용자가 브라우저 주소창에 /error/500.html을 직접 입력해 접근하는 것을 방지합니다.
4. 실전 프로덕션 nginx.conf 설정 예시
Tomcat 및 Spring Boot의 에러를 가로채고 커스텀 에러 화면으로 라우팅하는 nginx.conf 핵심 구성 예제입니다.
# 1. [Content Negotiation] 요청 헤더(Accept) 기반 응답 포맷 판별
# React 비동기 API 요청(JSON)과 일반 웹 브라우징(HTML)을 구분합니다.
map $http_accept $error_response_type {
default "html";
~*application/json "json";
~*text/json "json";
}
server {
listen 80;
server_name service.example.com;
# 2. [핵심] 뒷단 WAS(Tomcat/Spring Boot)의 3xx/4xx/5xx 에러를 가로챕니다.
proxy_intercept_errors on;
# 3. HTTP 상태 코드별 내부 에러 디스패처로 라우팅
error_page 404 = /handle_error_404;
error_page 500 = /handle_error_500;
error_page 502 = /handle_error_502;
error_page 504 = /handle_error_504;
# 기본 비즈니스 프록시 패스
location / {
proxy_pass http://backend_app;
# WAS 장애 시 빠른 Nginx 에러 핸들러 전환을 위한 타임아웃
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
# 4. [내부 디스패처] 헤더에 따라 JSON vs HTML 분기 반환
# internal 지시어로 외부 사용자의 직접 URL 호출을 차단합니다.
location = /handle_error_500 {
internal;
if ($error_response_type = "json") {
default_type application/json;
return 500 '{"status": 500, "code": "INTERNAL_SERVER_ERROR", "message": "서버 오류가 발생했습니다."}';
}
rewrite ^ /error/500.html break;
}
# 5. [WAS 다운 대응] Spring Boot 프로세스 다운(OOM 등) 시 502 처리
location = /handle_error_502 {
internal;
if ($error_response_type = "json") {
default_type application/json;
return 502 '{"status": 502, "code": "SERVICE_UNAVAILABLE", "message": "현재 서비스 점검 중입니다."}';
}
rewrite ^ /error/502.html break;
}
# 6. [정적 파일 격리] 커스텀 HTML 에러 페이지 서빙 (직접 접근 불가)
location /error/ {
internal;
alias /usr/share/nginx/html/error/;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
Nginx.conf 예시 코드
5. 심화 설계: React 연동 시 발생한 흰 화면(white Screen) 이슈와 해결
proxy_intercept_errors on;을 단순 적용하면 일반 웹 페이지는 정상 동작하지만, React SPA 환경에서는 치명적인 런타임 크래시가 발생합니다.
-
문제 상황 (SyntaxError 및 흰 화면):
React에서 axios/fetch로 API를 호출하던 중 백엔드가 500 에러를 뱉거나 톰캣이 다운(502)되면, Nginx가 error_page에 지정된 HTML 문서를 반환합니다. 프론트엔드는 이를 JSON으로 파싱하려다 SyntaxError: Unexpected token '<'를 일으키며 화면 전체가 하얗게 멈추는(white Screen) 현상이 발생합니다. -
해결 방법 (Nginx Content Negotiation):
Nginx의 map 모듈로 요청 헤더의 Accept 값을 판별하여, 일반 브라우징(text/html)에는 HTML 화면을, React 비동기 API 요청(application/json)에는 정형화된 JSON 에러 스키마를 즉시 반환하도록 분기 처리합니다. -
개선 효과:
Spring Boot가 완전히 다운되어도 React의 전역 Axios 인터셉터가 규격화된 JSON 응답을 받아 "서버 점검 중" 모달이나 토스트 알림을 안전하게 띄울 수 있게 되며, 완벽한 인프라 레벨 Fallback을 달성할 수 있습니다.
6. 결론: 계층 분리가 만들어낸 인프라 신뢰성
Nginx를 활용한 프록시 계층 에러 제어는 단순한 디자인 커스터마이징을 넘어, 인프라(Nginx)와 애플리케이션(Tomcat/Spring Boot)의 관심사를 분리(Separation of Concerns)하는 핵심 아키텍처입니다.
애플리케이션은 순수한 비즈니스 로직과 도메인 예외에 집중하고, 인프라 장애와 다운타임은 최전방 프록시가 가로채어 규격화된 형태로 응답하는 구조를 구축함으로써 서비스 안정성과 보안성을 동시에 확보할 수 있습니다.
TK