IT๊ธฐ์ดˆ

CORS๋ž€ ๋ฌด์—‡์ธ๊ฐ€ – ๋ธŒ๋ผ์šฐ์ €์—์„œ API ์š”์ฒญ์ด ๋ง‰ํžˆ๋Š” ์ด์œ 

๐Ÿฅ„ํ…Œํฌ ํ•œ ์Šคํ‘ผ 2026. 5. 31. 21:37

๋ชฉ์ฐจ

  1. ๊ฐœ๋ฐœํ•˜๋‹ค๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋ง‰ํžˆ๋Š” ๊ทธ ์˜ค๋ฅ˜
  2. ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…(Same-Origin Policy)์ด๋ž€?
  3. CORS๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
  4. CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ
  5. CORS ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•
  6. CORS ๊ด€๋ จ HTTP ํ—ค๋” ์ดํ•ดํ•˜๊ธฐ
  7. Preflight ์š”์ฒญ์ด๋ž€?
  8. ์ •๋ฆฌ ๋ฐ ๋‹ค์Œ ๋‹จ๊ณ„

1. ๊ฐœ๋ฐœํ•˜๋‹ค๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋ง‰ํžˆ๋Š” ๊ทธ ์˜ค๋ฅ˜

์›น ๊ฐœ๋ฐœ์„ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋ฉด ๋ฐ˜๋“œ์‹œ ํ•œ ๋ฒˆ์€ ๋งˆ์ฃผ์น˜๋Š” ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๋ฉด์„œ API ์š”์ฒญ์ด ๋ง‰ํžˆ๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.

Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy.

์ด ์˜ค๋ฅ˜๋ฅผ ์ฒ˜์Œ ๋ณด๋ฉด ๋ฌด์Šจ ์˜๋ฏธ์ธ์ง€, ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š”์ง€ ๋‹นํ™ฉ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” CORS๊ฐ€ ์™œ ์กด์žฌํ•˜๋Š”์ง€, ์˜ค๋ฅ˜๊ฐ€ ์™œ ๋ฐœ์ƒํ•˜๋Š”์ง€, ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š”์ง€๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…๋“œ๋ฆฝ๋‹ˆ๋‹ค.

2. ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…(Same-Origin Policy)์ด๋ž€?

CORS๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € **๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…(Same-Origin Policy)**์„ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €๋Š” ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜์—์„œ ์˜จ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ถœ์ฒ˜(Origin)๋Š” ํ”„๋กœํ† ์ฝœ + ๋„๋ฉ”์ธ + ํฌํŠธ์˜ ์กฐํ•ฉ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด http://localhost:3000๊ณผ http://localhost:8080์€ ํฌํŠธ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜์ž…๋‹ˆ๋‹ค. http://example.com๊ณผ https://example.com์€ ํ”„๋กœํ† ์ฝœ์ด ๋‹ฌ๋ผ ๋‹ค๋ฅธ ์ถœ์ฒ˜์ž…๋‹ˆ๋‹ค.

์ด ์ •์ฑ…์ด ์กด์žฌํ•˜๋Š” ์ด์œ ๋Š” ์•…์˜์ ์ธ ์‚ฌ์ดํŠธ๊ฐ€ ์‚ฌ์šฉ์ž ๋ชจ๋ฅด๊ฒŒ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์˜ API๋ฅผ ํ˜ธ์ถœํ•ด ๊ฐœ์ธ์ •๋ณด๋ฅผ ํƒˆ์ทจํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

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

**CORS(Cross-Origin Resource Sharing)**๋Š” ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์˜ ์˜ˆ์™ธ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ "ํŠน์ • ์ถœ์ฒ˜์—์„œ ์˜ค๋Š” ์š”์ฒญ์€ ํ—ˆ์šฉํ•œ๋‹ค"๊ณ  ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ์•Œ๋ ค์ฃผ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ฆ‰, CORS๋Š” ์„œ๋ฒ„๊ฐ€ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญ์„ ๋ง‰๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„๊ฐ€ ํ—ˆ์šฉ ์ถœ์ฒ˜๋ฅผ ๋ช…์‹œํ•ด์ฃผ์ง€ ์•Š์•„์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

4. CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ

๊ฐ€์žฅ ํ”ํ•œ CORS ์˜ค๋ฅ˜ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ http://localhost:3000์—์„œ ์‹คํ–‰ ์ค‘์ธ๋ฐ, ๋ฐฑ์—”๋“œ API๊ฐ€ http://localhost:8080์— ์žˆ์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ ๋ฐฐํฌ ์ฃผ์†Œ๊ฐ€ https://myblog.com์ธ๋ฐ, API ์„œ๋ฒ„๋Š” https://api.myblog.com์— ์žˆ์„ ๋•Œ๋„ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ API๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•  ๋•Œ ํ•ด๋‹น API ์„œ๋ฒ„๊ฐ€ CORS๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

5. CORS ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•

CORS ์˜ค๋ฅ˜๋Š” ์„œ๋ฒ„ ์ชฝ์—์„œ ํ—ˆ์šฉ ์ถœ์ฒ˜๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

