CORS 에러란 무엇인가?
브라우저가 보안상의 이유로 다른 도메인의 리소스 요청을 차단할 때 발생하는 오류입니다.
Same Origin과 Cross Origin : 요청 방식에 따른 CORS 발생 여부
1. <img>, <video>, <script>, <link> 태그
기본적으로 Cross-Origin 정책을 지원합니다. (다른 사이트의 리소스를 접근할 수 있다.)
2. XMLHttpRequest, Fetch API 스크립트
기본적으로 Same-Origin 정책을 지원합니다. (서로 다른 도메인에 대한 요청을 제한한다.)
브라우저는 기본적으로 자신의 서버 연결만 허용되도록 설정되어 있습니다.
fetch('<<a href=https://third-party-test.glitch.me/check.svg>https://third-party-test.glitch.me/check.svg</a>>')
.then(response => response.blob())
.then(imgBlob => {
const imageObjectURL = URL.createObjectURL(imgBlob); // 응답 받은 이미지를 blob 객체로 변환
const img = document.createElement('img'); // 이미지 태그를 생성하고
img.src = imageObjectURL; // 이미지 경로를 설정한뒤
document.body.append(img); // html에 추가
})
CORS
Cross-Origin Resource Sharing의 약자로, 서로 다른 도메인에서 리소스 공유에 대한 정책을 의미합니다.
Origin 출처란?

URL에서 Protocol + Host + Port를 합친 부분이 Origin이라 할 수 있습니다.
SOP: Same-Origin Policy
동일한 출처에서만 리소스를 공유할 수 있는 정책을 의미합니다.
동일 Origin 서버에 있는 리소스는 자유롭게 사용할 수 있지만, 다른 Origin 서버에 있는 리소스는 사용할 수 없습니다.
출처 비교와 차단은 브라우저가 담당한다.
출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 브라우저에 구현된 기능입니다.

- 서버는 리소스 요청에 대한 응답을 정상적으로 반환했다. 하지만, 브라우저가 해당 응답을 분석해서 동일 출처가 아니면, CORS 에러를 반환한다.
브라우저의 CORS 동작 살펴보기
- 클라이언트에서 HTTP 요청의 Header에 Origin을 담아 전달합니다.
- 브라우저는 요청 Header에 Origin이라는 필드에 출처를 함께 담아 보냅니다.
- 서버는 응답 Header에 Access-Control-Allow-Origin을 담아 클라이언트로 전달합니다.
- 서버가 요청에 대한 응답을 할 때, 응답 Header에 Access-Control-Allow-Origin 이라는 필드를 추가합니다. 반환 값으로 ‘이 리소스를 접근하는 것이 허용된 출처 URL’ 임을 알립니다.
- 클라이언트에서 본인 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교합니다.
- 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 허용/차단을 결정합니다.
- 만약 유효하지 않다면, 해당 응답을 사용하지 않고 CORS 에러 반환
- 유효한 경우, 다른 출처의 리소스를 성공적으로 사용합니다.
- 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 허용/차단을 결정합니다.
CORS 해결책
- 서버에서 Access-Control-Allow-Origin Header에 허용할 출처를 기재해서 클라이언트에 응답합니다.
- 프론트에서 Proxy Server 설정을 통해 이를 우회하여 처리할 수 있다.
Proxy
React → Proxy → Backend Server
1. 프론트 요청
fetch('/api/users') // <http://localhost:3000/api/users> 로 요청
- 리액트 서버는 이 요청을 받으면 프록시 설정에 따라 백엔드 서버로 요청을 보냅니다.
2. Set Proxy
{
"proxy": "<http://localhost:8080>"
}
- 이 설정을 통해, React 개발 서버는 /api, /board와 같은 요청을 자동으로 백엔드 서버(8000)으로 중계합니다.
- 브라우저는 여전히 localhost:3000으로 요청했기 때문에 CORS 문제 발생하지 않음
1. 사용자가 브라우저에서 React 앱 실행
- React에서 /api/something 요청
2. React Server가 Backend Server로 중간에서 요청 전달 (Proxy)
3. Backend Server → React Server → 브라우저로 전달
➡️ 프록시를 통해 브라우저는 리액트 서버에 요청을 보내고 응답을 받게 되어, CORS 에러가 발생하지 않습니다.
CORS 작동 방식 3가지 시나리오
Preflight Request
보통 브라우저는 요청을 보낼 때 한 번에 바로 보내지 않고, 먼저 Preflight 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보냅니다.
Preflight Request의 경우 브라우저 스스로 안전한 요청인지 미리 확인하는 것입니다.
이때 HTTP 메서드를 GET/POST 가 아닌 OPTIONS를 사용해 처리합니다.

