프로그래밍/트러블슈팅

[트러블슈팅] 개발 중 CORS 에러, dev server proxy로 깔끔하게 넘기기 (Vite / Webpack)

이슬먹는 개발자 2026. 7. 29. 18:47
728x90
반응형

프론트엔드 개발하다 보면 십중팔구 만나는 그 빨간 에러.

Access to fetch at 'http://localhost:8080/api/users' from origin
'http://localhost:5173' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

로컬에서 프론트 띄우고 API 좀 불렀을 뿐인데 요청이 막힌다. 오늘은 이 에러를 개발 환경에서 가장 깔끔하게 넘기는 방법, dev server proxy를 다룬다. Vite와 Webpack 둘 다 정리한다.


먼저: 이게 왜 나는지 딱 한 줄만 알고 가자

브라우저에는 동일 출처 정책(Same-Origin Policy)이라는 보안 규칙이 있다. 쉽게 말해, "지금 보고 있는 페이지의 출처(origin)와 다른 출처로 요청을 보내면 브라우저가 기본적으로 막는다"는 거다.

출처(origin)는 프로토콜 + 호스트 + 포트 세 개로 정해진다. 그래서 이 둘은 서로 다른 출처다.

프론트: http://localhost:5173   ← Vite 개발 서버
백엔드: http://localhost:8080   ← API 서버
                        ↑ 포트가 다름 = 다른 출처 = CORS 발생

localhost로 같아 보여도 포트가 다르면 다른 출처다. 그래서 개발할 때 이 에러가 그렇게 자주 나는 거다.

핵심은 이거다. CORS는 브라우저가 막는 거다. 서버끼리 통신할 땐 안 난다. 이 성질을 이용하는 게 오늘의 해법이다.


해결 아이디어: 브라우저를 속이지 말고, "같은 출처처럼" 만들기

CORS는 브라우저가 "출처가 다르다"고 판단할 때 난다. 그럼 브라우저 입장에서 출처가 같아 보이게 만들면 애초에 안 난다.

dev server proxy가 하는 일이 바로 이거다.

[브라우저]
   │  http://localhost:5173/api/users  로 요청
   │  (프론트랑 같은 출처! → CORS 안 남)
   ▼
[Vite 개발 서버]  ← 프록시가 여기서 요청을 가로챔
   │  http://localhost:8080/api/users  로 대신 전달
   ▼
[백엔드 API]

브라우저는 자기랑 같은 출처(localhost:5173)로 요청을 보낸다. 그럼 CORS가 안 난다. 그 요청을 개발 서버가 받아서, 뒤에서 진짜 백엔드로 대신 전달한다. 이 서버 간 전달은 브라우저가 관여하지 않으니 CORS와 무관하다.

즉, 프론트는 자기 자신한테 요청하는 것처럼 쓰고, 실제 전달은 개발 서버가 몰래 해주는 방식이다. 우회 트릭이 아니라 Vite/Webpack이 공식적으로 제공하는 개발용 기능이다.


Vite에서 설정하기

vite.config.js (또는 .ts)의 server.proxy에 설정한다.

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    proxy: {
      // '/api'로 시작하는 요청을 백엔드로 전달
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      },
    },
  },
})

이제 프론트 코드에서는 백엔드 주소를 직접 쓰지 않고, /api로 시작하는 상대 경로로 요청한다.

// 이렇게 하지 말고 (CORS 남)
fetch('http://localhost:8080/api/users')

// 이렇게 (프론트 자기 출처로 요청 → 프록시가 전달 → CORS 안 남)
fetch('/api/users')

fetch('/api/users')는 브라우저 입장에서 http://localhost:5173/api/users로 나간다. 같은 출처라 CORS가 안 나고, Vite가 이걸 받아서 http://localhost:8080/api/users로 넘겨준다.

