XMLHttpRequest와 Fetch API에 대하여

shinychan95·2021년 7월 21일
3
post-thumbnail

(위 사진은 chrome.webRequest의 Life cycle of requests이다)

의문점들은 지원서 결과 확인 매크로를 만들기 위해 네트워크 요청을 분석하다 결론적으로 XMLHttpRequest만을 매크로로 기억되도록 필터 조건을 걸면서 시작되었다.

참고한 자료

 

먼저 자바스크립트를 활용한 비동기 통신이 대제목이라고 볼 수 있다.

 

AJAX

  • XMLHttpRequest 를 검색했을 때, MDN Web Docs 내 Ajax 시작하기가 등장한다. 자바스크립트의 비동기 통신에 대해 알아보는 첫 단계로 AJAX를 알아보자.
  • AJAX란 비동기 자바스크립트와 XML(Asynchronous JavaScript And XML)을 말합니다. 간단히 말하면, 서버와 통신하기 위해 XMLHttpRequest 객체를 사용하는 것을 말합니다.
  • AJAX의 주요 두가지 특징은 아래의 작업을 할 수 있게 해줍니다.
    • 페이지 새로고침 없이 서버에 요청
    • 서버로부터 데이터를 받고 작업을 수행

+a blob → binary large object

그렇다면, 자바스크립트로 AJAX를 구현한 코드를 살펴보자.

 

XMLHttpRequest

// 출처 -> https://m.blog.naver.com/altmshfkgudtjr/221939445527
var xmlhttp;
document.getElementById("ajaxBtn").addEventListener('click', makeRequest);

function makeRequest() {
  // 1. XMLHttpRequest 객체를 생성
  xmlhttp = new XMLHttpRequest();
  // 2. 서버로 보낸 요청에 대한 응답을 받았을 때 동작할 함수
  xmlhttp.onreadystatechange = Callback;

  // 3. open(method, url, async or sync)
  xmlhttp.open('POST', < API URL >, true);

  var data = {'name': 'chanyoung'};
  // +a 4. setting header
  xmlhttp.setRequestHeader('Content-Type', 'application/json');

  // 5. 파라미터는 POST 방식으로 요구한 경우 서버로 보내고 싶은 어떠한 데이터
  // multipart/form-data, JSON, XML, SOAP 등과 같은 다른 형식(format)도 가능
	xmlhttp.send(JSON.stringify(data));
}

// 서버 응답에 대한 처리
function Callback() {
  try {
    if (xmlhttp.readyState === XMLHttpRequest.DONE) {
      if (xmlhttp.status === 200) {
        console.log("성공: ", xmlhttp.responseText);
      } else {
        console.log("실패");
      }
    }
  } catch (e) {
    console.log("에러: ", e);
  }
}
  • 상당히 가독성이 좋지 않은 것을 볼 수 있다. 참고한 블로그에서도 가독성이 심히 좋지 않다고 말하고 있다.
  • MDN 문서를 참고하며 보니 가독성이 좋은 코드 같기도 하다. 콜백을 이해하면.

 

비동기 콜백

자바스크립트 엔진과 Web API

  • 자바스크립트 엔진이란 자바스크립트 코드를 실행하는 프로그램 또는 인터프리터를 말한다. 대체로 웹브라우저에서 실행한다. 대표적인 예는 구글이 만든 V8 엔진이다.
  • 브라우저는 단순히 엔진 하나만으로 구성되어 있지 않고. DOM, AJAX, setTimeout 등의 브라우저에서 제공하는 Web API라고 하는 것이 있다. 이 Web API는 자바스크립트를 사용해 접근이 가능하다. 우리가 문서의 요소에 접근하여 배경 색상을 바꾸거나 하는 것들이 다 Web API를 사용하여 DOM에 접근하여 동적인 변화를 주는 것이다.
  • 아래는 Web API의 주요 목록이다.

 

비동기 콜백의 처리 과정

  1. 이벤트가 발생한다.
  2. http 요청과 같은 비동기 함수는 인터프리터에 의해 Call Stack에서 곧바로 지워지고, C++로 구현된 Web API로 넘어간다.
  3. Web API는 Callback function를 Callback Queue에 밀어넣는다. 이때 Web API는 함수의 실행 순서와 상관없이 끝난 순서대로 Callback Queue에 넣는다.
  4. Event Loop는 Call Stack과 Callback Queue 사이에서 Call Stack이 비어있는지 주시한다.
  5. 모든 함수의 실행이 완료되고 Call Stack이 비워지면, Event Loop는 Callback Queue에 담겨있는 함수들을 먼저 들어온 순서대로 Call Stack으로 넘겨준다.

 

