XMLHttpRequest, Fetch API

lingodingo·2020년 7월 5일
1

DailyStudy

목록 보기
3/3

ES6에서 다른 도메인의 데이터를 읽어야 하거나, 백엔드에서 API를 불러올때 XHR(XMLHttpRequest) API를 쓰는 경우는 이제 없을 것이라 생각한다~~(아님 말고)~~. 하지만 도대체 왜 한 시대를 풍미했던 이 API가 더 이상 코드에 보이지 않게 된 것일까?

😵

TL; DR(3줄 요약좀)

XMLHttpRequest API

  • 이벤트 기반 => 현재 Promise 기반과는 많이 다름
  • 초기 API 구현 때, 브라우저 간의 불일치가 존재
  • 이 브라우저간 차이를 메꾸기 위해 jQuery 사용 시작 => jQuery가 크로스 브라우징 이슈에 나름 큰 솔루션을 제공

Fetch API

  • Promise 기반 => 현재 비동기 처리 프로그래밍 방식과 잘 어울림
  • 이미 잘 명세된 API가 제공됨으로써 브라우저간의 불일치가 없음
  • XHR 에는 있지만 Specification에 논의되지 않은 몇몇 유용한 기능이 없음 => (setTimeout, abort, progress)
  • 나중에 추가 지원을 받을 수 있음

일단 XHR이 무엇인지 잘 모르는 사람들이 있을 까봐, 어떻게 생겨먹은건지 보도록 하자.

let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain/service');

// request state change event
xhr.onreadystatechange = function() {

  // request completed?
  if (xhr.readyState !== 4) return;

  if (xhr.status === 200) {
    // request successful - show response
    console.log(xhr.responseText);
  }
  else {
    // request error
    console.log('HTTP error', xhr.status, xhr.statusText);
  }
};

// start request
xhr.send();

XHR은 전통적인 Event 기반으로 동작한다. document.addEventListener와 비슷하다. XHR은 응답을 받을 때, state를 바꾸는 이벤트를 발생시키고, 이를 onreadystatechange를 통해 감지하게 된다.

다음으로 Fetch API를 보도록 하자.

fetch(
    'http://domain/service',
    { method: 'GET' }
  )
  .then( response => response.json() )
  .then( json => console.log(json) )
  .catch( error => console.error('error:', error) );

Fetch는 리턴 타입이 Promise이다. 따라서 then, catch와 같은 체이닝으로 작성할 수 있어서 가독성 및 작성력(Readability & Writeability)이 좋습니다. 여기에 Async / Await으로 callback hell을 쉽게 벗어날 수 있기도 하다.

이제 둘 간의 장/단점을 알아보자.


  • XHR: 초기 기획때부터 인터페이스를 제공하지 않아 브라우저간의 불일치가 발생했다.
  • Fetch: Header, Request, Response와 같은 인터페이스를 제공함으로써 이러한 불일치를 없앴다.

  • XHR: IE 지원.
  • Fetch: IE 대부분에서 동작하지 않음. 2017년 이전의 Chrome, Firefox에서 문제가 있을 수 있음. 하지만 XHR보다 지속적인 업데이트를 받을 수 있다.

  • XHR: 기본적으로 쿠키를 전송한다.
  • Fetch: 기본적으로 쿠키를 전송하지 않는다. 하지만 옵션을 통해 전달할 수 있다.

  • Fetch: 404 Page Not Found, 500: Internal Server Error는 Promise.reject로 잡아낼 수 없다. Promise.reject는 오직 네트워크 실패와 같은 에러에서만 reject로 잡아낼 수 있기 때문이다. (아마 백엔드에서 의도적으로 404 에러를 리턴할 수 있기 때문에 그렇지 않을까 생각)

  • XHR: 다음과 같이 connection timeout을 쉽게 정할 수 있다. 또한 파일 업로드와 같은 긴 시간이 필요한 경우에는 progress 상황을 알 수 있다.
// set timeout
xhr.timeout = 3000; // 3 seconds
xhr.ontimeout = () => console.log('timeout', xhr.responseURL);

// upload progress
xhr.upload.onprogress = p => {
  console.log( Math.round((p.loaded / p.total) * 100) + '%') ;
}
  • Fetch: Specification에서 논의되지 않았기 때문에~~(도대체 왜?)~~ Wrapper 함수를 통해 구현하여야 한다. 또는 Promise.race를 통해 해결할 수도 있다.
// fetch with a timeout
function fetchTimeout(url, init, timeout = 3000) {
  return new Promise((resolve, reject) => {
    fetch(url, init)
      .then(resolve)
      .catch(reject);
    setTimeout(reject, timeout);
  }
}
// fetch with Promise.race
Promise.race([
  fetch('http://url', { method: 'GET' }),
  new Promise(resolve => setTimeout(resolve, 3000))
])
  .then(response => console.log(response))

  • XHR: abort로 request를 쉽게 취소할 수 있다. 그리고 onabort로 이벤트를 감지하여 처리할 수 있다.
xhr.abort();
xhr.onabort = function() { ... };
  • Fetch: 브라우저가 AbortController API를 구현했다면, 이 API를 통해서 Fetch Request를 reject 시킬 수 있다.
const controller = new AbortController();

<hr>
  
fetch(
  'http://domain/service',
  {
    method: 'GET'
    signal: controller.signal
  })
  .then( response => response.json() )
  .then( json => console.log(json) )
  .catch( error => console.error('Error:', error) );

XHR을 안쓰는 개인적인 이유를 정리해 보자면,
**1. 가독성

  1. 크로스 브라우징 이슈
  2. Promise 기반이 아님**
    이 셋이 이 가장 큰 것 같다.
profile
Frontend developer

0개의 댓글