프론트 공부/자바스크립트

프론트에서 네트워크로 (AJAX, fetch, axios)

캥거루 2021. 5. 26. 02:10

프론트를 하다 보면 네트워크를 모를 수가 없는데요.

 

특히 API를 이용하거나, 서버와 내가 만든 웹페이지를 연결하거나 ... 할 때 네트워크를 이용한 통신 방법들에 대해서 급하게 알아보게 됩니다. (본인도 그중 한 명) 그러다가 알게 된 개념들을 정리해봅니다. (잘못된 정보나 궁금한 점들은 댓글로 남겨주세요!)

 

XMLHttpRequest (줄여서 XHR)

MDN 에서 정의한 바에 따르면 :

XMLHttpRequest (XHR) objects are used to interact with servers. You can retrieve data from a URL without having to do a full page refresh. This enables a Web page to update just part of a page without disrupting what the user is doing. XMLHttpRequest is used heavily in AJAX programming.

 

그러니까 full page refresh 없이도 특정 url로부터 데이터를 가져올 수 있다는 겁니다. 따라서 유저가 하고 있는 것을 방해하지 않고도 웹 페이지 일부를 업데이트할 수 있다는 것이죠. 그럼 원래는 웹 페이지 전체를 refresh 해야만 데이터를 가져올 수 있나요? 🤔

 

네. 그게 바로 동기(Synchronous) 방식입니다. 만약 네이버 메인에서 어떤 페이지를 클릭했는데 화면이 하얗게 됐다가 다른 화면이 뜬 경험이 있나요? 그것이 동기 방식입니다.

 

웹 페이지 일부만 업데이트 하는 방식은 그럼 비동기(Asynchronous) 방식이겠죠? 근데 정의를 다시 보시면 XHR이 AJAX 프로그래밍에서 헤비하게 사용된다네요. 그러면 AJAX는 무엇일까요??

AJAX

AJAX는 'the client-side to create asynchronous web applications.', 즉 비동기 웹 어플리케이션을 만들기 위해 클라이언트 단에서 쓰이는 웹 개발 기술들의 집합 입니다. (출처: 위키백과)

 

마크업과 스타일 정보를 알려주는 HTML과 CSS, 웹의 상호작용을 도와주는 javascript 기술들도 전부 AJAX에 포함될 수 있죠. 특히 XMLHttpRequest 객체 (또는 fetch)가 나온 이후로 AJAX 라고 하면 보통 이 xhr 객체나 fetch 함수를 이용한 비동기 통신 방식을 의미한다고 하네요.

 

너무 어렵게 생각하지 맙시다. 비. 동. 기. 통. 신. 다섯 글자만 생각하자구요. 🥴

 

한번 체험해 볼까요?

구글 검색창에 글자 하나씩 타이핑될 때마다 네트워크 탭에서 xhr 객체가 늘어나는 것을 확인할 수 있습니다.

 

초기화면

 

'ㅇ' 을 계속 입력했을 때

아래는 AJAX 를 구현하는 코드(그런데 XMLHttpRequest를 곁들인)입니다. xhr 에는 event와 status 가 존재한다는 사실을 생각하면서 읽어봅시다.

 

잘 모르겠다면 https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest 를 참고하세요.

// 1. Create a new XMLHttpRequest object
let xhr = new XMLHttpRequest();

// 2. Configure it: GET-request for the URL /article/.../load
xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. Send the request over the network
xhr.send();

// 4. This will be called after the response is received
xhr.onload = function() {
  if (xhr.status != 200) { // analyze HTTP status of the response
    alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
  } else { // show the result
    alert(`Done, got ${xhr.response.length} bytes`); // response is the server response
  }
};

xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    alert(`Received ${event.loaded} of ${event.total} bytes`);
  } else {
    alert(`Received ${event.loaded} bytes`); // no Content-Length
  }

};

xhr.onerror = function() {
  alert("Request failed");
};

 

xhr 객체에 계속 open, send, onload, onprogress, onerror라는 이벤트 핸들러를 새로 정의해서 할당해 주고 있네요. 이벤트 핸들러에 대한 내용은 아래에서 확인해 봅시다.

 

Using XMLHttpRequest

 

Using XMLHttpRequest - Web APIs | MDN

