본문 바로가기
개발지식/Web 지식

CORS 에러란 무엇인가?

by 감중에홍시 2023. 10. 8.

웹개발을 하다보면 아래와 같은 오류를 종종 볼 수 있다. 아래 오류는 CORS 오류에 대한 메시지로, CORS 오류는 보안 상의 이유로 브라우저에서 발생하는 오류이다.

CORS 오류 메시지
한 웹 어플리케이션이 다른 출처의 자원을 접근하려고 할 때, CORS 에러가 뜬다

CORS 정의

CORSCross-Origin Resource Sharing의 약자로 교차 출처 리소스 공유를 의미한다. HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 특정 자원에 접근할 수 있는 권한을 부여하도록 브라우저에서 알려주는 메커니즘이다.

보안의 이유로 브라우저는 (자바)스크립트에서 시작한 교차 출처 HTTP 요청을 제한한다. XMLHttpRequest와 FetchAPI는 동일 출처 정책을 따른다. CORS는 서버와 클라이언트 간의 상호 작용에서 중요한 역할을 한다.

즉, 이 API를 사용하는 웹 어플리케이션은 자신의 출처와 동일한 리소스만을 불러올 수 있으며, 다른 출처의 리소스를 불러오면, 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 한다.

그럼 여기서 "출처"라는 것은 어떤 것을 의미할까?

 출처는 자원에 접근할 때 사용하는 URL의 스키마(scheme, protocol), 도메인(domain), 포트(port)로 이루어져 있다. 여기서 같은 출처를 가졌다라는 의미는 두 출처의 스크마, 호스트, 포트가 모두 일치하는 경우, 같은 출처를 가졌다고 말할 수 있다.

일부 작업(이미지 렌더링, API 요청 등)은 동일 출처 콘텐츠로 제한되나, CORS를 통해 제한을 해제할 수 있다

URL에서의 스키마, 도메인, 포트

출처에 대한 예시

URL 동일 출처 여부
http://example.com/study/about
http://example.com/study/about
스키마와 호스트의 일치
http://example.com/study/about,
https://example.com/study/about 
다른 스키마
http://www.example.com,
http://myapp.example.com
다른 호스트
http://example.com:3000,
http://example.com:8080

다른 포트

 

 

CORS 동작원리

  • 단순 요청(Simple Request)
    • 다음 조건을 모두 만족하면 예비 요청을 하지 않고, 서버에 바로 요청한다.
      • GET, POST, HEAD의 HTTP 메서드
      • 유저 에이전트가 자동으로 설정한 헤더 외에 수동으로 설정할 수 있는 헤더가 다음과 같은 경우 -> Accept, Accept-Language, Content-Language, Content-Type
      • Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain인 경우

단순 요청의 조건을 모두 만족해서 서버로 바로 요청을 보내고, 응답을 받은 경우

 

  • 예비 요청(Preflight)
    • 단순 요청을 충족하지 않는 경우 브라우저는 예비 요청을 서버로 보낸다.
    • 브라우저는 OPTIONS 메서드를 사용하여 사전 요청을 보내고, 서버는 이 요청에 대한 응답으로, "허용되는 메서드, 허용되는 헤더, 캐시 유효 기간" 등을 명시한 헤더를 포함하여 응답한다. 이후 브라우저는 실제 요청을 보내기 전에 사전 요청에 대한 응답을 확인한다.
    • 응답으로는 오는 헤더값에는 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers등이 있는데, 이 헤더값에 없는 출처, 메서드, 헤더 등을 사용하는 경우, CORS 에러를 발생시킨다. 

OPTIONS 메서드를 사용한 사전 요청 예시

 

  • 인증 요청(Credential Request)
    • 인증된 요청 확인은 실제 요청을 보낼 때 브라우저가 사용자의 인증 정보를 함께 보내는 경우를 의미한다. 주로 쿠키나 HTTP 인증과 같은 인증 메커니즘을 사용하는 경우 해당된다.
    • 브라우저는 인증된 요청을 보낼 때 사전 요청이 아닌 일반적인 요청을 사용하며, 이때 서버는 브라우저가 보낸 인증 정보를 확인한다. 서버는 다음과 같은 헤더를 사용하여 브라우저에게 허용된 출처 및 인증 정보를 알려준다
      • Access-Control-Allow-Origin: 허용된 출처를 나타내는 헤더
      • Access-Control-Allow-Credentials: 브라우저가 쿠키와 HTTP 인증 정보를 인증된 요청에 포함시킬 수 있는지 여부를 나타내는 헤더
    • 브라우저는 이러한 헤더를 확인하고, 만약 서버에서 Access-Control-Allow-Credentials: true를 반환하면, 브라우저는 실제 요청에 사용된 자격 증명(쿠키 등)을 포함시켜 요청을 보낸다.
    • 서버는 이러한 인증 정보를 확인하고, 요청을 수락하거나 거부할 수 있다. 만약 서버에서 허용된 출처에 대해 자격 증명이 필요하다고 설정되어 있고, 브라우저에서도 자격 증명을 포함하여 요청을 보낸 경우에만 서버는 요청을 수락한다.
// 서버에서의 인증 요청 예시 코드
const express = require('express');

const app = express();
const PORT = 3000;


app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3001'); // 허용할 출처
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    // Preflight 요청에 대한 응답
    res.status(200).send();
  } else {
    // 실제 요청에 대한 처리
    next();
  }
});

 

// 클라이언트 단에서 인증 정보를 포함한 요청을 보내는 예시 코드
import axios from 'axios';

const login = async () => {
  try {
    const response = await axios.post(
      'http://localhost:3000/login',
      { /* 데이터 */ },
      {
        withCredentials: true, // 인증 정보를 포함하여 요청
      }
    );

    console.log(response.data); // 서버에서 반환한 데이터 처리
  } catch (error) {
    console.error('Login failed', error);
  }
};

login();

 

Request Header 타입의 예시

헤더명 설명
Access-Control-Request-Method HTTP 메소드를 서버에 알리기 위한 preflight 요청을 보낼때 사용.
Access-Control-Request-Headers HTTP헤더를 서버에 알리기 위한 preflight 요청을 보낼 때 사용됨.
ORIGIN 패치가 시작된 위치를 나타냄.

Response Header 타입의 예시

헤더 명 설명
Access-Control-Allow-Origin 응답 공유될 수 있는지를 나타냄
Access-Control-Allow-Credentials credentials 플래그가 true일때 요청에 대한 응답이 노출 될 수 있는지를 나타냄
Access-Control-Allow-Methods preflight 요청에 대한 응답으로 리소스에 접근할 때 허용되는 메소드를 명시
Access-Control-Allow-Headers preflight요청에 대한 응답으로 사용
Access-Control-Max-Age preflight 요청의 결과가 캐시되는 기간을 나타냄
Access-Control-Expose-Headers 어떤 헤더가 응답의 일부로 노출될 수 있는지를 나타냄

 

 

[출처]

CORS [https://developer.mozilla.org/ko/docs/Web/HTTP/CORS]

ORIGIN [https://developer.mozilla.org/ko/docs/Glossary/Origin]

HTTP 헤더 [https://developer.mozilla.org/ko/docs/Web/HTTP/Headers#cors]

HTTP 인증 [https://developer.mozilla.org/ko/docs/Web/HTTP/Authentication]

'개발지식 > Web 지식' 카테고리의 다른 글

브라우저 렌더링 과정  (1) 2024.01.06
npm, yarn  (46) 2023.11.19