발전하는 춘배
[원고, Nginx] 리버스프록시? 본문
게임 서버에서 Polling 방식의 한계를 극복하기 위해 Websocket을 사용할 수 있다는 취지의 발제를 준비하면서 실험용으로 각 방식으로 서버 2개를 띄워놓고 들어온 요청 횟수를 비교하는 걸 해보려고 했다.
그래서 8000번 포트에서는 ws 방식의 FastAPI 서버를, 8001번 포트에는 polling 방식의 FastAPI 서버를 띄워 놨는데, http://gdg-kes-websocket.duckdns.org:8000/
이런 식으로 접속하는건 뭔가 아쉬워서(배포는 google cloud vm 무료 크레딧을 썼고, 도메인은 무료 도메인 duckdns를 이용했다) 조금 더 있어보이게? 도메인/websocket, 도메인/polling으로 접속할 수 있게끔 해보자.
즉 지금 하려는 건 특정 경로로 들어온 요청을 서로 다른 포트로 분기(라우팅)하려는 것이다. 이는 Nginx같은 리버스 프록시를 이용하면 된다.
sudo nano /etc/nginx/sites-available/default
로 들어가서 아래를 넣어준다.
server {
listen 80;
server_name gdg-kes-websocket.duckdns.org;
# 1. /websocket 요청 -> 8000번 포트로 전달
location /websocket/ {
proxy_pass http://127.0.0.1:8000/;
# 기본 프록시 헤더 설정
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;
}
# 2. /polling 요청 -> 8001번 포트로 전달
location /polling/ {
proxy_pass http://127.0.0.1:8001/;
# 기본 프록시 헤더 설정
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;
}
}
그러고 나서 설정을 마친 후에는 반드시 sudo nginx -t 명령어로 문법 오류가 없는지 확인하고, sudo systemctl restart nginx를 실행해 반영해야 합니다.

안된다.
왜냐하면 google cloud에서 listing port인 80번 포트로의 접근을 열어놔야 한다. 네트워크 인터페이스 세부정보에서 vpc 방화벽 규칙에 tcp 80번 포트를 열어준다.



그럼 이제 접속은 잘 되는데 css랑 js 파일은 안 불러와진다.
왜냐하면..


그래서 index.html에서 기존의
<link rel="stylesheet" href="/static/style.css" />
<script src="/static/game.js"></script>
이거를
<link rel="stylesheet" href="/polling/static/style.css" />
<script src="/polling/static/game.js"></script>
이렇게 바꿔주면 된다. 처음에 gpt가 nginx 설정을 건드려보라길래 이것저것 해보다가 그냥 html에서 요청 경로만 수정해도 되길래 이렇게 했다.
근데 또 문제가 있다.

그럼 모든 api endpoint를 /polling/api/join처럼 수정해야되는건가?
분명히 이것보다 좋은 방법이 있을 것이다. prefix만 /polling을 붙여주면 되는 건데 방법이 없진 않겠지?
난 Nginx 설정으로 될 줄 알았는데 그건 사실 어렵다.
Nginx 설정만으로 기존 프론트의 /api/join 요청을 그대로 유지하면서 두 서버 중 어느 쪽으로 보낼지 구분할 수는 없다. /api/join이라는 동일한 요청에 대해 Nginx는 요청 자체만 보고 8000번 서버로 보낼지 8001번 서버로 보낼지 판단할 수 없기 때문이다. 따라서 외부 요청 경로에 /polling과 같은 구분자를 추가해야 했다.
근데 로컬 개발 환경에서도 돌아가긴 해야하니까 다음과 같이 수정한다.
js파일
const API_BASE_PATH = "/polling/api";
...
const response = await fetch(`${API_BASE_PATH}/join`, {
...
app.py
@app.post("/api/join")
@app.post("/polling/api/join")
async def join_game(data: dict[str, object]) -> dict[str, str]:
...
당연히 해결됐다.
HTML요청에 대해서는 잘 작동한다.
그런데 websocket 서버에서는

Join Game을 눌러도 Connection closed다.
이건 nginx 설정을 건드려줘야한다.
websocket을 사용하는 8000번포트 설정에 아래를 추가해줘야한다.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
websocket 연결은 HTTP 요청으로

이런 HTTP Upgrade 요청(핸드셰이크) 보내면서 시작된다.
그런데 중간에 nginx가 끼어 있기 때문에 WebSocket에 필요한 Upgrade, Connection 헤더를 upstream인 8000번 서버에 제대로 전달하도록 명시해주는 게 필요하다.
proxy_set_header Upgrade $http_upgrade;
이 설정이 브라우저가 보낸 Upgrade 헤더를 upstream인 8000번 포트에도 보내라는 설정이다.
proxy_set_header Connection "upgrade";
그리고 이 설정이 Connection 헤더 8000번 포트에 보내라는 설정이다.
드디어 성공했다.
'.원고' 카테고리의 다른 글
| [원고 / GCP, Docker, Code Server] GCP VM에 코드서버 띄우기 (0) | 2026.03.06 |
|---|---|
| [원고, C++/OOP] 로그라이크 게임 만들어보기 10 - 상태 머신 (0) | 2026.02.21 |
| [원고, C++/OOP] 로그라이크 게임 만들어보기 9 - 아이템과 인벤토리 2 : 엔티티 스탯 시스템, 아이템 데이터화, 데이터 로드, 테스트 (0) | 2026.02.21 |
| [원고, C++/OOP] 로그라이크 게임 만들어보기 8 - 아이템과 인벤토리 1 : 설계, 추상 클래스, 상속, 순환 참조, 전방 선언 (0) | 2026.02.19 |
| [원고, C++/OOP] 로그라이크 게임 만들어보기 7 - 카메라 : 카메라와 렌더링(출력) 아키텍처, AI 에이전트를 이용한 디버깅 (0) | 2026.02.14 |