오래된 프로그래머의 요즘 이야기

그리고 소소한 일상들...

허니의 소소한 일상들이 켜켜이 쌓여가고......

Tech Story

[Docker / Nginx] Proxy 설정

쭝허니 2025. 5. 31. 21:11

혼자 공부하는 Docker Nginx Reverse Proxy: 포트와 경로 기반 라우팅 마스터하기

오늘은 제가 며칠 밤낮으로 씨름했던 Docker와 Nginx를 이용한 Reverse Proxy 설정에 대한 꿀팁을 공유하려고 합니다.  사실 처음엔 막막했는데, 이제는 제법 자신감이 생겼어요!  (뿌듯)  이 글을 통해 여러분도 Reverse Proxy의 세계에 쉽게 발을 들여놓으실 수 있도록, 제가 겪었던 시행착오와 해결 과정을 상세히 설명해 드리겠습니다.  자, 시작해볼까요?

### 1. Reverse Proxy란 무엇일까요?

먼저 Reverse Proxy의 개념을 간단히 짚고 넘어가겠습니다.  Forward Proxy가 클라이언트의 외부 네트워크 접근을 중개하는 역할이라면, Reverse Proxy는  **여러 개의 내부 서버들을 외부 클라이언트에게 하나의 엔트리 포인트로 보여주는 역할**을 합니다.  마치 여러 개의 방을 가진 건물의 안내데스크 같은 거죠. 클라이언트는 데스크(Reverse Proxy)에 요청하고, 데스크는 요청에 맞는 방(내부 서버)으로 안내해줍니다.  상용 서비스 운영에 필수적인 기술이라고 생각하시면 됩니다.  웹 서버 여러 대를 운영하면서 부하 분산이나 보안 강화에 굉장히 유용하게 쓰이죠.

### 2. Docker와 Nginx를 이용한 Reverse Proxy 구현: 포트 기반 라우팅

자, 이제 본격적으로 Docker와 Nginx를 이용하여 Reverse Proxy를 구현하는 방법을 알려드리겠습니다.  저는 먼저 **포트 기반 라우팅**부터 시작했습니다.  각 내부 서버에 다른 포트를 할당하고, Nginx는 요청받은 포트에 따라 해당 서버로 요청을 전달하도록 설정하는 방식이죠.

**먼저, 아래와 같이 `docker-compose.yml` 파일을 작성합니다.**

```yaml
version: "3.9"
services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "8081:8081" # 내부 서버 A의 포트
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - server_a
      - server_b

  server_a:
    image: nginx:latest
    ports:
      - "8081:80" # 내부 서버 A의 포트
    volumes:
      - ./server_a:/usr/share/nginx/html
   server_b:
    image: nginx:latest
    ports:
      - "8082:80" # 내부 서버 B의 포트 (포트 8081을 사용하는 이유는 나중에 경로 기반 라우팅에서 설명)
    volumes:
      - ./server_b:/usr/share/nginx/html
```

**그리고 `nginx/nginx.conf` 파일은 다음과 같이 설정합니다.**  (이 부분이 가장 중요합니다!)

```nginx
upstream backend_a {
  server server_a:80;
}

upstream backend_b {
  server server_b:80;
}

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://backend_a;
    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-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location /api { # 포트 8081로 요청이 들어왔을 때 서버 A로 전달
    listen 8081;
    proxy_pass http://backend_a;
    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-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

    location /blog { # 포트 8082로 요청이 들어왔을 때 서버 B로 전달
    listen 8082;
    proxy_pass http://backend_b;
    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-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
```

위 설정에서 중요한 부분은 `upstream` 블록과 `location` 블록입니다. `upstream`은 내부 서버들을 정의하고, `location`은 특정 포트로 들어오는 요청을 어떤 `upstream`으로 전달할지 지정합니다.  `proxy_set_header` 지시어는 클라이언트의 IP 주소와 같은 정보를 내부 서버에 전달하는 역할을 합니다.  이 설정이 없으면 내부 서버는 클라이언트의 정보를 알 수 없게 되죠.  (로그인 시스템 등을 구현할 때 중요합니다!)

**(그림1) docker-compose.yml 파일 예시 이미지 삽입**
**(그림2) nginx/nginx.conf 파일 예시 이미지 삽입**

### 3. Docker와 Nginx를 이용한 Reverse Proxy 구현: 경로 기반 라우팅

포트 기반 라우팅은 간단하지만, 서버 수가 많아지면 관리하기 어렵습니다.  그래서 저는 **경로 기반 라우팅**도 시도해봤습니다.  이 방법은 URL 경로에 따라 다른 내부 서버로 요청을 전달하는 방식입니다.

이번에는 `docker-compose.yml` 파일은 위와 동일하게 사용하고, `nginx/nginx.conf` 파일을 아래와 같이 수정합니다.

```nginx
upstream backend_a {
  server server_a:80;
}

upstream backend_b {
  server server_b:80;
}

server {
  listen 80;
  server_name localhost;

  location /blog {
    proxy_pass http://backend_a;
    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-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location /community {
    proxy_pass http://backend_b;
    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-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
```

이제 `/blog` 경로로 접근하면 `backend_a` (서버 A), `/community` 경로로 접근하면 `backend_b` (서버 B)로 요청이 전달됩니다.  훨씬 간결하고 관리하기 쉽죠?

**(그림3) 수정된 nginx/nginx.conf 파일 예시 이미지 삽입**

### 4. 경로 재작성 (Rewrite)을 이용한 고급 라우팅

더 나아가,  기존 서비스에 새로운 서버를 추가할 때,  내부 서버의 경로 구조를 변경하지 않고 Reverse Proxy에서 경로만 변경하여 라우팅하는 방법도 있습니다.  이를 위해 `rewrite` 지시어를 사용할 수 있습니다.

예를 들어, 클라이언트가 `/blog/test.html`로 요청했을 때, 실제로는 `/test.html`로 내부 서버에 요청을 전달하고 싶다면, 아래와 같이 설정할 수 있습니다.

```nginx
server {
  listen 80;
  server_name localhost;

  location / {
    rewrite ^/blog(.*) $1 break;
    proxy_pass http://backend_a;
    # ... (기존 proxy_set_header 설정) ...
  }

  # ... (기타 location 설정) ...
}
```

`rewrite ^/blog(.*) $1 break;` 라인은 `/blog`로 시작하는 모든 요청을 `/blog` 접두어를 제거한 후 내부 서버로 전달하도록 설정합니다.  `break`는 다른 `location`으로의 무한 루프를 방지합니다.

**(그림4) rewrite 설정을 포함한 nginx/nginx.conf 파일 예시 이미지 삽입**

### 5.  Nginx Location Match Tester

Nginx 설정에서 여러 개의 `location`을 사용할 때, 어떤 URL이 어떤 `location`에 매치되는지 궁금할 때가 있죠.  이럴 때 유용한 사이트가 바로 **Nginx Location Match Tester ([https://nginx.viraptor.info/](https://nginx.viraptor.info/))**입니다.  여기에 `nginx.conf` 파일의 `server` 블록을 복사해서 붙여넣으면,  다양한 URL을 입력해보고 어떤 `location`으로 요청이 전달되는지 확인할 수 있습니다.  강력 추천합니다!

이것으로 Docker와 Nginx를 이용한 Reverse Proxy 설정에 대한 제 이야기는 끝입니다. 처음에는 어려웠지만, 하나씩 설정을 따라 해보면서 Reverse Proxy의 매력에 빠져들었네요. 여러분도 이 글을 통해 Reverse Proxy 설정에 자신감을 얻으셨기를 바랍니다!