CORS란 무엇일까?

Jvito
9 min readAug 18, 2022

--

1. Origin (출처)

URL에서 Protocol, Host, Port로 구성된 부분을 ‘Origin’이라 한다.

2. same-origin policy (SOP)

먼저, CORS를 탐구하기 전에 필요한 개념 SOP에 대해 알아보자.

SOP는 동일 출처 정책이라고 하며, 다른 Origin에서 리소스를 가지고 오거나, 보내는 상호작용 하는 것을 제한하는 보안 방식이다.

자바스크립트 XMLHttpRequest, Fetch API 등 script에서 AJAX요청을 보낼 때 이 SOP 따른다.

export const searchYoutube = async (options) => {
const YOUTUBE_URL = `https://www.googleapis.com/youtube/v3/search?key=${YOUTUBE_API_KEY}&part=snippet`
const res = await fetch(YOUTUBE_URL);
const data = await res.json();
return data;
}

Same Origin 예시

(https의 경우 기본 443 Port, http의 경우 80 or 8080 Port로 설정된다.)

이 SOP을 확인하는 주체는 ‘브라우저’이다.

만약 SOP를 위반하는 요청을 클라이언트에서 보내면 서버가 정상적으로 응답하여도 브라우저는 그 응답을 버리고, 처리하지 않는다.

왜 그럴까?

브라우저가 악당들로부터 우리를 지켜주기 위해서이다.

만약, 네이버에서 로그인 한후 브라우저 로컬 스토리지에 네이버에서 발급해준 인증 토큰을 저장한 상태라고 생각해보자.

그 후 어떤 악당이 보내 메일, 링크를 클릭했을 때 스크립트가 실행되어 악당이 만들어 놓은 서버에 요청을 보내고, 악당이 보낸 응답을 통해 인증 토큰을 포함하여 악당 서버에 다시 요청을 보낸다면 우리는 인증 토큰을 탈취당하여 우리 악당이 우리의 계정으로 접속할 수 있는 기회를 만들어 주게된다.

3. CORS (Cross-Origin Resource Sharing)

위에서 살펴본 브라우저가 보장해주는 SOP를 따르면 우리는 안전하게 되지만, 다른 Origin에 API 요청을 보내는 경우에 제한을 받게된다.

Info

실무 예를 들어보자. 보통 프론트엔드 개발자가 React와 같은 라이브러리를 사용해서 개발하는 경우 백엔드 서버와 별도의 프론트 서버가 존재한다.

프론트 개발을 할때, 로컬의 백엔드 서버에 연동하거나 개발 서버에 연결해서 API 연동을 하는데,프론트 서버의 URL이 http://localhost:3000이고, 백엔드 서버가 http://localhost:8080에 띄워져 있다고 하면이때 프론트 서버와 백엔드 서버는 다른 출처 (Origin)으로써 Same-Origin Policy 정책을 어긋나기 때문에,

서버로부터 응답이 넘어올 때 브라우저에서 CORS Policy 오류를 발생시킨다.

출처: https://inpa.tistory.com/entry/WEB-📚-CORS-💯-정리-해결-방법-👏[👨‍💻 Dev Scroll]

그래서 우리 브라우저는 추가 HTTP 헤더를 사용하는 CORS 통해 보다 안전하게 다른 Origin에 리소스를 접근할 수 있도록 해준다.

CORS 기본 동작과정

  • 클라이언트에서 HTTP 요청 헤더 ‘Origin’에 우리의(프론트 서버) URL의 Origin을 담아 요청을 한다.
  • 서버에서는 응답 헤더에 ‘Access-Control-Allow-Origin’에 우리가 보낸 ‘Origin’ 헤더에 값을 다시 담아서 보낸다. (와일드 카드(*)를 이용하여 모든 Origin을 허용한다고 보낼수도 있다.)
  • 브라우저에서는 우리의 요청 ‘Origin’과 서버가 보낸 ‘Access-Control-Allow-Origin’을 비교하여 일치하지 않는다면 차단하고 그 응답을 버린다.

이런 패러다임을 적용하여 최신 브라우저는 XMLHttpReques 또는 Fetch와 같은 API에서 CORS를 사용하여 교차 출처 HTTP 요청의 위험을 완화한다.

4. 예시

그렇다면 우리가 사용하는 ‘Fetch’ API를 통해 위에 CORS가 실제로 적용되고 있는지 살펴보자.

테스트를 위해 React를 이용한 Frontend, Node + express를 이용한 Backend를 구축하였다.

아래 예시는 프론트 서버 (http://localhost:3000)에서 node express server (http://localhost:8080)로 fetch API를 통해 유저 리소스를 가지고 오는 요청을 보내는 코드이다.

Frontend (React)

export async function fetchUsers() {
const endpoint = window.encodeURI("<http://localhost:8080>");
const res = await fetch(endpoint);
const { users } = await res.json();
return users;
}

크롬 콘솔의 통해 ‘CORS’ 오류가 확인된다.

이유를 확인해 보자.

  • ‘Fetch API’ 는 기본적으로 CORS를 따르도록 요청을 보냈을 때 Default로 ‘Origin’ 헤더가 정의되어 있는 것을 볼 수 있다.
  • 하지만 응답헤더에는 우리가 기대하던 ‘Access-Control-Allow-Origin’ 헤더가 보이지 않는다.

그럼 이제 우리가 만든 서버 Backend 쪽 코드를 살펴보자.

Backend (Node Express)

router.get("/", (req, res, next) => {
res.json(this.USERS);
});
  • ‘users’ 라우터에 ‘GET’ method 로 요청이 왔을 때 해더를 추가로 설정해 주지 않고 단순히 유저정보를 응답해주는 것을 볼 수 있다.

이제 이 문제를 해결해보자.

CORS 패키지를 사용해주어도 되지만, 지금은 직접 내가 해더를 정의해주도록 하자.

router.get("/", (req, res, next) => {
res.set("Access-Control-Allow-Origin", req.get("Origin"));
res.json(this.USERS);
});

오! 이제는 ‘Access-Control-Allow-Origin’ 헤더가 존재하고, 응답이 정상적으로 오는 것을 확인할 수 있다.

마지막으로 다른 공식 API(YouTube)에서도 동일하게 동작하고 있는지 확인해보자.

export const searchYoutube = async (options) => {
const YOUTUBE_URL = `https://www.googleapis.com/youtube/v3/search?key=${YOUTUBE_API_KEY}&part=snippet`
const res = await fetch(YOUTUBE_URL);
const data = await res.json();
return data;
}
  • 위와 같이 나의 사이트에서 YouTube API (외부 Origin)에 요청을 보냈을 때 응답헤더에 ‘Access-Control-Allow-Origin’가 존재하고, 외부 리소스를 성공적으로 받아오는 것을 볼 수 있다.

참고 자료

--

--