๊ฐœ๋ฐœ ์ง€์‹/WEB ์ง€์‹

๐ŸŒ ์•…๋ช… ๋†’์€ CORS ๊ฐœ๋… & ํ•ด๊ฒฐ๋ฒ• - ์ •๋ฆฌ ๋ํŒ์™• ๐Ÿ‘

์ธํŒŒ_ 2022. 11. 28. 09:34

cors-์—๋Ÿฌ-์ •๋ฆฌ

 

์•…๋ช… ๋†’์€ CORS ์—๋Ÿฌ ๋ฉ”์„ธ์ง€

์›น ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋ฉด ๋ฐ˜๋“œ์‹œ ๋งˆ์ฃผ์น˜๋Š” ๋ฉ๋ฉ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐ”๋กœ CORS ์ด๋‹ค. ์›น ๊ฐœ๋ฐœ์˜ ์‹ ์ž… ์‹ ๊ณ ์‹์ด๋ผ๊ณ  ํ•  ์ •๋„๋กœ, CORS๋Š” ๋ˆ„๊ตฌ๋‚˜ ํ•œ ๋ฒˆ ์ •๋„๋Š” ๊ฒช๊ฒŒ ๋œ๋‹ค๊ณ  ํ•ด๋„ ๊ณผ์–ธ์ด ์•„๋‹ˆ๋‹ค.

cors-์—๋Ÿฌ-์ •๋ฆฌ

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„  ์š”์ฒญ ์ฝ”๋“œ๋ฅผ ์ด์ƒํ•˜๊ฒŒ ์ ์€๊ฒƒ๋„ ์•„๋‹ˆ๊ณ , ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„  ์„œ๋ฒ„ ์ฝ”๋“œ๋‚˜ ์„ธํŒ…์ด ์ด์ƒํ•œ๊ฒƒ๋„ ์•„๋‹ˆ๋‹ค. ๋ชจ๋“ ๊ฒŒ ๋ฉ€์ฉกํ•œ๋ฐ ์™œ ์š”์ฒญํ•œ ์ž๋ฃŒ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์‹œ๋ป˜๊ฑด ์—๋Ÿฌ์ค„๋กœ ํ™•๋‹ตํ•˜๋Š”๊ฒŒ ๋ฌธ์ œ์ด๋‹ค. ๐Ÿคฌ

cors-์—๋Ÿฌ-์ •๋ฆฌ

์ด๋Ÿฌํ•œ ํ˜„์ƒ์ด ์ผ์–ด๋‚˜๋Š” ์ด์œ ๋Š”, ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” HTTP ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ์–ด๋–ค ์š”์ฒญ์„ ํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์š”์ฒญ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ค๋ฅธ CORS ๋ฐœ์ƒ ์—ฌ๋ถ€

 

1. <img>, <video>, <script>, <link> ํƒœ๊ทธ ๋“ฑ

 → ๊ธฐ๋ณธ์ ์œผ๋กœ Cross-Origin ์ •์ฑ…์„ ์ง€์›ํ•จ

  • <link> ํƒœ๊ทธ์˜ href ์—์„œ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์˜ .css ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ
  • <img> ํƒœ๊ทธ์˜ src ์—์„œ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์˜ .png, .jpg ๋“ฑ์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ
  • <script> ํƒœ๊ทธ์˜ src ์—์„œ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์˜ .js ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ (type="module" ์†์„ฑ์€ ์ œ์™ธ)
<link rel="stylesheet" href="…" />
<script src="…"></script>
<img src="…" />

 

2. XMLHttpRequest, Fetch API ์Šคํฌ๋ฆฝํŠธ

 → ๊ธฐ๋ณธ์ ์œผ๋กœ Same-Origin ์ •์ฑ…์„ ๋”ฐ๋ฆ„

  • ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์˜ ์†Œ์Šค์— ๋Œ€ํ•ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ajax ์š”์ฒญ API ํ˜ธ์ถœ์‹œ
  • ์›น ํฐํŠธ CSS ํŒŒ์ผ ๋‚ด @font-face์—์„œ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์˜ ํฐํŠธ ์‚ฌ์šฉ ์‹œ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ ์š”์ฒญ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๋ณด์•ˆ์ƒ ์ œํ•œํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ธฐ๋ณธ์œผ๋กœ ํ•˜๋‚˜์˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ๋งŒ ํ—ˆ์šฉ๋˜๋„๋ก ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์ฃผ๋กœ ์ž์‹ ์˜ ์„œ๋ฒ„)

 

์ฒ˜์Œ ๋ณด๋Š” ์šฉ์–ด๊ฐ€ ๋‚˜์˜จ๋‹ค. Same Origin ์ •์ฑ…๊ณผ Cross Origin ์ •์ฑ…์ด๋ž€ ๋Œ€์ฒด ๋ฌด์Šจ ์ •์ฑ…(Policy)์„ ๋งํ•˜๋Š” ๊ฒƒ์ผ๊นŒ? ์ด๋Ÿฌํ•œ ์ •์ฑ…๋“ค์ด ๋ญ๊ธธ๋ž˜ ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€๋ ค์„œ ๋ฐ›๋Š” ๊ฒƒ์ผ๊นŒ? 

๋ฐ”๋กœ ์ด Same Origin / Cross Origin ์ •์ฑ…์˜ ์ •๋ณด ๋ถ€์กฑ์œผ๋กœ ์ธํ•ด ๋‚˜๋„๋ชจ๋ฅด๊ฒŒ ์ •์ฑ…์„ ์œ„๋ฐ˜ํ•˜๋Š” ํ–‰๋™์„ ํ•˜๊ฒŒ ๋˜์–ด CORS ์—๋Ÿฌ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์ด๋‹ค. 

์š”์ฒญ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ค๋ฅธ CORS ๋ฐœ์ƒ ์—ฌ๋ถ€๋ฅผ ์ข€ ๋” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์•„๋ž˜ html ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž. ๋˜‘๊ฐ™์€ ์„œ๋ฒ„ ๋„๋ฉ”์ธ์œผ๋กœ ๋ถ€ํ„ฐ check.svg ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ, ๊ฐ๊ฐ <img> ํƒœ๊ทธ์˜ src ์†์„ฑ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ajax ์š”์ฒญ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์ด๋‹ค.

<body>
    <img src="https://third-party-test.glitch.me/check.svg" alt="์ด๋ฏธ์ง€">

    <script>
        fetch('https://third-party-test.glitch.me/check.svg')
            .then(response => response.blob())
            .then(imgBlob => {
                const imageObjectURL = URL.createObjectURL(imgBlob); // ์‘๋‹ต ๋ฐ›์€ ์ด๋ฏธ์ง€๋ฅผ blob ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
                const img = document.createElement('img'); // ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ 
                img.src = imageObjectURL; // ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•œ๋’ค
                document.body.append(img); // html์— ์ถ”๊ฐ€
            })
    </script>
</body>

ํŽ˜์ด์ง€ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ํ•˜๋‚˜๋งŒ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

cors-์—๋Ÿฌ-์ •๋ฆฌ

๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ Network ์ฐฝ์„ ๋ณด๋ฉด check.svg ์ด๋ฏธ์ง€์— ๋Œ€ํ•ด์„œ ๋‘๋ฒˆ ์š”์ฒญ์€ ํ–ˆ์ง€๋งŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ fetch Type์œผ๋กœ ์š”์ฒญํ•œ ๊ฒƒ์ด Status๊ฐ€ CORS error ์ž„์„ ๋ณผ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

cors-์—๋Ÿฌ-์ •๋ฆฌ

์›น ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋ฉด ๋‹ค๋ฅธ ๋„๋ฉ”์ธ ์„œ๋ฒ„์— ์žˆ๋Š” ์ž์›์„ ์ •๋ง ์ž์ฃผ ๊ฐ€์ ธ๋‹ค ์“ฐ๊ฑฐ๋‚˜ ํ˜น์€ ์ œ๊ณตํ•ด ์ค„ ํ…๋ฐ ์ด๋Ÿฌํ•œ ๊ธฐ๋ฐ˜ ์ง€์‹์ด ์—†๋‹ค๋ฉด ๋‚˜์ค‘์— ํฐ ๊ฑธ๋ฆผ๋Œ์ด ๋˜๊ฒŒ ๋œ๋‹ค. ์ง€๊ธˆ๋ถ€ํ„ฐ ์ด ์งœ์ฆ๋‚˜๋Š” CORS๋ฅผ ๋‘๋ฒˆ ๋‹ค์‹œ ๋งˆ์ฃผ์น˜์ง€ ์•Š๋„๋ก ์™„๋ฒฝํ•˜๊ฒŒ ์ •๋ณตํ•˜๋Ÿฌ ๊ฐ€๋ณด์ž โ—


CORS ์—๋Ÿฌ ํ•œ๋ฐฉ ์ดํ•ดํ•˜๊ธฐ

CORS๋Š” ํ•จ์ถ• ๋‹จ์–ด๋กœ์จ ์ด๋ฅผ ํ’€๋ฉด Cross-Origin Resource Sharing ์ด๋ผ๋Š” ๋‹จ์–ด๋กœ ์ด๋ฃจ์–ด ์ ธ ์žˆ๋‹ค. ์ด ๋ฌธ์žฅ์„ ์ง์—ญํ•˜๋ฉด "๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  ์ •์ฑ…"์ด๋ผ๊ณ  ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๊ต์ฐจ ์ถœ์ฒ˜๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์€ (์—‡๊ฐˆ๋ฆฐ) ๋‹ค๋ฅธ ์ถœ์ฒ˜๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด๋ฉด ๋œ๋‹ค.

cors-์—๋Ÿฌ-์ •๋ฆฌ

CORS๊ฐ€ ๋ฌด์–ผ ๋œปํ•˜๋Š”์ง€ ์•Œ์•˜์œผ๋‹ˆ, ํ•œ๋ฒˆ ์šฐ๋ฆฌ๋ฅผ ๊ดด๋กญํžˆ๋Š” ์•…๋ช… ๋†’์€ CORS ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ์ฐจ๊ทผ์ฐจ๊ทผ ํ•ด์„ํ•ด๋ณด์ž.

 

Warning !

Access to fetch at ‘https://myhompage.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

'https://myhomepage.com'์—์„œ 'https://localhost:3000' ์ถœ์ฒ˜๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์•ก์„ธ์Šค๊ฐ€ CORS ์ •์ฑ…์— ์˜ํ•ด ์ฐจ๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ๋œ ๋ฆฌ์†Œ์Šค์— 'Access-Control-Allow-Origin' ํ—ค๋”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ถˆํˆฌ๋ช…ํ•œ ์‘๋‹ต์ด ํ•„์š”์— ์ ํ•ฉํ•œ ๊ฒฝ์šฐ, ์š”์ฒญ ๋ชจ๋“œ๋ฅผ 'no-cors'๋กœ ์„ค์ •ํ•˜์—ฌ CORS๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค์‹ญ์‹œ์˜ค.

 

์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ๋ถˆ์นœ์ ˆํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ, ์•„๋ฌด๋ž˜๋„ ๋ฐฐ๊ฒฝ์ง€์‹์ด ๋ถ€์กฑํ•˜๋‹ค๋ณด๋‹ˆ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ• ์ง€ ๊ฐ์ด ์•ˆ์žกํžŒ๋‹ค.

