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์— ์˜ํ•ด์„œ ์ œ์–ด๋œ๋‹ค.

CORS Image From MDN

์•„๋ž˜๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์ผ€์ด์Šค์˜ ์˜ˆ์ด๋‹ค.

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