
CORS가 발생하는 이유는 웹 브라우저는 HTTP 요청에 대해서 어떤 요청을 하느냐에 따라 각기 다른 특징을 가지고 있기 때
문에 발생합니다.
1️⃣ <img>, <video>, script, link 태그 등
<link> 태그의 href에서 다른 사이트의 .css 리소스에 접근하는 것이 가능.<img> 태그의 src 에서 다른 사이트의 .png, .jpg 등의 리소스에 접근하는 것이 가능.<script> 태그의 src에서 다른 사이트의 .js 리소스에 접근하는 것이 가능2️⃣ XMLHttpRequest, Fetch API 스크립트
기본적으로 Same-Origin 정책을 따름.
자바스크립트에서의 요청은 기본적으로 서로 다른 도메인에 대한 요청을 보안상 제한합니다.
이미지를 가져올 때 <img> 태그의 src 속성으로 가져오는 방식과 JS에서 ajax요청으로 가져오는 방식의 코드입니다.
<body>
<img src="https://third-party-test.glitch.me/check.svg" alt="이미지">
<script>
fetch('https://third-party-test.glitch.me/check.svg')
.then(response => response.blob())
.then(imgBlob => {
const imageObjectURL = URL.createObjectURL(imgBlob);
const img = document.createElement('img');
img.src = imageObjectURL;
document.body.append(img);
})
</script>
</body>
이러면 이미지가 한 번만 출력이 됩니다.
CORS 에러 메세지를 보면
Access to fetch at ‘https://myhompage.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
이러한 에러가 발생합니다.
해석해보면 'https://myhomepage.com'에서 'https://localhost:3000' 출처로 가져올 수 있는 액세스가 CORS 정책에 의해 차단되었습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 불투명한 응답이 필요에 적합한 경우, 요청 모드를 'no-cors'로 설정하여 CORS가 비활성화된 리소스를 가져오십시오. 라는 의미입니다.
우리가 어떤 사이트를 접속할 때 인터넷 주소창에 우리는 URL이라는 문자열을 통해 접근하는데 이 URL은 하나의 문자열이 아닌 구성 요소가 존재합니다.

Protocal(Schema) : http, https
Host : 사이트 도메인
Port : 포트 번호
사이트 내부 경로, 요청의 key와 value값, 해시 태그
Origin은 딱 3개 Protocol + Host + Port입니다.
동일한 출처에서만 리소스를 공유할 수 있다는 의미입니다.
동일 출처 서버에 있는 리소스는 자유롭게 가져올 수 있지만, 다른 출처 서버에 있는 이미지나 영상은 상호작용이 불가능하다는 의미입니다.
동일 출처 정책이 필요한 이유
출처가 다른 두 애플리케이션이 소통할 수 있는 환경은 위험합니다. 제약이 없다면, 해커가 CSRF, XSS 등의 방법을 이용해서 우리가 만든 애플리케이션에서 해커가 심어놓은 코드가 실행하여 개인 정보를 가로챌 수 있습니다.
그림을 통해 보면

이렇게 SOP로 다른 출처의 스크립트가 실행되지 않도록 브라우저에서 사전에 방지하는 것입니다.
Origin이 동일한지로만 판단을 합니다.
이러한 출처 비교와 차단은 브라우저가 합니다. (서버라고 오해 X)

클라이언트 단 코드에서 API요청을 하는게 아니라, 서버 단 코드에서 다른 출처의 서버로 API 요청을 하면 CORS에러를 겪지 않게 된다. 이를 이용한 프록시 서버라는 것이 있다.
다른 출처의 리소스 공유에 대한 허용/비허용 정책입니다.
개발하다보면 기능상 어쩔 수 없이 다른 출처 간의 상호작용을 해야 하는 케이스가 존재하고 실무적으로 다른 회사의 서버 API를 이용해야 하는 상황이 존재합니다.
SOP 정책을 위반해도 CORS 정책을 따르면 다른 출처의 리소스라도 허용을 하도록 해준다는 것입니다.