์šฐ์„  CORS์˜ ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  ์ •์ฑ…(Cross Origin Resource Sharing)์ด ์–ด๋– ํ•œ ์ •์ฑ…์ธ์ง€์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณผ ํ•„์š”์„ฑ์ด ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๊ทธ ์ „์— ์•„๊นŒ๋ถ€ํ„ฐ ์ถœ์ฒ˜, ์ถœ์ฒ˜ ๊ฑฐ๋ฆฌ๋Š”๋ฐ ์ด ์ถœ์ฒ˜(Origin)์ด ๋ฌด์—‡์ธ์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ดํŽด๋ณด์ž.


์ถœ์ฒ˜(Origin) ๋ž€?

์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ค ์‚ฌ์ดํŠธ๋ฅผ ์ ‘์†ํ• ๋•Œ ์ธํ„ฐ๋„ท ์ฃผ์†Œ์ฐฝ์— ์šฐ๋ฆฌ๋Š” URL์ด๋ผ๋Š” ๋ฌธ์ž์—ด์„ ํ†ตํ•ด ์ ‘๊ทผํ•˜๊ฒŒ ๋œ๋‹ค.

์ด์ฒ˜๋Ÿผ URL์€ https://domain.com:3000/user?query=name&page=1 ๊ณผ ๊ฐ™์ด ํ•˜๋‚˜์˜ ๋ฌธ์ž์—ด ๊ฐ™์ง€๋งŒ, ์‚ฌ์‹ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

cors-origin

  • Protocol(Scheme) : http, https
  • Host : ์‚ฌ์ดํŠธ ๋„๋ฉ”์ธ
  • Port : ํฌํŠธ ๋ฒˆํ˜ธ
  • Path : ์‚ฌ์ดํŠธ ๋‚ด๋ถ€ ๊ฒฝ๋กœ
  • Query string : ์š”์ฒญ์˜ key์™€ value๊ฐ’
  • Fragment : ํ•ด์‹œ ํƒœํฌ

๋ช‡๋ช‡ ๋…์ž๋ถ„๋“ค ์ค‘์— ์ด๋ฏธ ๊ฐ URL์˜ ์†์„ฑ๋“ค์— ๋Œ€ํ•ด ๋‹ค ์•Œ๊ณ ์žˆ๋Š” ์ˆ˜์ค€์ด ๋†’์€ ๋ถ„๋“ค๋„ ์žˆ๊ณ , ์•„์ง์€ ์ƒ์„ธํžˆ ์ž˜ ๋ชจ๋ฅด๋Š” ๋ถ„๋“ค๋„ ๊ณ„์‹ค๊ฑฐ๋ผ ์ถ”์ธกํ•œ๋‹ค.

CORS๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ์žˆ์–ด ์ €๊ฒƒ๋“ค์„ ๋ชจ๋‘ ์•Œ์•„์•ผ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ๋”ฑ 3๊ฐ€์ง€๋งŒ ๊ธฐ์–ตํ•˜๋ฉด ๋œ๋‹ค.

  • Origin : Protocol + Host + Port

์ฆ‰, ์ถœ์ฒ˜(Origin) ๋ผ๋Š” ๊ฒƒ์€ Protolcol ๊ณผ Host ๊ทธ๋ฆฌ๊ณ  Port ๊นŒ์ง€ ๋ชจ๋‘ ํ•ฉ์นœ URL์„ ์˜๋ฏธํ•œ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ๋„ ํ˜„์žฌ ์‚ฌ์ดํŠธ์˜ Origin์„ ์•Œ์•„๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค.

console.log(location.origin); // "https://www.naver.com" (ํฌํŠธ ๋ฒˆํ˜ธ 80๋ฒˆ์€ ์ƒ๋žต๋จ)

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

์ถœ์ฒ˜(Origin)์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค์œผ๋‹ˆ ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Same Origin ์ •์ฑ…๊ณผ Cross Origin ์ •์ฑ…์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

๋จผ์ € SOP(Same Origin Policy) ์ •์ฑ…์€ ๋‹จ์–ด ๊ทธ๋Œ€๋กœ ๋™์ผํ•œ ์ถœ์ฒ˜์— ๋Œ€ํ•œ ์ •์ฑ…์„ ๋งํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด SOP ์ •์ฑ…์€ '๋™์ผํ•œ ์ถœ์ฒ˜์—์„œ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.'๋ผ๋Š” ๋ฒ•๋ฅ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์ฆ‰, ๋™์ผ ์ถœ์ฒ˜(Same-Origin) ์„œ๋ฒ„์— ์žˆ๋Š” ๋ฆฌ์†Œ์Šค๋Š” ์ž์œ ๋กœ์ด ๊ฐ€์ ธ์˜ฌ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹ค๋ฅธ ์ถœ์ฒ˜(Cross-Origin) ์„œ๋ฒ„์— ์žˆ๋Š” ์ด๋ฏธ์ง€๋‚˜ ์œ ํŠœ๋ธŒ ์˜์ƒ ๊ฐ™์€ ๋ฆฌ์†Œ์Šค๋Š” ์ƒํ˜ธ์ž‘์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋ง์ด๋‹ค.

cors-sop

 

๐Ÿ“œ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์ด ํ•„์š”ํ•œ ์ด์œ 

๊ทธ๋ ‡๋‹ค๋ฉด ๋™์ผ ์ถœ์ฒ˜๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ•˜๋Š” ์ด์œ ๋Š” ๋ญ˜๊นŒ?

์‚ฌ์‹ค ์ถœ์ฒ˜๊ฐ€ ๋‹ค๋ฅธ ๋‘ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ž์œ ๋กœ์ด ์†Œํ†ตํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์€ ๊ฝค ์œ„ํ—˜ํ•œ ํ™˜๊ฒฝ์ด๋‹ค. ๋งŒ์ผ ์ œ์•ฝ์ด ์—†๋‹ค๋ฉด, ํ•ด์ปค๊ฐ€ CSRF(Cross-Site Request Forgery)๋‚˜ XSS(Cross-Site Scripting) ๋“ฑ์˜ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•ด์ปค๊ฐ€ ์‹ฌ์–ด๋†“์€ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ํ•˜์—ฌ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ์€ SOP ์ •์ฑ…์ด ์—†๋Š” ์ƒํ™ฉ์—์„œ ์•…์˜์ ์ธ ํ™ˆํŽ˜์ด์ง€์— ์ ‘์†ํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ • ํ•œ ๊ฒƒ์ด๋‹ค.

SOP(Same-Origin Policy)

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์•…์„ฑ ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•œ๋‹ค.
  2. ์ด๋•Œ ํ•ด์ปค๊ฐ€ ๋ชฐ๋ž˜ ์‹ฌ์–ด๋†“์€ ์•…์˜์ ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋ฅด๋Š” ์‚ฌ์ด์— ์–ด๋Š ํฌํ„ธ ์‚ฌ์ดํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
  3. ๊ทธ๋Ÿผ ํฌํ„ธ ์‚ฌ์ดํŠธ์—์„œ ํ•ด๋‹น ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟ ํ‚ค๋ฅผ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ์„ ํ•˜๊ฑฐ๋‚˜ ๋“ฑ ์ƒํ˜ธ์ž‘์šฉ์— ๋”ฐ๋ฅธ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ์‘๋‹ต ๊ฐ’์„ ๋ฐ›์€๋’ค, ์‚ฌ์ดํŠธ์—์„œ ํ•ด์ปค ์„œ๋ฒ„(hacker.example.com)๋กœ ์žฌ์ฐจ ๋ณด๋‚ธ๋‹ค.
  4. ์ด์™ธ์—๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†์ค‘์ธ ๋‚ด๋ถ€๋ง์˜ ์•„์ดํ”ผ์™€ ํฌํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜, ํ•ด์ปค๊ฐ€ ์‚ฌ์šฉ์ž ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ”„๋ก์‹œ์ฒ˜๋Ÿผ ์•…์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ์•…์˜์ ์ธ ๊ฒฝ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด, SOP ์ •์ฑ…์œผ๋กœ ๋™์ผํ•˜์ง€ ์•Š๋Š” ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

๐Ÿท๏ธ ๊ฐ™์€ ์ถœ์ฒ˜์™€ ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๊ตฌ๋ถ„ ๊ธฐ์ค€

SOP ์ •์ฑ…์˜ ์ค‘์š”๋„์™€ ํ•„์š”์„ฑ์— ๋Œ€ํ•ด ์•Œ์•˜์œผ๋‹ˆ, ๋‘๊ฐœ์˜ ์ถœ์ฒ˜์˜ ๋‹ค๋ฆ„ ์œ ๋ฌด๋ฅผ ํŒ๋‹จํ•˜๋Š” ๊ธฐ์ค€์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณด์ž.

์ถœ์ฒ˜(Origin)์˜ ๋™์ผํ•จ์€ ๋‘ URL์˜ ๊ตฌ์„ฑ ์š”์†Œ ์ค‘ Protocol(Scheme), Host, Port ์ด 3๊ฐ€์ง€๋งŒ ๋™์ผํ•˜๋‹ค๋ฉด ๋™์ผ ์ถœ์ฒ˜๋กœ ํŒ๋‹จํ•œ๋‹ค.

cors-sop
๊ธฐ์ค€์ด ๋˜๋Š” ์ถœ์ฒ˜

๋‹ค์Œ์€ https://www.domain.com:3000 ์ถœ์ฒ˜์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ URL์— ๋”ฐ๋ฅธ ๋™์ผ ์ถœ์ฒ˜ ๋น„๊ต ํ‘œ ์ด๋‹ค.

URL ๋™์ผ ์ถœ์ฒ˜ ? ์ด์œ 
https://www.domain.com:3000/about O ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ, ํฌํŠธ ๋ฒˆํ˜ธ ๋™์ผ
https://www.domain.com:3000/about?username=inpa O ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ, ํฌํŠธ ๋ฒˆํ˜ธ ๋™์ผ
http://www.domain.com:3000 X ํ”„๋กœํ† ์ฝœ ๋‹ค๋ฆ„ (http ≠ https)
https://www.another.co.kr:3000 X ํ˜ธ์ŠคํŠธ ๋‹ค๋ฆ„
https://www.domain.com:8888 X ํฌํŠธ ๋ฒˆํ˜ธ ๋‹ค๋ฆ„
https://www.domain.com X ํฌํŠธ ๋ฒˆํ˜ธ ๋‹ค๋ฆ„ (443 ≠ 3000)

์ •๋ฆฌํ•˜์ž๋ฉด ๊ฐ™์€ ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ, ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๊ทธ ๋’ค์˜ ๋‹ค๋ฅธ ์š”์†Œ๋Š” ๋‹ค๋ฅด๋”๋ผ๋„ ๊ฐ™์€ ์ถœ์ฒ˜๋กœ ์ธ์ •๋œ๋‹ค.

๋ฐ˜๋Œ€๋กœ ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ, ํฌํŠธ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ž์‹ ์˜ ์ถœ์ฒ˜์™€ ๋‹ค๋ฅผ๊ฒฝ์šฐ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ •์ฑ…์ƒ ์ฐจ๋‹จํ•˜๊ฒŒ ๋œ๋‹ค.

