영광의 시대!/2022 개발자의 품격 부트캠프 1기

[개발자의품격][부트캠프][1기][10차시] JavaScript 주요 포인트 #12 | 고급 문법 - XMLHttpRequest, Fetch API, Promise, Async/Await

DandyNow 2022. 2. 9. 22:12
728x90
반응형

JavaScript 주요 포인트 #12

자바스크립트는 AJAX(Asynchronous JavaScript and XML) 이전과 이후로 나뉜다. AJAX로 인해 비동기 통신 및 웹페이지의 특정 일부분만 랜더링 하는 것이 가능해졌다. 이로 인해 웹이 급속도록 발전하게 되었다. AJAX는 동적인 웹 페이지를 만들기 위해 사용한다.

 

여기에서 다루게 될 내용은 XMLHttpRequest, Fetch API, Promise, Async/Await로서 자바스크립트 전체 문법을 통틀어 매우 중요한 부분이다. 이번 실습은 테스트 서버가 필요하다. 따라서 JSON Server를 실행한다.

 

JSON Server 실행

json-server --watch db.json

 


 

XMLHttpRequest

XMLHttpRequest(XHR) 객체는 서버와 상호작용하기 위해 사용한다. 지금은 XMLHttpRequest 객체를 직접적으로 사용할 일이 잘 없다. jQuery, fetch, axios을 사용하기 때문이다. 이 중에서도 axios를 주로 사용한다. 내부적으로 XMLHttpRequest가 돌아간다.

 

HTTP method(HTTP 요청 방식)

  • GET - 리소스 요청
  • POST - 리소스 생성
  • PUT - 리소스 수정
  • PATCH - 리소스 일부 수정(거의 안 씀)
  • DELETE - 리소스 삭제

 

JSON Server Resources

  • http://localhost:3000/posts
  • http://localhost:3000/comments
  • http://localhost:3000/profile

 

아래 코드 블록은 XMLHttpRequest 객체를 이용하여 서버의 데이터를 조회, 생성, 수정, 삭제하는 코드이다. 여기에서는 HTTP method(HTTP 요청 방식)와 JSON Server Resources를 이용한다.

<body>
  <button onclick="getData()">조회</button>
  <button onclick="postData()">생성</button>
  <button onclick="putData()">수정</button>
  <button onclick="deleteData()">삭제</button>
  <script>
    // 조회
    function getData() {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", "http://localhost:3000/posts"); // 요청방식과 서버URL을 넣어준다. 준비 상태이다.
      xhr.setRequestHeader("content-type", "application/json"); // 요청에 맞는 헤더 값을 설정해야 한다. 서버와는 일반적으로 json 통신하기 때문에 거의 이렇게 쓴다고 보면된다.
      xhr.send(); // 서버에 요청을 보낸다.

      // 서버로 요청하면 그 결과가 응답이 왔을때 onload에 있는 코드가 실행된다.
      xhr.onload = () => {
        console.log(xhr);
        // 상태 코드 200, 성공
        if (xhr.status === 200) {
          const res = JSON.parse(xhr.response); // object로 받아 온다.
          console.log(res);
          return res;
        } else {
          console.log(xhr.status, xhr.statusText);
        }
      };
    }

    // 생성
    function postData() {
      const xhr = new XMLHttpRequest();
      xhr.open("POST", "http://localhost:3000/posts");
      xhr.setRequestHeader("content-type", "application/json;charset=UTF-8"); // charset=UTF-8 한글이 깨지는 것 방지한다.
      const data = { title: "xmlhttprequest", author: "John Doe" }; // json은 유일한 키값이 항상 존재해하기 때문에 id가 반드시 있어야 한다. id번호는 자동으로 생성된다.
      xhr.send(JSON.stringify(data)); // object를 문자열로 변환한다.

      xhr.onload = () => {
        if (xhr.status === 201) {
          // 상태 코드 201, 생성만 201, 조회, 수정, 삭제는 200번이다.
          const res = JSON.parse(xhr.response);
          console.log(res);
        } else {
          console.log(xhr.status, xhr.statusText);
        }
      };
    }

    // 수정
    function putData() {
      const xhr = new XMLHttpRequest();
      xhr.open("PUT", "http://localhost:3000/posts/2"); // id 2번 수정
      xhr.setRequestHeader("content-type", "application/json;charset=UTF-8");
      const data = { title: "javascript", author: "jeremy" };
      xhr.send(JSON.stringify(data));

      xhr.onload = () => {
        if (xhr.status === 200) {
          const res = JSON.parse(xhr.response);
          console.log(res);
        } else {
          console.log(xhr.status, xhr.statusText);
        }
      };
    }

    // 삭제
    function deleteData() {
      const xhr = new XMLHttpRequest();
      xhr.open("DELETE", "http://localhost:3000/posts/2"); // id 2번 삭제
      xhr.setRequestHeader("content-type", "application/json;charset=UTF-8");
      xhr.send();

      xhr.onload = () => {
        if (xhr.status === 200) {
          const res = JSON.parse(xhr.response);
          console.log(res);
        } else {
          console.log(xhr.status, xhr.statusText);
        }
      };
    }
  </script>
</body>

 


 

Fetch API

Fetch API를 이용하면 XMLHttpRequest와 동일한 기능을 더 쉽게 구현할 수 있다. Fetch API는 그 자체가 Promise이다.

