Nginx가 왜 필요했는가
웹 애플리케이션을 배포하다 보면 자연스럽게 웹 서버와 보안에 대한 고민이 시작된다.
특히 프론트엔드와 백엔드가 분리된 구조에서는 어떻게 요청을 분기하고, 안전하게 데이터를 주고받을 것인가가 중요하다.
이때 가장 많이 사용되는 도구가 바로 Nginx다.
Nginx는 단순히 웹 서버 역할만 하는 것이 아니라, 리버스 프록시, 로드 밸런서, SSL 처리까지 담당하는 다재다능한 도구다.
이번에는 Nginx의 역할과 SSL 적용 방식에 대해 구체적으로 정리해보려고 한다.
Nginx 역할
1. 정적파일 제공
Nginx는 기본적으로 웹 서버이면서 동시에 리버스 프록시 서버로 많이 사용된다.
웹 서버라는 건, 쉽게 말해 정적 파일을 직접 제공하는 역할을 한다.
예를 들어, HTML, CSS, JavaScript 같은 파일들을 클라이언트에게 직접 전달한다.
Next.js 같은 프레임워크로 빌드한 정적 웹 페이지를 서빙할 때, Nginx가 프론트엔드 서버로서 정적 파일들을 효율적으로 제공할 수 있다.
2. 리버스 프록시
하지만 Nginx의 진짜 강점은 바로 리버스 프록시이다.
리버스 프록시는 클라이언트로부터 받은 요청을 내부 애플리케이션 서버로 중계하는 역할을 한다.
예를 들어, 사용자가 https://devinterview.shop에 접속하면,
Nginx는 / 경로로 들어온 요청은 프론트엔드 서버로,
/api/ 경로로 들어온 요청은 백엔드 서버로 분기 처리할 수 있다.
이 덕분에 사용자는 프론트와 백엔드가 구분되지 않은 하나의 도메인으로 접속할 수 있다.
3. 로드 밸런서
Nginx는 로드 밸런서로도 활용할 수 있다.
만약 백엔드 서버가 여러 대로 구성되어 있다면,
Nginx가 들어오는 트래픽을 여러 서버로 균등하게 분배해주어 서버 부하를 줄여준다.
이렇게 하면 한쪽 서버에 트래픽이 몰려서 터지는 상황을 방지할 수 있다.
4. SSL 처리
그리고 현대 웹 서비스에서 필수적인 역할 중 하나가 SSL 처리이다.
HTTPS를 통해 데이터를 안전하게 주고받기 위해선 서버와 클라이언트 간에 데이터를 암호화해야 하는데,
Nginx는 이 과정에서 SSL 인증서를 사용하여 암호화와 복호화를 담당한다.
즉, HTTPS로 들어오는 데이터를 Nginx가 해독한 후, 내부 서버로 전달한다.
덕분에 애플리케이션 서버는 암호화 처리를 신경 쓰지 않고 실제 로직 처리에 집중할 수 있다.
SSL 인증서의 구조와 역할
SSL 인증서는 웹 서버와 클라이언트 간의 통신을 암호화하는 데 사용된다.
특히, 요즘 같은 시대에는 로그인이나 결제 같은 민감한 데이터를 주고받을 때,
SSL 인증서 없이 HTTP로만 통신하면 중간자 공격에 쉽게 노출된다.
브라우저에서 자물쇠 아이콘이 나타나는 HTTPS 사이트가 안전한 이유도
SSL 인증서를 통해 데이터가 암호화되어 전송되기 때문이다.
SSL 인증서는 공개 키와 비밀 키로 구성된다.
Private Key (privkey.pem)는 서버가 가지고 있는 비밀 키로,
데이터 복호화에 사용된다.
이 비밀 키는 서버만이 알고 있어야 하며, 절대 외부로 유출되면 안 된다.
반면 Certificate (fullchain.pem)은 클라이언트에게 제공하는 공개 키로,
도메인 정보와 인증 기관의 서명이 포함되어 있다.
클라이언트는 이 공개 키를 이용해 데이터를 암호화하여 서버로 전송하고,
서버는 비밀 키로 그 데이터를 복호화하여 읽을 수 있게 된다.
Nginx와 SSL의 실제 적용
SSL 인증서를 발급받기 위해 나는 Let's Encrypt를 사용했다.
Let's Encrypt는 무료로 SSL 인증서를 발급해주는 공인 인증 기관(CA)이다.
인증서를 발급받는 과정에서 Certbot이라는 도구를 사용했다.
Certbot은 명령 한 줄로 SSL 인증서를 자동 발급해주고 Nginx 설정까지 자동으로 수정해주는 도구이다.
Certbot으로 인증서를 발급받으면, Nginx 설정 파일에 SSL 인증서 경로를 지정해주기만 하면 된다.
Nginx 설정 파일에는 다음과 같은 구문이 추가된다.
ssl_certificate /etc/letsencrypt/live/devinterview.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/devinterview.shop/privkey.pem;
이 부분이 있어야 Nginx가 HTTPS 요청을 처리할 때 인증서를 이용해 데이터를 복호화하고
해독된 데이터를 내부 애플리케이션 서버로 전달할 수 있다.
Nginx가 HTTPS를 처리하지 않으면 애플리케이션 서버가 직접 SSL 인증을 처리해야 하는데
이 경우 서버 부담이 커지기 때문에 비효율적이게 된다.
Nginx를 SSL 처리의 프론트 라인으로 세워서
암호화와 복호화를 모두 담당하게 함으로써 성능 저하를 방지할 수 있다.
왜 Nginx가 이 프로젝트에 필요했을까?
1. 단일 진입점(Entry Point)이 필요했다
내 프로젝트 구조는 다음과 같다
- 프론트엔드(Next.js): 3000 포트
- 백엔드(Spring Boot): 8080 포트
그런데 문제는, 사용자는 포트 따위는 몰라야 한다는 것이다.
유저는 그냥 http://43.201.208.238만 알면 되고, /는 프론트로, /api/는 백엔드로 가야 한다.
그래서 필요한 게 바로 리버스 프록시 역할을 해주는 Nginx였다.
[ 유저 요청 ]
↓
http://43.201.208.238/
↓
[Nginx 분기 처리]
- / → frontend:3000
- /api/** → backend:8080
2. 프론트와 백엔드를 외부에 직접 노출시키고 싶지 않았다
Nginx 없이 각각의 서비스를 외부에 노출하면
백엔드 서버 API 주소가 외부에 그대로 노출되고 보안 이슈 발생 가능성이 증가한다.
Nginx를 사용하면 백엔드는 오직 Nginx를 통해서만 접근 가능하고
Docker 내부 네트워크(backend, frontend) 이름으로만 연결된다.
즉, Nginx는 보안 Layer이자 유일한 진입점이 된다.
Nginx 설정 파일
server {
listen 80;
server_name devinterview.shop www.devinterview.shop;
# HTTP 요청을 HTTPS로 리디렉션
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name devinterview.shop www.devinterview.shop;
ssl_certificate /etc/letsencrypt/live/devinterview.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/devinterview.shop/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost:3000;
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;
}
location /api/ {
proxy_pass http://localhost:8080;
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;
}
}
- listen 80: HTTP 요청을 수신할 포트
- server_name: 이 설정이 적용될 도메인
- return 301
- 301 Moved Permanently 응답으로 HTTPS로 리디렉션.
- $host: 요청의 도메인
- $request_uri: 요청 경로와 쿼리 문자열
- listen 443 ssl: HTTPS 요청을 처리하기 위해 443 포트와 SSL 사용 명시
- ssl_certificate: 공개 키 경로
- ssl_certificate_key: 개인 키 경로
- ssl_protocols: 사용 가능한 SSL/TLS 버전
- ssl_ciphers:사용할 암호화 알고리즘 목록
- proxy_pass: 클라이언트 요청을 프론트엔드 서버or 백엔드 서버로 전달
- proxy_set_header
- Host: 원래 요청의 호스트 정보 전달
- X-Real-IP: 실제 클라이언트 IP 전달
- X-Forwarded-For: 프록시를 거친 IP 목록 전달
인증서는 도커 내부에서도 같은 경로에 있어야 Nginx가 이를 불러올 수 있다.
따라서 도커 볼륨 마운트를 통해 호스트의 인증서 경로를 컨테이너에 연결해야 한다.
Nginx 개념 정리
용어 | 정의 | 프로젝트내에서 역할 |
Nginx | HTTP 요청을 받아 내부 서비스에 전달 | 유저의 모든 요청을 프록시 처리 |
리버스 프록시 | 클라이언트는 내부 구조를 모름. 대신 프록시가 중계 | /api/** 요청을 백엔드에 연결 |
정적 파일 서버 | JS/CSS/이미지 등을 빠르게 서빙 | Next.js 결과물을 제공 |
보안 게이트웨이 | SSL 인증서, 속도 제한, IP 필터링 등 | HTTPS 진입점이자 방어선 역할 |
이 구조가 가져온 이점
- 하나의 도메인으로 통합: 유저는 http://mydomain.com만 알면 된다.
- 내부 서비스 분리: frontend/backend는 외부 노출이 없다.
- 보안 정책 일원화: SSL, 인증, 속도 제한 등 모두 Nginx에서 설정한다.
결론
Nginx는 단순히 리버스 프록시만 해주는 게 아니다.
이번 프로젝트를 통해 직접 경험하면서 Nginx가 배포의 핵심 요소라는 걸 느꼈다.
Nginx는 사용자 요청을 올바른 서비스로 분기 처리해주고 내부 구조를 외부로부터 숨기고 보호해주며
HTTPS와 같은 보안 처리를 담당한다.
즉, 프론트 + 백엔드 + DB + 외부 요청이 연결되는 중심 허브이다.