๐ŸŒ CORS ์ •์ฑ…, ๋ชจ๋“  ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ผญ ์•Œ์•„์•ผ ํ•  ๊ฒƒ

๋ฐœํ–‰: (2025๋…„ 12์›” 18์ผ ์˜ค์ „ 12:34 GMT+9)
4 ๋ถ„ ์†Œ์š”
์›๋ฌธ: Dev.to

Source: Dev.to

Cover image for ๐ŸŒ CORS Policies Every Backend Developer Must Know

CORS๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

CORS๋Š” ํ•œ ์ถœ์ฒ˜(๋„๋ฉ”์ธ)์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‹ค๋ฅธ ์ถœ์ฒ˜์— ์˜ํ•ด ์–ด๋–ป๊ฒŒ ์ ‘๊ทผ๋  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ œ์–ดํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.

Important: CORS๋Š” ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ๊ฐ•์ œ ์ ์šฉ๋˜์ง€๋งŒ, ์„ค์ •์€ ๋ฐฑ์—”๋“œ์—์„œ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ํ”ํ•œ CORS ์‹ค์ˆ˜

Access-Control-Allow-Origin: *

์ด ํ—ค๋”๋Š” ๋ชจ๋“  ์›น์‚ฌ์ดํŠธ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์˜ API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. API๊ฐ€ ์ฟ ํ‚ค, ํ† ํฐ, ์„ธ์…˜ ๋“ฑ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ด๋Š” ์‹ฌ๊ฐํ•œ ๋ณด์•ˆ ์œ„ํ—˜์ด ๋ฉ๋‹ˆ๋‹ค.

์•ˆ์ „ํ•œ CORS ์„ค์ •์„ ์œ„ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€

1๏ธโƒฃ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ถœ์ฒ˜ ํ—ˆ์šฉ ๋ฆฌ์ŠคํŠธ ์‚ฌ์šฉ

์•Œ๋ ค์ง„ ๋„๋ฉ”์ธ๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค:

const allowedOrigins = [
  'https://app.example.com',
  'https://admin.example.com'
];

2๏ธโƒฃ ์ž๊ฒฉ ์ฆ๋ช…๊ณผ ํ•จ๊ป˜ ์™€์ผ๋“œ์นด๋“œ ์‚ฌ์šฉ ๊ธˆ์ง€

์ฟ ํ‚ค, ํ—ค๋”์— ํฌํ•จ๋œ JWT, ์„ธ์…˜ ๋“ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ •ํ™•ํ•œ ์ถœ์ฒ˜๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com

Access-Control-Allow-Credentials๊ฐ€ true์ผ ๊ฒฝ์šฐ *๋Š” ํ—ˆ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ํ—ˆ์šฉํ•  HTTP ๋ฉ”์„œ๋“œ ์ œํ•œ

API๊ฐ€ ์‹ค์ œ๋กœ ํ•„์š”๋กœ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

๋ถˆํ•„์š”ํ•˜๊ฒŒ PATCH๋‚˜ OPTIONS์™€ ๊ฐ™์€ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋…ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”.

4๏ธโƒฃ ํ—ˆ์šฉ ํ—ค๋” ์ œํ•œ

์š”์ฒญ ํ—ค๋” ์ค‘ ํ—ˆ์šฉํ•  ํ•ญ๋ชฉ์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค:

Access-Control-Allow-Headers: Content-Type, Authorization

5๏ธโƒฃ ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ(OPTIONS) ์š”์ฒญ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ

๋ธŒ๋ผ์šฐ์ €๋Š” ํŠน์ • API ํ˜ธ์ถœ ์ „์— OPTIONS ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

๋ชจ๋ฒ” ์‚ฌ๋ก€:

  • ๋น ๋ฅด๊ฒŒ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: 204 No Content).
  • ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ ์‘๋‹ต์— ์ธ์ฆ์„ ์š”๊ตฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ ์ ˆํ•œ CORS ํ—ค๋”๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

6๏ธโƒฃ ํ™˜๊ฒฝ๋ณ„ CORS ๊ทœ์น™