์›น์˜ ํ‘์—ญ์‚ฌ์ธ Internet Explorer ๋ธŒ๋ผ์šฐ์ €๋Š” ์›ƒ๊ธฐ๊ฒŒ๋„ ์ถœ์ฒ˜ ๋น„๊ต์‹œ Port ๋ถ€๋ถ„์€ ๋ฌด์‹œํ•œ๋‹ค. ์ด๋Š” ๊ณง ๋ณด์•ˆ ์ทจ์•ฝ์œผ๋กœ ์ด์–ด์ง€๋ฉฐ ์™œ ๊ทธ๋ ‡๊ฒŒ ์š•์„ ์–ป์–ด๋จน๋Š”์ง€ ์— ๋Œ€ํ•œ ์ด์œ ์ค‘์— ํ•˜๋‚˜์ด๊ธฐ๋„ ํ•˜๋‹ค.
cors-sop
์ž˜๊ฐ€๋ผ IE (ํ•˜์ง€๋งŒ ์ข€๋น„๊ฐ€ ๋˜์–ด ์‚ด์•„์žˆ๋Š”๊ฑด ๋น„๋ฐ€)

 

๐ŸŒ ์ถœ์ฒ˜ ๋น„๊ต์™€ ์ฐจ๋‹จ์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•œ๋‹ค

์ƒˆ๋‚ด๊ธฐ ์›น๊ฐœ๋ฐœ์ž๋“ค์ด ์ฐฉ๊ฐํ•˜๋Š” ๋ถ€๋ถ„์ด ์œ„์˜ ์ถœ์ฒ˜ ๊ตฌ๋ถ„์„ ์„œ๋ฒ„๊ฐ€ ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์˜คํ•ดํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์•„๋ฌด๋ž˜๋„ ์„œ๋ฒ„์— ์š”์ฒญ์„ ํ–ˆ๋Š”๋ฐ ๋ฌด์–ธ๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋œจ๋ฉด ์„œ๋ฒ„๊ฐ€ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค์ˆ˜ ๋ฐ–์— ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ถœ์ฒ˜๋ฅผ ๋น„๊ตํ•˜๋Š” ๋กœ์ง์€ ์„œ๋ฒ„์— ๊ตฌํ˜„๋œ ์ŠคํŽ™์ด ์•„๋‹Œ ๋ธŒ๋ผ์šฐ์ €์— ๊ตฌํ˜„๋œ ์ŠคํŽ™์ด๋‹ค.

cors-sop
์„œ๋ฒ„๋Š” ์ž˜๋ชป์ด ์—†๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‚œ๋ฆฌ์ณ์„œ ์‹œ๋ป˜๊ฑด ์—๋Ÿฌ๊ฐ€ ๋œจ๋Š” ๊ฒƒ์ด๋‹ค.

์‚ฌ์‹ค ์„œ๋ฒ„๋Š” ๋ฆฌ์†Œ์Šค ์š”์ฒญ์— ์˜ํ•œ ์‘๋‹ต์€ ๋ง๋”ํžˆ ํ•ด์ฃผ์—ˆ๋‹ค. ์ž˜๋ชป์ด ์—†๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ด ์‘๋‹ต์„ ๋ถ„์„ํ•ด์„œ ๋™์ผ ์ถœ์ฒ˜๊ฐ€ ์•„๋‹ˆ๋ฉด, ์‹œ๋ป˜๊ฑด ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฟœ๋Š” ๊ฒƒ์ด๋‹ค. (์‚ฌ์‹ค ์„œ๋ฒ„๊ฐ€ ํ—ค๋” ์ •๋ณด๋ฅผ ๋œ ์ค˜์„œ ๊ทธ๋Ÿฐ๊ฒƒ์ด๋‹ค. ์ด๋Š” ๋’ค์—์„œ ๋‹ค๋ฃฌ๋‹ค)

๊ทธ๋ž˜์„œ ๋ธŒ๋ผ์šฐ์ €์—๋Š” ์—๋Ÿฌ๊ฐ€ ๋œจ์ง€๋งŒ, ์ •์ž‘ ์„œ๋ฒ„ ์ชฝ์—๋Š” ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ต์„ ํ–ˆ๋‹ค๊ณ  ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚œํ•ญ์€ ๊ฒช๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰, ์‘๋‹ต ๋ฐ์ดํ„ฐ๋Š” ๋ฉ€์ฉกํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ๋‹จ์—์„œ ๋ฐ›์„์ˆ˜ ์—†๋„๋ก ์ฐจ๋‹จ์„ ํ•œ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ CORS ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ์•ˆ ์ค‘ ํ•˜๋‚˜๋กœ ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ € ์„ค์ •์— SOP ์ •์ฑ…์„ ๋น„ํ™œ์„ฑํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ธด ํ•œ๋ฐ ๊ถŒ์žฅํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.
๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ •์ฑ…์œผ๋กœ ์ฐจ๋‹จ์„ ํ•œ๋‹ค๋Š” ๋ง์€, ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•˜์ง€ ์•Š๊ณ  ์„œ๋ฒ„ ๊ฐ„์— ํ†ต์‹ ์„ ํ• ๋•Œ๋Š” ์ •์ฑ…์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ง๊ณผ ๊ฐ™๋‹ค.
์ฆ‰, ํด๋ผ์ด์–ธํŠธ ๋‹จ ์ฝ”๋“œ์—์„œ API ์š”์ฒญ์„ ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„ ๋‹จ ์ฝ”๋“œ์—์„œ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์„œ๋ฒ„๋กœ API ์š”์ฒญ์„ ํ•˜๋ฉด CORS ์—๋Ÿฌ๋กœ๋ถ€ํ„ฐ ์ž์œ ๋กœ์›Œ ์ง„๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฅผ ์ด์šฉํ•œ ํ”„๋ก์‹œ(Proxy) ์„œ๋ฒ„๋ผ๋Š” ๊ฒƒ์ด ์žˆ๋‹ค. (ํ›„์ˆ )

 

๐Ÿค” ๊ทธ๋Ÿผ ์ฃ„๋‹ค ์ฐจ๋‹จํ•˜๋ฉด ์ธํ„ฐ๋„ท์ด ๋˜๋Š”๊ฐ€?

ํ•˜์ง€๋งŒ ์ธํ„ฐ๋„ท์€ ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์˜คํ”ˆ๋œ ํ™˜๊ฒฝ์ด๊ณ , ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ ์›นํŽ˜์ด์ง€์—์„œ ๋‹ค๋ฅธ ์ถœ์ฒ˜์— ์žˆ๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋Š” ์ผ์€ ๋งค์šฐ ํ”ํ•œ ์ผ์ด๋ผ ๋ฌดํ„ฑ๋Œ€๊ณ  ๋ง‰์„ ์ˆ˜๋Š” ์—†๋Š” ์ผ์ด๋‹ค.

cors-sop

๊ทธ๋ž˜์„œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์™ธ ์กฐํ•ญ์„ ๋‘๊ณ  ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค ์š”์ฒญ์ด๋ผ๋„ ์ด ์กฐํ•ญ์— ํ•ด๋‹นํ•  ๊ฒฝ์šฐ์—๋Š” ํ—ˆ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋Š”๋ฐ, ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ CORS ์ •์ฑ…์„ ์ง€ํ‚จ ๋ฆฌ์†Œ์Šค ์š”์ฒญ์ด๋‹ค.


๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  (Cross-Origin Resource Sharing)

์ด์ฒ˜๋Ÿผ ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ (Cross-Origin Resource Sharing, CORS)๋Š” ๋‹จ์–ด ๊ทธ๋Œ€๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ ์— ๋Œ€ํ•œ ํ—ˆ์šฉ/๋น„ํ—ˆ์šฉ ์ •์ฑ…์ด๋‹ค.

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

 

๐Ÿ’ฌ ์šฐ๋ฆฌ๊ฐ€ ์š•ํ–ˆ๋˜ CORS๋Š” ์‚ฌ์‹ค ํ•ด๊ฒฐ์ฑ…์ด์—ˆ๋‹ค

๊ฒฐ๊ตญ ์›น๊ฐœ๋ฐœ์ž๋ฅผ ๊ดด๋กญํžˆ๋˜ ์‹œ๋ป˜๊ฑด ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋Š” ์‚ฌ์‹ค ๋ธŒ๋ผ์šฐ์ €์˜ SOP ์ •์ฑ…์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฐจ๋‹จํ•˜๋ฉด์„œ ๋ฐœ์ƒ๋œ ์—๋Ÿฌ์ด๋ฉฐ, CORS๋Š” ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์–ป๊ธฐ์œ„ํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ ์ด์—ˆ๋˜ ๊ฒƒ์ด๋‹ค. ์š”์•ฝํ•˜์ž๋ฉด SOP ์ •์ฑ…์„ ์œ„๋ฐ˜ํ•ด๋„ CORS ์ •์ฑ…์— ๋”ฐ๋ฅด๋ฉด ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ผ๋„ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ CORS ์ •์ฑ…์„ ๋”ฐ๋ฅด๊ฒŒ ํ•˜์—ฌ SOP ์ •์ฑ…์„ ํšŒํ”ผํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์ด๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด์„  ๋ธŒ๋ผ์šฐ์ €์˜ CORS ๋™์ž‘ ๊ณผ์ •์„ ์‚ดํŽด ๋ณด์•„์•ผ ํ•œ๋‹ค.

 

๐Ÿ” ๋ธŒ๋ผ์šฐ์ €์˜ CORS ๊ธฐ๋ณธ ๋™์ž‘ ์‚ดํŽด๋ณด๊ธฐ

 

1. ํด๋ผ์ด์–ธํŠธ์—์„œ HTTP์š”์ฒญ์˜ ํ—ค๋”์— Origin์„ ๋‹ด์•„ ์ „๋‹ฌ

  1. ๊ธฐ๋ณธ์ ์œผ๋กœ ์›น์€ HTTP ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š”๋ฐ,
  2. ์ด๋•Œ ๋ธŒ๋ผ์šฐ์ €๋Š” ์š”์ฒญ ํ—ค๋”์— Origin ์ด๋ผ๋Š” ํ•„๋“œ์— ์ถœ์ฒ˜๋ฅผ ํ•จ๊ป˜ ๋‹ด์•„ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค.

cors-http

 

2. ์„œ๋ฒ„๋Š” ์‘๋‹ตํ—ค๋”์— Access-Control-Allow-Origin์„ ๋‹ด์•„ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌํ•œ๋‹ค.

  1. ์ดํ›„ ์„œ๋ฒ„๊ฐ€ ์ด ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•  ๋•Œ ์‘๋‹ต ํ—ค๋”์— Access-Control-Allow-Origin์ด๋ผ๋Š” ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฐ’์œผ๋กœ '์ด ๋ฆฌ์†Œ์Šค๋ฅผ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ํ—ˆ์šฉ๋œ ์ถœ์ฒ˜ url'์„ ๋‚ด๋ ค๋ณด๋‚ธ๋‹ค.

cors-http

 

