2. Fetch API&CORS
Fetch API VS XMLHttpRequest
Fetch API๋ ๋คํธ์ํฌ ํต์ ์ ํฌํจํ ๋ฆฌ์์ค ์ทจ๋์ ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค. XMLHttpRequest๋ณด๋ค ๊ฐ๋ ฅํ ๋์ฒด์ ์ด๋ค. ์ด ๊ธฐ๋ฅ์ Web Worker์์ ์ฌ์ฉํ ์ ์๋ค.
Web Worker๋?
์น ์์ปค(Web worker)๋ ์คํฌ๋ฆฝํธ ์ฐ์ฐ์ ์น ์ดํ๋ฆฌ์ผ์ด์ ์ ์ฃผ ์คํ ์ค๋ ๋์ ๋ถ๋ฆฌ๋ ๋ณ๋์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์คํํ ์ ์๋ ๊ธฐ์ ์ด๋ค. ์น ์์ปค๋ฅผ ํตํด ๋ฌด๊ฑฐ์ด ์์ ์ ๋ถ๋ฆฌ๋ ์ค๋ ๋์์ ์ฒ๋ฆฌํ๋ฉด ์ฃผ ์ค๋ ๋(๋ณดํต UI ์ค๋ ๋)๊ฐ ๋ฉ์ถ๊ฑฐ๋ ๋๋ ค์ง์ง ์๊ณ ๋์ํ ์ ์๋ค.
Fetch API
Promise๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๊น๋ํ๊ณ ๊ฐ๊ฒฐํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณต
์๋ต Response ๊ฐ์ฒด๋ก Promise๋ฅผ ๋ฐํํ๊ณ , json(), text() ์ ๊ณตํ์ฌ ํธ๋ฆฌํ๊ฒ ์๋ต ์ฒ๋ฆฌ
Headers ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ํค๋ ์์ ์ ์ํํ ์ ์์
๊ธฐ๋ณธ์ ์ผ๋ก ์๋ณธ ์ถ์ฒ ์ ์ฑ ์ ๋ฐ๋ฅด์ง๋ง CORS ์ญ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์
AbortController ๋ฐ AbortSignal ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ์ทจ์๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์
request.body ์คํธ๋ฆผ์ ์ฌ์ฉํ์ฌ ์๋ต ๋ณธ๋ฌธ ์คํธ๋ฆฌ๋ฐ์ ์ง์
XMLHttpRequest
๋ ์ค๋ซ๋์ ์ฌ์ฉ๋ API ์ด๋ฉฐ ์ฝ๋ฐฑ ๊ธฐ๋ฐ์ ์ ๊ทผ ๋ฐฉ์์ ์ ๊ณต
์๋ต์ ์ฒ๋ฆฌํ๋ ค๋ฉด ๋ ๋ง์ ์ฝ๋๊ฐ ํ์
ํค๋ ์์ ์ด ๋ ์ง๊ด์ ์ด๊ณ , setRequestHeader ๋ฉ์๋๋ฅผ ํตํ์ฌ ๊ด๋ฆฌ๋จ
CORS์ง์์ด ์ ํ์ ์ด๊ณ , ์ ์ ํ ํค๋๋ฅผ ํฌํจ์ํค๊ฑฐ๋ JSONP์ ๊ฐ์ ๊ธฐ์ ์ ์ฌ์ฉํ๋๋ก ์๋ฒ๋ฅผ ๊ตฌ์ฑํ์ฌ CORS๋ฅผ ์ฒ๋ฆฌํ ์๋ ์์
์์ฒญ ์ทจ์๊ฐ ๊น๋ค๋ก์
๊ธฐ๋ณธ์ ์ผ๋ก ์๋ต ๋ณธ๋ฌธ ์คํธ๋ฆฌ๋ฐ์ ์ง์ํ์ง ์์
Fetch ์ฌ์ฉํ๊ธฐ
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ
async function getData() {
// await ๋ฅผ ํ์ง ์๋๋ค๋ฉด Promise ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ค.
const response = await fetch('http://example.com/movies.json');
const data = await response.json();
console.log(data);
}
// POST ๋ฉ์๋ ๊ตฌํ ์์
async function postData(url = '', data = {}) {
// ์ต์
๊ธฐ๋ณธ ๊ฐ์ *๋ก ๊ฐ์กฐ
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE ๋ฑ
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
// no-referrer, *no-referrer-when-downgrade, origin,
// origin-when-cross-origin, same-origin, strict-origin,
// strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data), // body์ ๋ฐ์ดํฐ ์ ํ์ ๋ฐ๋์ "Content-Type" ํค๋์ ์ผ์นํด์ผ ํจ
});
return response.json(); // JSON ์๋ต์ ๋ค์ดํฐ๋ธ JavaScript ๊ฐ์ฒด๋ก ํ์ฑ
}
postData('https://example.com/answer', { answer: 42 }).then((data) => {
console.log(data); // JSON ๋ฐ์ดํฐ๊ฐ `data.json()` ํธ์ถ์ ์ํด ํ์ฑ๋จ
});
์๊ฒฉ ์ฆ๋ช
์ ํฌํจํ ์์ฒญ
์๊ฒฉ ์ฆ๋ช ์ด๋?
์ฟ ํค, HTTP ์ธ์ฆ, ํด๋ผ์ด์ธํธ ์ธก SSL ์ธ์ฆ์๋ฅผ credentials ์ต์ ์ ํฌํจํ ์ง ์ฌ๋ถ ์ง์
omit: ๊ธฐ๋ณธ ๊ฐ. ์ธ์ฆ์ด ํ์ํ์ง ์์ ๊ณต์ฉ ๋ฆฌ์์ค์ ์์ฒญ
same-origin: ํธ์ถ ์คํฌ๋ฆฝํธ์ ๋์ผํ ์๋ณธ์ ๋ํ ์์ฒญ์ด ์ด๋ฃจ์ด์ ธ์ผ๋ง ํ๋ ๊ฒฝ์ฐ
include: ์์ฒญ ์ถ์ฒ์ ์๊ด์์ด, ์์ฒญ์ ์๊ฒฉ ์ฆ๋ช ์ด ํฌํจ๋์ด์ผ ํจ์ ๋ํ๋
fetch('https://example.com', {
credentials: 'include', // include, same-origin, omit
});
credentials: 'include'๋ฅผ ์ถ๊ฐํ ๊ฒฝ์ฐ, Access-Control-Allow-Origin์ ์์ผ๋์นด๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์๊ฒฉ ์ฆ๋ช ์ ํฌํจํ๋ ค๋ ๊ฒฝ์ฐ์๋ ๋ฐ๋์ ์ ํํ ์ถ์ฒ๋ฅผ ์ง์ ํด์ผ ํ๋ค. CORS ํด์ ํ์ฅ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ๋๋ผ๋ ์์ผ๋์นด๋๋ฅผ ์ง์ ํ ์์ฒญ์ ์คํจํ๋ค. ์์ฒญ URL์ด ์คํฌ๋ฆฝํธ์ ๊ฐ์ ์ถ์ฒ์ผ ๋๋ง ์๊ฒฉ ์ฆ๋ช ์ ์ ์กํ๋ ค๋ฉด credentials: 'same-origin' ์ผ๋ก ์ค์ ํ๋ค.
CORS(Cross-Origin Resource Sharing)
๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ (CORS)๋ HTTP Header์ ๊ธฐ๋ฐํ ๋ฉ์ปค๋์ฆ์ผ๋ก์, ํ ์ถ์ฒ์์ ์คํ์ค์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ค๋ฅธ ์ถ์ฒ์ ์์์ ์ ๊ทผํ ์ ์๋๋ก ๊ถํ์ ๋ถ์ฌํ๋๋ก ๋ธ๋ผ์ฐ์ ์ ์๋ ค์ฃผ๋ ์ฒด์ ์ด๋ค.
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฆฌ์์ค๊ฐ ์์ ์ ์ถ์ฒ(๋๋ฉ์ธ, ์คํค๋ง ๋๋ ํฌํธ)์ ๋ค๋ฅผ ๋ CORS HTTP ์์ฒญ์ ์คํํ๋ค.
๊ต์ฐจ ์ถ์ฒ ์์ฒญ์ ์
https://domain-a.com(ํ๋ก ํธ์ค๋) ์์ fetch ๋ฅผ ์ฌ์ฉํ์ฌ https://domain-b.com/data.json ์ ๋ฐ์ดํฐ ์์ฒญ
์ CORS๋ผ๋ ๊ฒ์ด ์กด์ฌํ๋๊ฐ?
๋ณด์์์ ์ด์ ๋ก ๋ธ๋ผ์ฐ์ ๋ ์คํฌ๋ฆฝํธ์์ ์์๋ CORS ์์ฒญ์ ์ ํํ๋ค. fetch() ๋ฐ XMLHttpRequest๋ ๋์ผ ์ถ์ฒ ์ ์ฑ ์ ๋ฐ๋ฅธ๋ค. ์ฆ, ์ด๋ฌํ API๋ฅผ ์ฌ์ฉํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ์ถ์ฒ์ ์๋ต์ ์ฌ๋ฐ๋ฅธ CORS ํค๋๊ฐ ํฌํจ๋์ง ์๋ ํ ๋์ผํ ์ถ์ฒ์ ๋ฆฌ์์ค๋ง ์์ฒญํ ์ ์๋ค.
ํ์ง๋ง, ๊ทธ๋ ๋ค๊ณ ํญ์ ๊ฐ์ ์ถ์ฒ์ ๋ฆฌ์์ค๋ง ์์ฒญํ๊ณ ๋ฐ์์ฌ ์ ์๋ ๊ฒ์ ์๋๋ค. ํ๋ก ํธ์ ๋ฐฑ์๋์ ์ถ์ฒ๊ฐ ๋ค๋ฅผ ๊ฒฝ์ฐ, ์ด๋ฅผ ํ์ฉํ๋ ๋ชฉ๋ก์ ๋ฐฑ์๋ ์ชฝ์์ ์ถ๊ฐํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ , ์ด๋ ๊ฒ ์ถ์ฒ(origin)์ด ๋ค๋ฅธ ์์ฒญ ์, CORS์ ์ํด์ ์ ์ด๋๋ค.

