Vite νλ‘μ(Proxy) μ€μ κ°μ΄λ
1. νλ‘μ(Proxy)λ?
νλ‘μλ ν΄λΌμ΄μΈνΈμ μλ² μ¬μ΄μμ μμ²μ μ€κ³νλ μ€κ° μλ²μ
λλ€.
[λΈλΌμ°μ ] β [Vite Dev Server (νλ‘μ)] β [λ°±μλ API μλ²]
:5173 :5173/api/... :8080
Plain Text
볡μ¬
μ νλ‘μκ° νμνκ°? - CORS λ¬Έμ
λΈλΌμ°μ λ λμΌ μΆμ² μ μ±
(Same-Origin Policy) μ μν΄ λ€λ₯Έ λλ©μΈ/ν¬νΈλ‘μ μμ²μ μ°¨λ¨ν©λλ€.
νλͺ© | νλ‘ νΈμλ | λ°±μλ |
μ£Όμ | http://localhost:5173 | http://localhost:8080 |
ν¬νΈ | 5173 | 8080 |
κ²°κ³Ό |
νλ‘μλ₯Ό μ¬μ©νλ©΄: λΈλΌμ°μ λ κ°μ ν¬νΈ(5173)λ‘ μμ²νλ―λ‘ CORS λ¬Έμ κ° λ°μνμ§ μμ΅λλ€.
λΈλΌμ°μ β /api/member/list (5173) β Viteκ° 8080μΌλ‘ μ λ¬ β μλ΅ λ°ν
Plain Text
볡μ¬
2. Vite νλ‘μ κΈ°λ³Έ μ€μ
vite.config.js νμΌμ server.proxy μ΅μ
μΌλ‘ μ€μ ν©λλ€.
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
proxy: {
'/api': { // β νλ‘μ κ²½λ‘ μ λμ¬
target: '<http://localhost:8080>', // β‘ μ€μ λ°±μλ μλ² μ£Όμ
changeOrigin: true, // β’ Origin ν€λ λ³κ²½
rewrite: (path) => path.replace(/^\\/api/, ''), // β£ κ²½λ‘ μ¬μμ±
configure: (proxy) => { // β€ μΆκ° μ€μ
proxy.on('proxyReq', (proxyReq) => {
proxyReq.removeHeader('origin') // Origin ν€λ μ κ±°
})
}
}
}
}
})
JavaScript
볡μ¬
3. μ€μ μ΅μ μμΈ μ€λͺ
β νλ‘μ κ²½λ‘ μ λμ¬ (/api)
'/api': { ... }
JavaScript
볡μ¬
β’
/apiλ‘ μμνλ λͺ¨λ μμ²μ μ΄ νλ‘μ κ·μΉμΌλ‘ μ²λ¦¬ν©λλ€.
β’
μ: /api/member/list, /api/board/write λ±
β‘ target - λ°±μλ μλ² μ£Όμ
target: '<http://localhost:8080>'
JavaScript
볡μ¬
β’
μ€μ API μμ²μ μ λ¬ν λ°±μλ μλ²μ μ£Όμμ
λλ€.
β’
κ°λ° νκ²½μμλ λ³΄ν΅ λ‘컬 μ€νλ§λΆνΈ μλ² μ£Όμλ₯Ό μ¬μ©ν©λλ€.
β’ changeOrigin - Origin ν€λ λ³κ²½
changeOrigin: true
JavaScript
볡μ¬
κ° | λμ |
true | Host ν€λλ₯Ό target μλ²μ μ£Όμλ‘ λ³κ²½ |
false (κΈ°λ³Έ) | μλ Host ν€λ(localhost:5173) μ μ§ |
β’
CORS μ²λ¦¬ μ κ±°μ νμ trueλ‘ μ€μ ν©λλ€.
β’
λ°±μλ μλ²κ° Host ν€λλ₯Ό κ²μ¦ν λ νμν©λλ€.
β£ rewrite - κ²½λ‘ μ¬μμ±
rewrite: (path) => path.replace(/^\\/api/, '')
JavaScript
볡μ¬
λΈλΌμ°μ μμ² URL | λ°±μλ μ λ¬ URL |
/api/member/list | /member/list |
/api/board/write | /board/write |
/api/login | /login |
β’
μμ /api μ λμ¬λ₯Ό μ κ±°ν λ€ λ°±μλλ‘ μ λ¬ν©λλ€.
β’
λ°±μλ API κ²½λ‘μ /apiκ° μλ κ²½μ° μ¬μ©ν©λλ€.
rewrite μ¬μ© μ¬λΆ λΉκ΅
rewrite μλ κ²½μ°: /api/member/list β /member/list (μ λμ¬ μ κ±°)
rewrite μλ κ²½μ°: /api/member/list β /api/member/list (κ·Έλλ‘ μ λ¬)
Plain Text
볡μ¬
β€ configure - νλ‘μ μ΄λ²€νΈ ν
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq) => {
proxyReq.removeHeader('origin')
})
}
JavaScript
볡μ¬
β’
http-proxy λΌμ΄λΈλ¬λ¦¬μ μ΄λ²€νΈλ₯Ό μ§μ μ μ΄ν μ μμ΅λλ€.
β’
proxyReq: λ°±μλλ‘ λκ°λ μμ²μ κ°λ‘μ±λ μ΄λ²€νΈ
removeHeader('origin')μ΄ νμν μ΄μ
μΌλΆ λ°±μλ μλ²(Spring Security λ±)κ° Origin ν€λλ₯Ό μ격νκ² κ²μ¬ν©λλ€.
changeOrigin: true λ§μΌλ‘ ν΄κ²°λμ§ μμ λ, Origin ν€λ μ체λ₯Ό μ κ±°νλ©΄
λ°±μλκ° CORS κ²μ¬λ₯Ό μ°ννκ² λ©λλ€.
Origin ν€λ μμ λ: λ°±μλκ° CORS μ μ±
κ²μ¬ β κ±°λΆ κ°λ₯
Origin ν€λ μ κ±°: λ°±μλκ° μΌλ° HTTP μμ²μΌλ‘ μΈμ β νμ©
Plain Text
볡μ¬
4. μμ² νλ¦ μ 체 μμ
β μ»΄ν¬λνΈμμ fetch('/api/member/list') νΈμΆ
β
β‘ Vite Dev Serverκ° '/api' ν¨ν΄ κ°μ§
β
β’ rewrite: '/api/member/list' β '/member/list'
β
β£ changeOrigin: Host ν€λλ₯Ό 'localhost:8080'μΌλ‘ λ³κ²½
β
β€ configure: Origin ν€λ μ κ±°
β
β₯ '<http://localhost:8080/member/list>' λ‘ μμ² μ λ¬
β
β¦ μ€νλ§λΆνΈ μλ² μ²λ¦¬ ν μλ΅ λ°ν
β
β§ Viteκ° λΈλΌμ°μ λ‘ μλ΅ μ λ¬
Plain Text
볡μ¬
5. React μ½λμμ API νΈμΆ λ°©λ²
νλ‘μ μ€μ νμλ μ 체 URL λμ μλ κ²½λ‘λ§ μ¬μ©ν©λλ€.
// β μλͺ»λ λ°©λ² - μ§μ λ°±μλ μ£Όμ μ¬μ© (CORS μλ¬)
const res = await fetch('<http://localhost:8080/member/list>');
// β
μ¬λ°λ₯Έ λ°©λ² - μλ κ²½λ‘ μ¬μ© (Vite νλ‘μκ° μ€κ³)
const res = await fetch('/api/member/list');
JavaScript
볡μ¬
axios μ¬μ© μ
// axios κΈ°λ³Έ μ€μ (baseURL μ€μ λΆνμ)
import axios from 'axios';
// μμ² μμ
const res = await axios.get('/api/member/list');
const res = await axios.post('/api/member/login', { id, pwd });
JavaScript
볡μ¬
6. λ€μ€ νλ‘μ μ€μ
μ¬λ¬ κ²½λ‘μ λν΄ κ°κ° λ€λ₯Έ μλ²λ‘ νλ‘μν μ μμ΅λλ€.
server: {
proxy: {
'/api': {
target: '<http://localhost:8080>', // λ©μΈ λ°±μλ
changeOrigin: true,
rewrite: (path) => path.replace(/^\\/api/, '')
},
'/auth': {
target: '<http://localhost:9090>', // μΈμ¦ μλ²
changeOrigin: true,
rewrite: (path) => path.replace(/^\\/auth/, '')
},
'/upload': {
target: '<http://localhost:8080>', // νμΌ μ
λ‘λ
changeOrigin: true,
}
}
}
JavaScript
볡μ¬
7. κ°λ° vs μ΄μ νκ²½
νλ‘μ μ€μ μ κ°λ° νκ²½μμλ§ λμν©λλ€.
νκ²½ | λ°©λ² |
κ°λ° (npm run dev) | Vite νλ‘μ μ¬μ© |
μ΄μ (npm run build ν λ°°ν¬) | Nginx 리λ²μ€ νλ‘μ λλ λ°±μλ CORS μ€μ |
μ΄μ νκ²½ Nginx μμ
location /api/ {
proxy_pass <http://backend:8080/>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Plain Text
볡μ¬
8. νΈλ¬λΈμν
μ¦μ | μμΈ | ν΄κ²° λ°©λ² |
CORS error κ³μ λ°μ | νλ‘μ κ²½λ‘ λΆμΌμΉ | fetch('/api/...') κ²½λ‘μ νλ‘μ ν€('/api') νμΈ |
404 Not Found | rewrite μ€μ μ€λ₯ | λ°±μλ μ€μ κ²½λ‘μ rewrite κ²°κ³Ό λΉκ΅ |
401 Unauthorized | Origin ν€λ λ¬Έμ | configureμμ removeHeader('origin') μΆκ° |
νλ‘μ μ μ© μ λ¨ | μ λ URL μ¬μ© | http://localhost:8080/... β /api/... λ‘ λ³κ²½ |
μ€μ λ³κ²½ λ―Έμ μ© | Vite μλ² λ―Έμ¬μμ | npm run dev μ¬μ€ν |
ν΅μ¬ μ 리
νλ‘μ = CORS λ¬Έμ λ₯Ό κ°λ° νκ²½μμ μ°ννλ λꡬ
/api/* μμ² β Vite Dev Server β <http://localhost:8080/*>
(λΈλΌμ°μ ) (μ€κ° μ€κ³μ) (Spring Boot)
μ€μ μμΉ: vite.config.js > server > proxy
νΈμΆ λ°©λ²: fetch('/api/κ²½λ‘') β μ λ URL μ¬μ© κΈμ§
Plain Text
볡μ¬