3. ํด๋ผ์ด์–ธํŠธ์—์„œ Origin๊ณผ ์„œ๋ฒ„๊ฐ€ ๋ณด๋‚ด์ค€ Access-Control-Allow-Origin์„ ๋น„๊ตํ•œ๋‹ค.

  1. ์ดํ›„ ์‘๋‹ต์„ ๋ฐ›์€ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ž์‹ ์ด ๋ณด๋ƒˆ๋˜ ์š”์ฒญ์˜ Origin๊ณผ ์„œ๋ฒ„๊ฐ€ ๋ณด๋‚ด์ค€ ์‘๋‹ต์˜ Access-Control-Allow-Origin์„ ๋น„๊ตํ•ด๋ณธ ํ›„ ์ฐจ๋‹จํ• ์ง€ ๋ง์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
  2. ๋งŒ์•ฝ ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๋ฉด ๊ทธ ์‘๋‹ต์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฐ๋‹ค. (CORS ์—๋Ÿฌ !!)
  3. ์œ„์˜ ๊ฒฝ์šฐ์—๋Š” ๋‘˜๋‹ค http://localhost:3000์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ ํšจํ•˜๋‹ˆ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฌธ์ œ์—†์ด ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.

 

๐Ÿ’ฌ ๊ฒฐ๊ตญ CORS ํ•ด๊ฒฐ์ฑ…์€ ์„œ๋ฒ„์˜ ํ—ˆ์šฉ์ด ํ•„์š”

์œ„์˜ ๋ธŒ๋ผ์šฐ์ €์˜ CORS ๋™์ž‘ ๊ณผ์ •์„ ์‚ดํŽด๋ณด๋‹ˆ, ๊ธธ๊ณ  ๊ธธ์—ˆ๋˜ ์—ฌ์ •์˜ ๊ฒฐ๋ก ์€ ์„œ๋ฒ„์—์„œ Access-Control-Allow-Origin ํ—ค๋”์— ํ—ˆ์šฉํ•  ์ถœ์ฒ˜๋ฅผ ๊ธฐ์žฌํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ตํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ์ฆ‰, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ณ ์ณ์•ผ๋  ๋ถ€๋ถ„์ธ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฏธ๋ฆฌ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ origin ํ—ค๋”๊ฐ’์„ ์œ„์กฐํ•˜๋ฉด ๋˜์ง€ ์•Š์„๊นŒ ์‹ถ์ง€๋งŒ, ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ฐจ๋‹จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๋ก ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
cors-http

CORS ์ž‘๋™ ๋ฐฉ์‹ 3๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค

๋ฐ”๋กœ ์œ„์—์„œ ์‚ดํŽด๋ณธ CORS ๋™์ž‘ ํ๋ฆ„์€ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์ธ ์ž‘๋™ ํ๋ฆ„์„ ๋ณด์—ฌ์ค€ ๊ฒƒ์ด๊ณ , ์‹ค์ œ๋กœ๋Š” CORS๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹์€ ํ•œ ๊ฐ€์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ์„ธ ๊ฐ€์ง€์˜ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ๋˜๊ธฐ ๋•Œ๋ฌธ์—, CORS๋ฅผ ์ •๋ณตํ•˜๊ธฐ ์œ„ํ•ด์„  ์ด๋“ค์„ ๋ชจ๋‘ ์•Œ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. (๊ณต๋ถ€๊ฐ€ ๋์ด์—†๋‹ค โ˜น๏ธ)

๋‹ค๋งŒ ์ด ๋ถ€๋ถ„์€ ๋‹น์žฅ CORS๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ ์žˆ์–ด ํ•„์ˆ˜ ์ง€์‹์€ ์•„๋‹ˆ์ง€๋งŒ, ๋งŒ์ผ ๋…์ž๋ถ„์ด ๋‹จ์ˆœ ์š”์ฒญ์„ ๋– ๋‚˜ ์ฟ ํ‚ค๋‚˜ ํ† ํฐ๊ณผ ๊ฐ™์€ ์ธ์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์„œ๋ฒ„์— ์š”์ฒญ์„ ํ•ด์•ผํ•œ๋‹ค๋ฉด ์ด ์„น์…˜์˜ ์ง€์‹์€ ํ•„์ˆ˜์ด๋‹ค. ๋˜ํ•œ ์šฐ๋ฆฌ๊ฐ€ ์ธํ„ฐ๋„ท์„ ๋ฐฐ์šธ๋•Œ TCP / UDP์˜ ๋‚ด๋ถ€ ํ†ต์‹  ๊ณผ์ •์„ ๋ฐฐ์› ๋“ฏ์ด, ๋ธŒ๋ผ์šฐ์ €์˜ ์„ธ๋ถ€์ ์ธ CORS ํ†ต์‹  ๋™์ž‘ ๊ณผ์ •์„ ์‚ดํŽด๋ด์•ผ ๋‚˜์ค‘์— ์ตœ์ ํ™” ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•™์Šต์ด ๊ถŒ์žฅ๋˜๋Š” ๋ฐ”์ด๋‹ค.


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

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

์ฆ‰, ์˜ˆ๋น„ ์š”์ฒญ์˜ ์—ญํ• ์€ ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ๋ธŒ๋ผ์šฐ์ € ์Šค์Šค๋กœ ์•ˆ์ „ํ•œ ์š”์ฒญ์ธ์ง€ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋•Œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์˜ˆ๋น„์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ Preflight๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ, ์ด ์˜ˆ๋น„์š”์ฒญ์˜ HTTP ๋ฉ”์†Œ๋“œ๋ฅผ GET์ด๋‚˜ POST๊ฐ€ ์•„๋‹Œ OPTIONS๋ผ๋Š” ์š”์ฒญ์ด ์‚ฌ์šฉ๋œ๋‹ค๋Š” ๊ฒƒ์ด ํŠน์ง•์ด๋‹ค.

 

์˜ˆ๋ฅผ๋“ค์–ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋‹ค์Œ api ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

Preflight-Request

  1. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ fetch() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›์•„์˜ค๋ ค๊ณ  ํ•œ๋‹ค.
  2. ๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋ฒ„๋กœ HTTP OPTIONS ๋ฉ”์†Œ๋“œ๋กœ ์˜ˆ๋น„ ์š”์ฒญ(Preflight)์„ ๋จผ์ € ๋ณด๋‚ธ๋‹ค.
    1. Origin ํ—ค๋”์— ์ž์‹ ์˜ ์ถœ์ฒ˜๋ฅผ ๋„ฃ๋Š”๋‹ค.
    2. Access-Control-Request-Method ํ—ค๋”์— ์‹ค์ œ ์š”์ฒญ์— ์‚ฌ์šฉํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ์„ค์ •ํ•œ๋‹ค.
    3. Access-Control-Request-Headers ํ—ค๋”์— ์‹ค์ œ ์š”์ฒญ์— ์‚ฌ์šฉํ•  ํ—ค๋”๋“ค์„ ์„ค์ •ํ•œ๋‹ค.
  3. ์„œ๋ฒ„๋Š” ์ด ์˜ˆ๋น„ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์–ด๋–ค ๊ฒƒ์„ ํ—ˆ์šฉํ•˜๊ณ  ์–ด๋–ค๊ฒƒ์„ ๊ธˆ์ง€ํ•˜๊ณ  ์žˆ๋Š”์ง€์— ๋Œ€ํ•œ ํ—ค๋” ์ •๋ณด๋ฅผ ๋‹ด์•„์„œ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ณด๋‚ด์ค€๋‹ค.
    1. Access-Control-Allow-Origin ํ—ค๋”์— ํ—ˆ์šฉ๋˜๋Š” Origin๋“ค์˜ ๋ชฉ๋ก์„ ์„ค์ •ํ•œ๋‹ค.
    2. Access-Control-Allow-Methods ํ—ค๋”์— ํ—ˆ์šฉ๋˜๋Š” ๋ฉ”์†Œ๋“œ๋“ค์˜ ๋ชฉ๋ก์„ ์„ค์ •ํ•œ๋‹ค.
    3. Access-Control-Allow-Headers ํ—ค๋”์— ํ—ˆ์šฉ๋˜๋Š” ํ—ค๋”๋“ค์˜ ๋ชฉ๋ก์„ ์„ค์ •ํ•œ๋‹ค.
    4. Access-Control-Max-Age ํ—ค๋”์— ํ•ด๋‹น ์˜ˆ๋น„ ์š”์ฒญ์ด ๋ธŒ๋ผ์šฐ์ €์— ์บ์‹œ ๋  ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์„ ์ดˆ ๋‹จ์œ„๋กœ ์„ค์ •ํ•œ๋‹ค.
  4. ์ดํ›„ ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ณด๋‚ธ ์š”์ฒญ๊ณผ ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•ด์ค€ ์ •์ฑ…์„ ๋น„๊ตํ•˜์—ฌ, ํ•ด๋‹น ์š”์ฒญ์ด ์•ˆ์ „ํ•œ์ง€ ํ™•์ธํ•˜๊ณ  ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค.
  5. ์„œ๋ฒ„๊ฐ€ ๋ณธ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•˜๋ฉด ์ตœ์ข…์ ์œผ๋กœ ์ด ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋ฐ”์Šค๋ฆฝํŠธ๋กœ ๋„˜๊ฒจ์ค€๋‹ค.

 

โœ”๏ธ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ์˜ˆ๋น„ ์š”์ฒญ ํ™•์ธํ•˜๊ธฐ

์œ„์˜ ํ”Œ๋กœ์šฐ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ ๋„คํŠธ์›Œํฌ ํƒญ์„ ํ†ตํ•ด ๊ฐ„๋‹จํžˆ ์žฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์‹ค์ œ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋กœ api ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, ํฌ๋กฌ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ๋ณธ ์š”์ฒญ(xhr)์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์˜ˆ๋น„ ์š”์ฒญ(preflight) ํ†ต์‹ ์„ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ์ˆ˜ ์žˆ๋‹ค.

await fetch("http://localhost:4000/users/location-registration", {"method":"DELETE"})

cors-preflight
preflight์š”์ฒญ์ด ๋จผ์ € ์„œ๋ฒ„์— ์ „๋‹ฌ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
cors-preflight

์œ„์˜ ์‚ฌ์ง„์ƒ์—๋Š” ์š”์ฒญ ํ—ค๋”์˜ Origin๊ณผ ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Origin ์˜ URL๊ฐ’์ด ์„œ๋กœ ๊ฐ™์•„ ๋‹ค๋ฅธ ์ถœ์ฒ˜๋ผ๋„ CORS(๋‹ค๋ฅธ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ )๊ฐ€ ํ—ˆ์šฉ๋˜์„œ ์ •์ƒ ์‘๋‹ต์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

๋งŒ์ผ ์ด ๋‘˜์ด ๋‹ค๋ฅด๊ฒŒ๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ์š”์ฒญ์ด CORS ์ •์ฑ…์„ ์œ„๋ฐ˜ํ–ˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  ์•…๋ช… ๋†’์€ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฑ‰๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

โฐ ์˜ˆ๋น„ ์š”์ฒญ์˜ ๋ฌธ์ œ์ ๊ณผ ์บ์‹ฑ

์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์˜ˆ๋น„ ์š”์ฒญ์„ ๋ณด๋‚ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋Š” ๋ชฉ์ ์˜ ์ทจ์ง€๋Š” ์ข‹๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ตญ์€ ์‹ค์ œ ์š”์ฒญ์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚˜๊ฒŒ ๋˜์–ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ํฌ๋‚˜ํฐ ๋‹จ์ ์ด ์žˆ๋‹ค.

