(1) Origin๊ณผ SOP
์น ์ ํ๋ฆฌ์ผ์ด์
์ HTML, CSS, JavaScript๋ฟ๋ง ์๋๋ผ ์ด๋ฏธ์ง, ํฐํธ, ์ธ๋ถ ์คํฌ๋ฆฝํธ ๋ฑ ๋ค์ํ ๋ฆฌ์์ค๋ก ๊ตฌ์ฑ๋๋ค. ์ด๋ ๊ฒ ๋ค์ํ ์น ๋ฆฌ์์ค๋ค์ ์ด์ฉํ๊ธฐ ์ํด ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋จ์ผ ์๋ฒ๋ก๋ง ํฅํ์ง ์๊ณ , CDN, ์ธ๋ถ API ๊ฐ์ ์ธ๋ถ ์ถ์ฒ์ ์๋ฒ๋ก๋ ์ ๋ฌ๋๋ค.
์ธ๋ถ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ปจํ
์ธ ๋ฅผ ํ๋ถํ๊ฒ ๋ง๋ค์ด์ฃผ๊ณ , ๊ฐ๋ฐ์ ํจ์จ์ฑ์ ๋์ธ๋ค. ๊ทธ๋ฌ๋ ๋์์ ๋ณด์ ์ํ์ ์ด๋ํ ๊ฐ๋ฅ์ฑ๋ ํค์ด๋ค. ์๋ฅผ ๋ค์ด, ์
์์ ์ธ ์ 3์๊ฐ ์ฌ์ฉ์์ ๊ฐ์ธ์ ๋ณด๋ฅผ ํ์ทจํ๋ ๋ฑ ํ๊ฐ๋์ง ์์ ๋ด๋ถ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ค ํ ์ ์๋ค.
์ด๋ฌํ ์ํ์ ๋ฐฉ์ดํ๊ธฐ ์ํด ์น ๋ธ๋ผ์ฐ์ ๋ Origin๊ณผ SOP(Same-Origin Policy, ๋์ผ ์ถ์ฒ ์ ์ฑ
)๋ผ๋ ๊ธฐ๋ณธ์ ์ธ ๋ณด์ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ๊ณ ์๋ค.
1) Origin
Origin์ ์น ๋ณด์์ ๊ธฐ๋ณธ ๋จ์๋ก ์น ๋ฆฌ์์ค์ ์ถ์ฒ๋ฅผ ์๋ฏธํ๋ค.
ํ๋กํ ์ฝ(scheme)+ํธ์คํธ(host)+ํฌํธ(port)
์ ์กฐํฉ์ผ๋ก ํํ๋๋ฉฐ, URL์ ๊ตฌ์กฐ scheme://host[:port]
[/path][?query]
์์ scheme://host[:port]
์ ํด๋นํ๋ค. ์๋ ์์์์ ๋ฐ์ค์น ๋ถ๋ถ์ด Origin์ด๋ค.https://www.google.com/
search?q=cat
http://localhost:3000/
page1
์น ๋ธ๋ผ์ฐ์ ๋ Origin์ ๊ธฐ์ค์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ์ธ๋ถ ๋ฆฌ์์ค๊ฐ ์ ๊ทผํ๋ ๊ฒ์ ์ ์ดํ๋ค. ๋ํ ์น ์๋ฒ๋ Origin ์ ๋ณด์์ ์์ฒญ์ ์ถ์ฒ๋ฅผ ํ์
ํด ๋ด๋ถ ๋ฆฌ์์ค ์ ๊ทผ์ ์ ์ดํ๋ค.
2) SOP(๋์ผ ์ถ์ฒ ์ ์ฑ )
์์ ์ค๋ช
ํ ๋๋ก ์ธ๋ถ ๋ฆฌ์์ค๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ ์ ๊ทผํ๋ ๊ฒ์ ๋ณด์ ์ํ์ ์๋ฐํ๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ์ ์ธ ๋ณด์ ์ ์ฑ
์ด SOP์ด๋ค.
SOP(Same-Origin Policy, ๋์ผ ์ถ์ฒ ์ ์ฑ
)๋ ๊ฐ์ Origin์ ๋ฆฌ์์ค ์ ๊ทผ๋ง์ ํ์ฉํ๋ ๋ณด์ ์ ์ฑ
์ด๋ค. ์ธ๋ถ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ๊ณ ์๋ต์ ๋ฐ์ ๋, ๋ธ๋ผ์ฐ์ ๋ ํด๋น ์๋ต์ ์ถ์ฒ์ ํ์ฌ ์น ํ์ด์ง์ ์ถ์ฒ๋ฅผ ๋น๊ตํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ถ์ฒ๊ฐ ๋ค๋ฅธ ๋ฆฌ์์ค์ ์ ๊ทผ์ ์ฐจ๋จํจ์ผ๋ก์จ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณด์์ ์ ์งํ๋ค.
๋์ผ ์ถ์ฒ ํ๋จ:
http://example.com/dir/page.html
์์ ์์ฒญ์ ๋ณด๋ผ ๋
http://example.com/dir/page2.html
(ํ์ฉ)
http://example.com/dir2/page.html
(ํ์ฉ)
https
://example.com/dir/page.html
(์ฐจ๋จ, ํ๋กํ ์ฝ ๋ถ์ผ์น)
http://example.com
:81
/dir/page.html
(์ฐจ๋จ, ํฌํธ ๋ถ์ผ์น)
http://
news
.example.com/dir/page.html
(์ฐจ๋จ, ํธ์คํธ ๋ถ์ผ์น)
ย
SOP์ ์ ์ฉ ์ฌ๋ถ๋ ์์ฒญ์ ์ข
๋ฅ์ ๋ฐ๋ผ ๊ฒฐ์ ๋๋ค. ๋์ฒด๋ก HTML ํ๊ทธ๋ก ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ์ ํ์ฉ์ด ๋๋, JavaScript๋ก ์ด๋ฌํ ๋ฆฌ์์ค์ ๋ด์ฉ์ ์ฝ๊ฑฐ๋ ์กฐ์ํ๋ ค๊ณ ํ ๋ SOP๊ฐ ์๋ํ๋ค.
SOP ์๋ ์ฌ๋ก
- HTML ํ๊ทธ๋ฅผ ํตํ ์ธ๋ถ ๋ฆฌ์์ค ์์ฒญ: SOP ์ ํ ์์
- e.g.,
<img src="https://other-site.com/image.jpg">
<img>
, <link>
, <script>
, <iframe>
, <video>
, <audio>
, <object>
, <embed>
๋ฑ- ์ธ๋ถ ๋ฆฌ์์ค๋ฅผ JavaScript๋ก ์กฐ์: SOP ์ ํ ์ ์ฉ
- e.g.,
iframe.contentDocument.body.innerHTML
(์ฐจ๋จ)
<iframe src="https://other-site.com">
๋ด๋ถ์ DOM ์ ๊ทผ ๋ถ๊ฐ- JavaScript๋ฅผ ์ด์ฉํ ๋น๋๊ธฐ ํต์ : SOP ์ ํ ์ ์ฉ
- e.g.,
fetch('https://other-site.com/api')
(์ฐจ๋จ)
XMLHttpRequest, Fetch API๋ฅผ ํตํ ์์ฒญ์ ์๋ต ์ ํ
- ์น ํฐํธ: ๋์ฒด๋ก SOP ์ ํ ์ ์ฉ
- e.g.,
@font-face { src: url("https://other-site.com/font.woff2") ... }
(ํ์ฉ)
@font-face
๊ท์น์ ํตํด ๋ก๋๋๋ ์ธ๋ถ ํฐํธ ํ์ผ์ ์์ธ์ ์ผ๋ก SOP ์ ํ ์์SOP ์ ํ์ด ์ ์ฉ๋๋ ๊ฒฝ์ฐ, ๋ธ๋ผ์ฐ์ ๋ ์๋ฒ ์๋ต์ ํฌํจ๋
Access-Control-Allow-Origin
ํค๋๋ฅผ ํ์ธํ๋ค. ๊ทธ๋ฆฌ๊ณ ์์ฒญ์ ๋ณด๋ธ ์ถ์ฒ๊ฐ ํค๋์ ํฌํจ๋์ด ์๋์ง์ ๋ฐ๋ผ ๋ฆฌ์์ค์ ์ ๊ทผ์ ๊ฒฐ์ ํ๋ค. ์ด๋ ๊ฒ ๋ธ๋ผ์ฐ์ ๋ SOP๋ฅผ ๊ตฌํํ์ฌ ์ ๋ขฐํ ์ ์๋ ์ธ๋ถ ์ถ์ฒ๋ก๋ถํฐ์ ๋ฌด๋จ ์ ๊ทผ์ ์ฐจ๋จํ๊ณ , ์ฌ์ฉ์ ๊ฐ์ธ์ ๋ณด ์ ์ถ, ์ ์ฌ์ ์ธ ๊ณต๊ฒฉ ์ํ์ผ๋ก๋ถํฐ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณดํธํ ์ ์๋ค.
ย
(2) CORS
1) ์ดํด
SOP์ ์๊ฒฉํ ๊ท์น์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณด์์ ๊ฐํํ์ง๋ง, ๋์์ ์ ์์ ์ธ ์ธ๋ถ ์ถ์ฒ์ ๋ฆฌ์์ค๋ง์ ์ฐจ๋จํ๋ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ๋ค. ํ๋ ์น ๊ฐ๋ฐ์์๋ ํ๋ถํ ์ปจํ
์ธ ์ ๊ธฐ๋ฅ ํ์ฅ์ ์ํด ์ธ๋ถ ๋ฆฌ์์ค ์๋ฒ ๋, ์ธ๋ถ API ์ฌ์ฉ ๋ฑ์ ๊ธฐ๋ฅ์ด ์ฌ์ฉ๋๋ค. ์ด ๋ ํ์ฌ ์น ํ์ด์ง์ ๋ค๋ฅธ ์ถ์ฒ, ์ฆ ํฌ๋ก์ค ์ค๋ฆฌ์ง์ ํตํ ํต์ ์ด ํ์์ ์ด๋ฏ๋ก ์ด๋ฅผ ์ํด์ SOP ์ ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ํ์๊ฐ ์๋ค.
CORS(Cross-Origin Resource Sharing, ๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ )๋ ์ถ๊ฐ HTTP ํค๋๋ฅผ ์ฌ์ฉํด ํฌ๋ก์ค ์ค๋ฆฌ์ง(๊ต์ฐจ ์ถ์ฒ)์ ์ ๊ทผ์ ํ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๋ณด์ ๋ฉ์ปค๋์ฆ์ด๋ค. ์๋ฒ๊ฐ HTTP ์๋ต ํค๋์ ์ ๊ทผ ํ์ฉ ์กฐ๊ฑด์ ๋ช
์ํ๋ฉด, ์ด๋ฅผ ๋ง์กฑํ๋ ์์ฒญ์๋ SOP ์ ํ์ด ์ ์ฉ๋์ง ์๋๋ค. ์ด๋ฌํ ์กฐ๊ฑด์ ์ค์ ํ๋ ํค๋๋ฅผ CORS ํค๋๋ผ๊ณ ํ๋ฉฐ, ์ด๋ฅผ ํตํด ํด๋ผ์ด์ธํธ๋ ์ธ๋ถ ๋ฆฌ์์ค์ ์์ ํ๊ฒ ์ ๊ทผํ ์ ์๋ค.
2) CORS ํค๋
์๋ฒ๋ CORS ํค๋๋ก ์ด๋ค ์ถ์ฒ์ ์ด๋ค HTTP ๋ฉ์๋์ ์์ฒญ์ ํ์ฉํ ์ง ๋ฑ์ ์ง์ ํ ์ ์๋ค. ์๋๋ Expressยนโพ ํ๋ ์์ํฌ๋ก ๋ง๋ ์๋ฒ์ CORS ํค๋๋ฅผ ์ค์ ํ๋ ๊ฐ๋จํ ์์ ์ฝ๋์ด๋ค.
/* ์๋ฒ ์ธก ์ฝ๋ */ const express = require('express'); const app = express(); // ์๋ฒ ์ฑ ์์ฑ app.use((req, res, next) => { // CORS ํค๋๋ฅผ ์ค์ ํ๋ ๋ฏธ๋ค์จ์ด ๋ฑ๋ก res.header('Access-Control-Allow-Origin', 'https://request-origin.com'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.header('Access-Control-Allow-Credentials', 'true'); res.header('Access-Control-Expose-Headers', 'X-Custom-Header'); next(); });
์๋ต ํค๋์ ์ถ๊ฐํ ์ ์๋ CORS ๊ด๋ จ ์ค์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
Access-Control-Allow-Origin
: ํ์ฉํ ์ถ์ฒ๋ฅผ ์ง์ . ํ๋๋ง ์ง์ ํ ์ ์์ผ๋ฉฐ, URL ๋์*
(์์ผ๋์นด๋)๋ก ํ๊ธฐํ๋ฉด ๋ชจ๋ ์ถ์ฒ์ ์ ๊ทผ์ ํ๊ฐํ ์ ์์
Access-Control-Allow-Methods
: ํ์ฉํ HTTP ๋ฉ์๋ ์ง์
Access-Control-Allow-Headers
: ํ์ฉํ HTTP ํค๋ ์ง์
Access-Control-Allow-Credentials
: ์ฟ ํค, HTTP ์ธ์ฆ ๋ฑ ์๊ฒฉ ์ฆ๋ช ๋ฅผ ํฌํจํ ์์ฒญ ํ์ฉ ์ฌ๋ถ ์ง์
Access-Control-Expose-Headers
: ๋ธ๋ผ์ฐ์ ๊ฐ ์ ๊ทผํ ์ ์๋ ์ปค์คํ ํค๋ ์ง์
Access-Control-Max-Age
: ์ฌ์ ์์ฒญยฒโพ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ ์๊ฐ ์ง์
์ด ์ค
Access-Control-Allow-Origin
ํค๋๋ ํ์ฉ๋์ง ์์ ์ถ์ฒ์ ์์ฒญ์ ๋ํ ์๋ฒ ์๋ต์ ๋ธ๋ผ์ฐ์ ๊ฐ ์ฐจ๋จํ๋ ๋ฐ ์ฌ์ฉ๋๋ฏ๋ก, ํด๋ผ์ด์ธํธ ์ธก ๋ณด์ ๊ฐํ์ ํนํ ์ค์ํ๋ค.3) ์๋ ๋ฐฉ์
CORS ํค๋ ์ค์ ์ ํด๋ผ์ด์ธํธ์ ๋ฆฌ์์ค ์ ๊ทผ์ ์ ํํ์ง๋ง, ์๋ฒ๋ก ์ ์ก๋๋ ์์ฒญ ์์ฒด๋ฅผ ๋ง์ง๋ ๋ชปํ๋ค. ์๋ฅผ ๋ค์ด ๊ณต๊ฒฉ์๊ฐ ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ฅผ ํ์ทจํด ์๋ฒ์ DELETE ์์ฒญ์ ๋ณด๋ด๋ ๊ฒฝ์ฐ, ์๋ฒ๋ ์์ฒญ์ ์ถ์ฒ๋ฅผ ํ์ธํ์ง ์๊ณ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ญ์ ํ ์ ์๋ค. ์ด๋ ๊ฒ ํ์ฉ๋์ง ์์ ์ถ์ฒ๋ก๋ถํฐ์ ์์ฒญ์ด ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฑฐ๋ ์ญ์ ํ๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด, CORS๋ ๋จ์ ์์ฒญ๊ณผ ์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ์์ฒญ์ ๊ตฌ๋ถํ์ฌ ์ฒ๋ฆฌํ๋ค.
โ ๋จ์ ์์ฒญ
๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ์์ /์ญ์ ํ์ง ์๋ ์์ฒญ์ด๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ ์๋ ์กฐ๊ฑด์ ๋ชจ๋ ๋ง์กฑํด์ผ ํ๋ค. ๋จ์ ์์ฒญ ์ฒ๋ฆฌ์์ ๋ธ๋ผ์ฐ์ ๋ ์๋ฒ์ ๋ฐ๋ก ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ฒ์ ์๋ต ํค๋๋ฅผ ํ์ธํ์ฌ CORS ์ ์ฑ
์ ๊ฒ์ฌํ๋ค.
๋จ์ ์์ฒญ์ ์กฐ๊ฑด: ๋ชจ๋ ๋ง์กฑํ์ง ์์ผ๋ฉด ์ฌ์ ์์ฒญ ์ฒ๋ฆฌ๊ฐ ๋จ
- ๋ฉ์๋: GET, HEAD, POST ์ค ํ๋๋ฅผ ์ฌ์ฉ
- ํค๋: ์๋ ์ค์ ํค๋์
Accept
,Accept-Language
,Content-Language
,Content-Type
์ธ์ ์ปค์คํ ํค๋๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ
Content-Type
ํค๋ ์ฌ์ฉ์: ๊ทธ ๊ฐ์ดapplication/x-www-form-urlencoded
,multipart/form-data
,text/plain
์ค ํ๋์ธ ๊ฒฝ์ฐ
โก ์ฌ์ ์์ฒญ(Preflight ์์ฒญ)
๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ์์ /์ญ์ ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ์์ฒญ์ผ๋ก, ๋จ์ ์์ฒญ ์กฐ๊ฑด์ ๋ง์กฑํ์ง ์์ผ๋ฉด ๋ชจ๋ ์ฌ์ ์์ฒญ์ผ๋ก ์ฒ๋ฆฌ๋๋ค. ์ฌ์ ์์ฒญ ์ฒ๋ฆฌ์์๋ ๋ณธ ์์ฒญ ์ ์ ๋ธ๋ผ์ฐ์ ๊ฐ OPTIONS ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ฌ์ ์์ฒญ(Preflight)์ ๋ณด๋ธ๋ค. ์๋ฒ๋ ํ์ฉ๋๋ ๋ฉ์๋, ํค๋ ๋ฑ CORS ์ ์ฑ
์ ๋ณด๋ฅผ ์๋ตํ๊ณ , ๋ธ๋ผ์ฐ์ ๋ ๋ณธ ์์ฒญ์ด ํ์ฉ๋ ์ง ํ๋จํ ๋ค, ๊ทธ ์ฌ๋ถ์ ๋ฐ๋ผ ๋ณธ ์์ฒญ์ ๋ณด๋ด ์๋ต๊ฐ์ ์ฒ๋ฆฌํ๋ค.
์ฌ์ ์์ฒญ์ ๋ณธ ์์ฒญ ์ ์ ์๋ฒ์ CORS ์ ์ฑ
์ ํ์ธํจ์ผ๋ก์จ ๋ถํ์ํ ์์ฒญ์ ๋ฐฉ์งํ๊ณ ์ถ๊ฐ์ ์ธ ๋ณด์ ๊ณ์ธต์ ์ ๊ณตํ๋ค. ์ด ๊ณผ์ ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ฉฐ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ตฌํํ ํ์๋ ์๋ค.
![[๊ทธ๋ฆผ 2-6] ์ฌ์ ์์ฒญ(Preflight)](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F6d7fa230-23c2-4b9c-b287-fdeb076e90bd%2F07ef202f-b3cd-4649-8528-de326e92af00%2Fimage_22.png?table=block&id=a00bfeaf-ccff-4b9e-b796-ceec54611ec0&cache=v2)
4) ์ค์ต
โ CORS ์๋ฌ ์ดํด
CORS ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ์ฝ์์์ ์๋์ ๊ฐ์ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ธํ ์ ์๋ค.
![[๊ทธ๋ฆผ 2-7] ์
๋ช
๋์ CORS ์๋ฌ](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F6d7fa230-23c2-4b9c-b287-fdeb076e90bd%2F248f3dbf-1487-4603-9da2-ee6964926f25%2Fimage_13.png?table=block&id=82e31686-8f49-41a1-bdc6-6942f75dc18d&cache=v2)
์๋ฌ ๋ฉ์์ง ์ดํด
'http://127.0.0.1:5500'์ผ๋ก๋ถํฐ 'https://www.google.com/'์ fetch ํ๋ ค๋ ์ ๊ทผ์ด CORS ์ ์ฑ ์ ์ํด ์ฐจ๋จ๋์์ต๋๋ค.Access-Control-Allow-Origin
ํค๋๊ฐ ์์ฒญํ ๋ฆฌ์์ค์ ์์ต๋๋ค. ๋ถํฌ๋ช ํ ์๋ตยณโพ์ด ํ์ํ ๊ฒฝ์ฐ, ์์ฒญ ๋ชจ๋โดโพ๋ฅผno-cors
๋ก ์ค์ ํ๋ฉด CORS๋ฅผ ๋นํ์ฑํํ ์ํ๋ก ๋ฆฌ์์ค๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- ์์ฒญ ์ถ์ฒ:
http://127.0.0.1:5500
- ์๋ต ์ถ์ฒ:
https://www.google.com/
- CORS ์๋ฌ ๋ฐ์ ์ด์ : ์๋ฒ ์๋ต์
Access-Control-Allow-Origin
ํค๋๊ฐ ์์
์๋ฌ์ ์์ธ์ ๋ฉ์์ง ๋ด์์ ํ์ธ์ด ๊ฐ๋ฅํ๋ค. ์ฃผ๋ก ์๋ฒ ์ธก ์ค์ ์ ๋ณ๊ฒฝํด์ผ ํด๊ฒฐ์ด ๊ฐ๋ฅํ๋ฏ๋ก ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ๋ฐฑ์๋ ๊ฐ๋ฐ์์ ํ๋ ฅํ์ฌ ์ ์ ํ CORS ์ค์ ์ ๊ตฌํํด์ผ ํ๋ค.
โก ํด๋ผ์ด์ธํธ ์ธก CORS ์ค์
CORS ์๋ฌ ํด๊ฒฐ์ด ์ฃผ๋ก ์๋ฒ ์ธก ์ค์ ์ ์์กดํ๋ ์ด์ ๋ ์๋ฒ์ ๊ธฐ๋ณธ ์ค์ ์ด CORS๋ฅผ ๊ฑฐ๋ถํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ฉด ํด๋ผ์ด์ธํธ ์ธก์ ๊ธฐ๋ณธ ์ค์ ์ ๋ธ๋ผ์ฐ์ ๊ฐ CORS๋ฅผ ์ฌ์ฉํ๋๋ก ๋์ด ์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์๋ ์ผ๋ถ ์ํฉ์์ ์ด๋ฅผ ์ ์ดํ ์๋ ์๋ค.
- Fetch API์ CORS ์์ฒญ ๋ชจ๋
Fetch API๋ฅผ ์ฌ์ฉํ๋ฉด
mode
์ต์
์ผ๋ก CORS์ ์์ฒญ ๋ชจ๋๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.fetch(url, { mode: 'cors' }); // ๊ธฐ๋ณธ๊ฐ fetch(url, { mode: 'same-origin' }); fetch(url, { mode: 'no-cors' });
์์ฒญ ๋ชจ๋ | ์๋ฏธ |
cors(๊ธฐ๋ณธ๊ฐ) | CORS ์ฌ์ฉ. ์๋ฒ์ CORS ์ค์ ์ ๋ฐ๋ผ ์์ฒญ์ด ํ์ฉ๋๊ฑฐ๋ ๊ฑฐ๋ถ๋จ |
same-origin | CORS ๋ฏธ์ฌ์ฉ. ๋์ผ ์ถ์ฒ ์์ฒญ๋ง ํ์ฉํ๊ณ , ๋ค๋ฅธ ์ถ์ฒ๋ก์ ์์ฒญ์ ์๋ฌ ๋ฐ์ |
no-cors | CORS ๋ฏธ์ฌ์ฉ. ๊ต์ฐจ ์ถ์ฒ ์์ฒญ ์ค ๋จ์ ์์ฒญ๋ง ํ์ฉํ๊ณ , ๋ถํฌ๋ช
ํ ์๋ตยณโพ์ ๋ฐ์ |
- HTML ์์์ crossorigin ์์ฑ
HTML ์์์์ ์ ์ก๋๋ ์์ฒญ์ ๊ธฐ๋ณธ์ ์ผ๋ก CORS๋ฅผ ์ฌ์ฉํ์ง ์์ง๋ง,
crossorigin
์์ฑ์ ์ถ๊ฐํ๋ฉด CORS ์์ฒญ์ ํ์ฑํํ ์ ์๋ค.<!-- CORS ๋ฏธ์ฌ์ฉ --> <img src="https://cross-origin.com/image.jpg"> <!-- ๊ธฐ๋ณธ๊ฐ --> <!-- CORS ์ฌ์ฉ --> <img src="https://cross-origin.com/image.jpg" crossorigin> <!-- anonymous์ ๊ฐ์ --> <img src="https://cross-origin.com/image.jpg" crossorigin="anonymous"> <img src="https://cross-origin.com/image.jpg" crossorigin="use-credentials">
crossorigin | ์๋ฏธ |
์์ฑ ์์(๊ธฐ๋ณธ๊ฐ) | CORS ๋ฏธ์ฌ์ฉ. ๊ต์ฐจ ์ถ์ฒ ์์ฒญ ์ค ๋จ์ ์์ฒญ๋ง ํ์ฉํ๊ณ , ๋ถํฌ๋ช
ํ ์๋ตยณโพ์ ๋ฐ์ |
anonymous | CORS ์ฌ์ฉ. ์ฟ ํค, HTTP ์ธ์ฆ ๋ฑ ์๊ฒฉ ์ฆ๋ช
์๋ CORS ์์ฒญ |
use-credentials | CORS ์ฌ์ฉ. ์๊ฒฉ ์ฆ๋ช
์ ํฌํจํ CORS ์์ฒญ. CORS ํค๋์ ํน์ URL ์ง์ ํ์ |
โข ์ฟ ํค๋ฅผ ํฌํจํ๋ ์์ฒญ์ CORS ์ค์
HTTP์ ๋ฌด์ํ์ฑ์ผ๋ก ์ธํด ์น ์ ํ๋ฆฌ์ผ์ด์
์ ์ํ ์ ๋ณด ์ ์ง์ ์ฟ ํค๋ฅผ ์ฌ์ฉํ๋ค. ๊ทธ๋ฐ๋ฐ ๋ธ๋ผ์ฐ์ ๋ ๋ณด์์ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ต์ฐจ ์ถ์ฒ ์์ฒญ์ ์ฟ ํค๋ฅผ ํฌํจ์ํค์ง ์๋๋ค. ์ฟ ํค๋ฅผ ํฌํจํ ๊ต์ฐจ ์ถ์ฒ ์์ฒญ์ ํ์ฉํ๋ ค๋ฉด ํด๋ผ์ด์ธํธ์ ์๋ฒ ์์ธก ๋ชจ๋์ ์ถ๊ฐ ์ค์ ์ด ํ์ํ๋ค.
- ํด๋ผ์ด์ธํธ ์ธก ์ค์
// XMLHttpRequest ์ฌ์ฉ์ const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://cross-origin.com/', true); xhr.withCredentials = true; // ๊ธฐ๋ณธ๊ฐ false xhr.send();
// Fetch API ์ฌ์ฉ์ fetch('https://cross-origin.com/', { mode: 'cors', credentials: 'include' // ๊ธฐ๋ณธ๊ฐ 'same-origin' });
// Axios ์ฌ์ฉ์ axios.get('https://cross-origin.com/', { withCredentials: true // ๊ธฐ๋ณธ๊ฐ false });
credentials
์ ์ฟ ํค, HTTP ์ธ์ฆ ๋ฑ ์๊ฒฉ ์ฆ๋ช
์ ์๋ฏธํ๋ค. ํด๋ผ์ด์ธํธ ์ธก์์๋ ํต์ ์ ์๊ฒฉ ์ฆ๋ช
์ ํฌํจํ ๊ฒ์ ์ง์ ์ค์ ํด์ฃผ์ด์ผ ํ๋ค. XMLHttpRequest, Axios ์ฌ์ฉ์์ ์ด๋ฅผ ๋ถ๋ฆฐ ๊ฐ์ผ๋ก ํํํ์ง๋ง, Fetch API์์๋ ์ธ๊ฐ์ง ์ต์
์ด ์ฃผ์ด์ง๋ค. credentials | ์๋ฏธ |
omit | ์ฟ ํค ์ ์ก ์ ํจ |
same-origin(๊ธฐ๋ณธ๊ฐ) | ๋์ผ ์ถ์ฒ ์์ฒญ์๋ง ์ฟ ํค ํฌํจ, ๋ค๋ฅธ ์ถ์ฒ๋ก ์ฟ ํค ์ ์ก ๋ฐฉ์ง |
include | ๋ชจ๋ ์์ฒญ์ ์ฟ ํค ํฌํจ |
- ์๋ฒ ์ธก ์ค์
const express = require('express'); const app = express(); // ์๋ฒ ์ฑ ์์ฑ app.use((req, res, next) => { // CORS ํค๋๋ฅผ ์ค์ ํ๋ ๋ฏธ๋ค์จ์ด ๋ฑ๋ก res.header('Access-Control-Allow-Credentials', 'true'); res.header('Access-Control-Allow-Origin', 'https://specific-domain.com'); next(); });
Access-Control-Allow-Credentials
: true
๊ฐ์ผ๋ก ์ฟ ํค๋ฅผ ํฌํจํ ๊ฒ์ ์ค์ Access-Control-Allow-Origin
: ํ์ฉํ ์ถ์ฒ๋ก ํน์ URL ์ง์ . *
(์์ผ๋์นด๋) ์ฌ์ฉ ๋ถ๊ฐโฃ Proxy ์๋ฒ๋ฅผ ์ด์ฉํ ์์ฒญ ์ฐํ
๋ง์ง๋ง์ผ๋ก ์๋ฒ ์ธก ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ CORS ์๋ฌ๋ฅผ ํผํ ์ ์๋ ๋ฐฉ๋ฒ์ ์๊ฐํ๋ค. ๋ฐ๋ก ํด๋ผ์ด์ธํธ์ ์๋ฒ์ ์ค๊ฐ ์๋ฒ์ธ proxy ์๋ฒ(์ดํ, ํ๋ก์)๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
![[๊ทธ๋ฆผ 2-8] ํ๋ก์ ์ค๊ฐ ์๋ฒ](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F6d7fa230-23c2-4b9c-b287-fdeb076e90bd%2F4c4e8c57-1678-4b68-abaf-3e8020bd77e3%2FFrame_2.png?table=block&id=fa59b8d4-002a-449d-98b4-ec428d452189&cache=v2)
์ด ๋ฐฉ๋ฒ์์ ํ๋ก์๋ ์น ์๋ฒ์ ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ ์ฌ์ด์ ์์นํ๋ค. [๊ทธ๋ฆผ 2-8]์์๋ ์น ์๋ฒ๊ฐ ํ๋ก์ ์์
์ ๋ณํํ๊ณ ์๋ค.
ํ๋ก์ ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ ๋์ผ ์ถ์ฒ๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ฐ์ ๋ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์์ฒญ์ ์ฐํํด์ ๊ต์ฐจ ์ถ์ฒ์ ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ๋ก ์ ๋ฌํ๋๋ฐ, ์ด ๋์ ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ฑฐ์น์ง ์๋ ์๋ฒ๊ฐ์ ํต์ ์ด๋ฏ๋ก ์ญ์๋ CORS ์ ํ์ด ์ ์ฉ๋์ง ์๋๋ค.
ํ๋ก์ ์ฐํ ์๋๋ฆฌ์ค
https://example.com
์ฌ์ดํธ์์ 'user'๋ผ๋ ์ฌ์ฉ์์ ํ๋กํ ์ ๋ณด๊ฐ ํ์ํจ
- ๋ธ๋ผ์ฐ์ ๋
https://example.com/api/@user
์ฃผ์๋ก ์๋ฒ์ ์์ฒญ์ ๋ณด๋
- 2๋ฒ์ ๋์ผ ์ถ์ฒ ์์ฒญ์ด๊ธฐ ๋๋ฌธ์ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์
- ์น ์๋ฒ์์
/api
๊ฒฝ๋ก๋ก ๋ค์ด์จ ์์ฒญ์ ํ๋ก์ ์ค์ ์ ์ํด ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒhttps://cross-origin.com/@user
๋ก ์ฐํํ์ฌ ์ ๋ฌ๋จ
- 4๋ฒ์ ์๋ฒ ๊ฐ ํต์ ์ด๋ฏ๋ก ๋ธ๋ผ์ฐ์ ์ SOP ์ํฅ์ ๋ฐ์ง ์์ CORS ์ ํ ์์
- ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ๊ฐ ์๋ตํ๋ฉด ํ๋ก์๊ฐ ์ด๋ฅผ ์ ์์ฒญ์์ธ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌํจ
ยนโพ Express๋ Node.js๋ก ์น ์๋ฒ๋ฅผ ์ฝ๊ฒ ๊ฐ๋ฐํ ์ ์๋๋ก ํด์ฃผ๋ ๋ํ์ ์ธ ํ๋ ์์ํฌ์ด๋ค. ์ด ์ฑ
์ ์๋ฒ ์ธก ์ฝ๋๋ฅผ ์ต์ํ์ผ๋ก๋ง ๋ค๋ฃจ๋ฉฐ Express๋ฅผ ๋ชฐ๋ผ๋ ์ดํดํ ์ ์๋๋ก ์์ ๋์๋ค.
ยณโพ opaque response. ๋ณด์ ๋ชฉ์ ์ ์ํด ๋ฆฌ์์ค ์์ฒด๋ ์คํ๋๊ณ ํ๋ฉด์ ๋ณด์ด์ง๋ง JavaScript๋ก ์ ๊ทผํ ์ ์๋ ๋ฆฌ์์ค. ์๋ฅผ ๋ค์ด, ๋ฆฌ์์ค๊ฐ ์ด๋ฏธ์ง์ผ ๊ฒฝ์ฐ ํ๋ฉด์ ํ์๋ ๋์ง๋ง JavaScript๋ก ํฝ์
๋ฐ์ดํฐ์ ์ ๊ทผ ๋ถ๊ฐํจ
ย