In this guide, we'll take a look at how to use XMLHttpRequest to issue HTTP requests in order to exchange data between the web site and a server

developer.mozilla.org

이 영상에서 위의 내용들을 더욱 자세하게 설명해주고 있습니다. 코드를 직접 시연하니 함 보세요.

 

fetchaxios

아까 XMLHttpRequest 말고 fetch 도 있다고 했죠?

fetch는 API 입니다. Promise 객체를 반환하는 API요. 혹시 Promise 를 모르시는 건 아니겠죠?

Promise 객체의 정의: 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다. (출처 : MDN)

MDN은 Promise 객체가 최종 완료 콜백, 실패 콜백 을 직접 전달해 주는 게 아니라, 첨부하는 방식이라고 설명하고 있습니다.

(remind) Promise 의 특징

  • 콜백은 자바스크립트 Event Loop이 현재 실행중인 콜 스택을 완료하기 이전에는 절대 호출되지 않습니다.
  • 비동기 작업이 성공하거나 실패한 뒤에 then() 을 이용하여 추가한 콜백의 경우에도 위와 같습니다.
  • then() 을 여러번 사용하여 여러개의 콜백을 추가할 수 있습니다. 그리고 각각의 콜백은 주어진 순서대로 하나하나 실행되게 됩니다.

fetch

자 다시 fetch로 돌아와서, fetch는 API 라고 했죠? Promise 객체만 가지고는 웹 통신을 할 수 없어요. 그 통신을 도와주는 API가 바로 fetch 입니다. 대신 반환하는 객체가 Promise 일 뿐입니다.

 

그럼 XMLHttpRequest 대신에 fetch를 쓰는 이유는 무엇일까요? 우리의 친구 stack overflow가 다음과 같이 잘 설명해주고 있습니다. (16년도 글이라 지금은 조금 달라졌을 수도!)


  • You can use the Cache API with the request and response objects;
  • You can perform no-cors requests, getting a response from a server that doesn't implement CORS. You can't access the response body directly from JavaScript, but you can use it with other APIs (e.g. the Cache API);
  • Streaming responses (with XHR the entire response is buffered in memory, with fetch you will be able to access the low-level stream). This isn't available yet in all browsers, but will be soon.

There are a couple of things that you can do with XHR that you can't do yet with fetch, but they're going to be available sooner or later (read the "Future improvements" paragraph here: https://hacks.mozilla.org/2015/03/this-api-is-so-fetching/):

  • Abort a request (this now works in Firefox and Edge, as @sideshowbarker explains in his comment)
  • Report progress.

간단하게 얘기하면 XHR 은 모든 요청을 한 번에 받아오는 반면, fetch는 나눠서 가져오기 때문에 캐싱도 가능하고, 진행도(progress) 를 알 수 있다고 하네요. 또 no-cors request (cors는 저번에 설명했었죠) 도 가능하구요. [no-cors 요청 시 서버에서 no-cors 옵션으로 설정해 준 건 받아올 수 있음]

 

아래를 보시면 여러 옵션을 선택할 수 있다는 걸 알 수 있죠!

 

// Example POST method implementation:

postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(JSON.stringify(data))) // JSON-string from `response.json()` call
  .catch(error => console.error(error));

function postData(url = '', data = {}) {
  // Default options are marked with *
    return 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
        referrer: 'no-referrer', // no-referrer, *client
        body: JSON.stringify(data), // body data type must match "Content-Type" header
    })
    .then(response => response.json()); // parses JSON response into native JavaScript objects
}

 

다만 XHR과의 차이점이라고 한다면, (fetch의 경우) Content-type 헤더를 뺄 수가 없다는 것, 그리고 데이터가 나눠서 들어오기 때문에 데이터 다 받아 놓고도 abort 되는 오류가 발생한다는 것. 데이터 전체 길이를 미리 알 수 없다는 것, SetTimeout을 할 수 없다는 것 (엄밀히 말하면 할 수는 있는데 fetch 자체적으론 구현이 안되어 있다는 의미) 등... 이 있다고 하네요. (자세한 건 아래에서)

 

Fetch API vs XMLHttpRequest

 

Fetch API vs XMLHttpRequest