ํŠนํžˆ ์ˆ˜ํ–‰ํ•˜๋Š” API ํ˜ธ์ถœ ์ˆ˜๊ฐ€ ๋งŽ์œผ๋ฉด ๋งŽ์„ ์ˆ˜๋ก ์˜ˆ๋น„ ์š”์ฒญ์œผ๋กœ ์ธํ•ด ์„œ๋ฒ„ ์š”์ฒญ์„ ๋ฐฐ๋กœ ๋ณด๋‚ด๊ฒŒ ๋˜๋‹ˆ ๋น„์šฉ ์ ์ธ ์ธก๋ฉด์—์„œ ํ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ(Cache) ๋ฅผ ์ด์šฉํ•ด Access-Control-Max-Age ํ—ค๋”์— ์บ์‹œ๋  ์‹œ๊ฐ„์„ ๋ช…์‹œํ•ด ์ฃผ๋ฉด, ์ด Preflight ์š”์ฒญ์„ ์บ์‹ฑ ์‹œ์ผœ ์ตœ์ ํ™”๋ฅผ ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋‹ค.

preflight-cache
600์ดˆ ๋™์•ˆ ์˜ˆ๋น„ ์š”์ฒญ์„ ์บ์‹ฑ

์˜ˆ๋น„ ์š”์ฒญ ์บ์‹ฑ ๊ธฐ๊ฐ„์— ๋Œ€ํ•ด์„œ๋Š”, ํŒŒ์ด์–ดํญ์Šค ๋ธŒ๋ผ์šฐ์ €๋Š” 86400์ดˆ(24์‹œ๊ฐ„) ๊นŒ์ง€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ํฌ๋กœ๋ฏธ์—„ ๊ธฐ๋ฐ˜ ๋ธŒ๋ผ์šฐ์ €๋Š” 7200์ดˆ(2์‹œ๊ฐ„)์ด ์ตœ๋Œ€์ด๋‹ค.

์˜ˆ๋น„ ์š”์ฒญ ์บ์‹œ๋Š” ๋‹ค๋ฅธ ์บ์‹ฑ ๋งค์ปค๋‹ˆ์ฆ˜๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค.

preflight-cache

  1. ๋ธŒ๋ผ์šฐ์ €๋Š” ์˜ˆ๋น„(Preflight) ์š”์ฒญ์„ ํ•  ๋•Œ๋งˆ๋‹ค, ๋จผ์ € Preflight ์บ์‹œ๋ฅผ ํ™•์ธํ•˜์—ฌ ํ•ด๋‹น ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  2. ๋งŒ์ผ ์‘๋‹ต์ด ์บ์‹ฑ ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด,  ์„œ๋ฒ„์— ์˜ˆ๋น„ ์š”์ฒญ์„ ๋ณด๋‚ด ์ธ์ฆ ์ ˆ์ฐจ๋ฅผ ๋ฐŸ๋Š”๋‹ค.
  3. ๋งŒ์ผ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ Access-Control-Max-Age ์‘๋‹ต ํ—ค๋”๋ฅผ ๋ฐ›๋Š”๋‹ค๋ฉด ๊ทธ ๊ธฐ๊ฐ„ ๋™์•ˆ ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ์— ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•œ๋‹ค.
  4. ๋‹ค์‹œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๋งŒ์ผ ์‘๋‹ต์ด ์บ์‹ฑ ๋˜์–ด ์žˆ๋‹ค๋ฉด, ์˜ˆ๋น„ ์š”์ฒญ์„ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ๋Œ€์‹  ์บ์‹œ๋œ ์‘๋‹ต์„ ์‚ฌ์šฉํ•œ๋‹ค.

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

๋‹จ์ˆœ ์š”์ฒญ์€ ๋ง๊ทธ๋Œ€๋กœ ์˜ˆ๋น„ ์š”์ฒญ(Prefilght)์„ ์ƒ๋žตํ•˜๊ณ  ๋ฐ”๋กœ ์„œ๋ฒ„์— ์งํ–‰์œผ๋กœ ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ธ ํ›„, ์„œ๋ฒ„๊ฐ€ ์ด์— ๋Œ€ํ•œ ์‘๋‹ต์˜ ํ—ค๋”์— Access-Control-Allow-Origin ํ—ค๋”๋ฅผ ๋ณด๋‚ด์ฃผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ CORS์ •์ฑ… ์œ„๋ฐ˜ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

simple-Request

๋‹ค๋งŒ, ์‹ฌํ”Œํ•œ ๋งŒํผ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์˜ˆ๋น„ ์š”์ฒญ์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ ์•„๋ž˜ 3๊ฐ€์ง€ ๊ฒฝ์šฐ๋ฅผ ๋งŒ์กฑ ํ• ๋•Œ ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

  1. ์š”์ฒญ์˜ ๋ฉ”์†Œ๋“œ๋Š” GET, HEAD, POST ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค.
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width ํ—ค๋”์ผ ๊ฒฝ์šฐ ์—๋งŒ ์ ์šฉ๋œ๋‹ค.
  3. Content-Type ํ—ค๋”๊ฐ€ application/x-www-form-urlencoded, multipart/form-data, text/plain์ค‘ ํ•˜๋‚˜์—ฌ์•ผํ•œ๋‹ค. ์•„๋‹ ๊ฒฝ์šฐ ์˜ˆ๋น„ ์š”์ฒญ์œผ๋กœ ๋™์ž‘๋œ๋‹ค.

์ด์ฒ˜๋Ÿผ ๋‹ค์†Œ ๊นŒ๋‹ค๋กœ์šด ์กฐ๊ฑด๋“ค์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์—, ์œ„ ์กฐ๊ฑด์„ ๋ชจ๋‘ ๋งŒ์กฑ๋˜์–ด ๋‹จ์ˆœ ์š”์ฒญ์ด ์ผ์–ด๋‚˜๋Š” ์ƒํ™ฉ์€ ๋“œ๋ฌผ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„ HTTP API ์š”์ฒญ์€ text/xml ์ด๋‚˜ application/json ์œผ๋กœ ํ†ต์‹ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— 3๋ฒˆ์งธ Content-Type์ด ์œ„๋ฐ˜๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ ๋Œ€๋ถ€๋ถ„์˜ API ์š”์ฒญ์€ ๊ทธ๋ƒฅ ์˜ˆ๋น„ ์š”์ฒญ(preflight)์œผ๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค ๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.


์ธ์ฆ๋œ ์š”์ฒญ (Credentialed Request)

์ธ์ฆ๋œ ์š”์ฒญ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„์—๊ฒŒ ์ž๊ฒฉ ์ธ์ฆ ์ •๋ณด(Credential)๋ฅผ ์‹ค์–ด ์š”์ฒญํ• ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์š”์ฒญ์ด๋‹ค.

์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ์ž๊ฒฉ ์ธ์ฆ ์ •๋ณด๋ž€ ์„ธ์…˜ ID๊ฐ€ ์ €์žฅ๋˜์–ด์žˆ๋Š” ์ฟ ํ‚ค(Cookie) ํ˜น์€ Authorization ํ—ค๋”์— ์„ค์ •ํ•˜๋Š” ํ† ํฐ ๊ฐ’ ๋“ฑ์„ ์ผ์ปซ๋Š”๋‹ค.

์ฆ‰, ํด๋ผ์ด์–ธํŠธ์—์„œ ์ผ๋ฐ˜์ ์ธ JSON ๋ฐ์ดํ„ฐ ์™ธ์—๋„ ์ฟ ํ‚ค ๊ฐ™์€ ์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•ด์„œ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์„œ๋ฒ„๋กœ ์ „๋‹ฌํ• ๋•Œ CORS์˜ ์„ธ๊ฐ€์ง€ ์š”์ฒญ์ค‘ ํ•˜๋‚˜์ธ ์ธ์ฆ๋œ ์š”์ฒญ์œผ๋กœ ๋™์ž‘๋œ๋‹ค๋Š” ๋ง์ด๋ฉฐ, ์ด๋Š” ๊ธฐ์กด์˜ ๋‹จ์ˆœ ์š”์ฒญ์ด๋‚˜ ์˜ˆ๋น„ ์š”์ฒญ๊ณผ๋Š” ์‚ด์ง ๋‹ค๋ฅธ ์ธ์ฆ ํ˜•ํƒœ๋กœ ํ†ต์‹ ํ•˜๊ฒŒ ๋œ๋‹ค.

 

1. ํด๋ผ์ด์–ธํŠธ์—์„œ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•˜๊ธฐ

๊ธฐ๋ณธ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์š”์ฒญ API ๋“ค์€ ๋ณ„๋„์˜ ์˜ต์…˜ ์—†์ด ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ธ์ฆ๊ณผ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๋ถ€๋กœ ์š”์ฒญ ๋ฐ์ดํ„ฐ์— ๋‹ด์ง€ ์•Š๋„๋ก ๋˜์–ด์žˆ๋‹ค.

์ด๋•Œ ์š”์ฒญ์— ์ธ์ฆ๊ณผ ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์˜ต์…˜์ด ๋ฐ”๋กœ credentials ์˜ต์…˜์ด๋‹ค. ์ด ์˜ต์…˜์—๋Š” 3๊ฐ€์ง€์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ ๊ฐ’๋“ค์ด ๊ฐ€์ง€๋Š” ์˜๋ฏธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์˜ต์…˜ ๊ฐ’ ์„ค๋ช…
same-origin(๊ธฐ๋ณธ๊ฐ’) ๊ฐ™์€ ์ถœ์ฒ˜ ๊ฐ„ ์š”์ฒญ์—๋งŒ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค.
include ๋ชจ๋“  ์š”์ฒญ์— ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค.
omit ๋ชจ๋“  ์š”์ฒญ์— ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์ง€ ์•Š๋Š”๋‹ค.

๋งŒ์ผ ์ด๋Ÿฌํ•œ ๋ณ„๋„์˜ ์„ค์ •์„ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ์ฟ ํ‚ค ๋“ฑ์˜ ์ธ์ฆ ์ •๋ณด๋Š” ์ ˆ๋Œ€๋กœ ์ž๋™์œผ๋กœ ์„œ๋ฒ„์—๊ฒŒ ์ „์†ก๋˜์ง€ ์•Š๋Š”๋‹ค.

์„œ๋ฒ„์— ์ธ์ฆ๋œ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” fetch ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ axios, jQuery ๋ผ์ด๋ธŒ๋ฆฌ๋ฆฌ ๋“ฑ ๋‹ค์–‘ํ•˜๋‹ค. ์–ด๋–ค ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ ์•ฝ๊ฐ„ credentials ์˜ต์…˜์„ ์ง€์ •ํ•˜๋Š” ๋ฌธ๋ฒ•์ด ๋‹ค๋ฅด๋‹ˆ ์ด๋“ค์„ ๋ชจ๋‘ ์†Œ๊ฐœํ•ด ๋ณธ๋‹ค.