Access-Control-Allow-Origin이라는 필드를 추가하고 값으로 이 리소스를 접근하는 것이 허용된 출처 URL을 내려보냅니다.
이게 결국 해결책입니다
서버에서 Access-Control-Allow-Origin 헤더에 허용할 출처를 기재해서 클라이언트에 응답하면 됩니다.
사실 브라우저는 요청을 보낼 때 한 번에 바로 보내지않고, 먼저 예비 요청을 보내 서버와 잘 통신되는지 확인 후 본 요청을 보냅니다.

1️⃣ JS로 API 요청을 보낼 때 JS의 fetch() 메서드를 통해 리소스를 받아옵니다.
2️⃣ 브라우저는 서버로 OPTIONS 메서드로 예비 요청을 먼저 보낸 후 Origin 헤더에 자신의 출처를 넣습니다.
Access-Control-Request-Method/Headers 에 실제 요청에 사용할 메서드와 헤더들을 설정합니다.
3️⃣ 이후 서버는 이 예비 요청에 대한 응답으로 어떤 것을 허용하고 어떤 것을 금지하고 있는지에 대한 헤더 정보를 담아서 브라우저로 보내줍니다.
4️⃣ 브라우저는 보낸 요청과서버가 응답해준 정책을 비교해서 해당 요청이 안전한지 확인하고 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 JS로 넘겨줍니다.
JS코드로 API 요청을 보내면, 크롬 개발자 도구에서 클라이언트와 서버가 본 요청(xhr)을 보내기 전에 예비 요청 통신을 하고 있는 것을 볼 수 있습니다.



Origin과 Access-Control-Aloow-Orign 출처를 비교!!!
이 요청이 다르게 되면 CORS 정책을 위반했다고 에러가 발생하는 것입니다.
예비 요청으로 OPTIONS 메서드로 예비 요청을 보내 보안을 강화하는 목적은 좋지만 결국 실제 요청에 걸리는 시간이 늘어나게 되어 애플리케이션 성능에 영향을 미치는 단점이 존재합니다.
따라서 브라우저 캐시를 이용해서 Access-Control-Max-Age 헤더에 캐시될 시간을 명시해주면, 이 PreFlight 요청을 캐싱시켜 최적화가 가능합니다.
예비 요청을 생략하고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답 헤더에 Access-Control-Allow-Origin 헤더를 보내주면 브라우저가 CORS 정책 위반 여부를 검사합니다.

Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우 에만 적용.application/x-www-form-urlencoded, multipart/form-data, text/plain중 하나여야한다. 아닐 경우 예비 요청으로 동작.대부분 HTTP API 요청은 text/xml 이나 application/json 으로 통신하기 때문에 Content-Type이 위반된다.
따라서 그냥 예비 요청을 이루어진다라고 이해.
클라이언트에서 서버에게 자격 인증 정보(Credential)를 실어 요청할 때 사용되는 요청입니다.
요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 credentials 옵션.
same-origin(기본값) : 같은 출처 간 요청에만 인증 저보를 담음.
include : 모든 요청에 인증 정보를 담음.
omit : 모든 요청에 인증 정보를 담지 않음.
이러한 별도의 설정을 해주지 않으면 쿠키 등의 인증 정보는 절대로 자동으로 서버에게 전송되지 않습니다.
클라이언트에서 인증 정보 보내기
fetch 메서드
fetch("https://example.com:1234/users/login", {
method: "POST",
credentials: "include", // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
body: JSON.stringify({
userId: 1,
}),
})
axios 라이브러리
axios.post('https://example.com:1234/users/login', {
profile: { username: username, password: password }
}, {
withCredentials: true // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
})
jQuery 라이브러리
$.ajax({
url: "https://example.com:1234/users/login",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
xhrFields: {
withCredentials: true // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
},
success: function (retval, textStatus) {
console.log( JSON.stringify(retval));
}
});
서버에서 인증된 요청에 대한 헤더 설정하기
서버도 인증된 요청에 대해 일반적인 CORS 요청과는 다르게 대응해야 합니다.
즉 분명한 Origin으로 설정해야 합니다.

이러한 에러들을 발견했다면 반드시 위 사항들을 확인해보도록!!!
🖇️ https://chuckchoiboi.github.io/cors-tutorial/
🖇️ CORS 정리와 해결법