<body>
  <button onclick="getData()">조회</button>
  <button onclick="postData()">생성</button>
  <button onclick="putData()">수정</button>
  <button onclick="deleteData()">삭제</button>
  <script>
    // 조회
    function getData() {
      fetch("http://localhost:3000/posts")
        // .then(function (response) {
        //   return response.json(); // json 으로 변환
        // })
        // .then(function (json) {
        //   console.log(json);
        // });
        // 위 조회 코드를 화살표 함수로 구현
        .then((response) => response.json()) // json 으로 변환
        .then((json) => console.log(json));
    }

    // 생성
    function postData() {
      fetch("http://localhost:3000/posts", {
        method: "POST",
        body: JSON.stringify({
          title: "xmllhttprequest",
          author: "John Doe",
        }),
        headers: {
          "content-type": "application/json;charset=UTF-8",
        },
      })
        .then((response) => response.json())
        .then((json) => console.log(json));
    }

    // 수정
    function putData() {
      // id 3번 수정
      fetch("http://localhost:3000/posts/3", {
        method: "PUT",
        body: JSON.stringify({
          title: "xmlhttprequest",
          author: "John Doe",
        }),
        headers: {
          "content-type": "application/json;charset=UTF-8",
        },
      })
        .then((response) => response.json())
        .then((json) => console.log(json));
    }

    // 삭제
    function deleteData() {
      // id 3번 삭제
      fetch("http://localhost:3000/posts/3", {
        method: "DELETE",
      })
        .then((response) => response.json())
        .then((json) => console.log(json));
    }
  </script>
</body>

 


 

Promise

Promise는 비동기 처리에 사용되는 객체이다. 비동기란 언제 끝날지 모르는 작업을 기다리지 않고 다음 작업을 처리하는 것을 말한다.

Promise 미적용

<script>
  function getData() {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "http://localhost:3000/posts"); // 요청방식과 서버URL을 넣어준다. 준비 상태이다.
    xhr.setRequestHeader("content-type", "application/json"); // 요청에 맞는 헤더 값을 설정해야 한다. 서버와는 일반적으로 json 통신하기 때문에 거의 이렇게 쓴다고 보면된다.
    xhr.send(); // 서버에 요청을 보낸다.

    // 서버로 요청하면 그 결과가 응답이 왔을때 onload에 있는 코드가 실행된다.
    xhr.onload = () => {
      console.log(xhr);
      // 상태 코드 200, 성공
      if (xhr.status === 200) {
        const res = JSON.parse(xhr.response); // object로 받아 온다.
        return res;
      } else {
        console.log(xhr.status, xhr.statusText);
      }
    };
  }

  const data = getData();
  console.log(data); // undefined(시차 발생, 서버가 요청에 대한 응답을 보내주기 전에 실행되었다.)
  console.log("다음 코드 실행");
</script>

 

Promise 적용

<script>
  // Promise 문법은 다음과 같다.
  // const promise = new Promise((resolve, reject) => {
  //   if ("처리성공") {
  //     resolve("결과 데이터");
  //   } else {
  //     reject("에러");
  //   }
  // });
  function getData2(url) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", url);
      xhr.setRequestHeader("content-type", "application/json");
      xhr.send();

      xhr.onload = () => {
        if (xhr.status === 200) {
          const res = JSON.parse(xhr.response);
          resolve(res);
        } else {
          console.log(xhr.status, xhr.statusText);
          reject(xhr.status);
        }
      };
    });
  }

  // Promise를 적용하면 호출하는 쪽에서 then을 정의하여 사용할 수 있다.
  // 서버로부터 응답받기 전에 실행되어 버리면 문제가 되는 코드를 then 함수 내에 작성한다.
  getData2("http://localhost:3000/posts").then((res) => console.log(res)); // 2순위 실행
  getData2("http://localhost:3000/comments").then((res) => console.log(res)); // 3순위 실행
  console.log("다음 코드 실행"); // 1순위 실행

  // getData1().then(getData2().then(getData3())) // 참고로 Promise는 이렇게 연결하여 사용할 수 있지만 복잡해진다.
  // 아래는 위 코드와 같다.
  // const data1 = getData1();
  // const data2 = getData2(data1);
  // const data3 = getData3(data2);
</script>

 

Promise 없는 then은 error

<script>
  function getCommentData(url) {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.setRequestHeader("content-type", "application/json");
    xhr.send();

    xhr.onload = () => {
      if (xhr.status === 200) {
        const res = JSON.parse(xhr.response);
        return res;
      } else {
        console.log(xhr.status, xhr.statusText);
      }
    };
  }

  // promise를 적용하지 않았는데 호출하는 쪽에서 then을 사용하면 에러가 발생한다.
  getCommentData("http://localhost:3000/posts").then((res) =>
    console.log(res)
  ); // Uncaught TypeError: Cannot read properties of undefined (reading 'then')
</script>

 


 

Async/Await

Async/Await는 Promise와 동일한 목적으로 사용한다. 비동기 통신이 완벽히 끝날 때까지 기다린다. 서버와 통신 중 순차적으로 코드를 실행할 때 주로 사용한다. 데이터가 너무 많아서 아래 코드가 마냥 기다리게 하는 상황이 발생할 수 있다. 따라서 서버로 부터 응답 받은 데이터를 다음 코드에서 반드시 필요로 할때 사용한다. 그런 상황이 아니라면 데이터를 덜 받았더라도 나머지 코드가 실행되도록 해야 한다. 상황에 따라 잘 사용하자!

<body>
  <button onclick="getData()">조회</button>
  <script>
    // function 앞에 async를 붙인다.
    async function getData() {
      const res1 = await fetch("http://localhost:3000/posts"); // 서버로 부터 값이 오기를 기다렸다가 res1에 넣겠다.
      // const res1Json = res1.json(); // 콘솔창에 "Promise {<pending>}" 표시되었다. res1Json에 배열 요소가 들어오지 않은 상태이다.
      const res1Json = await res1.json(); // await으로 인해 res1Json에 배열 요소가 모두 들어온다.
      console.log(res1Json);
    }
  </script>
</body>
728x90
반응형