Node.js (Express) ์„œ๋ฒ„์—์„œ CORS ์„ค์ • cors ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋ฏธ๋“ค์›จ์–ด๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

npm install cors
 
const cors = require('cors');
app.use(cors({ origin: 'http://localhost:3000' }));

๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด origin: '*'์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ, ์‹ค์„œ๋น„์Šค์—์„œ ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ณด์•ˆ์ƒ ์ข‹์ง€ ์•Š์œผ๋ฏ€๋กœ ํ—ˆ์šฉํ•  ์ถœ์ฒ˜๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Nginx์—์„œ CORS ํ—ค๋” ์ถ”๊ฐ€ Nginx ์„ค์ • ํŒŒ์ผ์˜ location ๋ธ”๋ก์— ์•„๋ž˜ ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

add_header 'Access-Control-Allow-Origin' 'https://myblog.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

Spring Boot์—์„œ CORS ์„ค์ • ์ปจํŠธ๋กค๋Ÿฌ๋‚˜ ์„ค์ • ํด๋ž˜์Šค์—์„œ @CrossOrigin ์–ด๋…ธํ…Œ์ด์…˜ ๋˜๋Š” WebMvcConfigurer๋ฅผ ์‚ฌ์šฉํ•ด ํ—ˆ์šฉ ์ถœ์ฒ˜๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

6. CORS ๊ด€๋ จ HTTP ํ—ค๋” ์ดํ•ดํ•˜๊ธฐ

CORS๋Š” HTTP ํ—ค๋”๋ฅผ ํ†ตํ•ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์š” ํ—ค๋”๋ฅผ ์•Œ์•„๋‘๋ฉด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ•ด์„ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ ์‘๋‹ต์— ํฌํ•จํ•˜๋Š” ํ—ค๋”

Access-Control-Allow-Origin์€ ์–ด๋–ค ์ถœ์ฒ˜์—์„œ ์˜ค๋Š” ์š”์ฒญ์„ ํ—ˆ์šฉํ• ์ง€ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. https://myblog.com ์ฒ˜๋Ÿผ ํŠน์ • ๋„๋ฉ”์ธ์„ ์ง€์ •ํ•˜๊ฑฐ๋‚˜, *๋กœ ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Access-Control-Allow-Methods๋Š” ํ—ˆ์šฉํ•˜๋Š” HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. GET, POST, PUT, DELETE ์ฒ˜๋Ÿผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

Access-Control-Allow-Headers๋Š” ์š”์ฒญ์— ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ํ—ค๋”๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. Authorization, Content-Type ์ฒ˜๋Ÿผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

Access-Control-Max-Age๋Š” Preflight ์š”์ฒญ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•˜๋Š” ์‹œ๊ฐ„(์ดˆ)์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

7. Preflight ์š”์ฒญ์ด๋ž€?

๋ธŒ๋ผ์šฐ์ €๋Š” ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์„œ๋ฒ„๊ฐ€ ํ•ด๋‹น ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๋Š”์ง€ ๋จผ์ € ํ™•์ธํ•˜๋Š” Preflight ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Preflight ์š”์ฒญ์€ OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์ „์†ก๋˜๋ฉฐ, ์„œ๋ฒ„๊ฐ€ ํ•ด๋‹น ์ถœ์ฒ˜์™€ ๋ฉ”์„œ๋“œ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค๊ณ  ์‘๋‹ตํ•˜๋ฉด ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

Preflight ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ์กฐ๊ฑด์€ GET, POST, HEAD ์ด์™ธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, Content-Type์ด application/json์ธ ๊ฒฝ์šฐ, ์ปค์Šคํ…€ ํ—ค๋”(์˜ˆ: Authorization)๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„์—์„œ OPTIONS ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด Preflight ๋‹จ๊ณ„์—์„œ CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

8. ์ •๋ฆฌ ๋ฐ ๋‹ค์Œ ๋‹จ๊ณ„

์˜ค๋Š˜ ๋ฐฐ์šด ํ•ต์‹ฌ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ… ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค ์š”์ฒญ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.
  • CORS๋Š” ์„œ๋ฒ„๊ฐ€ ํŠน์ • ์ถœ์ฒ˜์˜ ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๋„๋ก ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ ค์ฃผ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.
  • CORS ์˜ค๋ฅ˜๋Š” ์„œ๋ฒ„์—์„œ Access-Control-Allow-Origin ํ—ค๋”๋ฅผ ์„ค์ •ํ•ด ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
  • CORS ์˜ค๋ฅ˜๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋ฉฐ, Postman ๊ฐ™์€ ๋„๊ตฌ์—์„œ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์‹ค์„œ๋น„์Šค์—์„œ๋Š” ๋ชจ๋“  ์ถœ์ฒ˜(*)๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ๋ง๊ณ  ํ•„์š”ํ•œ ๋„๋ฉ”์ธ๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๊ธ€์—์„œ๋Š” ์›น์†Œ์ผ“(WebSocket)์ด๋ž€ ๋ฌด์—‡์ธ์ง€, ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…์ด ๋˜๋Š” ์›๋ฆฌ๋ฅผ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.