// fetch ๋ฉ”์„œ๋“œ
fetch("https://example.com:1234/users/login", {
	method: "POST",
	credentials: "include", // ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ํ†ต์‹ ํ• ๋•Œ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ธ์ฆ ์ •๋ณด ๊ฐ’์„ ๊ณต์œ ํ•˜๊ฒ ๋‹ค๋Š” ์„ค์ •
    body: JSON.stringify({
        userId: 1,
    }),
})
// axios ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
axios.post('https://example.com:1234/users/login', { 
    profile: { username: username, password: password } 
}, { 
	withCredentials: true // ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ํ†ต์‹ ํ• ๋•Œ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ธ์ฆ ์ •๋ณด ๊ฐ’์„ ๊ณต์œ ํ•˜๊ฒ ๋‹ค๋Š” ์„ค์ •
})
// jQuery ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
$.ajax({
	url: "https://example.com:1234/users/login",
	type: "POST",
	contentType: "application/json; charset=utf-8",
	dataType: "json",		
	xhrFields: { 
    	withCredentials: true // ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ํ†ต์‹ ํ• ๋•Œ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ธ์ฆ ์ •๋ณด ๊ฐ’์„ ๊ณต์œ ํ•˜๊ฒ ๋‹ค๋Š” ์„ค์ •
    },
	success: function (retval, textStatus) {
		console.log( JSON.stringify(retval));
	}
});

 

2. ์„œ๋ฒ„์—์„œ ์ธ์ฆ๋œ ์š”์ฒญ์— ๋Œ€ํ•œ ํ—ค๋” ์„ค์ •ํ•˜๊ธฐ

์„œ๋ฒ„๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด๋Ÿฌํ•œ ์ธ์ฆ๋œ ์š”์ฒญ์— ๋Œ€ํ•ด ์ผ๋ฐ˜์ ์ธ CORS ์š”์ฒญ๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ ๋Œ€์‘ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

  1. ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Credentials ํ•ญ๋ชฉ์„ true๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
  2. ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Origin ์˜ ๊ฐ’์— ์™€์ผ๋“œ์นด๋“œ ๋ฌธ์ž("*")๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  3. ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Methods ์˜ ๊ฐ’์— ์™€์ผ๋“œ์นด๋“œ ๋ฌธ์ž("*")๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  4. ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Headers ์˜ ๊ฐ’์— ์™€์ผ๋“œ์นด๋“œ ๋ฌธ์ž("*")๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

์ฆ‰, ์‘๋‹ต์˜ Access-Control-Allow-Origin ํ—ค๋”๊ฐ€ ์™€์ผ๋“œ์นด๋“œ(*)๊ฐ€ ์•„๋‹Œ ๋ถ„๋ช…ํ•œ Origin์œผ๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•˜๊ณ , Access-Control-Allow-Credentials ํ—ค๋”๋Š” true๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €์˜ CORS ์ •์ฑ…์— ์˜ํ•ด ์‘๋‹ต์ด ๊ฑฐ๋ถ€๋œ๋‹ค. (์ธ์ฆ ์ •๋ณด๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถœ์ฒ˜๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค)

๋งŒ์ผ ์ด๋ฅผ ์–ด๊ธธ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์€ ๋˜๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ CORS ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ์ ‘ํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

cors-Credentialed-Request
Access-Control-Allow-Credentials ์„ค์ • ์•ˆํ–ˆ์„ ๊ฒฝ์šฐ
cors-Credentialed-Request
Access-Control-Allow-Origin๊ฐ€ * ๋กœ ์„ค์ •๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ

 

์œ„์˜ ๊ณผ์ •์„ ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

cors-Credentialed-Request

์ฐธ๊ณ ๋กœ ์ธ์ฆ๋œ ์š”์ฒญ ์—ญ์‹œ ์—ญ์‹œ ์˜ˆ๋น„ ์š”์ฒญ ์ฒ˜๋Ÿผ preflight๊ฐ€ ๋จผ์ € ์ผ์–ด๋‚œ๋‹ค. ์œ„์˜ ๊ทธ๋ฆผ์—์„œ๋Š” ๋‹จ์ˆœ GET ์š”์ฒญ์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ๋น„ ์š”์ฒญ์€ ์ƒ๋žต ๋˜์—ˆ๋‹ค.


CORS 3๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค ์ž‘๋™ ์ฒดํ—˜ ์‚ฌ์ดํŠธ

์œ„์˜ 3๊ฐ€์ง€ CORS ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ฝ”๋“œ๋กœ ๋ฏธ๋ฆฌ ์ฒดํ—˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ์‚ฌ์ดํŠธ๊ฐ€ ์žˆ์–ด์„œ ์†Œ๊ฐœํ•ด ๋ณธ๋‹ค.

์„œ๋ฒ„์—์„œ์˜ ์„ค์ • ํ˜•ํƒœ์™€ ํด๋ผ์ด์–ธํŠธ์—์„œ์˜ ์š”์ฒญ ํ˜•ํƒœ์— ๋”ฐ๋ผ CORS ์—๋Ÿฌ๊ฐ€ ๋‚˜๊ธฐ๋„ ํ•˜๊ณ  ํ†ต๊ณผ๋˜๊ธฐ ํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

CORS Tutorial

 

chuckchoiboi.github.io

cors-์—๋Ÿฌ-์ •๋ฆฌ
๋ฉ”๋‰ด ์ƒ๋‹จ์— 3๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ณ ๋ฅผ ์ˆ˜ ์žˆ๋‹ค
cors-์—๋Ÿฌ-์ •๋ฆฌ
cors-์—๋Ÿฌ-์ •๋ฆฌ


CORS๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ• ์ด์ •๋ฆฌ ๐Ÿ™Œ

 

1. Chrome ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ ์ด์šฉ

Chrome์—์„œ๋Š” CORS ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์„ ์ œ๊ณตํ•ด์ค€๋‹ค. 

์•„๋ž˜ ๋งํฌ์—์„œ 'Allow CORS: Access-Control-Allow-Origin' ํฌ๋กฌ ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์„์„ ์„ค์น˜ ํ•ด์ค€๋‹ค.

 

Allow CORS: Access-Control-Allow-Origin

Easily add (Access-Control-Allow-Origin: *) rule to the response header.

chrome.google.com

๊ทธ๋Ÿฌ๋ฉด ๋ธŒ๋ผ์šฐ์ € ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์—์„œ ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์„ ํ™œ์„ฑํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœ๊ทธ๋žจ์„ ํ™œ์„ฑํ™” ์‹œํ‚ค๊ฒŒ ๋˜๋ฉด, ๋กœ์ปฌ(localhost) ํ™˜๊ฒฝ์—์„œ API๋ฅผ ํ…Œ์ŠคํŠธ ์‹œ, CORS ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์„œ๋ฒ„ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๊ณ ๋ฏผํ•˜์ง€ ์•Š๊ณ  ๋น ๋ฅด๊ฒŒ CORS๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ ์ข‹์€ ์„ ํƒ์ง€์ผ ๊ฒƒ์ด๋‹ค.

cors-์—๋Ÿฌ-์ •๋ฆฌ


2. ํ”„๋ก์‹œ ์‚ฌ์ดํŠธ ์ด์šฉํ•˜๊ธฐ

ํ”„๋ก์‹œ(Proxy)๋ž€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ์‚ฌ์ด์˜ ์ค‘๊ณ„ ๋Œ€๋ฆฌ์ ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์ฆ‰, ํ”„๋ก ํŠธ์—์„œ ์ง์ ‘ ์„œ๋ฒ„์— ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญ์„ ํ–ˆ๋”๋‹ˆ ์„œ๋ฒ„์—์„œ ๋”ฐ๋กœ ์„ค์ •์„ ์•ˆํ•ด์ค˜์„œ CORS ์—๋Ÿฌ๊ฐ€ ๋œฌ๋‹ค๋ฉด, ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•œ ์„œ๋ฒ„ ๋Œ€๋ฆฌ์ ์„ ํ†ตํ•ด ์š”์ฒญ์„ ํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

cors-proxy

๋‹ค๋งŒ ํ˜„์žฌ ๋ฌด๋ฃŒ ํ”„๋ก์‹œ ์„œ๋ฒ„ ๋Œ€์—ฌ ์„œ๋น„์Šค๋“ค์€ ๋ชจ๋‘ ์•…์šฉ ์‚ฌ๋ก€ ๋•Œ๋ฌธ์— api ์š”์ฒญ ํšŸ์ˆ˜ ์ œํ•œ์„ ๋‘์–ด ์‹ค์ „์—์„œ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ๋ฌด๋ฆฌ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ์šฉ์ด๋‚˜ ๋ง›๋ณด๊ธฐ์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๋˜, ์‹ค์ „์—์„œ๋Š” ์ง์ ‘ ํ”„๋ก์‹œ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

 

heroku ํ”„๋ก์‹œ ์„œ๋ฒ„

  1. http://cors-anywhere.herokuapp.com/corsdemo
  2. ์œ„์˜ ์‚ฌ์ดํŠธ๋กœ ๊ฐ€์„œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  ๋ฐ๋ชจ ์„œ๋ฒ„๋ฅผ ํ™œ์„ฑํ™” ์‹œํ‚จ๋‹ค.
  3. ๋‹ค๋งŒ ์‹œ๊ฐ„ ์ œํ•œ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ผ์‹œ์ ์ธ ํ•ด๊ฒฐ ๋ฐฉํŽธ ์ด๋‹ค.

cors-proxy

const url = 'https://google.com' // ์ด ๋ถ€๋ถ„์„ ์ด์šฉํ•˜๋Š” ์„œ๋ฒ„ URL๋กœ ๋ณ€๊ฒฝ

fetch(`https://cors-anywhere.herokuapp.com/${url}`)
    .then((response) => response.text())
    .then((data) => console.log(data));

 

cors proxy app ํ”„๋ก์‹œ ์„œ๋ฒ„

  1. raravel๋‹˜์ด ๋งŒ๋“œ์‹  ์„œ๋น„์Šค
  2. axios ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
<script src='https://cdnjs.cloudflare.com/ajax/libs/axios/1.1.3/axios.min.js'></script>
<script>
    axios({
        url: 'https://cors-proxy.org/api/',
        method: 'get',
        headers: {
            'cors-proxy-url' : 'https://google.com/' // ์ด ๋ถ€๋ถ„์„ ์ด์šฉํ•˜๋Š” ์„œ๋ฒ„ URL๋กœ ๋ณ€๊ฒฝ
        },
    }).then((res) => {
        console.log(res.data);
    })
</script>

 

cors.sh ํ”„๋ก์‹œ ์„œ๋ฒ„

  1. https://cors.sh/
  2. ์‹ค์ „์—์„œ ์ด์šฉํ•˜๋ ค๋ฉด ์œ ๋ฃŒ ๊ฒฐ์ œ๊ฐ€ ํ•„์š”ํ•˜๋“ฏ ํ•˜๋‹ค. (free๋„ ๊ฐ€๋Šฅ)
const url = 'https://google.com' // ์ด ๋ถ€๋ถ„์„ ์ด์šฉํ•˜๋Š” ์„œ๋ฒ„ URL๋กœ ๋ณ€๊ฒฝ

fetch(`https://proxy.cors.sh/${url}`)
    .then((response) => response.text())
    .then((data) => console.log(data));

3. ์„œ๋ฒ„์—์„œ Access-Control-Allow-Origin ํ—ค๋” ์„ธํŒ…ํ•˜๊ธฐ

