티스토리 뷰

프론트엔드로서 백엔드와 처음 협업할 때 가장 무서워했던 게 바로 CORS 에러였다. 

뭔진 모르겠지만 우선 빨간색에 API요청이 안되는 게 혼란 그 자체였는데 MERN 프로젝트를 진행하면서 백엔드의 cors 라이브러리를 사용해보게 됐다. 

 

CORS Cross-Origin Resurece Sharing의 약자로, 자신이 속하지 않은 다른 도메인/ 프로토콜/ 포트에 있는 리소스를 요청하는 cross-origin HTTP 요청방식이다. 

중요한 점은 서버는 기본적으로 CORS 방식을 제한해둔다. 왜냐하면 특정 서버 리소스에 다른 임의의 웹 사이트들이 request를 보낼 수 있다면 악의적으로 특정 서버의 세션을 탈취하거나 서버에 무리가 가는 행위를 일으킬 수 있기 때문이다. 그렇다면 서버가 직접 특정 도메인들만 허용하는 방식이나 아예 퍼블릭으로 리소스에 접근할 수 있게 CORS를 허용하려면 어떻게 해야할까

 

cors 라이브러리

node.js 에서는 express에서 관리하는 cors라는 라이브러리가 존재한다. 자세한 내용은 express에서 확인해볼 수 있다. 

노드 패키지 매니저(yarn or npm)을 통해서 cors를 내 프로젝트에 설치해주고 cors를 require해와 app.use(cors())를 해주면 모두에게 오픈된다(퍼블릭). 

 

1. 모두에게 허용

const express = require("express");
const app = express();
const path = require("path");
const { logger } = require("./middleware/logger");
const errorHandler = require("./middleware/errorHandler");
const cookieParser = require("cookie-parser");
const cors = require("cors");

const PORT = process.env.PORT || 3500;

app.use(logger);
//퍼블릭에서도 API접근이 가능하게 하기위해서
app.use(cors());

2. 특정 도메인에만 허용

corsOprions 변수에 자신이 허용할 도메인을 추가하고, app.use(cors(corsOptions))

cors함수의 인자로 세팅한 corsOptions를 넘겨준다. 옵션으로 설정한 도메인만 제한없이 서버에 요청을 보내고 응답을 받을 수 있다. 

let corsOptions = {
    origin: 'https://www.domain.com',
    credentials: true
}

app.use(cors(corsOptions));

프리플라이트라는 걸 많이 보긴했는데 이게 뭔지 궁금해서 찾아보게 됐다. (크롬 개발자 네트워크 탭에서..)

🤔프리플라이트 개념

네트워크 요청에서 프리플라이트는 요청을 보내기 전에 사전에 계획없이 요청을 보낼 수 있는 기능을 의미한다. 

일반적으로 비동기적으로 작동하며, JS의 XMLHttpRequest 객체 또는 fetch API를 사용하여 구현된다.(axios도 가능하겠죠)

 

+ CORS 상황에서 보안을 확인하기 위해 브라우저가 제공하는 기능이기도 하다(크롬의 경우 기본설정).

프리플라이트는 미리 통신을 함으로써 문제가 있는 요청에 대해 일부러 에러를 발생시킨다.  

🌝프리플라이트 방식

Cross-Origin HTTP 요청이 보안 상의 이유로 허용되는지 확인하기 위해 브라우저가 서버에 사전 요청을 보내는 것을 의미한다. 이를 통해 서버가 요청을 수락할 수 있는지 여부를 브라우저가 확인하고, 요청이 안전한지 확인한다. 이러한 사전 요청은 HTTP OPTIONS 메서드를 사용하여 수행된다. 

CORS 기능과 같이 사용하여 서로 다른 도메인간의 안전한 통신을 가능하게 한다. 브라우저에서 실행되며 요청을 보내는 도메인과 응답을 받는 도메인이 다를 경우 프리플라이트 방식으로 요청을 검증하고 서버가 CORS 정책을 지원하는 지 확인한다. 

