최근에 recoil을 사용하여 어드민 페이지를 작업할 일이 생겼습니다.
recoil에 대해 간단히 조사해보았습니다.
recoil: 페이스북에서 만든 상태관리 라이브러리
context API 기반으로 구현되어 있으며, 함수형 컴포넌트에서만 사용할 수 있습니다.
redux에서 비동기처리를 해야하는경우에 redux thunk, redux saga등에 의존하여 처리를 해왔는데, recoil은 비동기 처리를 기반으로 작성되어 있어, 동시성 모드도 지원하고 있습니다.
⭐️⭐️ atom -> selector를 거쳐 컴포넌트까지 전달되는 하나의 data-flow를 가지고 있습니다.
atom: 작은 단위로 컴포넌트들이 구독할 수 있는 단위입니다.
selector: 동기적/비동기적으로 상태를 변환합니다.
기존의 redux, mobx처럼 복잡한 상태구조를 가지고 있지 않으며 사용법 또한 매우 간단한 편입니다.
⭐️⭐️ Concurrent Mode 지원
Concurrent Mode(동시성이란??): 흐름이 여러개가 존재하는 경우를 의미합니다. 즉, 리액트에서 알아서 렌더링 동작의 우선순위를 정하여 적절한 때에 렌더링을 해준다는 개념입니다.
recoil에서는 비동기 selector를 만들고 suspense로 감싸면 쉽게 동시성 모드를 구현할 수 있습니다.
⭐️ 캐싱지원
selector를 통해 비동기 작업을 진행할 때 사용되는 atom에 자동으로 의존성이 걸리게 됩니다.
즉, atom의 값이 변경될 때마다 selector가 변경되는데, atom의 값이 같으면 내부적으로 반환값을 메모이즈 하고 있어 캐싱된 값을 반환하게 됩니다.
⭐️⭐️ 비동기 데이터를 Loadable이라는 객체로 변환이 가능합니다.
useRecoilValue를 사용하면 state, contents를 반환해주어 try,catch 구문 사용하지 않아도 됩니다.
보통은 아래와 같이 try/catch구문을 사용하여 비동기처리 작업을 진행합니다.
function getData() {
try {
const response = await fetch(url);
return response.data;
catch(e) {
// 에러처리
}
}
그러나 recoil에서 제공해주는 useRecoilValue를 사용하게 lodable객체가 state 상태값과 contents에는 결과값을 리턴해주어 api에 대한 상태값을 따로 state로 저장하지 않아도 되어 참 편하게 사용했던 부분인 것 같습니다. 👍🏼👍🏼
function getData() {
const response = await fetch(url);
return response.data;
}
function Component() {
const lodable = useRecoilValue(getData());
// lodable => state(상태값), contents(비동기작업 리턴값) 반환
switch(lodable.state) {
case 'hasValue':
case 'loading':
case 'hasError':
}
}
개인적으로 recoil의 가장 큰 장점이라고 한다면 atom, selector만 알고서도 어느정도 구현이 가능하다는 것입니다. 또한
redux를 처음 학습할 때보다 시간이 적게 걸리며 비교적 쉽다고 느껴졌습니다.
이러한 장점이 있음에도 불구하고 사용해보니 단점들도 있었습니다.
사용하면서 가장 불편했던 점은
🙈 캐싱을 지원하는 부분이었습니다.
작업했던 프로젝트가 어드민 페이지이다보니 검색을 하거나 실시간 데이터를 조회해야하는 부분들이 많았습니다.
그럴때마다 캐싱처리를 위해 atom값을 불필요하게 업데이트해주면서 작업을 진행하였습니다.
const inputValue = atom({
key: 'inputValue',
default: '',
});
const searchList = selector({
key: 'searchList',
get: async ({get}) => {
// atom에 value가 동일한 경우에 이전에 메모이즈된 값을 보여주고 있습니다.
const value = get(inputValue);
const response = await fetch(url, value);
return data.json();
}
});
🙈 에러 핸들링?
제가 잘못이해하는 부분일 수도 있습니다.
recoil을 잘못 사용하여 에러가 발생하였을 때 에러에 대한 내용이 recoil오류라는 것을 알려주지 않고 쌩뚱맞은 다른 컴포넌트에서 에러가 발생했다고 알려주어 작업하면서 좀 애를 먹었던 적이 있습니다. ㅠ
🙈 직관적이지 않다?
redux같은 경우에는 dispatch 발생시에 리듀서가 실행되는 구조로 이해하기 비교적 쉬웠습니다.
그러나 recoil은 selector에서 atom을 참조하고 있는 경우에 atom이 업데이트 될 때 실행되기 때문에
atom을 참조하고 있는 곳이 여러곳이 있다면, 생각보다 직관적으로 코드를 보기가 어렵다는 생각이 들었습니다.
예를 들어 redux의 경우에는 버튼 클릭시에 dipatch를 통해 reducer를 실행한다 이러한 구조인데 반해,
recoil의 경우 버튼 클릭시에 atom값을 업데이트하여 selector내부에서 atom값을 참조하는 경우 업데이트되는 구조입니다.
recoil은 이해하고 사용하기 쉬운 라이브러리입니다.
(todoList를 간단히 만들고서 실제 운영되는 어드민 페이지에 적용해서 작업해본 결과, todoList로 느꼇던것과는 다르다.. 이제서야 어느정도 조금씩 이해가 되고 있습니다. ㅎㅎ)
프로젝트 성향에 맞게 사용하면 좋을 것 같습니다.
redux와 같이 전역에서 많은 데이터를 처리하면 나중에 성능상 이슈가 생길 수도 있어,
글로벌한 내용들은 전역에서 redux를 사용하여 관리하고, 그 외 데이터들은 관심사의 분리로서 atom에서 관리하고자한다고 한다.
좋은 내용 잘 읽었습니다. ^^
캐싱관련해서 단점이라고 언급해주셨던거는
cachePolicy_UNSTABLE: {
// Only store the most recent set of dependencies and their values
eviction: "most-recent",
}, 요걸 추가하시면 바로바로 데이터 갱신이 가능할것으로 보입니다. ^^
https://immigration9.github.io/react,recoil/2021/08/01/reading-patchnote-recoil04.html 요기 참고하시면 좋을것 같습니다.