학원에서 팀 프로젝트로 진행한 2차, 3차 프로젝트는 STS3에서 Spring MVC Project로 진행해서
클라이언트 단은 WEB-INF의 views에서 처리되고, 서버단은 각각 JSP, JPA로 만들어서 하나의 프로젝트 폴더 안에서 클라이언트와 서버가 모두 구현된 구조였기 때문에 톰캣 서버만 추가해서 프로젝트를 배포하면 바로 실행할 수 있는 방식이었다.
반면, 지금 진행 중인 개인 프로젝트는 프론트엔드와 백엔드가 분리된 구조로 개발되고 있다. 프론트엔드는 VSCode에서 React를 사용해 개발 중이고, 백엔드는 STS3에서 Spring Boot로 개발하고 있다.
Spring Boot는 내장 톰캣을 사용하기 때문에, 외부 톰캣과 함께 배포하려고 하다 보니 문제가 발생했다.
프로젝트에 SSL 인증서를 적용하고 443 포트를 사용하려고 설정했는데 내장 톰캣(Sprint Boot)과 외부 톰캣(Spring MVC)이 같은 443 포트를 사용하려고 해서 포트 충돌이 발생했다.
이로 인해 두 서버를 동시에 실행할 수 없는 상황이 됐다.
어떻게 해결해야 할 지 검색해 보니까 해볼 만한 방법은 2가지!
✅ 프론트 빌드해서 Spring Boot 프로젝트에 통합해서 war로 빌드한 후 외부 톰캣으로 배포하기
✅ 리버스 프록시 사용하기
1️⃣ war로 빌드해서 외부 톰캣으로 배포하기
처음 시도한 방법은 vscode에서 리액트로 만든 프론트단을 빌드해서
npm run build
나온 정적 파일들을 Spring Boot 프로젝트의 static에 넣어주고
Spring Boot 프로젝트 자체를 빌드해서 war로 만든다음
./gradlew build
외부 톰캣으로 war를 배포하려고 했는데 계속 경로상 오류인 건지 war 파일을 못 읽고 이상한 경로에서 읽으려고 하고
war 자체를 직접 풀어서 폴더로 만들어주고 경로 설정도 했는데 안 돼서 포기
2️⃣ 리버스 프록시 설정하기
사실 처음에 내장 톰캣과 외부 톰캣을 동시에 실행할 수 없다는 걸 알았을 때, 포트 충돌 문제를 해결하기보다는 필요할 때만 외부 톰캣을 끄고 내부 톰캣을 실행해서 데이터를 모으는 방식으로 임시 해결하며 사용했었다.
이 문제는 언젠가는 해결해야 할 문제라고 생각했고 직접 해결해 보기로 결심했다. 하지만 처음엔 감이 전혀 잡히지 않아 친구찬스를 썼다!
(이쪽으로 진로 바꾸고 학원 알아보는 거부터 수료 이후까지 개발자 친구덕을 제대로 보는 중,,)
지금 상황을 얘기해 주고 어떻게 해결해야 하는지 조언을 구했는데 지피티랑 똑같은 해결법을 알려줬다!
(방법만 제시해 주고 스스로 공부하게 하는 참된 개발자..)
그래서 이번에는 Nginx로 리버스 프록시를 설정해보려고 한다.
일단.. 리버스 프록시가 뭐지?
💡리버스 프록시(Reverse proxy)
리버스 프록시란 클라이언트(사용자)와 서버 간의 중계 역할을 하는 서버 구성 방식 중 하나로
클라이언트가 리버스 프록시 서버에 요청을 보내면
리버스 프록시는 이를 실제 백엔드 서버로 전달하고, 백엔드 서버의 응답을 다시 클라이언트로 전달하는 방식으로 동작한다.
리버스 프록시를 사용하게 되면 여러가지 장점이 있는데
- 포트 충돌 방지
- 보안 강화
- 캐싱 및 가속화
- 서버 부하 분산
등등이 있다고 한다. 나는 여기서 포트 충돌 방지의 목적으로 사용하려고 했다.
현재 외부 톰캣과 내부 톰캣 모두 SSL을 사용하여 HTTPS 환경을 구성했는데, 두 서버가 같은 443 포트를 사용하려고 하면서 포트 충돌 문제가 발생했다. 이런 상황에서 리버스 프록시를 통해 443 포트를 리버스 프록시(Nginx)가 차지하도록 하고, 리버스 프록시가 요청을 각 서버로 분배하게 했다.
처음에는 리버스 프록시 없이 클라이언트에서 공인 IP:8081 포트를 사용해 직접 내부 톰캣에 요청을 보내려고 했다.
하지만 HTTPS 페이지에서 HTTP 요청을 보낼 경우, 브라우저 보안 정책에 의해 Mixed Content(혼합 콘텐츠)문제가 발생했다. Mixed Content 문제는 HTTPS 페이지가 HTTP 요청을 안전하지 않은 요청으로 간주하고 차단하기 때문에 발생하는 문제다.
결국.. 클라이언트와 서버 간 모든 통신을 HTTPS로 유지하기 위해 리버스 프록시를 사용하기로 했다.
먼저 리버스 프록시를 설정하기 위해, 속도와 안정성 면에서 널리 인정받는 Nginx를 선택했다.
Nginx는 리버스 프록시 외에도 정적 파일 서빙, 로드 밸런싱 등 다양한 기능을 제공한다고 한다.
http://nginx.org/en/download.html 에 접속해서 windows 버전으로 다운받았다.
그리고 압축을 푼 후 nginx의 conf 설정을 해줬다.
먼저 HTTPS 요청을 처리하기 위해 인증서 경로를 잡아주면서 인증서 설정을 해줬다.
다음으로는 클라이언트를 배포하고 있는 외부 톰캣의 dist 파일의 경로를 잡아줬다.
그러고 가장 중요한 /bbangmap/의 요청을 Spring boot 내부 서버인 localhost:8081로 전달하게 했다.
(백엔드는 8081 포트를 사용하고 있다.)
그 후 Spring MVC 프로젝트들을 프록싱해줬다.
2차 프로젝트인 BBOL, 3차 프로젝트인 CarPlanet 경로를 잡아줬다.
그러고 nginx 실행!
# HTTPS 요청 처리
server {
listen 443 ssl;
server_name www.ys2ys2.com;
# 인증서 설정
ssl_certificate C:/OpenSSL/certificates/certificate.crt;
ssl_certificate_key C:/OpenSSL/certificates/private.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# React 정적 파일 서빙
location / {
root C:\team_dev\workspace_spring\apache-tomcat-9.0.46\webapps\dist;
index index.html;
try_files $uri /index.html; # React 라우팅 지원
}
# Spring Boot API 프록시 설정
location /bbangmap/ {
proxy_pass http://localhost:8081/bbangmap/;
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;
}
# Spring MVC 프로젝트 프록시 설정 (CarPlanet)
location /CarPlanet/ {
proxy_pass http://localhost/CarPlanet/;
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;
}
# Spring MVC 프로젝트 프록시 설정 (BBOL)
location /BBOL/ {
proxy_pass http://localhost/BBOL/;
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;
}
# 에러 페이지 설정
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
처음에는 그냥 bbangmap만 설정했는데 2차 3차 프로젝트들에 해당하는 주소로 들어가니까 500 에러가 떴다.
왜 그런가 찾아보니까 Nginx에서는 요청 경로에 대한 처리가 명시되지 않으면 기본 경로로 설정된 디렉토리인 / 또는 root 설정값을 사용한다고 한다.
/BBOL이나 /CarPlanet에 대한 요청이 들어왔을 때, Nginx는 디렉토리 내부에서 해당 경로를 처리하려고 시도했지만, 실제로 그런 경로에 파일이나 디렉토리가 존재하지 않아 500 Internal Server Error를 반환한 것이다.
그래서 Nginx 설정에 /BBOL과 /CarPlanet 설정을 추가해 줬다.
작동 원리는 클라이언트가 https://www.ys2ys2.com/CarPlanet에 요청을 보내면 Nginx는 먼저 이 요청을 수신하고
이후 Nginx는 설정 파일에 명시된 프록시 경로를 확인해서
해당 요청을 외부 톰캣에서 실행 중인http://localhost/CarPlanet/으로 전달한다.
이 과정에서 Nginx는 단순히 요청을 전달하는 역할만 수행하고 실제 요청 처리와 응답 생성은 외부 톰캣이 담당한다.
외부 톰캣은 Spring MVC 프로젝트(/CarPlanet)를 실행하고 있으므로 요청에 대해 필요한 작업을 수행한 뒤 결과를 생성해 응답을 반환한다.
톰캣에서 반환된 응답은 다시 Nginx로 전달되고 Nginx는 이 응답을 클라이언트에게 그대로 전달하게 된다.
결과적으로 클라이언트는 Nginx를 통해 외부 톰캣의 Spring MVC 프로젝트로부터 처리된 응답을 받게 되는 것이었다.
이렇게 각각 500 에러도 해결했다.
bbangmap 프로젝트 흐름 살펴보기
Nginx에서 /bbangmap/로 시작하는 모든 요청을 Spring Boot 서버로 프록싱하기
proxy_pass는 http://localhost:8081/bbangmap/으로 전달
Spring boot 내부 톰캣은 받은 요청을 처리하고 반환한다.
Nginx는 받은 값들을 다시 클라이언트로 전달한다.
이렇게 되면 포트 충돌과 Mixed Content 없이 서버가 실행되게 된다!
location /bbangmap/ {
proxy_pass http://localhost:8081/bbangmap/;
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;
}
클라이언트는 443 포트를 통해 Nginx와 통신하고
Nginx는 8081 포트에 요청하고 받은 값을 다시 클라이언트에 제공한다.
이렇게 포트 충돌 없이 2차, 3차 프로젝트와 개인 프로젝트를 배포할 수 있게 되었다!!
이제 열심히 데이터 모을 일만 남았다!!!
'Develop > Study' 카테고리의 다른 글
SSL인증서를 통해 프로젝트 서버를 HTTPS로 전환하기 (0) | 2024.11.27 |
---|