์ง์ ‘ ์„œ๋ฒ„์—์„œ HTTP ํ—ค๋” ์„ค์ •์„ ํ†ตํ•ด ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•˜๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฐ€์žฅ ์ •์„์ ์ธ ํ•ด๊ฒฐ์ฑ…์ด๋‹ค.

์„œ๋ฒ„์˜ ์ข…๋ฅ˜๋„ ๋…ธ๋“œ ์„œ๋ฒ„, ์Šคํ”„๋ง ์„œ๋ฒ„, ์•„ํŒŒ์น˜ ์„œ๋ฒ„ ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์œผ๋‹ˆ, ์ด์— ๋Œ€ํ•œ ๊ฐ๊ฐ ํ•ด๊ฒฐ์ฑ…์„ ๋‚˜์—ดํ•ด๋ณธ๋‹ค.

๊ฐ ์„œ๋ฒ„์˜ ๋ฌธ๋ฒ•์— ๋งž๊ฒŒ ์œ„์˜ HTTP ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

์ฐธ๊ณ ๋กœ CORS์— ์—ฐ๊ด€๋œ HTTP ํ—ค๋” ๊ฐ’์œผ๋กœ๋Š” ๋‹ค์Œ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค. (์ด๋“ค์„ ๋ชจ๋‘ ์„ค์ •ํ•  ํ•„์š”๋Š” ์—†๋‹ค)

# ํ—ค๋”์— ์ž‘์„ฑ๋œ ์ถœ์ฒ˜๋งŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•จ.
# * ์ด๋ฉด ๋ชจ๋“  ๊ณณ์— ๊ณต๊ฐœ๋˜์–ด ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค. 
Access-Control-Allow-Origin : https://naver.com

# ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜๋Š” HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•ด ์ฃผ๋Š” ํ—ค๋”
Access-Control-Request-Methods : GET, POST, PUT, DELETE

# ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๋Š” ํ•ด๋”.
Access-Control-Allow-Headers : Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization

# ํด๋ผ์ด์–ธํŠธ์—์„œ preflight ์˜ ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๊ธฐ๊ฐ„์„ ์ง€์ •
# 60์ดˆ ๋™์•ˆ preflight ์š”์ฒญ์„ ์บ์‹œํ•˜๋Š” ์„ค์ •์œผ๋กœ, ์ฒซ ์š”์ฒญ ์ดํ›„ 60์ดˆ ๋™์•ˆ์€ OPTIONS ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ๋น„ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.
Access-Control-Max-Age : 60

# ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์ด ์ฟ ํ‚ค๋ฅผ ํ†ตํ•ด์„œ ์ž๊ฒฉ ์ฆ๋ช…์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— true. 
# ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์š”์ฒญ์—์„œ credentials๊ฐ€ include์ผ ๋•Œ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
Access-Control-Allow-Credentials : true

# ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ๋…ธ์ถœ์ด ๋˜์ง€ ์•Š์ง€๋งŒ, ๋ธŒ๋ผ์šฐ์ € ์ธก์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ—ˆ์šฉํ•ด์ฃผ๋Š” ํ—ค๋”๋ฅผ ์ง€์ •
Access-Control-Expose-Headers : Content-Length

 

Node.js ์„ธํŒ…

์„œ๋ฒ„์— response ํ—ค๋”(Header) ๊ฐ’์œผ๋กœ Access-Control ์„ค์ •์„ ํ•ด์ค€๋‹ค.

var http = require('http');

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

var httpServer = http.createServer(function (request, response) {
    // Setting up Headers
    response.setHeader('Access-Control-Allow-origin', '*'); // ๋ชจ๋“  ์ถœ์ฒ˜(orogin)์„ ํ—ˆ์šฉ
    response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ ํ—ˆ์šฉ
    response.setHeader('Access-Control-Allow-Credentials', 'true'); // ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์— ์ฟ ํ‚ค ์ฃผ๊ณ ๋ฐ›๊ธฐ ํ—ˆ์šฉ

    // ...

    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end('ok');
});

httpServer.listen(PORT, () => {
    console.log('Server is running at port 3000...');
});

์ด๋•Œ Access-Control-Allow-origin ํ—ค๋” ๊ฐ’์œผ๋กœ * ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  Origin์—์„œ ์˜ค๋Š” ์š”์ฒญ์„ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋ฏ€๋กœ ๋‹น์žฅ์€ ํŽธํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๋ฐ”๊ฟ”์„œ ์ƒ๊ฐํ•˜๋ฉด ์ •์ฒด๋„ ๋ชจ๋ฅด๋Š” ์ด์ƒํ•œ ์ถœ์ฒ˜์—์„œ ์˜ค๋Š” ์š”์ฒญ๊นŒ์ง€ ๋ชจ๋‘ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ์€ ๋” ํ—ˆ์ˆ ํ•ด์ง„๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ๊ฐ€๊ธ‰์ ์ด๋ฉด ๊ท€์ฐฎ๋”๋ผ๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถœ์ฒ˜๋ฅผ ์ง์ ‘ ๋ช…์‹œํ•ด์ฃผ๋„๋ก ํ•˜์ž.

response.setHeader('Access-Control-Allow-origin', 'https://inpa.tistory.com');

 

Express.js ์„ธํŒ…

> npm i cors
const express = require('express')
const cors = require("cors"); // cors ์„ค์ •์„ ํŽธ์•ˆํ•˜๊ฒŒ ํ•˜๋Š” ํŒจํ‚ค์ง€
const app = express();

// ...

app.use(cors({
    origin: "https://naver.com", // ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ๋„๋ฉ”์ธ
    credentials: true, // ์‘๋‹ต ํ—ค๋”์— Access-Control-Allow-Credentials ์ถ”๊ฐ€
    optionsSuccessStatus: 200, // ์‘๋‹ต ์ƒํƒœ 200์œผ๋กœ ์„ค์ •
}));

// ...
 

[NODE] ๐Ÿ“š CORS ์„ค์ •ํ•˜๊ธฐ (cors ๋ชจ๋“ˆ)

CORS ๋ž€? [WEB] ๐Ÿ“š CORS ๐Ÿ’ฏ ์ •๋ฆฌ & ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๐Ÿ‘ CORS(Cross Origin Resource Sharing) CORS ์ •์ฑ…์€ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ ธ์˜ค๋Š” ๋ฆฌ์†Œ์Šค๋“ค์ด ์•ˆ์ „ํ•œ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ด€๋ฌธ์ด๋‹ค. ์›น๊ฐœ๋ฐœ์„ ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์€ ์ด CORS ์ •์ฑ…์œ„๋ฐ˜์œผ๋กœ ์ธํ•ด

inpa.tistory.com

 

JSP / Servlet ์„ธํŒ…

import javax.servlet.*;

public class CORSInterceptor implements Filter {

    private static final String[] allowedOrigins = {
            "http://localhost:3000", "http://localhost:5500", "http://localhost:5501",
            "http://127.0.0.1:3000", "http://127.0.0.1:5500", "http://127.0.0.1:5501"
    };

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String requestOrigin = request.getHeader("Origin");
        if(isAllowedOrigin(requestOrigin)) {
            // Authorize the origin, all headers, and all methods
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", requestOrigin);
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Headers", "*");
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods",
                    "GET, OPTIONS, HEAD, PUT, POST, DELETE");

            HttpServletResponse resp = (HttpServletResponse) servletResponse;

            // CORS handshake (pre-flight request)
            if (request.getMethod().equals("OPTIONS")) {
                resp.setStatus(HttpServletResponse.SC_ACCEPTED);
                return;
            }
        }
        // pass the request along the filter chain
        filterChain.doFilter(request, servletResponse);
    }

    private boolean isAllowedOrigin(String origin){
        for (String allowedOrigin : allowedOrigins) {
            if(origin.equals(allowedOrigin)) return true;
        }
        return false;
    }
}

 

Spring ์„ธํŒ…

// ์Šคํ”„๋ง ์„œ๋ฒ„ ์ „์—ญ์ ์œผ๋กœ CORS ์„ค์ •
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        	.allowedOrigins("http://localhost:8080", "http://localhost:8081") // ํ—ˆ์šฉํ•  ์ถœ์ฒ˜
            .allowedMethods("GET", "POST") // ํ—ˆ์šฉํ•  HTTP method
            .allowCredentials(true) // ์ฟ ํ‚ค ์ธ์ฆ ์š”์ฒญ ํ—ˆ์šฉ
            .maxAge(3000) // ์›ํ•˜๋Š” ์‹œ๊ฐ„๋งŒํผ pre-flight ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์บ์‹ฑ
    }
}
// ํŠน์ • ์ปจํŠธ๋กค๋Ÿฌ์—๋งŒ CORS ์ ์šฉํ•˜๊ณ  ์‹ถ์„๋•Œ.
@Controller
@CrossOrigin(origins = "*", methods = RequestMethod.GET) 
public class customController {

	// ํŠน์ • ๋ฉ”์†Œ๋“œ์—๋งŒ CORS ์ ์šฉ ๊ฐ€๋Šฅ
    @GetMapping("/url")  
    @CrossOrigin(origins = "*", methods = RequestMethod.GET) 
    @ResponseBody
    public List<Object> findAll(){
        return service.getAll();
    }
}

 

Apache ์„ธํŒ…

httpd.conf์—์„œ <IfModule mod_headers.c> ํƒœ๊ทธ ์•ˆ์— ํ—ค๋” ์„ค์ •์„ ๋„ฃ์–ด์ค€๋‹ค. ๋˜๋Š” VirualHost ๋ถ€๋ถ„์— ๋„ฃ์–ด๋„ ๋œ๋‹ค.

<IfModule mod_headers.c>
	Header set Access-Control-Allow-Origin "*"
</IfModule>

 

Tomcat

ํ†ฐ์บฃ ํด๋” ๊ฒฝ๋กœ์˜ conf/web.xml ํ˜น์€ webapps ๋‚ด์˜ ํ”„๋กœ์ ํŠธ ํด๋” ๋‚ด WEB-INF/web.xml ํŒŒ์ผ์— ์•„๋ž˜ ํ—ค๋” ์„ค์ •์„ ์ถ”๊ฐ€

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>*</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
    </init-param>
    <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
    </init-param>
    <init-param>
    	<!-- ์ฟ ํ‚ค ํ†ต์‹ ์„ ์•ˆํ•˜๋Š”๋ฐ ์ด๊ฑธ true๋กœ ํ•˜๋ฉด 4XX ์„œ๋ฒ„ ์—๋Ÿฌ๊ฐ€ ๋œฌ๋‹ค -->
        <param-name>cors.support.credentials</param-name>
        <param-value>false</param-value> 
    </init-param>
    <init-param>
        <param-name>cors.preflight.maxage</param-name>
        <param-value>10</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

Nginx

nginx.conf ํŒŒ์ผ ์•ˆ์— location / ๋ถ€๋ถ„์— add_header ๊ฐ’์œผ๋กœ ํ—ค๋” ์„ค์ •์„ ์ถ”๊ฐ€

location / {
    root html;
    add_header 'Access-Control-Allow-Origin' '*';
    index  index.html index.htm;
}

 

