본문 바로가기

[Nginx] Nginx 리버스 프록시 서버 구축

@bum0w02025. 8. 1. 17:37

Proxy

Proxy(프록시)는 네트워크에서 요청을 중개하거나 우회하는 개념적인 기술을 말한다.

이 역할을 실제로 수행하는 시스템을 프록시 서버(Proxy Server) 라고 부른다.

 

프록시는 클라이언트와 서버 사이에 위치해 요청을 대신 전달하거나 응답을 가로채는 식으로 동작한다.

요청을 전달하는 방향에 따라 크게 Forward Proxy와 Reverse Proxy로 나뉜다.

Forward Proxy

https://www.upguard.com/blog/reverse-proxy-vs-load-balancer

 

Forward Proxy는 클라이언트 입장에서의 프록시다.

클라이언트가 직접 서버에 요청을 보내는 대신 프록시 서버를 거쳐 요청을 보내게 된다.

 

주로 내부에서 외부로 나가는 요청을 감시하거나 필터링할 때 사용되며 사용자의 IP를 숨기고, 접근 제한 우회 등에 활용되기도 한다.

Ex. 회사 내 내부망에서 외부 인터넷 접속 시 프록시 서버를 통해야 하는 경우

 

Reverse Proxy

https://www.upguard.com/blog/reverse-proxy-vs-load-balancer

 

Reverse Proxy는 서버 입장에서의 프록시다.

클라이언트는 프록시 서버(리버스 프록시)로 요청을 보내고,

이 프록시가 내부에 존재하는 실제 서버들 중 하나에 요청을 전달해 처리 결과를 클라이언트에 응답해준다.

 

 

Nginx는 웹 서버이자 리버스 프록시 서버이다.

Nginx는 정적 파일을 직접 제공하는 웹 서버 역할도 하지만,

내부에 여러 백엔드 서버가 있을 때 그 요청을 대신 처리해주는 리버스 프록시 서버 역할도 한다.

 

웹 서버(Web Server)

  • 클라이언트의 요청을 받아 HTML, CSS, JS 같은 정적 파일을 직접 응답하는 것

리버스 프록시 서버(Reverse Proxy Server)

  • 클라이언트 요청을 받아 내부 백엔드 서버에 대신 전달하고 응답을 다시 클라이언트에게 넘겨주는 중개자 역할을 한다.

만약 Nginx 앞단에 Node.js, Django, Spring 등의 애플리케이션 서버가 있다면 Nginx는 그 서버들과 클라이언트 사이를 연결해주는 게이트웨이 역할을 하게되는 것이다.

 

 

리버스 프록시는 왜 사용할까?

백엔드 서버 숨김 (보안성)

  • 클라이언트는 내부 서버의 위치(IP, 포트)를 직접 알 수 없기 때문에 시스템 보호에 유리하다.

로드 밸런싱

  • 여러 백엔드 서버에 요청을 분산시켜 시스템을 안정적으로 운영할 수 있다.

SSL 종료(SSL Termination)

  • HTTPS 처리를 프록시 서버가 담당하고, 내부 서버는 HTTP로만 동작할 수 있어 성능 부담을 줄인다.

캐싱, 압축, 압력 해소

  • 정적 자산 캐싱이나 gzip 압축을 통해 전체적인 성능 최적화도 가능하다.

 

Nginx의 리버스 프록시 기능

시작하기 전, 요청 흐름을 간단하게 보면 아래와 같다.

  1. 클라이언트 → Nginx에 요청
  2. Nginx → 내부 서버(Node.js, Spring 등)에 전달
  3. 내부 서버 → 처리 후 Nginx에 응답
  4. Nginx → 클라이언트에 최종 응답 전송

요청 흐름

이제 실제로 Nginx의 리버스 프록시 기능을 활용하여 Spring Boot 서버를 배포해보자

 

1. JDK 17 설치하기

 

Spring Boot 3.x로 작성된 애플리케이션을 실행하려면 JDK 17 이상이 필요하다.

$ sudo apt update
$ sudo apt install openjdk-17-jdk -y

# 설치 확인
$ java -version

 

2. Spring Boot 서버 배포

 

테스트용 Spring Boot 프로젝트를 클론해 온다.

$ cd ~
$ git clone https://github.com/bum0w0/nginx-study.git
$ cd nginx-study

 

Spring Boot 애플리케이션을 빌드하고 실행한다.

# 테스트를 제외하고 프로젝트를 빌드
$ ./gradlew clean build -x test

# 빌드된 JAR 파일이 있는 디렉토리로 이동
$ cd build/libs      

# 백그라운드에서 서버 실행
$ nohup java -jar NginxBackend-0.0.1-SNAPSHOT.jar --server.port=8081 &

 

Spring Boot 애플리케이션은 기본적으로 8080 포트에서 실행되므로 해당 포트를 사용 중인 프로세스가 있는지 확인해보자.

# 정상적으로 실행 중이라면 아래와 같은 형태로 출력된다.
# java 49937 ubuntu 9u IPv6 318138 0t0 TCP *:http-alt (LISTEN)
$ lsof -i:8080

 

