React 프론트엔드를 개발하면서 json-server를 백엔드 서버로 사용할 경우, 클라이언트 요청 경로(/api/...)와 서버의 실제 경로(/books, /users 등)가 다를 수 있습니다. 이때 두 가지 방식으로 이를 해결할 수 있습니다.
Vite의
proxy와rewrite설정은 개발 환경에서만 작동함
vite.config.js에 설정하는 proxy와 rewrite는 Vite 개발 서버가 실행 중일 때만 동작합니다.
즉, 이 설정은 npm run dev나 vite 명령으로 개발 환경에서 앱을 실행할 때만 적용됩니다.
반대로 npm run build로 앱을 프로덕션(운영) 환경에 배포하면, Vite는 정적인 HTML/CSS/JS 파일만 생성하고, proxy 설정은 전혀 작동하지 않게 됩니다.
따라서 프로덕션 서버에서는 Nginx, Express, AWS API Gateway 등의 리버스 프록시 설정 또는 백엔드 API 주소 직접 연결을 통해 프록시 기능을 따로 구성해야 합니다.
| 환경 | 경로 처리 방식 |
|---|---|
개발 환경 (vite dev) | /api/books → 프록시로 /books 변환됨 |
프로덕션 (vite build 후 배포) | /api/books → 그대로 API 서버에 요청됨 (프록시 없음) |
Vite의 개발 서버는 프록시 기능을 제공하여 클라이언트 요청을 다른 서버로 리다이렉트할 수 있습니다. 이 기능을 활용하면 클라이언트가 /api/books와 같은 경로로 요청을 보낼 때, Vite 개발 서버가 이 요청을 가로채서 /api 접두사를 제거한 후 json-server로 전달합니다.
이 방식은 실제 프로덕션 환경(실제 사용자들이 사용하는 환경)과 유사한 API 경로 구조를 유지하면서도, json-server의 기본 라우팅 체계를 그대로 활용할 수 있게 해줍니다.
React 앱에서는
/api/books로 요청하지만, json-server는/books만 이해할 수 있습니다. 그래서 Vite 개발 서버가 요청 주소에서/api를 자동으로 제거해/books로 바꿔주는 역할을 합니다.
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
proxy: {
"/api": {
target: "http://localhost:3001", // json-server 주소
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, "") // /api 제거
}
}
}
});
rewrite 함수는 요청 경로에서 /api 접두사를 제거하는 역할을 합니다.changeOrigin: true 설정은 호스트 헤더를 대상 URL의 호스트로 변경하여 CORS 문제를 방지합니다.1. 클라이언트(브라우저)가 `http://localhost:3000/api/books`로 요청을 보냅니다.
2. Vite 개발 서버가 이 요청을 감지하고 `/api`로 시작하는 경로를 프록시 규칙과 매칭합니다.
3. `rewrite` 함수가 경로에서 `/api`를 제거하여 `/books`로 변환합니다.
4. 변환된 요청이 `http://localhost:3001/books`로 전달됩니다.
5. json-server가 `/books` 엔드포인트에 대한 응답을 처리합니다.
6. 응답이 클라이언트로 반환됩니다.
| 요청 경로 | 처리 방식 |
|---|---|
/api/books | → http://localhost:3001/books |
/api/users | → http://localhost:3001/users |
// axiosInstance.js
import axios from "axios";
const axiosInstance = axios.create({
baseURL: "/api", // 모든 요청에 /api 접두사 추가
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});
export default axiosInstance;
// api 호출
// booksService.js
import axiosInstance from "./axiosInstance";
export const booksService = {
// 모든 책 가져오기
getAllBooks: async () => {
const response = await axiosInstance.get('/books');
return response.data;
},
// 특정 카테고리의 책 가져오기
getBooksByCategory: async (category) => {
const response = await axiosInstance.get(`/books?category=${category}`);
return response.data;
},
// 특정 ID의 책 상세 정보 가져오기
getBookById: async (id) => {
const response = await axiosInstance.get(`/books/${id}`);
return response.data;
}
};
프록시에서 rewrite 없이, json-server에서 경로를 직접 매핑하도록 설정합니다.
{
"/api/books": "/books",
"/api/books/:id": "/books/:id",
"/api/users": "/users",
"/api/users/:id": "/users/:id",
"/api/categories/:category/books": "/books?category=:category"
}
/api/books → /books 로 내부 매핑/api/orders/1 → /orders/1 로 매핑/api/books와 같은 요청을 json-server의 /books 리소스로 매핑합니다. 더 복잡한 경로 패턴도 정의할 수 있으며, URL 파라미터도 지원합니다.export default defineConfig({
server: {
port: 3000,
proxy: {
"/api": {
target: "http://localhost:3001",
changeOrigin: true
// rewrite 함수 없음
}
}
}
});
rewrite 함수가 없으며, 단순히 /api로 시작하는 모든 요청을 json-server로 전달합니다. 경로 변환은 json-server의 routes.json에서 처리됩니다.npx json-server --watch db.json --routes routes.json --port 3001
routes.json 파일에 정의된 사용자 정의 라우팅 규칙을 적용합니다.| 옵션 | 설명 |
|---|---|
--watch db.json | 데이터 파일 감시 |
--routes routes.json | 라우팅 규칙 적용 |
--port 3001 | 서버 포트 설정 |
| 구분 | rewrite 방식 (Vite) | routes.json 방식 (json-server) |
|---|---|---|
| 설정 위치 | vite.config.js | routes.json + 실행 명령어 필요 |
| API 요청 경로 | /api/books | /api/books |
| 실제 서버 내부 경로 | /books | /books |
| 프론트 코드에서의 URL | /api/... | /api/... |
| json-server 경로 | 기본 경로(/books)만 사용 | /api/books처럼 프론트 URL과 동일하게 유지 |
| 프론트엔드 코드 | /api/books 요청 시 내부적으로 /books | /api/books → routes.json에서 처리 |
| 유지보수 편의성 | 간단함, 빠른 테스트에 적합 | API 버전 관리 등 정형화에 유리 |
| 추천 용도 | 개인 프로젝트, 학습용 | 구조적 API 경로 관리가 필요한 경우 (API 경로를 명확하게 관리) |
project-root
┣ frontend
┃ ┣ vite.config.js
┃ ┗ src/...
┣ backend
┃ ┣ db.json
┃ ┣ routes.json
┃ ┗ ...