Node.js, Express.js 에서 CORS 이해와 설정 방법

CORS(Cross-Origin Resource Sharing)는 웹 페이지가 다른 도메인의 리소스를 요청하는 것을 가능하게 하는 웹 보안 표준입니다.

웹 브라우저는 기본적으로 보안 상 Same-Origin Policy를 따르기 때문에, 현재 웹 페이지와 다른 출처에서 오는 리소스에 대한 요청은 제한됩니다.

이 때문에, 웹 페이지가 다른 도메인의 API를 요청하려면 CORS 설정이 필요하게 됩니다.

Node.js에서의 CORS 설정

Node.js에서 CORS 설정을 위해 'cors' 라이브러리를 사용할 수 있습니다. 먼저, 이 라이브러리를 설치해야 합니다.

Setting Cors

npm install cors

가장 기본적인 Cors 설정

모든 도메인에서 들어오는 요청을 허용하도록 설정합니다.

하지만 모든 도메인에서 들어오는 요청을 허용하게 될 경우, 문제가 발생할 수 있기 때문에 세부 설정을 진행해야합니다.

문제란, 다른 도메인에서 리소스를 요청하는 경우를 얘기합니다.

use cors

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors());

CORS 초급 설정

보다 세부적인 설정을 하려면, 'cors' 함수에 옵션 객체를 전달할 수 있습니다.

origin' 속성에 허용하고 싶은 도메인을 지정할 수 있습니다. 또한, 'credentials' 속성을 'true'로 설정하면, 이 도메인에서의 요청이 쿠키와 같은 자격 증명을 포함하도록 허용합니다.

이렇게 설정하면, 브라우저가 서버에 요청을 보낼 때 'Access-Control-Allow-Origin'과 'Access-Control-Allow-Credentials' 헤더가 추가되어, CORS 정책을 준수하게 됩니다.

이상으로 Node.js에서 CORS를 설정하는 방법에 대해 알아봤습니다. 이 기능을 활용하면, 안전하게 다른 도메인의 리소스를 요청하고 사용할 수 있습니다.

basic cors setting

app.use(
  cors({
    origin: 'http://localhost:3300',
    credentials: true,
  }),
);

Cors 세부 설정

  1. origin: 클라이언트 사이트의 URL을 지정합니다. 이 경우, http://localhost:3300에서 오는 요청만 허용됩니다.
  2. methods: 허용할 HTTP 메서드를 지정합니다. 이 경우, GET과 POST 메서드를 사용하는 요청만 허용됩니다.
  3. allowedHeaders: 서버가 수락할 수 있는 요청 헤더를 지정합니다. 이 경우, 요청 헤더에 Content-Type과 Authorization이 포함되어야 합니다. 예시 보기
  4. credentials: 응답 헤더에 Access-Control-Allow-Credentials를 추가합니다. 이 경우, 요청이 쿠키 및 인증 정보를 포함할 수 있습니다. 예시 보기
  5. maxAge: 사전 전달 요청의 결과를 캐시할 시간을 초 단위로 지정합니다. 이 경우, 사전 전달 요청의 결과는 600초 동안 캐시됩니다. 자세히 알아보기

detail cors setting

const express = require('express');
const cors = require('cors');
const app = express();

app.use(
  cors({
    origin: 'http://localhost:3300',
    methods: ['GET', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
    maxAge: 600,
  }),
);

allowedHeaders 예시

try문 내부에 headers 설정을 확인할 수 있습니다.

allowedHeaders

async function sendData() {
  try {
    const response = await fetch('http://localhost:3300/sample', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'token sample',
      },
      body: JSON.stringify({
        title: 'title',
        description: 'description',
      }),
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const data = await response.json()
    console.log(data)
  } catch (error) {
    console.error('Error:', error)
  }
}

sendData()

credentials 예시

credentials: 'include' 설정하면 클라이언트는 쿠키를 요청에 포함시킬 수 있습니다.

반대로 'omit'으로 설정하면, 클라이언트는 쿠키를 요청에 포함시키지 않습니다.

서버에서 credentials: true를 설정했다고 반드시 자격 증명을 포함해야 하는 건 아닙니다. 반대로 credentials가 false경우에는 자격 증명을 포함 할 수 없습니다.

credentials

fetch('http://localhost:3300/sample', {
  method: 'GET',
  credentials: 'include',
})

fetch('http://localhost:3300/sample', {
  method: 'GET',
  credentials: 'omit',
})

maxAge 예시

maxAge를 사용하면 동일한 요청에 대해 서버에게 불필요한 요청을 막는 시간값입니다.

즉 Preflight 요청은 실제 요청을 보내기 전에 보내지는 요청으로, 서버가 해당 요청을 처리할 수 있는지 확인하고 HTTP OPTIONS 메서드를 사용합니다.

maxAge는 캐시 시간을 뜻하고 위 예시에서 600을 입력했으니까, 10분 동안 요청에 대한 결과를 가지고 있으니, 다시 요청할거면 10분동안 요청을 보낼 수 없게 막아버립니다.

이렇게하면 동일한 요청에 대해 서버에게 불필요한 preflight 요청을 줄일 수 있습니다.

하지만 maxAge를 사용하더라도 클라이언트는 캐시를 무시하고 preflight 요청을 계속 다시 보낼 수 있습니다.