-
CORS์ SOP, ํด๊ฒฐ๋ฐฉ๋ฒ์?์นดํ ๊ณ ๋ฆฌ ์์ 2023. 3. 20. 01:20
CORS์ ๋ํด ์์๋ณด๊ธฐ ์ , ๋จผ์ SOP๋ฅผ ์์๋ด์ผ ํ๋ค. CORS๋ ๊ฒฐ๊ตญ SOP๊ฐ ์ง์ผ์ง์ง ์์์ ๋ ๋ฐ์ํ๋ ์ค๋ฅ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
SOP (Same-Origin-Policy)
SOP๋ ๋ง ๊ทธ๋๋ก ๋์ผํ ์ถ์ฒ์์๋ง ๋ฆฌ์์ค๋ฅผ ๊ณต์ ํ ์ ์๋ค๋ ๊ท์น์ ๊ฐ์ง ์ ์ฑ ์ด๋ค. ๋ URL๊ฐ์ Scheme, Host, Port์ค ํ๋๋ผ๋ ๋ค๋ฅด๋ฉด SOP๋ฅผ ์๋ฐํ ๊ฒ์ด ๋๋ค.
์๋ ์์๋ก ๊ฐ์ Origin์ธ์ง ์กฐ๊ธ ๋ ์์๋ณด์
๋์ ๋ธ๋ก๊ทธ ์ถ์ฒ์ธhttps://minju-k.tistory.com/
์ ์ถ์ฒ๋ฅผ ํ๋ฒ ๋น๊ตํด๋ณด์.CORS (Cross-Origin Resource Sharing)
CORS๋ ๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ ๋ผ๊ณ ํด์ํ ์ ์๋๋ฐ, ๊ต์ฐจ ์ถ์ฒ๋ผ๋ ๊ฒ์ ๊ฒฐ๊ตญ
๊ฐ์ ์ถ์ฒ
๊ฐ ์๋๋ผ๋ ๊ฒ์ด๋ค. ์์ ์ดํด๋ณธ SOP์ ํด๋น๋๋ ๊ฒ ์๋๋ฉด ๊ต์ฐจ ์ถ์ฒ๋ผ๊ณ ๋ณด๋ ๊ฒ์ด๊ณ , ์ด๋ ๊ฒ SOP ๊ฐ ์๋ ๊ณณ์์ ์์์ ๋ฐ์์ค๋ ๊ฒ์ ๋ง์์ฃผ๋๊ฒ CORS์ ์ฑ ์ด๋ผ๊ณ ํ ์ ์๋ค.์ฌ๊ธฐ์ ๊ธฐ์ตํด์ผ ํ ์ฌ์ค์ ์ด๋ ๊ฒ ์ถ์ฒ๋ฅผ ๋น๊ตํ๋ ๋ก์ง์ด ์๋ฒ์ ๊ตฌํ๋ ์คํ์ด ์๋๋ผ ๋ธ๋ผ์ฐ์ ์ ๊ตฌํ๋์ด ์๋ ์คํ์ด๋ผ๋ ๊ฒ์ด๋ค. ๋ง์ฝ ์ฐ๋ฆฌ๊ฐ CORS ์ ์ฑ ์ ์๋ฐํ๋ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ๋๋ผ๋, ํด๋น ์๋ฒ๊ฐ ๊ฐ์ ์ถ์ฒ์์ ๋ณด๋ธ ์์ฒญ๋ง ๋ฐ๊ฒ ๋ค๋ ๋ก์ง์ ๊ฐ์ง๊ณ ์๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์๋ฒ๋ ์ ์์ ์ผ๋ก ์๋ต์ ํ๊ณ , ์ดํ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ฒ๊ฐ ๋ณด๋ธ ์๋ต์ ๋ถ์ํด์ CORS ์ ์ฑ ์๋ฐ์ด๋ผ๊ณ ํ๋จ๋๋ฉด ๊ทธ ์๋ต์ ์ฌ์ฉํ์ง ์๊ณ ๋ฒ๋ฆฌ๋ ์์ผ๋ก ์๋ํ๋ค.
CORS๋ ๋ธ๋ผ์ฐ์ ์ ์ฑ ์ด๊ธฐ ๋๋ฌธ์, ์๋ฒ ๊ฐ ํต์ ํ ๋์๋ ์ด ์ ์ฑ ์ด ์ ์ฉ๋์ง ์์ผ๋ฉฐ, CORS ์ ์ฑ ์ ์๋ฐํ๋ ๋ฆฌ์์ค ์์ฒญ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๊ณ ํด๋ ์๋ฒ ์ชฝ ๋ก๊ทธ์๋ ์ ์์ ์ผ๋ก ์๋ตํ๋ค๋ ๋ก๊ทธ๋ง ๋จ๋๋ค๋ ๊ฒ์ ๊ธฐ์ตํด์ผ ํ๋ค.
CORS์ ๋์๋ฐฉ์
- ๊ธฐ๋ณธ์ ์ผ๋ก ํด๋ผ์ด์ธํธ์์ ์๋ฒ์ ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ ๋์๋ HTTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์์ฒญ์ ๋ณด๋ด๋๋ฐ, ์ด ๋ ๋ธ๋ผ์ฐ์ ๋ ์์ฒญ ํค๋์
Origin
์ด๋ผ๋ ํ๋ฑ ์์ฒญ์ ๋ณด๋ด๋ ์ถ์ฒ๋ฅผ ํจ๊ป ๋ด์ ๋ณด๋ธ๋ค. - ์ดํ ์๋ฒ๊ฐ ์์ฒญ์ ๋ํ ์๋ต์ ํ ๋, ์๋ต ํค๋์
Access-Control-Allow-Origin
์ด๋ผ๋ ๊ฐ์ ์ด ๋ฆฌ์์ค๋ฅผ ์ ๊ทผํ๋ ๊ฒ์ด ํ์ฉ๋ ์ถ์ฒ๋ฅผ ๋ด๋ ค์ฃผ๋ฉด - ๋ธ๋ผ์ฐ์ ๋ ์์ ์ด ๋ณด๋๋ ์์ฒญ์
Origin
๊ฐ๊ณผ ์๋ฒ๊ฐ ๋ณด๋ด์คAccess-Control-Allow-Origin
์ ๋น๊ตํ ํ ์๋ต์ด ์ ํจํ ์๋ต์ด๋ผ๋ฉด ์ฌ์ฉ, ์๋๋ผ๋ฉด CORS์๋ฌ๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ด๋ค.
๊ธฐ๋ณธ์ ์ธ ํ๋ฆ์ ์์ ๊ฐ๊ณ , CORS๊ฐ ๋์ํ๋ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ด ์ธ ๊ฐ์ง์ ์๋๋ฆฌ์ค์ ๋ฐ๋ผ ๋ณ๊ฒฝ๋๋ค.
1. Preflight Request
ํ๋ฆฌํ๋ผ์ดํธ ๋ฐฉ์์ ์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ๋ฐํ ๋ ๊ฐ์ฅ ๋ง์ด ๋ง์ฃผ์น๋ ์๋๋ฆฌ์ค์ด๋ค. ์ด ์๋๋ฆฌ์ค๋ ๋ณธ ์์ฒญ์ ์์ HTTP OPTIONS๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ์ค์ค๋ก ์ด ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ด ์์ ํ์ง ํ์ธํ๋ ์ ์ฐจ์ธ๋ฐ ์ด๋ฅผ Preflight ์์ฒญ์ด๋ผ๊ณ ํ๋ค.
mdn์์ ๊ฐ์ ธ์จ ์์ ๋ก ํ๋ฒ ์ดํด๋ณด์.
๋ด๊ฐ ๋ค์๊ณผ ๊ฐ์ด POST๋ก ์ด๋ค ์์ฒญ์ ๋ณด๋ธ๋ค๊ณ ํด๋ณด์. Content-type์ด application/xml์ด๊ณ , ์ฌ์ฉ์ ์ ์ ํค๋๊ฐ ์ค์ ๋์๊ธฐ ๋๋ฌธ์ ์ด ์์ฒญ์ ๋ธ๋ผ์ฐ์ ์ ์ํด preflighted ์ฒ๋ฆฌ๊ฐ ๋๋ค.// preflight ์์ฒญ ์์ const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://bar.other/resources/post-here/'); xhr.setRequestHeader('Ping-Other', 'pingpong'); xhr.setRequestHeader('Content-Type', 'application/xml'); xhr.onreadystatechange = handler; xhr.send('<person><name>Arun</name></person>');
์ ์์ฒญ์ ์ดํด๋ณด๋ฉด ๋ณธ ์์ฒญ์ ์์ Preflight request๊ฐ ์ผ์ด๋ ๊ฒ์ ๋ณผ ์ ์๋ค. OPTIONS ์์ฒญ์ผ๋ก Origin์ ์ ๋ฌํ๊ณ , ์ด์ ๋ํ ์๋ต์ผ๋ก ์๋ฒ๋ Access-Control-Allow-Origin์ ํตํด ์ด ๋ฆฌ์์ค์ ์ ๊ทผ์ด ๊ฐ๋ฅํ ์ถ์ฒ๊ฐ https:/foo.example์ด๋ผ๊ณ ์๋ ค์ฃผ์๋ค. ์์ฒญ์ ๋ณด๋ธ Origin๊ณผ ์๋ฒ๊ฐ ์๋ต์ผ๋ก ์ค ์ถ์ฒ๊ฐ ๊ฐ์ผ๋ฏ๋ก CORS ์ ์ฑ ์ ์๋ฐํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ ์์ ์ผ๋ก ๋ณธ ์์ฒญ์ด ์งํ๋ ์ ์๋ค.
๋ง์ฝ ์ฌ๊ธฐ์ ์๋ฒ๊ฐ ๋ด๋ ค์ค Access-Control-Allow-Origin๊ฐ์ด https://minju-k.com์ด๋ผ๊ณ ํ๋๋ผ๋ ์๋ต์ ํ์ธํด๋ณด๋ฉด ์ ์์ ์ผ๋ก 200 OK๊ฐ ๋ฌ์ ๊ฒ์ด๊ณ , ํ์ง๋ง CORS์๋ฌ๊ฐ ๋ฌ์ ๊ฒ์ด๋ค. ์ค์ ๋ก ๋ธ๋ผ์ฐ์ ๊ฐ ์ถ์ฒ๋ฅผ ํ์ธํ๋ ๊ณผ์ ์ ์๋ฒ์์ ์๋ต์ ๋ฐ์ ํ ์ด๊ธฐ ๋๋ฌธ์, ์ค์ํ ๊ฒ์ ์๋น ์์ฒญ์ ์ฑ๊ณต/์คํจ ์ฌ๋ถ๊ฐ ์๋๋ผ, ์๋ต ํค๋์ ์ ํจํ Access-Control-Allow-Origin ๊ฐ์ด ์กด์ฌํ๋๊ฐ์ด๋ค.
2. Simple Request
๋ค์๊ณผ ๊ฐ์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ผ๋ถ ์์ฒญ์ CORS preflight๊ฐ ์ผ์ด๋์ง ์๊ณ ๋ฐ๋ก ๋ณธ ์์ฒญ์ ํ๋ Simple Request๋ง ์ผ์ด๋๋ค.
- ์์ฒญ์ ๋ฉ์๋๋
GET
,HEAD
,POST
์ค ํ๋์ฌ์ผ ํ๋ค. Accept
,Accept-Language
,Content-Language
,Content-Type
,DPR
,Downlink
,Save-Data
,Viewport-Width
,Width
๋ฅผ ์ ์ธํ ํค๋๋ฅผ ์ฌ์ฉํ๋ฉด ์๋๋ค.- ๋ง์ฝ
Content-Type
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋application/x-www-form-urlencoded
,multipart/form-data
,text/plain
๋ง ํ์ฉ๋๋ค.
์์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ๋ฐ๋ก ์์ฒญ์ ํ๋ฉฐ Origin์ ๋ณด๋ด๋ฉด, ์๋ฒ๋ ์ด์ ๋ํ ์๋ต์ผ๋ก Access-Control-Allow-Origin ๊ฐ์ ๋ด๋ ค์ค๋ค.
3. Credentialed Request
์ธ ๋ฒ์งธ ์๋๋ฆฌ์ค๋ ์ธ์ฆ๋ ์์ฒญ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ผ๋ก, ์ด ์๋๋ฆฌ์ค๋ CORS์ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์์ด๋ผ๊ธฐ ๋ณด๋ค๋ ๋ค๋ฅธ ์ถ์ฒ ๊ฐ ํต์ ์์ ๋ณด์์ ๋ ๊ฐํํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๊ธฐ๋ณธ์ ์ธ XMLHttpRequest ๊ฐ์ฒด๋ fetch API๋ ๋ณ๋์ ์ต์ ์์ด ๋ธ๋ผ์ฐ์ ์ ์ฟ ํค ์ ๋ณด๋ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ํค๋๋ฅผ ํจ๋ถ๋ก ์์ฒญ์ ๋ด์ง ์๋๋ฐ, ์ด ๋ ์์ฒญ์ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ ๋ด์ ์ ์๊ฒ ํด์ฃผ๋ ์ต์ ์ด ๋ฐ๋ก
credentials
์ต์ ์ด๋ค.์ด ์ต์ ์๋ ์๋ 3 ๊ฐ์ง ๊ฐ์ ์ฌ์ฉํ ์ ์๋ค.
๋ง์ฝ ๋ด๊ฐ same-origin์ด๋ include์ ๊ฐ์ ์ต์ ์ ์ฌ์ฉํ์ฌ ๋ฆฌ์์ค ์์ฒญ์ ์ธ์ฆ์ ๋ณด๊ฐ ํฌํจ๋๋ค๋ฉด, ๋ธ๋ผ์ฐ์ ๋ ์ถ์ฒ๋ฅผ ํ์ธํ ๋ Access-Control-Allow-Origin ์ธ์ ์ถ๊ฐ๋ก ๋ ๊ฒ์ฌ ์กฐ๊ฑด์ ๊ฐ์ ธ๊ฐ ํ์ธํ๊ฒ ๋๋ค.
๊ทธ ๋ ๊ฐ์ง ์กฐ๊ฑด์ ์๋์ ๊ฐ๋ค.
Access-Control-Allow-Origin
์๋*
๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ๋ช ์์ ์ธ URL์ด์ด์ผํ๋ค.- ์๋ต ํค๋์๋ ๋ฐ๋์
Access-Control-Allow-Credentials: true
๊ฐ ์กด์ฌํด์ผํ๋ค.
๋ฐ๋ผ์ ๋ง์ฝ ๋ด๊ฐ ์๋ ์ฒ๋ผ credentials ํ๋์ include ๊ฐ์ ๋ฃ์ด ๋ณด๋๋ค๋ฉด, ์๋ฒ์ ์์ผ๋ ์นด๋๊ฐ ๋์ด ์์ด๋ CORS ์๋ฌ๊ฐ ๋๋ฉฐ, Access-Control-Allow-Credentials๊ฐ์ด ์๋ค๋ฉด CORS ์๋ฌ๊ฐ ๋๋ค๋ ๊ฒ์ด๋ค.
fetch('https://minju-k.com/feed.xml', { credentials: 'include', // Credentials ์ต์ ๋ณ๊ฒฝ! });
CORS ์๋ฌ ํด๊ฒฐํ๊ธฐ
CORS ์๋ฌ๊ฐ ๋ฌด์์ธ์ง ํ์ธํด ๋ณด์์ผ๋, ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์์์ผ ํ๊ฒ ๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ 1. Access-Control-Allow-Origin
์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ์๋ฒ์ Access-Control-Allow-Origin์ ๋ด๊ฐ ์์ฒญํ๋ Origin์ ๋ฃ์ด์ฃผ๋ ๋ฐฉ๋ฒ์ด๋ค. Origin๊ณผ ํด๋น ๊ฐ์ด ๋ค๋ฅผ ๋์ CORS๊ฐ ๋ฐ์ํ๋ฏ๋ก ์ด ๊ฐ์ ์ธํ ํ๋ฉด CORS ์๋ฌ๊ฐ ํด๊ฒฐ๋๋ ๊ฒ์ ๋๋ฌด๋ ๋น์ฐํ ๊ฒ ๊ฐ๋ค.
*
์์ผ๋ ์นด๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ ์ถ์ฒ์์ ์ค๋ ์์ฒญ์ ๋ค ๋ฐ๊ฒ ๋ค๊ณ ์ค์ ํ ์๋ ์๊ฒ ์ง๋ง, ์ด๋ ๊ฒ ์ค์ ํ ๊ฒฝ์ฐ ์ด์ํ ์ถ์ฒ์์ ์ค๋ ์์ฒญ๋ ๋ค ๋ฐ๊ฒ ๋๋ฏ๋ก ๋ณด์์ ์ธ ์ด์๊ฐ ๋ฐ์ํ ์๋ ์๊ธฐ์ URL์ ๋ช ์ํด ์ฃผ๋ ๊ฒ์ด ์ข์ ๋ฐฉ์์ด๋ค.ํด๊ฒฐ๋ฐฉ๋ฒ 2. Webpack Dev Server๋ก ๋ฆฌ๋ฒ์ค ํ๋ก์ฑํ๊ธฐ
ํ๋ก ํธ์๋ ํ๊ฒฝ์์ ๊ฐ๋ฐํ๋ค๋ณด๋ฉด ์๋ฒ์์
http://localhost:3000
๊ณผ ๊ฐ์ ๋ฒ์ฉ์ ์ถ์ฒ๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฒฝ์ฐ๊ฐ ๋๋ฌผ๊ธฐ ๋๋ฌธ์ CORS ์๋ฌ๋ฅผ ๋ง์ฃผ์น ํ๋ฅ ์ด ๋๋ค. ์ด๋ด ๋์, ์๋ฒ์ ํด๋น ์ฃผ์๋ฅผ ํค๋์ Access-Control-Allow-Origin์ ๋ฃ์ด๋ฌ๋ผ๊ณ ํ๋ ๊ฒ ์ธ์ ํ๋ก ํธ์๋ ๋จ์์ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ webpack-dev-server๊ฐ ์ ๊ณตํ๋ ํ๋ก์ ๊ธฐ๋ฅ์ ์ด์ฉํ๋ ๊ฒ์ด๋ค.module.exports = { devServer: { proxy: { '/api': { target: 'https://api.minju-k.com', changeOrigin: true, pathRewrite: { '^/api': '' }, }, } } }
์์ ๊ฐ์ด ์ค์ ํ๊ฒ ๋๋ฉด
/api
๋ก ์์ํ๋ URL๋ก ๋ณด๋ด๋ ์์ฒญ์ ๋ํด ๋ธ๋ผ์ฐ์ ๋localhost:8000/api
๋ก ์์ฒญ์ ๋ณด๋ธ ๊ฒ์ผ๋ก ์๊ณ ์์ง๋ง, ์นํฉ์ดhttps://api.minju-k.com
์ผ๋ก ์์ฒญ์ ํ๋ก์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์ CORS ์ ์ฑ ์ ์งํจ ๊ฒ์ฒ๋ผ ๋ธ๋ผ์ฐ์ ๋ฅผ ์์ด๋ฉด์๋ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์๋ฒ์ ํต์ ์ ์์ ๋กญ๊ฒ ํ ์ ์๋ค.๋ค๋ง, ์ด ๋ฐฉ๋ฒ์ ์ค์ ํ๋ก๋์ ํ๊ฒฝ์๋ ์ ์ฉ๋์ง ์๋๋ค. ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ์นํฉ์ด ์์ฒญ์ ํ๋ก์ํด์ฃผ์ง๋ง ์ค์ ํ๋ก์ ํธ๋ฅผ ๋น๋ํ๊ณ ์๋ฒ์ ์ฌ๋ฆฌ๋ฉด ๋์ด์ webpack-dev-server๊ฐ ๊ตฌ๋ํ๋ ํ๊ฒฝ์ด ์๋๊ธฐ ๋๋ฌธ์ ์ฃผ์ํด์ผ ํ๋ค.
๋ฐฐํฌํ ๋, API๊ฐ ๋๋ฉ์ธ์์ ์ ๊ณต์ด ๋๋ ๊ฒฝ์ฐ์๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง, API์ ๋๋ฉ์ธ๊ณผ ์๋น์ค์ ๋๋ฉ์ธ์ด ๋ค๋ฅด๋ค๋ฉด axios์ ๊ธ๋ก๋ฒ baseURL ์ ์ค์ ํด์ฃผ๋ฉด ๋๋ค.
axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? '/' : '{API ๋๋ฉ์ธ}';
์ด๋ ๊ฒ ํด์ฃผ๋ฉด ๊ฐ๋ฐํ๊ฒฝ์์๋ ํ๋ก์ ์๋ฒ๋ก ์์ฒญํ๊ณ , ์ค์ ํ๋ก๋์ ์์๋ ์ค์ API ์๋ฒ๋ก ์์ฒญ์ ํ๊ฒ ๋๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ํด๋ผ์ด์ธํธ์์ ์๋ฒ์ ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ ๋์๋ HTTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์์ฒญ์ ๋ณด๋ด๋๋ฐ, ์ด ๋ ๋ธ๋ผ์ฐ์ ๋ ์์ฒญ ํค๋์