리액트를 하면 상태 관리라는 단어를 많이 들을 것이다.
일단 state의 유형부터 알아보자.
state의 유형
- local state
- 특정 컴포넌트 안에서만 영향을 끼치는 상태.
- Cross-Component State
- 여러가지 컴포넌트에서 영향을 끼치는 상태. Props Drilling 방식 필요
- global state
- 모든 컴포넌트에 영향을 끼치는 상태. Props Drilling 방식 필요
Props Drilling?
Props Drilling은 말그대로 드릴처럼 밑으로 구멍을 뚫어 프로퍼티를 하위 컴포넌트로 전달하는 과정이라고 생각하면 된다.
하지만 이 과정이 깊어진다면 props를 추적하기 힘들어진다.
이런 문제를 해결하기 위해 나온 것이 바로 State Management, 상태관리 라이브러리이다.
대표적으로 Redux, Recoil, Context API, React-Query 등이 있는데 각각 특징들이 있기 때문에 본인이 하고있는 프로젝트에 알맞게 선택하면 된다.
나는 여기서 React-Query로 상태 관리하는 방법을 간단하게 설명하고자 한다.
React-Query
React-Query를 접하기 전, 나는 Redux를 사용했었다.
Redux로 서버 데이터를 활용하려면 Redux-saga, Redux-Thunk 같은 미들웨어를 사용해야 한다.
하지만 이번 프로젝트에서는 전역 상태 관리할 것들이 많지 않았기 때문에 Redux를 사용하지 않았다.
대신 React-Query라는 상태 관리 라이브러리를 사용했다. 이 라이브러리는 서버 데이터를 관리하기 쉽게 해준다. 또한, Redux에 비해 러닝커브가 낮기 때문에 쉽게 익힐 수 있었다.
React-Query에서는 global state를 server state와 client state를 분리해서 바라본다.
- server state
- 서버로부터 불러오는 데이터(특정 시점의 데이터)
- 클라이언트가 제어 및 소유 X
- 비동기적인 상태
- client state
- 클라이언트가 제어 및 소유하는 데이터
- 동기적인 상태
React-Query는 server state를 관리하기 때문에 client state를 관리하기 위한 라이브러리로 보통 Recoil을 사용한다고 한다.
하지만 나는 위에서 말했다시피 전역 상태 관리할 것들이 적었기 때문에 context API로 간단하게 client state를 관리했다.
server state는 서버에서 특정 시점에서 데이터를 불러오기 때문에 서버의 상태가 변하면 그에 맞게 업데이트를 해주어야 한다. 그래서 Redux에서는 이런 비동기 요청을 위해 Redux-Saga, Redux-Thunk를 추가로 사용하게 된다.
하지만 React-Query를 사용하면 생산성이 향상되고 코드가 훨씬 줄어든다.
React-Query의 라이프 사이클
- fetching: 데이터 요청 중인 쿼리
- fresh: 데이터가 신선한 상태인 쿼리.
- 최신화된 데이터로 간주하여 쿼리 인스턴스가 새롭게 mount 되어도 데이터를 다시 요청하지 않는다.
- stale: 데이터가 신선하지 않은 상태인 쿼리.
- 최신화가 필요한 데이터로 간주하여 쿼리 인스턴스가 새롭게 mount 되면 데이터를 다시 요청한다.
- inactive: 사용하지 않는 쿼리
- 일정 시간이 지나면 가비지 컬렉터가 캐시에서 제거한다.
- delete - 가비지 컬렉터에 의해 캐시에서 제거된 쿼리
캐싱
Option
- staleTime: 데이터가 fresh -> stale 상태로 변경되는데 걸리는 시간
- defaultValue: 0 (0분)
- cacheTime: 데이터가 inactive 상태일 때 캐싱된 상태로 남아있는 시간
- defaultValue: 1000 * 60 * 5 (5분)
- cacheTime이 지나면 가비지 컬렉터에 수집된다.
- enabled: 쿼리가 자동으로 실행되지 않도록 하려면 false로 설정
- retry: 실패한 쿼리를 재시도하는 횟수
- defaultValue: 3 (3회)
React-Query의 Data Fetching
1. Queries
- 서버로부터 데이터를 조회할 때 사용
기본 형태
const result = useQuery(queryKey, queryFn);
queryKey는 unique Key 값이다. Key 값이 고유의 값이어야 한다는 것이다.
이 queryKey로 query 캐싱을 관리하도록 도와준다.
queryFn은 query Function으로 promise 처리가 이루어지는 함수이다. 서버에 api 요청하는 함수라고 생각하면 된다. 단, useQuery는 데이터를 조회할 때 사용하기 때문에 get 요청만 가능하다.
ex)
const result = useQuery(['users'], () => axios.get('http://localhost:8080/users'));
2. Mutations
- 서버의 데이터를 변경할 때 사용
기본 형태
const mutation = useMutation(mutationFn);
mutationFn은 mutation Function으로 queryFn과 같이 promise 처리가 이루어지는 함수이고 서버에 API를 요청하는 함수다. useMutation은 데이터를 변경하는 post, put, delete 등의 요청을 할 때 사용한다.
ex)
const mutation = useMutation((user) => axios.post('http://localhost:8080/register', user));
mutate를 사용하여 변경된 데이터를 보내줄 수 있다.
mutation.mutate(user)
보통 useQuery, useMutation같은 훅을 이용할 때 필요한 메소드만 골라쓰기 위해 구조분해할당을 사용하곤 한다.
또한 옵션을 추가하여 성공 시 로직, 에러 시 로직을 구현할 수도 있다.
const { data, isLoading, isError } = useQuery(['users'], () => axios.get('http://localhost:8080/users'), {
onSuccess: (res) => {
console.log(res.data);
},
onError: (error) => {
console.log(error);
},
});
const { mutate } = useMutation((user) => axios.post('http://localhost:8080/register', user), {
onSuccess: (res) => {
console.log(res.data);
},
onError: (error) => {
console.log(error);
},
});
이외에도 다양한 옵션이 있으니 공식문서를 참고하기 바란다.
React-Query를 쓰면서 느꼈던 점
React-Query가 Redux-Saga, Redux-Thunk 보다 러닝커브가 낮고 간단해서 금방 익힐 수 있었다.
React-Query에서 제공하는 다양한 기능, 옵션들을 통해 쉽게 server state를 관리할 수 있었고 API 처리에 대한 로직도 쉽게 구현할 수 있었다.
하지만 제공하는 hook들로 server state 관리와 data fetching이 편해졌기 때문에 코드 구조에 조금 더 신경 써야했다. 무분별하게 사용하다가는 어디서 API가 호출이 되고있는지 파악이 안되기 때문이다.
내가 했던 프로젝트에서는 query와 mutation을 커스텀 hook으로 만들어서 각각 queries, mutations 폴더에 두었다.
그리고 특정 페이지에서만 hook이 쓰이는 경우에는 페이지 내 hooks 폴더를 만들어 분리했다.
커스텀 hook으로 만들어두었을 때 장점
- 재사용 시에 default options를 설정할 수 있음
- 어디에 무엇이 쓰이는지 파악할 수 있게 됨
참고
'React' 카테고리의 다른 글
Spotify API 사용기 (1) - 앱 생성 및 로그인 기능 구현 (with Next.js) (0) | 2023.03.24 |
---|---|
[React] 상태 관리 라이브러리의 이해 - Redux 동작 원리 (1) | 2023.02.02 |
[React] props? state? (0) | 2022.09.01 |
[React] onclick 이벤트와 onblur 이벤트가 동시에 일어난다면 실행 순서가 어떻게 될까? (0) | 2022.07.12 |
[React] FormData로 image와 json 파일을 저장하여 axios 요청을 보냈지만 안되는 문제 (0) | 2022.07.12 |