XMLHttpRequest

zimablue·2023년 8월 8일

javascript

목록 보기
20/30

JavaScript에서 HTTP 요청을 만들 수 있는 내장 브라우저 개체입니다.
XML 형식뿐만 아니라 모든 데이터에서 작동할 수 있으며 업로드/다운로드하고 진행 상황을 추적하는 등의 작업을 할 수 있습니다.





사용 예시

html의 template 형식을 사용하여 XMLHttpRequest을 통해 서버에서 받아온 데이터를 리스트로 나열하려고 합니다.

아래는 javascript 예시를 보기전 알아둬야할 내용입니다.

template 태그

요소 내의 콘텐츠는 페이지가 로드될 때 즉시 렌더링되지 않으며, 따라서 사용자에게는 보이지 않습니다.
하지만 나중에 자바스크립트를 사용하여, 해당 콘텐츠를 복제한 후 보이도록 렌더링할 수 있습니다.


open

요청의 기본 매개변수를 지정합니다.

xhr.open(method, URL, [async, user, password])
  • method – HTTP 방식입니다.
  • URL – 요청할 URL입니다.
  • async – 명시적으로 설정되면 false요청이 동기식입니다.
  • user, password– 기본 HTTP 인증을 위한 로그인 및 암호가 필요한 경우 사용합니다.

responseType

JSON.parse() 대신 사용하는 XMLHttpRequest 객체에 내장된 기능입니다.


onload

load가 성공적으로 이루어지면 트리거 할 수 있는 이벤트입니다.

xhr은 아래의 이벤트를 수신합니다.

  • load – 요청이 완료되고(HTTP 상태가 400 또는 500과 같은 경우에도) 응답이 완전히 다운로드된 경우.
  • error – 요청을 할 수 없는 경우(예: 네트워크 다운 또는 유효하지 않은 URL).
  • progress – 응답이 다운로드되는 동안 주기적으로 트리거되며 다운로드된 양을 보고합니다.

importNode

importNode(다른 문서에서 가져올 노드, 다른 문서에서 노드를 가져올 때 노드의 자식 요소들을 포함하여 가져올 것인지에 대한 여부)

현재 문서가 아닌 외부 문서의 노드를 복사하여 현재 문서에 넣을 수 있도록 해줍니다.


send

xhr.send([body])

연결을 열고 서버에 요청을 보냅니다.


<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Http</title>
    <link rel="stylesheet" href="assets/styles/app.css" />
    <script src="assets/scripts/app.js" defer></script>
  </head>
  <body>
    <!-- 데이터 생성시 사용할 탬플릿 -->
    <template id="single-post">
      <li class="post-item">
        <h2></h2>
        <p></p>
        <button>DELETE</button>
      </li>
    </template>
    <section id="new-post">
      <!-- 데이터 생성시 데이터 입력 폼 -->
      <form>
        <div class="form-control">
          <label for="title">Title</label>
          <input type="text" id="title" />
        </div>
        <div class="form-control">
          <label for="content">Content</label>
          <textarea rows="3" id="content"></textarea>
        </div>
        <button type="submit">ADD</button>
      </form>
    </section>
    <section id="available-posts">
      <button>FETCH POSTS</button>
      <!-- 응답 받은 데이터를 보여줄 리스트 -->
      <ul class="posts"></ul>
    </section>
  </body>
</html>
// app.js

const listElement = document.querySelector(".posts");
const postTemplate = document.getElementById("single-post");

const xhr = new XMLHttpRequest();

xhr.open("GET", "https://jsonplaceholder.typicode.com/posts");

xhr.responseType = "json";

xhr.onload = function () {
  const listOfPosts = xhr.response;

  // 응답은 비동기적으로 발생하므로 이 익명 함수 내부에서 사용
  for (const post of listOfPosts) {
    const postEl = document.importNode(postTemplate.content, true);
    postEl.querySelector("h2").textContent = post.title.toUpperCase();
    postEl.querySelector("p").textContent = post.body.toUpperCase();
    listElement.append(postEl);
  }
};

xhr.send();





사용 예시2

비동기 처리로 데이터를 요청하고 생성하고 삭제하는 코드입니다.

setRequestHeader()

HTTP요청 헤더의 값을 설정합니다.
open()뒤에 호출하여야 하며, send()가 호출되기 전에 호출해야 합니다.

myReq.setRequestHeader(설정 될 값을 가진 헤더의 이름, 헤더의 본문(body)에 설정될 값);

closest('찾을 요소')

일치하는 요소를 찾을 때까지, 자기 자신을 포함해 위쪽(부모 방향, 문서 루트까지)으로 문서 트리를 순회합니다.


async/ awit

function 앞에 async를 붙이면 해당 함수는 항상 프라미스를 반환합니다.
await는 프라미스가 처리될 때까지 기다리고 결과를 반환합니다.


// app.js

const listElement = document.querySelector(".posts");
const postTemplate = document.getElementById("single-post");
const form = document.querySelector("#new-post form");
const fetchButton = document.querySelector("#available-posts button");
const postList = document.querySelector("ul");


// 서버와 통신
function sendHttpRequest(method, url, data) {
  const promise = new Promise((resolve, reject) => {
    // HTTP 요청 객체 생성
    const xhr = new XMLHttpRequest();
    // 헤더 추가
    xhr.setRequestHeader('Content-Type', 'application/json')
    // 요청에 대한 설정
    xhr.open(method, url);
    // 응답 파싱
    xhr.responseType = "json";
    // load 완료시 이벤트
    xhr.onload = function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        // 성공시 프로미스에 담길 데이터
        resolve(xhr.response);
      } else {
        // 실패시 프로미스에 담길 데이터(서버와의 오류)
        reject(new Error("Something went wrong"));
      }
    };
    // 실패시 프로미스에 담길 데이터(네트워크 오류)
    xhr.onerror = function () {
      reject(new Error("Failed to send request"));
    };
    // 서버에 요청
    xhr.send(JSON.stringify(data));
  });
  return promise;
}


// 데이터 요청
async function fetchPosts() {
  try {
    const responseData = await sendHttpRequest(
      "GET",
      "https://jsonplaceholder.typicode.com/posts"
    );
    const listOfPosts = responseData;
    for (const post of listOfPosts) {
      const postEl = document.importNode(postTemplate.content, true);
      postEl.querySelector("li").id = post.id;
      postEl.querySelector("h2").textContent = post.title.toUpperCase();
      postEl.querySelector("p").textContent = post.body;
      listElement.append(postEl);
    }
  } catch (error) {
    alert(error.message);
  }
}

fetchButton.addEventListener("click", fetchPosts);


// 데이터 생성
async function createPost(title, content) {
  const userId = Math.random();
  const post = {
    title: title,
    body: content,
    userId: userId,
  };
  sendHttpRequest("POST", "https://jsonplaceholder.typicode.com/posts", post);
}

form.addEventListener("submit", (event) => {
  event.preventDefault();
  const enteredTitle = event.currentTarget.querySelector("#title").value;
  const enteredContent = event.currentTarget.querySelector("#content").value;
  createPost(enteredTitle, enteredContent);
});


// 데이터 삭제
postList.addEventListener("click", (event) => {
  if (event.target.tagName === "BUTTON") {
    const postId = event.target.closest("li").id;
    sendHttpRequest(
      "DELETE",
      `https://jsonplaceholder.typicode.com/posts/${postId}`
    );
  }
});

0개의 댓글