프리플라이트 방식은 요청을 처리하는 데 추가 시간과 리소스가 소비되기 때문에, 가능한 경우 간단한 요청에 대해서는 프리플라이트 방식을 사용하지 않는 것이 좋다. 대부분의 요청은 단순한 GET, POST 등의 메서드로 수행될 수 있으며, 이러한 요청은 프리플라이트 방식이 없이 처리될 수 있다.

 

🤸‍♀️프리플라이트가 일어나는 상황

1. OPTIONS 

브라우저에서 옵션즈를 던져 해당 사이트에서 사용 가능한 methods 정보를 가져오게 될 때 프리플라이트가 일어난다. 

2. 단순 요청

사용자 정의 Header 정보를 추가, 수정하게 되면 단순 요청에 프리플라이트가 발생한다.

예외적으로 사용자 정의 Header가 Content-type일 때 type이 text/plain, multipart/form-data, x-www-form-urlencoded 일 경우엔 preflight가 일어나지 않는다.

*사용자 정의 Header : Accept, Accept-language, content-language, content-type

* Simple Request : GET, POST, HEAD

3.단순 요청을 제외한 나머지 요청

4. 쿠키 세팅

쿠키를 다른 써드 파티에 보내고 싶을 때, with Credential 을 이용하게 되는데 이때 프리플라이트가 발생한다.

 

출처: https://yoonlangcow.tistory.com/44

 

preflight가 발생하는 4가지

웹에서 preflight가 발생하는 4가지는 무엇이 있는지 알아보도록 하겠습니다 : ) preflight란 CORS상황에서 보안을 확인하기 위해 브라우저가 제공하는 기능입니다. preflight는 미리 통신을 함으로써 문

yoonlangcow.tistory.com

 

Q. 크롬에서 프리플라이트 방식은 기본값인가?

그렇다. 보안상의 이유로 보안 정책이 프리플라이트 방식으로 설정되어 있다. 

다른 도메인에서의 요청이나 AJAX 호출 등의 일부 HTTP 요청에 대한 추가적인 보안 검사를 수행하여 보안을 강화하는 것이다. 실제 HTTP 요청을 보내기 전에 브라우저에서는 적합성 여부를 다시 확인하고 결정한다. 

 

Q. 프리플라이트는 기본 설정? 

기본적으로 설정되어있지는 않고 개발자가 요청을 언제 보내고 어떤 자원을 미리가져올 지 결정해야 한다. 

일반적으로 프리플라이트 요청을 수행하려면 먼저 자원을 불러와야 한다. 예를들어, JS파일을 가져오는 프리플라이트 요청을 수행하려면, 먼저 HTML 파일에 script 태그를 추가하고 src 속성에 JS 파일의 경로를 지정해야 한다. 

프리플라이트 요청은 일반적으로 브라우저에서 사용되며, 일부 브라우저에서는 프리플라이트를 자동으로 처리하는 기능을 제공한다. (브라우저마다 다르게 구현되어 있으므로 적절히 고려해야 함)

 

Q. 프리플라이트 요청은 클라이언트? 백엔드 설정? 

일반적으로 클라이언트(브라우저)에서 수행된다. 즉, 프리플라이트 요청을 설정하는 것은 클라이언트의 역할이다. 

하지만, 백엔드에서 CORS 정책을 설정하여 프리플라이트 요청을 허용할 수 있다.

따라서 프리플라이트 요청을 설정하려면 클라이언트에서 요청을 보내기전에 백엔드에서 CORS 정책을 설정해야 한다.

-> 요청은 클라이언트지만 백엔드의 허용이 필요하기 때문에 양쪽에서 설정이 필요하다.

 

Q. 클라이언트에서 프리플라이트 요청 세팅은 어떻게 하나?

http 요청 headers에 `Origin` 헤더를 추가해주면 된다. Origin의 값으로는 요청을 보내는 클라이언트의 도메인 주소가 들어간다. 추가적으로 http와 https가 둘 다 존재할 경우, 서로 다른 프로토콜이므로 나머지 주소가 같더라도 서버에서는 다른 도메인으로 인식한다. (최신 브라우저에서 동작하는 방식이며 위에서 말한 HTTP OPTIONS 메서드는 이전에 사용하던 방식이다)

 

댓글