CORS(Cross-Origin Resource Sharing)

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ณด์•ˆ์„ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…(SOP)์„ ๋”ฐ๋ฅด์ง€๋งŒ, ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ •๋‹นํ•œ ์ƒํ™ฉ(API ํ˜ธ์ถœ ๋“ฑ)์„ ์œ„ํ•ด ๊ณ ์•ˆ๋œ ์˜ˆ์™ธ ์กฐํ•ญ์ด์ž ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค.

Same-Origin Policy(๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…)

๋ธŒ๋ผ์šฐ์ €๋Š” ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‹œ์ž‘๋œ ๊ต์ฐจ ์ถœ์ฒ˜ HTTP ์š”์ฒญ์„ ์ œํ•œํ•˜๋Š” Same-Origin Policy(SOP) ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค.

์ถœ์ฒ˜(Origin)์˜ ์ •์˜

์ถœ์ฒ˜๋Š” ์•„๋ž˜์˜ ์š”์†Œ๋“ค๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ์ด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋‹ค๋ฅด๋ฉด ๋‹ค๋ฅธ ์ถœ์ฒ˜(Cross-Origin)๋กœ ๊ฐ„์ฃผ๋œ๋‹ค.

  • ๋‹ค๋ฅธ ๋„๋ฉ”์ธ (example.com - test.com)

  • ๋‹ค๋ฅธ ํ•˜์œ„ ๋„๋ฉ”์ธ (example.com - store.example.com)

  • ๋‹ค๋ฅธ ํฌํŠธ (example.com:80 - example.com:90)

  • ๋‹ค๋ฅธ ํ”„๋กœํ† ์ฝœ (https://example.com - http://example.com)

์—ฌ๊ธฐ์„œ https://example.com๊ณผ https://www.example.com๋„ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์œผ๋กœ ๊ฐ„์ฃผ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.

์˜ˆ์‹œ(๊ธฐ์ค€ http://store.ogufamily.com)

URL
Result
Reason

http://store.ogufamily.com/babyogu.html

same origin

path๋งŒ ๋‹ค๋ฅธ๊ฒฝ์šฐ

http://store.ogufamily.com/ogu.html

same origin

path๋งŒ ๋‹ค๋ฅธ๊ฒฝ์šฐ

https://store.ogufamily.com

cross origin

ํ”„๋กœํ† ์ฝœ์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ

http://store.ogufamily.com:81

cross origin

ํฌํŠธ๊ฐ€ ๋‹ค๋ฅธ๊ฒฝ์šฐ

http://news.ogufamily.com

cross origin

๋„๋ฉ”์ธ์ด ๋‹ค๋ฅธ๊ฒฝ์šฐ

CORS ๋™์ž‘ ์‹œ๋‚˜๋ฆฌ์˜ค

CORS ์š”์ฒญ์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ์š”์ฒญ์˜ ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋‚˜๋‰œ๋‹ค.

1. Simple Request (๋‹จ์ˆœ ์š”์ฒญ)

์˜ˆ๋น„ ์š”์ฒญ(Preflight) ์—†์ด ๋ฐ”๋กœ ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์ด๋‹ค. ์•„๋ž˜ ์กฐ๊ฑด์„ ๋ชจ๋‘ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.

  • ๋ฉ”์„œ๋“œ: GET, HEAD, POST ์ค‘ ํ•˜๋‚˜

  • ํ—ค๋”: Accept, Accept-Language, Content-Language, Content-Type ๋“ฑ ํ—ˆ์šฉ๋œ ํ—ค๋”๋งŒ ์‚ฌ์šฉ

  • Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain ์ค‘ ํ•˜๋‚˜

ํ•˜์ง€๋งŒ ์ตœ์‹  ์›น API๋Š” ๋Œ€๋ถ€๋ถ„ application/json์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋‹จ์ˆœ ์š”์ฒญ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•ด Preflight๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

2. Preflight Request(์˜ˆ๋น„ ์š”์ฒญ)

๋‹จ์ˆœ ์š”์ฒญ์˜ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•  ๊ฒฝ์šฐ(์˜ˆ: Content-Type: application/json ๋˜๋Š” ์ปค์Šคํ…€ ํ—ค๋” ์‚ฌ์šฉ), ๋ธŒ๋ผ์šฐ์ €๋Š” ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์•ˆ์ „ํ•œ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์˜ˆ๋น„ ์š”์ฒญ์„ ๋จผ์ € ๋ณด๋‚ธ๋‹ค.

  1. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ ์ •๋ณด(Origin, Method, Header)๋ฅผ ๋‹ด์•„ ์„œ๋ฒ„์— ์ „์†ก

  2. ์„œ๋ฒ„๋Š” ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ๋‹ด์€ ํ—ค๋”(Access-Control-Allow-*)์™€ ํ•จ๊ป˜ ์‘๋‹ต

  3. ๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋ฒ„ ์‘๋‹ต์„ ํ™•์ธํ•˜๊ณ  ์•ˆ์ „ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด ์‹ค์ œ ์š”์ฒญ์„ ์ „์†ก

  4. ๋ธŒ๋ผ์šฐ์ €๋Š” Preflight ๊ฒฐ๊ณผ๋ฅผ Access-Control-Max-Age ์‹œ๊ฐ„๋งŒํผ ์บ์‹ฑํ•˜์—ฌ ๋งค๋ฒˆ ์˜ˆ๋น„ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š๋„๋ก ์ตœ์ ํ™”

3. Credentialed Request (์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ)

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฟ ํ‚ค๋‚˜ ์ธ์ฆ ํ—ค๋”(Authorization)๋ฅผ ํฌํ•จํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์„ค์ •: credentials: 'include' (fetch API) ๋˜๋Š” withCredentials = true (axios/XHR) ์„ค์ • ํ•„์š”

  • ์„œ๋ฒ„ ์„ค์ • ์ œ์•ฝ ์‚ฌํ•ญ

    • Access-Control-Allow-Origin์—๋Š” ์™€์ผ๋“œ์นด๋“œ(-)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ๋ฐ˜๋“œ์‹œ ๊ตฌ์ฒด์ ์ธ Origin(์˜ˆ: http://localhost:3000) ๋ช…์‹œ ํ•„์š”

    • Access-Control-Allow-Credentials: true ํ—ค๋” ์ถ”๊ฐ€ ํ•„์š”

์ฃผ์š” ์‘๋‹ต ํ—ค๋”

์„œ๋ฒ„๋Š” ๋‹ค์Œ ํ—ค๋”๋“ค์„ ํ†ตํ•ด CORS ์ •์ฑ…์„ ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ฆฐ๋‹ค.

  • Access-Control-Allow-Origin: ์š”์ฒญ์„ ํ—ˆ์šฉํ•  ์ถœ์ฒ˜. ๋‹จ์ผ ๋„๋ฉ”์ธ ๋˜๋Š” -

  • Access-Control-Allow-Methods: ํ—ˆ์šฉํ•  HTTP ๋ฉ”์„œ๋“œ (GET, POST, PUT, OPTIONS ๋“ฑ)

  • Access-Control-Allow-Headers: ํ—ˆ์šฉํ•  ์ปค์Šคํ…€ ํ—ค๋”

  • Access-Control-Allow-Credentials: ์ฟ ํ‚ค ๋“ฑ ์ธ์ฆ ์ •๋ณด ํ—ˆ์šฉ ์—ฌ๋ถ€

  • Access-Control-Max-Age: Preflight ์š”์ฒญ์˜ ์บ์‹œ ์‹œ๊ฐ„(์ดˆ)

์ฐธ๊ณ ์ž๋ฃŒ

Last updated

Was this helpful?