서버에 네트워크 요청을 보내고 새로운 정보를 받아 promise 객체로 return 합니다.
fetch(resource, options)
return되는 객체는, API 호출이 성공했을 경우에는 response 객체를 resolve하고, 실패했을 경우 error 객체를 reject합니다.
fetch(resource, options)
.then((response) => console.log(response))
.catch((error) => console.log(error));
취득하려는 리소스의 URL을 제공하는 문자열 또는 URL처럼 문자열 변환자를 포함한 객체.
요청에 적용하고자 하는 사용자 지정 설정을 포함하는 객체입니다. 사용 가능한 설정은 다음과 같습니다.
fetch() 는 디폴트로 GET 방식으로 작동하고 GET 방식은 요청 전문을 받지 않기 때문에 옵션 인자가 필요가 없습니다.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => console.log(response)
);
// response
Response {
body: ReadableStream
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: ""
type: "cors"
url: "https://jsonplaceholder.typicode.com/posts/1"
[[Prototype]]: Response
}
대부분의 REST API들은 JSON 형태의 데이터를 응답하기 때문에, response 객체는 응답 본문을 JSON으로 파싱한 결과로 이행하는 프로미스를 반환하는 json()을 인스턴스 메서드로 제공합니다.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => response.json())
.then((data) => console.log(data));
// data
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit↵suscipit recusandae consequuntur …strum rerum est autem sunt rem eveniet architecto"
}
HTTP 방식(method): "POST", HTTP 요청 헤더(headers): JSON 포맷, HTTP 요청 전문(body): JSON 포맷으로 직렬화한 데이터 생성 요청합니다.
fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Test",
body: "I am testing!",
userId: 1,
}),
}).then((response) => console.log(response));
// response
Response {}
body: ReadableStream
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 201
statusText: ""
type: "cors"
url: "https://jsonplaceholder.typicode.com/posts"
[[Prototype]]: Response
DELETE 방식에서는 보낼 데이터가 없기 때문에, HTTP 요청 헤더(headers)와 HTTP 요청 전문(body) 옵션이 필요가 없습니다.
fetch("https://jsonplaceholder.typicode.com/posts/1", {
method: "DELETE",
})
.then((response) => response.json())
.then((data) => console.log(data));
// data
{}
JSON.parse()에는 response의 body만 받을 수 있습니다.
response.json() 메서드에서는 응답 header, body를 포함한 response 객체 자체를 받아서 body만 읽으며 Promise 객체를 반환합니다.
<!-- 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 form = document.querySelector("#new-post form");
const fetchButton = document.querySelector("#available-posts button");
const postList = document.querySelector("ul");
function sendHttpRequest(method, url, data) {
//promise 객체를 return
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status >= 200 && response.status < 300) {
// fetch는 파싱된 응답이 아닌 스트리밍된 응답을 반환하기 때문에 .json()을 사용하여 프로미스로 반환
// fetch의 응답은 header가 도착하자마자 주어지며 body가 없기 때문에 pending 상태의 프로미스
// .json()은 JSON.parse()대신 사용하며 스트림이 완료될 때까지 기다린 후에 body의 텍스트를 프로미스로 반환
return response.json();
} else {
// 서버와의 오류로 인한 에러일 경우
return response.json().then((errData) => {
console.log(errData);
throw new Error("Something went wrong - server-side");
});
}
})
.catch((error) => {
// 네트워크 에러일 경우
console.log(error);
throw new Error("Something went wrong");
});
}
// 데이터 요청
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("h2").textContent = post.title.toUpperCase();
postEl.querySelector("p").textContent = post.body;
postEl.querySelector("li").id = post.id;
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}`
);
}
});
async function post(host, path, body, headers = {}) {
const url = `https://${host}/${path}`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
...headers,
},
body: JSON.stringify(body),
};
const res = await fetch(url, options);
const data = await res.json();
if (res.ok) {
return data;
} else {
throw Error(data);
}
}
post("jsonplaceholder.typicode.com", "posts", {
title: "Test",
body: "I am testing!",
userId: 1,
})
.then((data) => console.log(data))
.catch((error) => console.log(error));