CORS 란?
CORS 는 Cross Origin Resource Sharing의 약자입니다.
Cross : 교차, Origin : 출처, Resource : 자원, Sharing : 공유
말 그대로 서로 다른 출처끼리 자원을 공유하겠다는 뜻입니다.
(출처: MDN 공식 문서)
좌측의 웹 페이지는 domain-a.com 에 접속하면 볼 수 있는 웹 페이지입니다.
domain-a.com 에 접속하면 웹 페이지를 렌더링하기 위해서 총 5개의 API 요청이 발생하고 있습니다.
이 중 셋은 Same Origin 이고 둘은 Cross Origin 입니다.
상단의 파란색 네모 박스 안에 있는 API 는 Same Origin 입니다.
domain-a.com 에서 웹 서버 domain-a.com 으로 요청을 보냈기 때문에 Same Origin 입니다.
반면 하단의 빨간색 네모 박스 안에 있는 API 는 Cross Origin 입니다.
domain-a.com 에서 웹서버 domain-b.com 으로 요청을 보냈기 때문입니다.
서버는 허용되지 않은 Origin 으로부터 요청을 받으면 요청을 거부하기 때문에 하단의 빨간색 네모 박스에 있는 컨텐츠는 서버로부터 제공받지 못지 못합니다.
정리하면,
요청을 보내는 Client 와 요청을 받는 Server 의 도메인이 다르면 Cross Origin 이라 부르고 Client 의 요청은 거부됩니다.
CORS 는 언제 발생하는가?
CORS 는 Cross(서로 다른) Origin 끼리 통신할 때 발생합니다.
참고) Origin 이란?
예시 URL 을 보며 Origin 에 대해 알아보겠습니다.
https://developer.mozilla.org:443/ko/docs/Web/HTTP/CORS
- https : 프로토콜
- developer.mozilla.org : 호스트
- :443 : 포트
Origin 은 프로토콜, 호스트, 포트를 포함하는 개념입니다.
Origin 은 셋 중 하나만 달라져도 CORS 가 발생합니다.
몇 가지 예시를 통해 CORS 여부를 확인해보겠습니다.
예시 | CORS 여부 |
http://google.com -> https://google.com | O, 프로토콜이 다르기 때문 |
https://api.google.com -> https://google.com | O, 호스트가 다르기 때문 |
https://google.com:8080 -> https://google.com | O, 포트가 다르기 때문 |
https://google.com/console -> https://google.com | X |
https://google.com/console?id=1 -> https://google.com | X |
그렇다면 Cross(서로 다른) Origin 간의 통신은 절대 불가능한 걸까요?
=> Cross(서로 다른) Origin 간의 통신 가능합니다!
서로 다른 Origin 간의 통신이 필요할 수도 있는데, 그땐 어떻게 해야 할까요?
=> HTTP Header 를 조작하면 됩니다. 구체적으로는 HTTP Header 에 요청을 보내는 Origin 에 대한 정보를 추가하면 됩니다.
CORS 를 허용하는 방법
CORS 를 허용하는 방법은 Preflight, Simple Request 가 있습니다.
Preflight 란?
Preflight 는 브라우저가 main(본) 요청을 보내기 전에 보내는 사전 요청입니다.
사전 요청은 API 요청을 보낼 때마다 매번 보내는 걸까요?
=> 그렇지 않습니다.
Simple Request
main(본) 요청이 Simple Request 의 조건을 만족한다면 Preflight 를 보낼 필요가 없습니다.
Simple Request 의 조건은 다음과 같습니다.
- HTTP METHOD 가 GET, HEAD, POST 중 하나일 때
- Request 객체 생성 시 자동으로 생성되는 헤더들(Accept, Accept-Language, Content-Language, Content-Type) 외의 다른 HTTP Header 가 조작되지 않은 요청
- Content-Type 의 값이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나일 때
- XHR 객체로 API 요청을 보낼 때 xhr.upload.addEventListener() 를 호출하지 않은 요청
- API 요청을 보낼 때 ReadableStream 객체가 사용되지 않은 요청
Preflight 의 역할
Preflight 는 실제 요청을 보내기 전, 실제 요청이 안전한지 판단하기 위해 보내는 사전 요청입니다.
Preflight 는 어떻게 실제 요청의 안전 여부를 판단한다는 걸까요?
=> Preflight 의 Request, Response 객체를 보면 알 수 있습니다.
Preflight Request
Preflight 의 Request 객체에서는 main(본) 요청의 정보들을 Access-Control-Request- 헤더들에 저장합니다.
Preflight Request 의 HTTP METHOD 는 OPTIONS 입니다.
만약 main(본) 요청에서 HTTP 헤더를 조작해 다른 헤더를 추가한다면 Access-Control-Request-Headers에 [추가된 Header 이름] 이 저장됩니다. 이는 새로 추가된 헤더도 허용해 달라는 의미입니다.
Preflight Response
서버에서는 Preflight Request 의 헤더를 기반으로 Preflight Response 를 응답합니다. 구체적으로 말하자면 Access-Controller-Allow- 로 시작하는 헤더를 추가해서 응답합니다.
그런데 이 과정은 서버가 자동으로 해주지 않고 개발자가 직접 설정해줘야 하는 부분입니다.
// 모듈 import
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'http://example.com', // 허용하는 Origin 추가
methods: ['GET', 'PUT', 'POST'], // 허용하는 Method 추가
allowedHeaders: "Something", // 허용하는 Header 추가
credentials: true, // Access-Control-Allow-Credentials CORS header 설정
});
출처
https://evan-moon.github.io/2020/05/21/about-cors/