
🌐 SOP와 CORS의 등장
🛡️SOP(Same-Origin Policy)란?
SOP(동일 출처 정책)는 보안을 위해 같은 출처(origin)에서만 리소스를 로드할 수 있도록 제한하는 정책이다.

📌 출처(Origin)란?
출처는 Protocol(스키마) + Host(도메인) + Port로 구성된다.
예를 들어, https://lucylog.tistory.com/manage/newpost/4?type=something 라면 https://lucylog.tistory.com/ 까지가 출처이다.

🔒SOP가 없으면?
- 해커가 악성 스크립트를 포함해 사용자를 해킹할 수 있다.
- 아래와 같은 방식(XSS)으로 쿠키 탈취 및 세션 하이재킹이 가능하다.
fetch('http://google.com').then(...) // 이후 공격자에게 전송
⚙️ SOP의 동작 원리
브라우저는 다른 출처에서 데이터를 가져오는 fetch 요청을 차단한다.
서버와의 통신 자체는 정상적으로 이루어질 수 있으나, 브라우저가 응답을 차단한다.
❗️SOP의 단점
- 외부 API 서버를 사용할 때 불편함
- 다른 출처의 리소스를 로드할 때 제한이 많음
🚀 CORS(Cross-Origin Resource Sharing)의 등장
CORS(교차 출처 리소스 공유)는 SOP의 불편함을 해결하기 위해 등장했다.
🌎 CORS 동작 방식
- 웹 클라이언트가 HTTP 요청을 보낼 때, 브라우저는 요청 헤더에 Origin 필드를 포함
- 서버는 응답할 때 Access-Control-Allow-Origin 값을 추가
- 브라우저는 Origin과 Access-Control-Allow-Origin을 비교 후 응답을 허용할 지 결정
🔎 CORS 검사는 언제 발생할까?

- Postman에서는 정상 응답이지만, 브라우저에서는 CORS 오류가 발생할 수 있음
- 서버는 요청을 처리하고 응답하지만, 브라우저가 보안 정책을 위반하면 응답을 폐기함
⚡️CORS 요청의 3가지 시나리오

- Simple Request (단순 요청)
- GET, HEAD, POST 요청만 허용
- 특정한 Content-Type (application/x-www-form-urlencoded, multipart/form-data, text/plain)만 사용 가능 - Preflight Request (예비 요청)
- OPTIONS 메서드를 사용하면 먼저 서버에 허용 여부를 확인.
- 서버는 Access-Control-Allow-Methods, Access-Control-Allow-Headers 등을 포함하여 응답 - Credential Request (인증 요청)
- 쿠키, 토큰 등 인증 정보를 포함하는 요청
- fetch 요청 시 credentials 옵션을 설정해야 함
- 서버는 Access-Control-Allow-Credentials: true 헤더를 설정해야 인증 정보를 받을 수 있음

예비 요청의 응답 예시:
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
🛠️ CORS 오류 해결 방법
- 서버에서 Access-Control-Allow-Origin 설정
- 특정 출처만 허용하거나, API 서버라면 *(와일드카드) 사용 가능 - 리버스 프록시 설정 (로컬 개발 환경에서 활용)
- Webpack Dev Server, Next.js rewrites 등을 이용해 API 요청을 프록시 처리
Webpack Dev Server 설정 예시
// webpack.config.js
module.exports = {
// ...
devServer: {
proxy: {
'/api': {
target: 'https://api.test.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
},
};
Next 설정 예시
//next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async rewrites() {
return [
{
source: '/api/:path*',
destination: `API주소/:path*`,
},
];
},
};
module.exports = nextConfig;
이 설정은 로컬 개발 환경에서 '/api'로 시작하는 URL로 요청을 보낼 때, 실제로는 'https://api-test.com'로 요청을 프록시한다.
이렇게 하면 브라우저는 CORS 정책을 지킨 것처럼 보이지만, 실제로는 웹팩이 요청을 프록시하여 CORS 문제를 우회한다.
이 방법은 로컬 개발 환경에서만 사용할 수 있으며, 운영 환경에서는 근본적인 해결 방법이 아니다. 운영 환경에서는 백엔드 개발자가 서버 응답 헤더에 올바른 Access-Control-Allow-Origin 값을 설정해야 한다.
출처