- JS → fetch()를 통해 리소스 요청
- 브라우저는 서버로 HTTP OPTIONS 메서드로 예비 요청을 보냅니다.
- Origin (출처)
- Access-Control-Request-Method (실제 요청 메서드들)
- Access-Control-Request-Headers (실제 요청 헤더들)
- 서버는 예비 요청에 대한 응답으로, 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 정보를 Header에 담아 반환합니다.
- Access-Control-Allow-Origin (허용 출처)
- Access-Control-Allow-Methods (허용 메서드)
- Access-Control-Allow-Headers (허용 헤드)
- Access-Control-Max-Age (허용 브라우저 캐싱 시간)
- 브라우저는 보낸 요청과 서버가 응답해 준 정책을 비교하여, 안전한 요청인지 확인합니다.
Preflight 문제점과 캐싱
예비 요청을 한 번더 보냄으로써 실제 요청에 걸리는 시간이 늘어나, 애플리케이션 성능에 영향을 주는 문제가 있습니다.
이를 브라우저 캐시를 이용해 Access-Contrl-Max-Age 헤더에 캐시 될 시간만큼, 예비 요청을 캐싱합니다.
Simple Request
단순 요청은 서버에 바로 본 요청을 보내는 것을 의미합니다.
서버가 이에 대한 응답의 Header에 Access-Control-Allow-Origin을 보내고, 브라우저가 CORS 정책에 위반하는지 검사합니다.

the Condition of Simple Request
- HTTP METHOD : GET, HEAD, POST
- Header : Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우 에만 적용된다.
- Header(Content-Type) : application/x-www-form-urlencoded, multipart/form-data, text/plain
📌 위 3가지 조건을 모두 충족해야 단순 요청으로 처리됩니다.
Credentialed Request
인증된 요청은 기본적인 CORS 처리 방식에 인증 정보(쿠키, 토큰)을 추가한 형태입니다.
일반적인 CORS에 더해 withCredentials 옵션 설정과 서버에서 Access-Control-Allow-Credentilas를 설정해줘야 합니다.

// frontend
axios.get('<http://localhost:8080/api/data>', {
withCredentials: true // 인증 정보 포함
})
// backend
Access-Control-Allow-Origin: <http://localhost:3000>
Access-Control-Allow-Credentials: true
인증된 요청이 필요한 상황
- 쿠키 기반 인증
- 클라이언트가 서버에 요청할 때 브라우저가 자동으로 쿠키를 전송할 때 사용합니다.
- 기본적으로 CORS 요청 시 보안 이슈 때문에 브라우저는 쿠키를 보내지 않는다.
- 브라우저 기본 인증 방식 (Basic Auth)
- [Authorization: Basic …] 헤더를 브라우저가 자동으로 추가할 때
JWT 인증 방식에서 쿠키를 통해 인증을 처리하는 것이 아니고, Header를 통해서만 전달한다면 굳이 인증된 요청에 대한 설정을 할 필요가 없다.
참고자료
🌐 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏
악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이
inpa.tistory.com
'Web' 카테고리의 다른 글
HTML에서 Checkbox로 여러 개의 값 전달하는 방법 - form과 input 사용법 (1) | 2023.12.03 |
---|---|
[JS] javascript 장바구니 기능 구현, Node.js 여러 개의 상품 결제 - form 컬렉션 전송 (0) | 2023.12.03 |
[JS] Javascript 동적으로 데이터 불러오기, select 태그 사용하여 동적인 웹 사이트 구현 (0) | 2023.12.02 |
CORS 에러란 무엇인가?
브라우저가 보안상의 이유로 다른 도메인의 리소스 요청을 차단할 때 발생하는 오류입니다.
Same Origin과 Cross Origin : 요청 방식에 따른 CORS 발생 여부
1. <img>, <video>, <script>, <link> 태그
기본적으로 Cross-Origin 정책을 지원합니다. (다른 사이트의 리소스를 접근할 수 있다.)
2. XMLHttpRequest, Fetch API 스크립트
기본적으로 Same-Origin 정책을 지원합니다. (서로 다른 도메인에 대한 요청을 제한한다.)
브라우저는 기본적으로 자신의 서버 연결만 허용되도록 설정되어 있습니다.
fetch('<<a href=https://third-party-test.glitch.me/check.svg>https://third-party-test.glitch.me/check.svg</a>>')
.then(response => response.blob())
.then(imgBlob => {
const imageObjectURL = URL.createObjectURL(imgBlob); // 응답 받은 이미지를 blob 객체로 변환
const img = document.createElement('img'); // 이미지 태그를 생성하고
img.src = imageObjectURL; // 이미지 경로를 설정한뒤
document.body.append(img); // html에 추가
})
CORS
Cross-Origin Resource Sharing의 약자로, 서로 다른 도메인에서 리소스 공유에 대한 정책을 의미합니다.
Origin 출처란?

