자바스크립트/개념

자바스크립트 fetch API 알아보기 (feat. ajax)

Jake Seo 2022. 6. 26. 02:40

개요

네트워크에서 HTTP 통신을 이용하여 리소스를 가져오는(fetch) 역할을 한다. Fetch API 이전에는 XMLHttpRequestAjax 가 있었다. 다만, XMLHttpRequest 는 스펙 자체가 사용하기 조금 불편하다는 단점이 있었고, Ajax 는 JQuery 의존성이 걸려있다는 단점이 있었다.

이러한 문제들을 해결한 유연하고 강력한 API 가 fetch API 이다.

특징

요청으로 Request 인터페이스를 이용하고 반환 값으로 Response 오브젝트를 이용하는데, 응답은 비동기이기에 이를 resolve() 하여 Promise<Response> 형태로 반환한다.

Promise 에 대한 설명

fetch() 의 결과로 반환되는 Promise 객체는 오직 네트워크 에러가 발생했을 때만 reject 를 하고 HTTP 상태 코드만 있다면 404 와 같은 HTTP 응답 코드에 대해서는 resolve 하여 fulfilled 상태가 된다.

이로 인해, HTTP 에서 200 OK 가 아닌 다른 응답코드를 주더라도 then() 에서 응답 코드에 따른 처리가 가능하다. Response.ok 와 같이 200 OK 인지 아닌지 나타내는 프로퍼티도 존재하고 Response.status 와 같이 상태코드 자체를 나타내는 프로퍼티도 있어서 편한대로 쓰면 된다.

호출 시 호출하는 웹페이지의 connect-src(Content Security Policy) 지시자에 따라 제어되어 잘 동작하지 않는다면 서버의 보안 설정을 한번 더 확인해보자.

fetch() 메서드의 파라미터는 Request 객체의 생성자와 같다.

Ajax 와의 차이

  • ajax200 OK 외에 404 Not Found 같은 응답코드가 왔을 때 이를 에러처리 하는데, fetch API 는 네트워크 문제가 있지 않는 이상 모든 HTTP 코드를 resolve 한다.
  • fetch() 메서드는 구형 브라우저에서 사용했을 때, credentials 와 함께 사용되지 않는이상, cross-origin 요청에서 쿠키를 보내지 않는다. 또한 응답으로 돌아온 쿠키도 세팅하지 않는다.
  • Ajax 는 jqXHR 라는 자체적으로 생성한 XMLHTTPRequest 의 상위 집합을 사용하는 반면 fetchPromise<Response> 오브젝트를 리턴한다.

사용하기

fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));
  • Promise<Response>
    • 콜백에서 Response.json() 을 반환
  • Promise<Json> 받기
    • 받은 Json을 이용해 비즈니스 로직 처리

실제 JSON 을 가지기까지 Promise 를 2번이나 까줘야(?) 한다. .then() 의 첫번째 콜백에 상태코드에 따른 처리를 해주면 되고, 두번째 .then() 에서는 넘겨받은 (아마) JSON 을 가지고 비즈니스에 관여하는 로직을 만들면 된다.

지원하는 요청 옵션 살펴보기

fetch API 는 2번째 파라미터로 init 오브젝트를 받는다. 이 오브젝트를 이용하여 HTTP 요청에 이용되는 다양한 옵션을 커스터마이징할 수 있다.

// Example POST method implementation:
async function postData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    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 data type must match "Content-Type" header
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://example.com/answer', { answer: 42 })
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

인증 정보 함께 보내기

fetch('https://example.com', {
  credentials: 'include'
});

위와 같이 fetch API 의 두번째 오브젝트에 credentials: 'include' 를 담아 보내면, same-origin 혹은 cross-origin 으로 보낼 때 모두 credentials 를 함께 보내게 된다.

JSON 보내기

const data = { username: 'example' };

fetch('https://example.com/profile', {
  method: 'POST', // or 'PUT'
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data);
})
.catch((error) => {
  console.error('Error:', error);
});

Content-Type 을 잘 맞춰주고, 자바스크립트의 JSON 오브젝트 타입을 그대로 사용할 수는 없으니 당연히 JSON.stringify() 를 해주어야 한다.

파일 업로드하기

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

FormData 오브젝트를 이용하면 상대적으로 쉽게 가능하다.

여러 파일 업로드하기

const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');

formData.append('title', 'My Vegas Vacation');
for (let i = 0; i < photos.files.length; i++) {
  formData.append(`photos_${i}`, photos.files[i]);
}

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData,
})
.then(response => response.json())
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

FormData 를 이용하는 것은 비슷한데, <Input /> 태그에서 파일을 여러 개 선택할 수 있도록 해주면 된다.

문법

fetch(resource)
fetch(resource, init)

레퍼런스

반응형