AWS (S3 ํ˜ธ์ŠคํŒ…)

  1. S3 ์ฝ˜์†” ๋ฉ”๋‰ด์— ๋“ค์–ด๊ฐ€ ๋ฒ„ํ‚ท์„ ์„ ํƒํ•œ๋‹ค.
  2. ๊ถŒํ•œ(Permissions) ํƒญ์„ ์„ ํƒํ•œ๋‹ค.
  3. ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  ์ฐฝ์—์„œ [ํŽธ์ง‘] ์„ ํƒํ•œ๋‹ค.
  4. ํ…์ŠคํŠธ ์ƒ์ž์— ์•„๋ž˜ JSON CORS ๊ทœ์น™์„ ์ž…๋ ฅํ•œ๋‹ค.

s3-cors

[
  {
    "AllowedHeaders": [
      "Authorization"
    ],
    "AllowedMethods": [
      "GET",
      "HEAD"
    ],
    "AllowedOrigins": [
      "http://www.example.com"
    ],
    "ExposeHeaders": [
      "Access-Control-Allow-Origin"
    ]
  }
]

์ถ”๊ฐ€์ ์œผ๋กœ ์•Œ์•„์•ผ ํ•˜๋Š” CORS ์‚ฌ๋ก€

์••๋„์ ์ธ ํฌ์ŠคํŒ…๋Ÿ‰์„ ์ด๊ฒจ๋‚ด๊ณ  ์—ฌ๊ธฐ๊นŒ์ง€ ์Šคํฌ๋กคํ•˜์—ฌ ํ•™์Šตํ•œ ๋…์ž๋ถ„๋“ค๊ป˜ ์กด๊ฒฝ์˜ ๋ฐ•์ˆ˜๋ฅผ ๋ณด๋‚ธ๋‹ค. ํ•˜์ง€๋งŒ ๊ฒจ์šฐ๊ฒจ์šฐ CORS ์ •์ฑ…์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ณ  ํ•ด๊ฒฐ๋ฒ•์„ ์ ์šฉ ์‹œ์ผฐ๋”๋‹ˆ ๋˜ ๊ณต๋ถ€ํ• ๊ฒŒ ์žˆ๋‹ค๊ณ  ํ•˜๋‹ˆ ๋ฉ˜ํƒˆ์ด ํ”๋“ค๋ฆฐ๋‹ค. ๋‹ค๋งŒ ์ด ๋ถ€๋ถ„์€ ์‹ค๋ฌด์— ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ง€๊ธˆ ๋‹น์žฅ ํ•™์Šตํ•  ํ•„์š”์„ฑ์€ ์ ์œผ๋ฉฐ ์กฐ๊ธˆ ์‹ฌํ™”์ ์ธ ๋‚ด์šฉ์ด๋ผ ๋‚œ์ด๋„๋„ ์žˆ์–ด, ์‹œ๊ฐ„์žˆ์„๋•Œ ๋‚˜์ค‘์— ํ•™์Šตํ•˜๊ธธ ๊ถŒ์žฅํ•œ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ์•Œ์•„์•ผ ํ•˜๋Š” CORS ์‚ฌ๋ก€

 

CORS์˜ ๋ณด์•ˆ ์ทจ์•ฝ์  ๊ฐ€์ด๋“œ

๊ฐ€๋งŒ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ์›๋ž˜๋Š” SOP ์ •์ฑ…์œผ๋กœ ๋ง‰ํ˜”์–ด์•ผํ•  ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋“ค์ด CORS ์ •์ฑ…์œผ๋กœ ์–ต์ง€๋กœ ๋šซ์–ด์คฌ์œผ๋‹ˆ ๊ณต๊ฒฉ์— ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋˜๋Š”๊ฑด ๋‹น์—ฐํ•œ ์ˆ˜์ˆœ์ผ์ง€ ๋ชจ๋ฅธ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด CORS ์ •์ฑ…์— ๋Œ€ํ•ด ์„œ๋ฒ„์—์„œ ๋„ˆ๋ฌด ์œ ์—ฐํ•˜๊ฒŒ ๋ฆฌ์†Œ์Šค ํ—ˆ์šฉ ์„ค์ •์„ ํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ, ํ•ด๋‹น ์›น์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ๋ฆ„์„ ์•…์šฉํ•˜์—ฌ ํƒ€์ธ์˜ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ํ•ดํ‚นํ•  ์œ„ํ—˜์„ฑ์ด ์žˆ๊ฒŒ ๋œ๋‹ค.

๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ ํ—ˆ์šฉํ•  ๋„๋ฉ”์ธ๋“ค์„ ์„ค์ •ํ•˜๊ธฐ ๊ท€์ฐฎ๋‹ค๊ณ  ์™€์ผ๋“œ ์นด๋“œ(*)๋กœ ํ‰์น˜๋Š”๊ฑธ ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์„œ๋ฒ„ ๊ฐœ๋ฐœ์ž๊ฐ€ CORS ์ •์ฑ…์„ ์ž˜๋ชป ๊ตฌ์„ฑํ•  ๊ฒฝ์šฐ ์‹ฌ๊ฐํ•œ ๋ณด์•ˆ ์œ„ํ˜‘์ด ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค. ์ด์— ๋Œ€ํ•œ ๋ณด์•ˆ ๋ฌธ์ œ์  ์˜ˆ๋ฐฉ ๊ฐ€์ด๋“œ์— ๋Œ€ํ•ด ๋‹ค์Œ ํฌ์ŠคํŒ…์œผ๋กœ ๋‚˜๋ˆ ๋ณธ๋‹ค.

 

[WEB] ๐ŸŒ CORS ๋ณด์•ˆ ์ทจ์•ฝ์  ์˜ˆ๋ฐฉ ๊ฐ€์ด๋“œ

CORS์˜ ๋ณด์•ˆ ๋ฌธ์ œ์  ๋‹ค๋ฅธ ์ถœ์ฒ˜(Origin)์˜ ์„œ๋ฒ„์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ์•ฝ์—†์ด ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ XSS(Cross-Site Scripting)๋‚˜ CSRF(Cross-Site Request Fogery)์™€ ๊ฐ™์€ ์Šคํฌ๋ฆฝํŒ… ๊ณต๊ฒฉ์„ ๋‹นํ•  ์œ„ํ—˜์„ฑ์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ํƒ„์ƒํ•œ

inpa.tistory.com


Chorme PNA CORS ๋ฌธ์ œ

๋งŒ์ผ ์œ„์—์„œ ๋ณธ ์ผ๋ฐ˜์ ์ธ CORS ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ์•„๋‹Œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋˜๋‹ค๋ฅธ CORS ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ๋‚˜์™”๋‹ค๋ฉด, ๋ณ„๋„์˜ ์‚ฌ์„ค๋ง ์ ‘๊ทผ(private network access)์— ๋Œ€ํ•œ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค.

Chorme PNA CORS

"The request client is not a secure context and the resource is in more-private adress space local"

๊ฐ„๋‹จํžˆ ๋งํ•˜์ž๋ฉด ๊ณต์ธ ๋„คํŠธ์›Œํฌ์˜ ์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์„ค ๋„คํŠธ์›Œํฌ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ํ˜ธ์ถœํ• ๋•Œ ์‚ฌ์„ค๋ง ์ ‘๊ทผ ๊ด€๋ จ CORS ์—๋Ÿฌ๊ฐ€ ๋œจ๊ฒŒ ๋˜๋Š”๋ฐ, ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค.

 

๐ŸŒ ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ € PNA ๊ถŒํ•œ๊ณผ CORS ํ•ด๊ฒฐํ•˜๊ธฐ

Chrome PNA (Private Network Access) ์‚ฌ์„ค๋ง ์ ‘๊ทผ(private network access) ์ด๋ž€, ๋น„์ธ์ฆ๋œ ๊ณต์ธ(public) ์›น์‚ฌ์ดํŠธ์—์„œ, ์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธํ•œ ์‚ฌ์šฉ์ž์˜ ์™€ ๊ฐ™์€ ์‚ฌ์„ค ๋„คํŠธ์›Œํฌ๋ง(localhost(127.0.0.1) or 192.168.0.* ์•„์ดํ”ผ) ์—”๋“œ

inpa.tistory.com


๋ธŒ๋ผ์šฐ์ € Cache์— ์˜ํ•œ CORS ๋ฌธ์ œ

๋ธŒ๋ผ์šฐ์ €๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋ฏธ์ง€์™€ ๊ฐ™์€ ๊ณ ์šฉ๋Ÿ‰ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•ด์„œ ํ•œ๋ฒˆ ์š”์ฒญํ•ด์„œ ์‘๋‹ต๋ฐ›์œผ๋ฉด ์ž๋™์œผ๋กœ cacheํ•˜์—ฌ ์ €์žฅํ•ด๋†“๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋‚˜์ค‘์— ๋˜‘๊ฐ™์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์žฌ์š”์ฒญํ•ด๋„ ์บ์‹œ ์ €์žฅ์†Œ์—์„œ ๋Œ์–ด๋‹ค ์“ฐ๋ฉด ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์—†์ด ๋น ๋ฅด๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด ์„ฑ๋Šฅ ์ƒ ๊ต‰์žฅํ•œ ์ด์ ์„ ์–ป๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์บ์‹œ์— ๋Œ€ํ•ด์„œ ๊ธฐ๋ณธ์ ์ธ ์ง€์‹์ด ์žˆ๋Š” ๋…์ž๋ถ„๋“ค์ด๋ผ๋ฉด ์ด ์บ์‹œ ๊ธฐ๋Šฅ ๋•Œ๋ฌธ์— ์ตœ์‹  ๋ฆฌ์†Œ์Šค ๋ถˆ์ผ์น˜ ํ˜„์ƒ ๋ฐ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ž์ž˜ํ•œ ๋ฌธ์ œ์ ์ด ์กด์žฌํ•œ๋‹ค๋Š” ๋‹จ์ ์€ ์•Œ๊ณ  ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ฆ‰, CORS ์—ญ์‹œ ์ด๋Ÿฌํ•œ ์บ์‹œ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ๋œฌ๊ธˆ์—†์ด ๋ฐœ์ƒ๋ ์ˆ˜๋„ ์žˆ๋‹ค.

 

๐ŸŒ ๋ฆฌ์†Œ์Šค ์บ์‹œ๋กœ ์ธํ•œ CORS ์—๋Ÿฌ ํ˜„์ƒ ๊ณ ์ฐฐ

๋ธŒ๋ผ์šฐ์ € ์บ์‹œ๋กœ ์ธํ•œ CORS ๋ฌธ์ œ CORS(Cross-Origin Resource Sharing)๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜(Origin)์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์„๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ •์ฑ…์„ ๋งํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๋Š” SOP(Same Origin Policy) ์ •์ฑ…์„ ๋”ฐ๋ฅด

inpa.tistory.com


# ์ฐธ๊ณ ์ž๋ฃŒ

https://evan-moon.github.io/2020/05/21/about-cors/

https://core-research-team.github.io/2021-04-01/Easy-to-understand-Web-security-model-story-1%28SOP,-CORS%29

https://blog.devgenius.io/cors-34e947046600

https://westsideelectronics.com/cors-and-how-to-fix/ 

https://blog.bitsrc.io/4-ways-to-reduce-cors-preflight-time-in-web-apps-1f47fe7558