Fetch API

Fetch API는 네트워크 통신을 포함한 리소스 취득을 위한 인터페이스가 정의되어 있습니다. XMLHttpRequest와 같은 비슷한 API가 존재합니다만, 새로운 Fetch API는 좀더 강력하고 유연한 조작이 가능합니다.

  • 링크를 참고하면, 10년이 넘는 세월동안 사용된 XMLHttpRequest 대신 fetch가 생겨난 배경에 대해 소개한다.
    • XHR은 입력, 출력, 그리고 상태 모두를 하나의 객체로 관리해야 했다.
    • XHR의 상태 값은 이벤트를 통해 추적해야 했다.
    • 이벤트 기반 모델요즘의 Promise 기반 비동기 프로그래밍 방식과 그다지 잘 어울리지 않습니다.
      (Promise 내부 동작도 알아보자!)
  • Fetch API 는 HTTP 프로토콜의 모든 개념을 JS 에 똑같이 도입함으로써 문제를 해결합니다. Fetch API 는 유틸리티 함수 fetch() 를 도입합니다. fetch() 함수는 네트워크로부터 리소스를 가져오는 동작을 간결하게 표현합니다.
// 출처 -> https://m.blog.naver.com/altmshfkgudtjr/221939445527
let data = new FormData();
data.append('name', 'NB');

fetch(<API URL>, {
  method: 'POST',
  header: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
  // etc..
}).then(res => res.json())
.then((res)=> {
  console.log(JSON.stringify(res));
}).catch((err)=> {
  console.log("에러: ", err);
});

 

Promise에 대하여

참 잘 정리된 링크 > ECMAScript 6 Promise와 비동기 프로그램밍

  • fetch의 비동기 프로그래밍을 제대로 이해하기 위해서는 ES6 Promise에 대해 알 필요가 있다. 기존의 Event 모델 및 Callback 패턴과 어떤 점이 다른지 자세하게 정리되어 있다.

 

간단하게 정리하면,

  • Promise는 비동기 연산의 결과를 위한 Placeholder이다.
// readFile은 미래의 어떤 시점에서 완료할 것을 약속합니다.
let promise = readFile("example.txt");
  • readFile()은 실제로 파일 읽기를 즉시 시작하지 않습니다. 그것은 나중에 일어날 일입니다. 대신이 함수는 비동기 읽기 작업을 나타내는 Promise 객체를 리턴하므로 향후 이 작업을 수행할 수 있습니다.
  • 리턴하는 즉시 상태는 Pending State이고, Fulfilled(완료됨), Rejected(거절됨) 상태가 존재한다.
  • 그리고 then() 메서드를 사용하여 Promise의 State가 바꿀때 특정 동작을 취할 수 있습니다.
  • 아래 블로그에 나온 예제를 보면, 추가로 이해가 가능하다.
// 출처 - https://infoscis.github.io

// Node.js example
let fs = require("fs");

function readFile(filename) {
    return new Promise(function(resolve, reject) {
        // 비동기 동작 트리거
        fs.readFile(filename, { encoding: "utf8" }, function(err, contents) {
            // check for errors
            if (err) {
                reject(err);
                return;
            }
            // the read succeeded
            resolve(contents);
        });
    });
}

let promise = readFile("example.txt");

// 수행과 거절 모두를 처리한다.
promise.then(function(contents) {
    // 수행
    console.log(contents);
}, function(err) {
    // 거절
    console.error(err.message);
});

 

결론적으로,

자바스크립트를 활용한 비동기 프로그래밍에 대해 조금 익혔다.

매크로로 기억될 네트워크 송수신을 필터링 하는 과정에서 어떤 조건으로 해야할 지 고민이었다. 보통 HTML 파일을 받고, 필요한 리소스를 요청하는 것과 POST 요청을 통해 인증이나 쿠키를 받는 요청 그리고 리다이렉트 요청도 있다.

각각 어떤 조건으로 구별할 지 배경지식이 없던 와중에 이번 분석을 통해 조금이나마 감을 익혔다.

물론 현재 chrome.webRequest가 request 및 response를 처리하는 프로세스 내에 listener를 추가하여 request 및 response의 details 내 type으로 필터링을 하고 있지만, 이는 이 request에 종속적인 방법임을 깨닫는다. 특히 xmlhttprequest가 아닌 다른 객체 요청으로 변경될 가능성이 크다.

따라서 더 나은 방법이라면 request와 response를 묶어 객체에 저장하여 response 내 Content-Type을 통해 필터링을 하는 방법이 더 낫지 않나 싶다.

profile
개발자로 일하는 김찬영입니다.

0개의 댓글