비동기 통신의 대표적인 방법은 axios와 fetch가 있다.
공식문서에 따르면 axios란 node.js와 브라우저를 위한 promise 기반 http 클라이언트라고 소개한다. 다시 말해 http를 이용해서 서버와 통신하기 위해 사용하는 패키지라고 생각하면 된다.
yarn add axios
get은 서버의 데이터를 조회할 때 사용한다.
// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정을 추가할 수 있습니다.
// config는 axios 공식문서에서 확인하세요.
axios.get(url[, config]) // GET
기본적으로 GET요청을 할 때, path variable로 해야할지, query로 보내야할지는 API를 만든 사람이 하라는대로 해야 한다.
보다시피 실습으로 활용한 json-server는 공식문서를 보면, 전체 정보다 상세 정보는 path variable로 url을 작성하도록 되어있고, filter와 같은 기능을 위해서 GET요청을 하고자 할 때는 query로 보내라고 명시하고 있다.
axios.post(url[, data[, config]]) // POST
post는 보통 서버에 데이터를 추가할 때 사용한다. 다만 post 요청에 대한 로직은 BE 개발자가 구현하는 것이기 때문에 추가외에 다른 용도로 사용될 수 있다. 하지만 보통은 클라이언트의 데이터를 body 형태로 서버에 보내고자 할 때 사용한다.
delete는 저장되어 있는 데이터를 삭제하고자 요청을 보낼 때 사용한다.
axios.delete(url[, config]) // DELETE
axios.patch(url[, data[, config]]) // PATCH
patch는 보통 어떤 데이터를 수정하고자 서버에 요청을 보낼 때 사용하는 메서드이다.
ex)
// src/App.js
import React, { useEffect, useState } from "react";
import axios from "axios";
const App = () => {
const [todo, setTodo] = useState({
title: "",
});
const [todos, setTodos] = useState(null);
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:3001/todos");
setTodos(data);
};
const onSubmitHandler = (todo) => {
axios.post("http://localhost:3001/todos", todo);
};
// 새롭게 추가한 삭제 버튼 이벤트 핸들러
const onClickDeleteButtonHandler = (todoId) => {
axios.delete(`http://localhost:3001/todos/${todoId}`);
};
useEffect(() => {
fetchTodos();
}, []);
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitHandler(todo);
}}
>
<input
type="text"
onChange={(ev) => {
const { value } = ev.target;
setTodo({
...todo,
title: value,
});
}}
/>
<button>추가하기</button>
</form>
<div>
{todos?.map((todo) => (
<div key={todo.id}>
{todo.title}
{/* 디자인이 요상하긴 하지만..! 삭제 버튼 추가 */}
<button
type="button"
onClick={() => onClickDeleteButtonHandler(todo.id)}
>
삭제하기
</button>
</div>
))}
</div>
</>
);
};
export default App;
axios가 npm명령어나 yarn명령어로 설치를 해야만 하는 패키지였다면, fetch는 자바스크립트에서 자체적으로 제공하는 내장 라이브러리이다. 따라서 별도의 설치나 import를 필요로 하지 않는다.
앞으로 axios위주로 사용하게 될텐데 그건 fetch가 가진 몇가지 단점들 때문이다.
const url = "https://jsonplaceholder.typicode.com/todos";
fetch(url)
.then((response) => response.json())
.then(console.log);
const url = "https://jsonplaceholder.typicode.com/todos";
axios.get(url).then((response) => console.log(response.data));
const url = "https://jsonplaceholder.typicode.com/todos";
axios
.get(url)
.then((response) => console.log(response.data))
.catch((err) => {
console.log(err.message);
});
error handling
이 가능하다. 예를들면 이렇게.const url = "https://jsonplaceholder.typicode.com/todos";
// axios 요청 로직
axios
.get(url)
.then((response) => console.log(response.data))
.catch((err) => {
// 오류 객체 내의 response가 존재한다 = 서버가 오류 응답을 주었다
if (err.response) {
const { status, config } = err.response;
// 없는 페이지
if (status === 404) {
console.log(`${config.url} not found`);
}
// 서버 오류
if (status === 500) {
console.log("Server error");
}
// 요청이 이루어졌으나 서버에서 응답이 없었을 경우
} else if (err.request) {
console.log("Error", err.message);
// 그 외 다른 에러
} else {
console.log("Error", err.message);
}
});
const url = "https://jsonplaceholder.typicode.com/todos";
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(
`This is an HTTP error: The status is ${response.status}`
);
}
return response.json();
})
.then(console.log)
.catch((err) => {
console.log(err.message);
});
fetch의 경우, catch()가 발생하는 경우는 오직 네트워크 장애 케이스이다. 따라서 개발자가 일일히 then() 안에 모든 케이스에 대한 HTTP 에러 처리를 해야 한다.