URL에서 Protocol + Host + Port를 합친 부분이 Origin이라 할 수 있습니다.
SOP: Same-Origin Policy
동일한 출처에서만 리소스를 공유할 수 있는 정책을 의미합니다.
동일 Origin 서버에 있는 리소스는 자유롭게 사용할 수 있지만, 다른 Origin 서버에 있는 리소스는 사용할 수 없습니다.
출처 비교와 차단은 브라우저가 담당한다.
출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 브라우저에 구현된 기능입니다.

- 서버는 리소스 요청에 대한 응답을 정상적으로 반환했다. 하지만, 브라우저가 해당 응답을 분석해서 동일 출처가 아니면, CORS 에러를 반환한다.
브라우저의 CORS 동작 살펴보기
- 클라이언트에서 HTTP 요청의 Header에 Origin을 담아 전달합니다.
- 브라우저는 요청 Header에 Origin이라는 필드에 출처를 함께 담아 보냅니다.
- 서버는 응답 Header에 Access-Control-Allow-Origin을 담아 클라이언트로 전달합니다.
- 서버가 요청에 대한 응답을 할 때, 응답 Header에 Access-Control-Allow-Origin 이라는 필드를 추가합니다. 반환 값으로 ‘이 리소스를 접근하는 것이 허용된 출처 URL’ 임을 알립니다.
- 클라이언트에서 본인 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교합니다.
- 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 허용/차단을 결정합니다.
- 만약 유효하지 않다면, 해당 응답을 사용하지 않고 CORS 에러 반환
- 유효한 경우, 다른 출처의 리소스를 성공적으로 사용합니다.
- 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 허용/차단을 결정합니다.
CORS 해결책
- 서버에서 Access-Control-Allow-Origin Header에 허용할 출처를 기재해서 클라이언트에 응답합니다.
- 프론트에서 Proxy Server 설정을 통해 이를 우회하여 처리할 수 있다.
Proxy
React → Proxy → Backend Server
1. 프론트 요청
fetch('/api/users') // <http://localhost:3000/api/users> 로 요청
- 리액트 서버는 이 요청을 받으면 프록시 설정에 따라 백엔드 서버로 요청을 보냅니다.
2. Set Proxy
{
"proxy": "<http://localhost:8080>"
}
- 이 설정을 통해, React 개발 서버는 /api, /board와 같은 요청을 자동으로 백엔드 서버(8000)으로 중계합니다.
- 브라우저는 여전히 localhost:3000으로 요청했기 때문에 CORS 문제 발생하지 않음
1. 사용자가 브라우저에서 React 앱 실행
- React에서 /api/something 요청
2. React Server가 Backend Server로 중간에서 요청 전달 (Proxy)
3. Backend Server → React Server → 브라우저로 전달
➡️ 프록시를 통해 브라우저는 리액트 서버에 요청을 보내고 응답을 받게 되어, CORS 에러가 발생하지 않습니다.
CORS 작동 방식 3가지 시나리오
Preflight Request
보통 브라우저는 요청을 보낼 때 한 번에 바로 보내지 않고, 먼저 Preflight 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보냅니다.
Preflight Request의 경우 브라우저 스스로 안전한 요청인지 미리 확인하는 것입니다.
이때 HTTP 메서드를 GET/POST 가 아닌 OPTIONS를 사용해 처리합니다.

