배포 시 프론트에서 백엔드로 요청이 안 될 때 체크리스트
개요
상황
WEB WAS DB 3-Tier 구조로 베포 전 테스트를 진행하고 있었음.
WEB 서버에서 WAS로 요청을 보내는데 요청이 정상적으로 실행 및 응답이 오지 않음.
환경
- AWS
- Public Subnet: WEB (Vite + React.js)
- Private Subnet: WAS (Java, Spring Boot)
- Private Subnet 2: DB (MySQL)
- 인프라: EC2 인스턴스 (Amazon Linux, t2.medium)
조치
보안그룹 확인
curl -X GET http://10.0.101.62:8080/api/v1/test/1
{"status":"OK","code":"S001","message":"유저 정보 조회 성공","data":null}[ec2-user@ip-10-0-1-72 test]$
🧠 근본적인 문제의 원인
기존(실패): 요청 아이피를 WAS의 사설 아이피 대역으로 보냄
import axios from 'axios';
const VITE_BASE_URL = 'http://10.0.101.62/api/v1';
export const baseInstance = axios.create({
baseURL: VITE_BASE_URL,
});
수정(성공): 요청 아이피를 본인이 속한 인스턴스의 공인 아이피로 보냄
import axios from 'axios';
const VITE_BASE_URL = 'http://43.203.125.200/api/v1';
export const baseInstance = axios.create({
baseURL: VITE_BASE_URL,
});
추가 테스트:요청 아이피를 본인이 속한 인스턴스의 사설 아이피로 보냄(실패)
const VITE_BASE_URL = 'http://10.0.1.72/api/v1';
export const baseInstance = axios.create({
baseURL: VITE_BASE_URL,
});
요청의 주체가 내 컴퓨터(로컬) 이므로 웹서버의 Private IP로는 접근이 불가능함.
프록시를 알았어야 했다
우리가 웹서핑을 하면 흔히 볼 수 있는 페이지는
자바스크립트, HTML, CSS 등이 혼합된 페이지이다.
즉 개발자가 페이지를 만들어 놓고 사용자가 해당 URL로 접속을 하면 만들어 놓은 페이지를 웹서버가 클라이언트에게 서빙을 해준다.
해당 페이지는 내 컴퓨터(로컬)의 브라우저에 의해 보이는 페이지이다.
웹 서버에 접속을 해서 실시간으로 보이는 화면이 아니다.
즉 우리가 데이터를 입력 후 전송을 누르면 전송의 주체는 내 컴퓨터(로컬) 브라우저가 전송의 주체가 된다.
백엔드 서버 WAS는 Private Subnet에 있기 때문에 공인 IP가 없다 그러므로 외부에서 직접적인 접속이 불가능하다
const VITE_BASE_URL = 'http://WAS의 사설 아이피/api/v1';
export const baseInstance = axios.create({
baseURL: VITE_BASE_URL,
});
결정적인 실수
나의 경우에는 요청의 주체가 WEB Server라고 생각을 해서 Axios요청에 WAS의 사설아이피를 적어 놓았다.
어차피 WEB Server와 WAS는 같은 VPC에 있으므로 둘끼리는 통신이 가능하기 때문이다.
하지만 요청의 주체가 브라우저이므로 해당 axios요청은 웹서버가 보내는 게 아닌
내가 보내게 된다 그러므로 WAS와 통신이 안되었던 것이다.
그럼 왜 로컬에서는 잘 됐을까?
- 로컬에서는
localhost:5173 Vite가
뜨고 - 요청을
localhost:8080
(Spring Boot)으로 보냄 - 둘 다 같은 로컬이니까 당연히 통신이 잘 됨
→ VPC/사설망 개념이 아예 필요 없음
추가
위 구조를 곰곰이 생각해 보면 로컬 환경에서 테스트할 때는 당연히 잘 되는 게 맞았다.
WAS에 공인 아이피를 달아주고 Axios 요청에 WAS의 공인아이피를 쓴다면 이 또한 잘 전달이 될 것이다.
프록시 역할을 누가 할까?
Vite는 일반적으로 개발서버이다.
✅ 개발 서버란?
우리가 개발할 때만 임시로 띄워서 사용하는 서버임.
npm run dev 하면 Vite가 돌아감
. jsx,. ts,. css,. html 등을 실시간으로 컴파일해서 빠르게 브라우저에 보여줌
✅ 배포할 때
npm run build 하면 Vite가 번들링 해줌
dist/ 폴더에 정적 파일들을 만들어줌
위에서 만든 정적파일들을 Nginx로 서빙
즉 개발할 때만
npm run dev
를 해주는 것이고. 실제로는
npm run build
를 통해 정적파일을 만든 후 nginx로 서빙을 한다.
Vite는 프록시 서버는 아니지만 기능을 확장할 수 있다.
Vite 자체가 프록시 서버는 아니고,
vite.config.js 에서 server.proxy 설정하면 그 경로만 프록시처럼 동작하게 만들어주는 거야.
Vite의 프록시 기능
import axios from 'axios';
const VITE_BASE_URL = 'http://웹서버공인IP/api/v1';
export const baseInstance = axios.create({
baseURL: VITE_BASE_URL,
});
위 부분을 클라이언트가 실행을 시키면 클라이언트의 요청은 웹서버로 다시 갈 것이다.
vite.config.js
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
host: '0.0.0.0',
port: 5173,
proxy: {
'/api/v1': {
target: 'http://WAS사설IP:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/v1/, '/api/v1'),
},
},
},
});
해당 요청이 들어온다면 Vite는 “아 이 요청을 WAS로 보내달라는 거구나?”라고 알아듣는다.
그렇다면 요청의 주체는 Vite가 될 것이므로 WAS로의 요청 또한 잘 갈 것이다.
NGINX를 사용해서 프록시를 해보자
DockerFile
FROM node:18 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN rm -rf node_modules package-lock.json
RUN npm install
COPY . .
RUN npm run build
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
default.conf
server {
listen 80;
server_name 43.203.125.200;
root /usr/share/nginx/html;
location / {
try_files $uri /index.html;
}
location /api/v1/ {
proxy_pass http://10.0.101.62:8080/api/v1/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
그 외 자잘한 트러블 슈팅들
보안 그룹을 안 열었다면
Request failed with status code 504'