I know that Fetch API uses Promises and both of them allow you to do AJAX requests to a server. I have read that Fetch API has some extra features, which aren't available in XMLHttpRequest (and in...

stackoverflow.com

axios

axios 는 뭘까요? axios 는 라이브러리 중 하나로, React 에서 많이 사용됩니다.

그럼 fetch 를 안쓰고 axios 를 쓰는 이유는 뭘까요?

 

바로 axios가 좀 더 간편하기 때문입니다. 기본적인 것만 크게 두 가지로 본다면

  1. body 부분에서 data 를 넘겨주기 위해 JSON.stringify 를 호출하지 않아도 된다는 것
  2. url 을 변수로 따로 설정해서 넘겨주지 않고 처음부터 안에 넣어주면 된다는 점

🤗simple 해 보이는 차이가 있지요. 하지만 fetchvanilla JS에 기본으로 포함되어있는 기능이기 때문에, 순수하게 라이브러리 없이 코드를 작성하고자 한다면 굳이 나쁜 선택은 아니며, 관점의 차이가 있을 수 있다고 생각합니다.

 

하지만 axios 가 지원하는 기능이 더 많은 건 사실입니다. (인정할 건 합시다)

자세한 건 이 블로그에서 설명을 잘 해놓았으니 참고하시면 될 듯합니다. (코드 포함)

Axios 와 Fetch

 

Axios 와 Fetch

이 내용은 아래의 주소를 참조하여 작성되었습니다. (https://blog.logrocket.com/axios-or-fetch-api/) Axios 와 Fetch 둘 다, 서버와 클라이언트간 비동기 통신을 위해서 사용되는 javascript 라이브러리다. * A..

sdy-study.tistory.com

읽다 보면 fetch 에서 부족하다고 느낀 부분들을 보충해 주는 방식으로 라이브러리가 구성되어 있다는 것을 느끼실 수 있을 거예요.

둘의 차이점

이제 그럼 코드로 좀 파봅시다!

axios 도 결국 fetch를 이용한 라이브러리이니, 둘 다 Promise 객체를 반환하겠죠? (당연)😉

그럼 차이가 무엇일까요? 아래 코드를 확인해 봅시다.

 

fetch POST json request

let url = 'https://someurl.com';
let options = {
            method: 'POST',
            mode: 'cors',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json;charset=UTF-8'
            },
            body: JSON.stringify({
                property_one: value_one,
                property_two: value_two
            })
        };
let response = await fetch(url, options);
let responseOK = response && response.ok;
if (responseOK) {
    let data = await response.json();
    // do something with data
}

 

axios POST json request

let url = 'https://someurl.com';
let options = {
            method: 'POST',
            url: url,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json;charset=UTF-8'
            },
            data: {
                property_one: value_one,
                property_two: value_two
            }
        };
let response = await axios(options);
let responseOK = response && response.status === 200 && response.statusText === 'OK';
if (responseOK) {
    let data = await response.data;
    // do something with data
}

 

url과 json 타입 변수 (options) 에는 별로 차이가 없지만, response를 받는 부분에서 차이가 있지요?

 

  1. url까지 파라미터로 입력해주는가
  2. response에 대해서 status check를 해주는가

1번의 이유는 fetch 는 url을 body에 포함하지 않는 반면, axios 는 url 이 이미 객체에 포함되어 있기 때문인데요.

axios 의 객체를 확인해보시면 이를 확실히 알 수 있습니다.

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

//https://github.com/axios/axios/blob/master/lib/core/Axios.js

 

보시다시피 request 내부는 method, url, data 로 구성되어 있죠. 그니까 전달할 때부터 바로 넘겨주면 됩니다.

 

2번의 경우는 궁금해서 statusText 부분을 열심히 찾아봤는데 못 찾겠네요. ⇒ 찾으면 추가하겠습니다.

쓰다보니 이 파트는 너무 길어질 것 같아서 다음에 axios를 더 파보는 시간😋을 따로 갖도록 하겠습니다!

 

하하

 


참고 자료

 

AJAX란 무엇인가?

XMLHttpRequest

What is difference between Axios and Fetch?

axios/axios

XMLHttpRequest

Fetch API - Web API | MDN

Promise - JavaScript | MDN

React | axios란? (feat. Fetch API)