JavaScript에서 HTTP 요청을 만들 수 있는 내장 브라우저 개체입니다.
XML 형식뿐만 아니라 모든 데이터에서 작동할 수 있으며 업로드/다운로드하고 진행 상황을 추적하는 등의 작업을 할 수 있습니다.
html의 template 형식을 사용하여 XMLHttpRequest을 통해 서버에서 받아온 데이터를 리스트로 나열하려고 합니다.
아래는 javascript 예시를 보기전 알아둬야할 내용입니다.
요소 내의 콘텐츠는 페이지가 로드될 때 즉시 렌더링되지 않으며, 따라서 사용자에게는 보이지 않습니다.
하지만 나중에 자바스크립트를 사용하여, 해당 콘텐츠를 복제한 후 보이도록 렌더링할 수 있습니다.
요청의 기본 매개변수를 지정합니다.
xhr.open(method, URL, [async, user, password])
JSON.parse() 대신 사용하는 XMLHttpRequest 객체에 내장된 기능입니다.
load가 성공적으로 이루어지면 트리거 할 수 있는 이벤트입니다.
xhr은 아래의 이벤트를 수신합니다.
importNode(다른 문서에서 가져올 노드, 다른 문서에서 노드를 가져올 때 노드의 자식 요소들을 포함하여 가져올 것인지에 대한 여부)
현재 문서가 아닌 외부 문서의 노드를 복사하여 현재 문서에 넣을 수 있도록 해줍니다.
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();
비동기 처리로 데이터를 요청하고 생성하고 삭제하는 코드입니다.
HTTP요청 헤더의 값을 설정합니다.
open()뒤에 호출하여야 하며, send()가 호출되기 전에 호출해야 합니다.
myReq.setRequestHeader(설정 될 값을 가진 헤더의 이름, 헤더의 본문(body)에 설정될 값);
일치하는 요소를 찾을 때까지, 자기 자신을 포함해 위쪽(부모 방향, 문서 루트까지)으로 문서 트리를 순회합니다.
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}`
);
}
});