CORS 오류는 다른 출처의 리소스를 요청할 때 발생할 수 있다.
자신의 출처(도메인, 프로토콜, 포트)와 다른 서버에 요청을 보낼 때 CORS 오류를 발생시키지 않으려면 별도의 세팅을 해야한다.
정석적으로는 백엔드에서 처리하지만 백엔드와 소통이 안되는 경우 혹은 공공 API를 사용하는 경우에 프록시 서버를 통해 클라이언트에서도 처리할 수 있다.
⭐ 원리 설명
1. 웹 브라우저가 서버에 리소스를 요청할 경우 자동적으로 요청 헤더의 Origin에 출처를 담아서 보낸다.
2. 서버에서는 모든 요청에 대해 환영하고 응답을 보내는 데 응답 헤더의 Access-Control-Allow-Origin에 허용할 출처를 담아서 응답한다.
3. 웹 브라우저가 응답을 받기 전에 요청 헤더의 Origin과 응답 헤더의 Access-Control-Allow-Origin를 비교하여,
- 일치 => 응답을 수용한다.
- 불일치 => 응답을 버린다. 그리고 cors 오류를 발생시킴
CORS 오류 예시
프론트엔드 주소는 http://localhost:5173/ 이다.
카테고리를 조회하는 API 주소는 http://localhost:8000/category 이다.
아래는 postman으로 카테고리 조회를 성공한 결과이다. 즉 로직 상의 오류는 없다.

하지만 웹 브라우저(프론트엔드)에서 API를 요청하면 cors 오류가 발생한다. 개발자 도구의 네트워크 탭에서 Origin과 Access-Control-Allow-Origin를 확인할 수 있다.


요청 헤더의 Origin은 http://localhost:5173이다. 하지만 응답 헤더의 Access-Control-Allow-Origin은 없었고, 이런 경우도 불일치로 판단하여 cors 오류를 발생시켰던 것이였다.
해결하는 방법
1. 백엔드에서 해결
Access-Control-Allow-Origin을 웹 브라우저와 같은 출처(Origin)로 설정해주면 된다.
Node.js의 express는 cors라는 미들웨어를 제공하고 있으므로 이를 활용하였다.

Origin과 Access-Control-Allow-Origin가 일치하여 api가 성공적으로 요청받은 것을 확인할 수 있다.

2. 프론트엔드에서 해결
- vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
// Proxy 설정
proxy: {
// api로 시작하는 모든 api
"/api": {
// 서버 base 주소
target: "http://localhost:8000",
// target으로 변경
changeOrigin: true,
// 요청 경로에서 '/api' 제거
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
설명
'/api'로 시작하는 모든 api에 프록시 요청을 보내도록 설정해주었다.
따라서 서버 api가 http://localhost:8000/category인 경우, 나중에 요청할 때 /api/category로 요청해야한다
- Category.jsx
api를 날리는 컴포넌트
모든 api의 endpoint 시작부분에 /api를 붙여야한다.
import axios from "axios";
import { useEffect, useState } from "react";
const Cors = () => {
const [categorys, setCategorys] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get("/api/category");
console.log(1);
setCategorys(response.data);
} catch (err) {
console.log(err);
}
};
fetchData();
}, []);
return (
<div>
<ul>
{categorys.map((category) => (
<li key={category.category_id}>{category.category_name}</li>
))}
</ul>
</div>
);
};
export default Cors;
proxy로 요청하면 응답 헤더의 Access-Control-Allow-Origin: * 이 되므로, 성공

쿠키 전달 : credentials
⭐ 만약 자격 증명 API(ex. 쿠키 전달) 경우에는 Access-Control-Allow-Origin가 * 가 아니라 정확한 출처를 기입해야한다. 또한 credentials: true 로 옵션을 주어야 한다.
- express의 cors 미들웨어

이 때, origin : * 이거나 credentials : false(기본값) 이면 CORS 오류가 발생한다.
- react
프론트엔드에서도 withCredentials : true 로 옵션을 주어야 한다.
import axios from "axios";
const Cors = () => {
const email = "qqqqq@email.com";
const password = "qwer";
const fetchData = async () => {
try {
await axios.post(
"http://localhost:8000/users/login",
{
email,
password,
},
{
withCredentials: true, // 설정
}
);
} catch (err) {
console.log(err);
}
};
const handleLogin = () => {
fetchData();
};
return (
<div>
<button onClick={() => handleLogin()}>Login</button>
</div>
);
};
export default Cors;
withCredentials : false 라도 오류는 발생하지 않지만, 쿠키를 받을 수가 없다.
추가적으로 만약 프론트에서 proxy 서버를 사용한다면 cors의 origin 뿐만 아니라 `credentials: true` 와 같은 옵션을 백엔드에 설정하지 않아도 될 수 있다. 이는 프록시 서버의 설정에 따라 상이하다.
이전 코드에서의 vite의 proxy는 가능한 것을 확인하였음!