react-router-dom
같은 라이브러리를 사용하지 않고 폴더 구조로 라우팅을 편하게 할 수 있다.클라이언트에서 전역으로 관리해야할 상태가 많지 않다.
Redux, Mobx, Recoil과 같은 상태관리 도구는 클라이언트 쪽 상태를 관리하기에는 적합하나 서버의 데이터들을 관리하기에는 적합하지 않다. 클라이언트 상태관리 도구에서 서버의 상태를 fetch해서 함께 관리하면 클라이언트 상태와 서버 상태가 섞여 구조가 복잡해진다.
두 개의 상태가 묶이면서 서버 데이터를 위한 로직이 과도하게 커지고, 클라이언트 상태를 관리하는 것이 아닌 서버상태를 관리하는 것과 같은 모습이 된다.
서버상태관리 도구를 사용한다면 서버와 클라이언트 데이터를 분리할 수 있다.
제공해야할 대부분의 정보는 서버에서 가져와야하고 순위 시스템의 경우 최신화가 중요하기 때문에 서버 데이터를 효율적으로 캐싱하고 데이터를 지속적으로 동기화하고 업데이트하는 작업을 도와주는 서버상태관리 도구를 도입하기로 하였다.
서버상태관리 도구를 사용하지 않았을 때 비동기 처리를 할 경우 loading, error state들이 비대하게 늘어나 코드의 관심사 분리가 힘들고 지저분해지는 경우가 있는데 직관적이고 짧은 코드로 대체할 수 있게 된다.
아래는 Redux로 서버의 데이터를 비동기적으로 받아오는 로직을 작성했을 때이다.
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import axios from 'axios';
const SET_TODOS = "SET_TODOS";
export const rootReducer = (state = { todos: [] }, action) => {
switch (action.type) {
case SET_TODOS:
return { ...state, todos: action.payload };
default:
return state;
}
};
export const App = () => {
const todos = useSelector((state) => state.todos);
const dispatch = useDispatch();
useEffect(() => {
const fetchPosts = async () => {
const { data } = await axios.get("/api/todos");
dispatch({
type: SET_TODOS,
payload: data}
);
};
fetchPosts();
}, []);
return (
<ul>{todos.length > 0 && todos.map((todo) => <li>{todo.text}</li>)}</ul>
);
};
아래는 React-query로 작성한 코드이다. 두 코드의 길이를 비교한다면 확연한 차이를 볼 수 있다.
import React from "react";
import { useQuery } from "react-query";
import axios from "axios";
// 캐싱 설정을 어느곳에서든 정의할 수 있습니다
const fetchTodos = () => {
const { data } = axios.get("/api/todos");
// 필요하다면 데이터 유효성 검사를 여기서 수행하여, 실제로 값을 사용하는 곳에서는 검증된 값을 사용할 수 있습니다
return data;
};
const App = () => {
// 데이터가 필요한 곳에서 호출하면 됩니다
const { data } = useQuery("todos", fetchTodos);
return data ? (
<ul>{data.length > 0 && data.map((todo) => <li>{todo.text}</li>)}</ul>
) : null;
};
서버상태 관리 도구에 서버 데이터 페칭과 캐싱 역할을 위임하고 다른 기능 구현에 집중할 수 있다.
별도의 설정 없이 즉시 사용할 수 있고 react-hook과 같은 구조로 사용할 수 있어 사용방법이 쉽다.