์๋๋ ๋ธ๋ผ์ฐ์ ์์ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ด๋ ์ผ์ด์ค์ ์์ด๋ค.
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
์์ Request Header ์์ ์ฃผ๋ชฉํด์ผ ํ ๊ฒ์ Origin ์ด๋ค. Origin ์ ๋ณด๋ฉด ํด๋น ์์ฒญ์ด https://foo.example์ผ๋ก๋ถํฐ ์จ ๊ฒ์ ์ ์ ์๋ค.
์๋๋ ์๋ฒ์์ ์๋ตํ๋ ์์ด๋ค.
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[โฆXML Dataโฆ]
์ด์ ๋ํ ์๋ต์ผ๋ก ์๋ฒ๋ Access-Control-Allow-Origin ํค๋๋ฅผ Access-Control-Allow-Origin๊ณผ ํจ๊ป * ๋ฅผ ๋ฐํํ๋ค. ์ด๋ ๋ชจ๋ ์ค๋ฆฌ์ง์์ ๋ฆฌ์์ค์ ์ก์ธ์คํ ์ ์์์ ์๋ฏธํ๋ค.
Access-Control-Allow-Origin: *
๊ทธ๋ฌ๋ ๋ง์ฝ https://bar.other ์ ๋ฆฌ์์ค ์์ ์๊ฐ https://foo.example์์์ ์์ฒญ์ผ๋ก๋ง ๋ฆฌ์์ค์ ๋ํ ์์ธ์ค๋ฅผ ์ ํํ๋ ค๋ ๊ฒฝ์ฐ, ์ฆ ๋ค๋ฅธ ๋๋ฉ์ธ์์๋ CORS ๋ฐฉ์์ผ๋ก ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ค๋ฉด Access-Control-Allow-Origin ์ ๋ํ ์ ๋ณด๋ ์๋์ ๊ฐ๋ค.
Access-Control-Allow-Origin: https://foo.example
Last updated