- JS → fetch()를 통해 리소스 요청
- 브라우저는 서버로 HTTP OPTIONS 메서드로 예비 요청을 보냅니다.
- Origin (출처)
- Access-Control-Request-Method (실제 요청 메서드들)
- Access-Control-Request-Headers (실제 요청 헤더들)
- 서버는 예비 요청에 대한 응답으로, 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 정보를 Header에 담아 반환합니다.
- Access-Control-Allow-Origin (허용 출처)
- Access-Control-Allow-Methods (허용 메서드)
- Access-Control-Allow-Headers (허용 헤드)
- Access-Control-Max-Age (허용 브라우저 캐싱 시간)
- 브라우저는 보낸 요청과 서버가 응답해 준 정책을 비교하여, 안전한 요청인지 확인합니다.
Preflight 문제점과 캐싱
예비 요청을 한 번더 보냄으로써 실제 요청에 걸리는 시간이 늘어나, 애플리케이션 성능에 영향을 주는 문제가 있습니다.
이를 브라우저 캐시를 이용해 Access-Contrl-Max-Age 헤더에 캐시 될 시간만큼, 예비 요청을 캐싱합니다.
Simple Request
단순 요청은 서버에 바로 본 요청을 보내는 것을 의미합니다.
서버가 이에 대한 응답의 Header에 Access-Control-Allow-Origin을 보내고, 브라우저가 CORS 정책에 위반하는지 검사합니다.

the Condition of Simple Request
- HTTP METHOD : GET, HEAD, POST
- Header : Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우 에만 적용된다.
- Header(Content-Type) : application/x-www-form-urlencoded, multipart/form-data, text/plain
📌 위 3가지 조건을 모두 충족해야 단순 요청으로 처리됩니다.
Credentialed Request
인증된 요청은 기본적인 CORS 처리 방식에 인증 정보(쿠키, 토큰)을 추가한 형태입니다.
일반적인 CORS에 더해 withCredentials 옵션 설정과 서버에서 Access-Control-Allow-Credentilas를 설정해줘야 합니다.

// frontend
axios.get('<http://localhost:8080/api/data>', {
withCredentials: true // 인증 정보 포함
})
// backend
Access-Control-Allow-Origin: <http://localhost:3000>
Access-Control-Allow-Credentials: true
인증된 요청이 필요한 상황
- 쿠키 기반 인증
- 클라이언트가 서버에 요청할 때 브라우저가 자동으로 쿠키를 전송할 때 사용합니다.
- 기본적으로 CORS 요청 시 보안 이슈 때문에 브라우저는 쿠키를 보내지 않는다.
- 브라우저 기본 인증 방식 (Basic Auth)
- [Authorization: Basic …] 헤더를 브라우저가 자동으로 추가할 때
JWT 인증 방식에서 쿠키를 통해 인증을 처리하는 것이 아니고, Header를 통해서만 전달한다면 굳이 인증된 요청에 대한 설정을 할 필요가 없다.
참고자료
🌐 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏
악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이
inpa.tistory.com
'Web' 카테고리의 다른 글
HTML에서 Checkbox로 여러 개의 값 전달하는 방법 - form과 input 사용법 (1) | 2023.12.03 |
---|---|
[JS] javascript 장바구니 기능 구현, Node.js 여러 개의 상품 결제 - form 컬렉션 전송 (0) | 2023.12.03 |
[JS] Javascript 동적으로 데이터 불러오기, select 태그 사용하여 동적인 웹 사이트 구현 (0) | 2023.12.02 |