ํ™˜๊ฒฝ์ •์ฑ…
Developmentlocalhost ํ—ˆ์šฉ
Stagingํ…Œ์ŠคํŠธ ๋„๋ฉ”์ธ ํ—ˆ์šฉ
Production์—„๊ฒฉํ•œ ํ—ˆ์šฉ ๋ฆฌ์ŠคํŠธ ์ ์šฉ

์ด ์ ‘๊ทผ๋ฒ•์€ ๊ฐœ๋ฐœ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•˜๋ฉด์„œ๋„ ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๋ณด์•ˆ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

7๏ธโƒฃ CORS๋ฅผ ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ ์˜ค์ธํ•˜์ง€ ๋ง ๊ฒƒ

CORS๋Š” ๋‹ค์Œ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

  • ์ธ์ฆ
  • ์ธ๊ฐ€
  • ์†๋„ ์ œํ•œ
  • ์ž…๋ ฅ ๊ฒ€์ฆ

๊ณต๊ฒฉ์ž๋Š” ์—ฌ์ „ํžˆ Postman์ด๋‚˜ curl ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด API๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ: ์•ˆ์ „ํ•œ Node.js CORS ์„ค์ •

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

app.use(cors({
  origin: ['https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // seconds
}));

๋ฉด์ ‘ ํŒ

Q: CORS๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์ธ๊ฐ€์š”, ๋ฐฑ์—”๋“œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์ธ๊ฐ€์š”?
A: CORS๋Š” ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ๊ฐ•์ œ ์ ์šฉ๋˜์ง€๋งŒ, ์„ค์ •์€ ๋ฐฑ์—”๋“œ์—์„œ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ ์ƒ๊ฐ

ํ›Œ๋ฅญํ•œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋Š”:

  • ์ตœ์†Œ ๊ถŒํ•œ ์›์น™์— ๋งž๋Š” CORS ์„ค์ •์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์™€์ผ๋“œ์นด๋“œ๋ฅผ ํ”ผํ•ฉ๋‹ˆ๋‹ค.
  • ํ™˜๊ฒฝ๋ณ„ ์„ค์ •์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
  • CORS๊ฐ€ ์‹ค์ œ ๋ณด์•ˆ ์ œ์–ด๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

์˜ฌ๋ฐ”๋ฅธ CORS ์„ค์ •์€ ์ „๋ฌธ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์˜ ์„ฑ์ˆ™๋„๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

Express.js์™€ Node.js๋ฅผ ์‚ฌ์šฉํ•œ ๊ฐ„๋‹จํ•œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ ๋ฐ ์‹ค์šฉ์ ์ธ ํ”„๋กœ์ ํŠธ

๊ฐ„๋‹จํ•œ Node.js ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ ์—ฌ๋Ÿฌ๋ถ„, ์•ˆ๋…•ํ•˜์„ธ์š”. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์— ์ง„์ž…ํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๋Š” ๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ด ...

Bulk redirects UI, API ๋ฐ CLI๊ฐ€ ์ด์ œ ์ผ๋ฐ˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค

Vercel ์‚ฌ์šฉ์ž๋Š” ์ด์ œ UI, API ๋˜๋Š” CLI๋ฅผ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด ๋ฐฐํฌ ์—†์ด ๋Œ€๋Ÿ‰ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Vercel์€ ํ”„๋กœ์ ํŠธ๋‹น ์ตœ๋Œ€ ๋ฐฑ๋งŒ ๊ฐœ์˜ static URL redirects๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค....

์ธ๋„ ๋„์‹œ๋ฅผ ์œ„ํ•œ ๋ฉ”ํŠธ๋กœ ๊ฒฝ๋กœ ํ”Œ๋ž˜๋„ˆ ์›น์‚ฌ์ดํŠธ ๊ตฌ์ถ•

์†Œ๊ฐœ: ์ธ๋„์—์„œ MetroYatra ๋˜๋Š” โ€œmetro travelโ€์€ ๋น ๋ฅด๊ณ  ์ €๋ ดํ•˜๋ฉฐ ๋„๋ฆฌ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค โ€” ํ•˜์ง€๋งŒ ์—ฌํ–‰์„ ๊ณ„ํšํ•˜๋Š” ๊ฒƒ์ด ํ•ญ์ƒ ๊ฐ„๋‹จํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋…ธ์„ ์ด ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค,โ€ฆ