3. Nginx 리버스 프록시 설정

 

Nginx 설정 파일을 통해서 요청을 전달할 내부 서버 주소를 작성하자.

 

conf.d 폴더 바로 아래에 api.nginx.r-e.kr.conf 파일을 생성

$ sudo vi /etc/nginx/conf.d/api.nginx.r-e.kr.conf

 

파일 안에 리버스 프록시 설정 작성

server {
    # api.nginx.r-e.kr:80으로 들어온 요청은 이 server 블록이 처리
    listen 80;
    server_name api.nginx.r-e.kr;

    location / {
        # 들어오는 모든 요청을 내부의 로컬 8080 포트로 전달 (Spring Boot 와 같은 백엔드 서버)
        proxy_pass http://localhost:8080;
    }
}
proxy_pass는 리버스 프록시가 요청을 어디로 보낼지 정하는 역할을 하며, Nginx는 이를 통해 client 요청을 내부 서버로 대신 전달한다.
$ sudo nginx -t
$ sudo nginx -s reload

 

 

Spring Boot 서버에 도메인 적용

지난번에 https://내도메인.한국 에서 도메인을 발급받아 EC2 인스턴스 IP와 연결했었다.

이번에는 여기에 Nginx 설정 파일에 작성했던 자신의 서브 도메인을 추가로 등록하고 EC2 IP 주소와 연결해주자.

 

리버스 프록시가 제대로 설정되었는지 확인하기 위해 실행 중인 스프링부트 애플리케이션의 게시글 조회 API를 테스트해 보았다.

 

정상적인 JSON 데이터가 응답되는 것을 확인함으로써, Nginx 리버스 프록시 설정이 반영되어 잘 작동하고 있다는 것을 알 수 있다.

 

이제 마지막으로 Spring Boot 서버에 HTTPS 를 적용하고 확인해 본다.

$ sudo certbot --nginx -d api.nginx.r-e.kr

 

 

IP 당 요청 수 제한

지금까지 Nginx를 활용해 리버스 프록시 설정을 완료했다.

하지만 리버스 프록시 서버가 단순히 요청을 전달하는 역할만 하는 것은 아니다.

대표적인 기능 중 하나로 클라이언트 IP별 요청 수 제한(rate limiting)이 있다.

 

이 기능을 통해 특정 IP에서 과도하게 반복되는 요청을 제어할 수 있고 서버에 과부하를 주거나 악의적인 공격(Ex. DoS, DDoS)을 방지할 수 있다.

 

Nginx 설정 파일 수정

$ sudo vi /etc/nginx/conf.d/api.nginx.r-e.kr.conf

 

api.nginx.r-e.kr.conf 설정 파일

# 1. 클라이언트 IP별 요청 수 제한(zone 정의)
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=3r/s;

server {
    	# 2. 요청 제한 적용
        limit_req zone=mylimit;
        # 3. 제한 초과 시 429 상태 코드 반환
        limit_req_status 429;
        server_name api.nginx.r-e.kr;

        location / {
                proxy_pass http://localhost:8080;
        }

    listen 443 ssl; 
    ssl_certificate /etc/letsencrypt/live/api.nginx.r-e.kr/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/api.nginx.r-e.kr/privkey.pem; 
    include /etc/letsencrypt/options-ssl-nginx.conf; 
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 

}
server {
    if ($host = api.nginx.r-e.kr) {
        return 301 https://$host$request_uri;
    } 


        listen 80;
        server_name api.nginx.r-e.kr;
    return 404;


}
$ sudo nginx -t
$ sudo nginx -s reload

 

요청 수 제한 적용 확인

 

요청 제한을 설정한 후, 해당 주소(api.nginx.r-e.kr)로 여러 번 새로고침을 시도하면 일정 횟수 이후부터는 429(Too Many Requests) 응답이 반환되는 것을 확인할 수 있다.

 


 

 

지금까지 백엔드(Spring Boot) 서버 앞단에 Nginx를 리버스 프록시로 두고 다음과 같은 기능을 적용해보았다.

  • HTTPS 적용을 통해 외부와의 통신을 암호화하고
  • 요청 빈도 제한 기능을 통해 트래픽 과부하나 무분별한 요청으로부터 서버를 보호했다.

 

이번 설정을 통해 단순히 서비스를 배포하는 것을 넘어, 실제 운영 환경에서 고려해야 할 보안 요소들에 대해 감을 잡을 수 있었다.

아직 완벽하다고 할 수는 없지만, 최소한의 보안 조치들을 직접 적용해보면서 실무에서도 비슷한 원리로 시스템이 보호된다는 것을 체감할 수 있었다.

 

다만 인증서 자동 갱신이나 더 세밀한 요청 제어 방식 등 아직 개선할 부분이 많아 보여서 다양한 운영 환경에서 사용되는 보안/성능 최적화 방법들에 대해 더 찾아보려고 한다.

bum0w0
@bum0w0 :: bum0w0 님의 블로그

bum0w0 님의 블로그 입니다.

목차