옵션 설명

  • target: 요청을 실제로 보낼 백엔드 주소.
  • changeOrigin: true: 전달할 때 요청의 Host 헤더를 target 기준으로 바꿔준다. 백엔드가 Host를 검사하는 경우가 많아서 보통 켜둔다. 웬만하면 true로 두면 된다.

경로를 바꿔야 할 때 (rewrite)

백엔드에는 /api 접두어가 없고 /users로 받는다면, 전달할 때 /api를 떼줘야 한다.

server: {
  proxy: {
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, ''),
      // 프론트: /api/users  →  백엔드: /users
    },
  },
}

rewrite는 프론트에서는 /api를 붙여 구분하되, 실제 백엔드 경로에는 그걸 빼고 보내고 싶을 때 쓴다.


Webpack에서 설정하기 (webpack-dev-server)

CRA(Create React App)나 webpack 기반이면 devServer.proxy에 설정한다. 개념은 완전히 똑같다.

// webpack.config.js
module.exports = {
  // ...
  devServer: {
    proxy: [
      {
        context: ['/api'],
        target: 'http://localhost:8080',
        changeOrigin: true,
        pathRewrite: { '^/api': '' },  // Vite의 rewrite와 같은 역할
      },
    ],
  },
}

Vite와 이름만 조금 다르다. rewrite 대신 pathRewrite를 쓴다는 정도.


자주 하는 실수 체크리스트

1. 설정 바꾸고 서버 재시작 안 함
vite.config.jswebpack.config.js는 서버 시작할 때 한 번 읽힌다. 프록시 설정을 바꿨으면 개발 서버를 껐다 켜야 반영된다. 이거 몰라서 "설정했는데 왜 안 돼" 하는 경우가 정말 많다.

2. 프론트 코드에서 아직 절대경로로 요청함
프록시를 걸었는데 코드에서는 여전히 fetch('http://localhost:8080/...')로 부르면 프록시를 안 탄다. fetch('/api/...')처럼 상대경로로 바꿔야 한다.

3. 경로 접두어가 안 맞음
프론트는 /api/users로 부르는데 백엔드는 /users로 받는다면 rewrite/pathRewrite로 맞춰줘야 한다. 404가 뜨면 이걸 의심하자.


그런데 이건 "개발 환경"에서만이다

중요한 포인트. dev server proxy는 개발 서버(npm run dev)가 돌 때만 동작한다. 빌드해서 배포하면 개발 서버가 없으니 이 프록시도 없다.

그래서 실제 배포 환경에서는 CORS를 다른 방식으로 처리해야 한다. 보통은 이렇게 간다.

  • 프론트와 백엔드를 같은 도메인에 두거나
  • 앞단의 Nginx 같은 리버스 프록시/api를 백엔드로 넘겨주게 하거나
  • 백엔드에서 CORS를 정식으로 열어주거나

즉, dev server proxy는 "개발할 때 편하게 넘기는 용도"고, 운영 환경 CORS는 별개로 챙겨야 한다는 걸 기억하자. (배포 환경 CORS 처리는 나중에 따로 다뤄보겠다.)


정리

  • 로컬 CORS는 프론트와 백엔드의 포트가 달라서 난다. (다른 출처)
  • dev server proxy는 프론트가 자기 출처로 요청하게 하고, 개발 서버가 그걸 백엔드로 대신 전달한다. 그래서 브라우저 입장에서 CORS가 안 난다.
  • Vite는 server.proxy, Webpack은 devServer.proxy에 설정한다. (rewritepathRewrite만 이름이 다름)
  • 설정 후 서버 재시작, 코드에서 상대경로 사용, 경로 접두어 확인 이 세 가지가 실수 포인트.
  • 이건 개발 환경 전용이다. 운영 환경 CORS는 별도로 처리해야 한다.

로컬에서 CORS 에러 만나면 백엔드 건드리기 전에 dev server proxy부터 걸어보자. 개발 흐름을 안 끊고 가장 빠르게 넘어가는 방법이다.

728x90
반응형