# 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 사용하기

#### 기본적인 사용

```javascript
async function getData() {
  // await 를 하지 않는다면 Promise 객체를 리턴한다.
  const response = await fetch('http://example.com/movies.json');
  const data = await response.json();
  console.log(data);
}
```

```javascript
// 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: 요청 출처에 상관없이, 요청에 자격 증명이 포함되어야 함을 나타냄

```javascript
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(프론트앤드)](https://domain-a.xn--com\(\)-lf0u83pv72amt0a71e) 에서 fetch 를 사용하여 <https://domain-b.com/data.json> 의 데이터 요청

## 왜 CORS라는 것이 존재하는가?

**보안상의 이유로 브라우저는 스크립트에서 시작된 CORS 요청을 제한한다.** fetch() 및 XMLHttpRequest는 동일 출처 정책을 따른다. 즉, 이러한 API를 사용하는 웹 애플리케이션은 다른 출처의 응답에 올바른 CORS 헤더가 포함되지 않는 한 동일한 출처의 리소스만 요청할 수 있다.

하지만, 그렇다고 항상 같은 출처의 리소스만 요청하고 받아올 수 있는 것은 아니다. 프론트와 백엔드의 출처가 다를 경우, 이를 허용하는 목록을 백엔드 쪽에서 추가할 수 있다. 그리고, 이렇게 출처(origin)이 다른 요청 은, CORS에 의해서 제어된다.

![CORS Image From MDN](https://1894437320-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvEIP2mtvcOYnL1HUyi7v%2Fuploads%2Fgit-blob-eff69f862cc490d6c68dd0f24cb0156e69bb3514%2Fimg-cors.png?alt=media)

아래는 브라우저에서 서버로 요청을 보내는 케이스의 예이다.

```teminal
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>으로부터 온 것을 알 수 있다.

아래는 서버에서 응답하는 예이다.

```teminal
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과 함께 \* 를 반환한다. 이는 모든 오리진에서 리소스에 액세스할 수 있음을 의미한다.

```terminal
Access-Control-Allow-Origin: *
```

그러나 만약 <https://bar.other> 의 리소스 소유자가 <https://foo.example>에서의 요청으로만 리소스에 대한 엑세스를 제한하려는 경우, 즉 다른 도메인에서는 CORS 방식으로 리소스에 접근할 수 없다면 **Access-Control-Allow-Origin** 에 대한 정보는 아래와 같다.

```terminal
Access-Control-Allow-Origin: https://foo.example
```
