최근에 진행하게된 사이드 프로젝트에서 Recoil을 사용하게 되어서 학습하고자 기본에 대해 정리하려고 합니다. 이번 글에서는 Recoil의 핵심 컨셉과 atom, selector 등 주요 개념들에 대해서 알아보도록 하겠습니다.
Recoil
우선 Recoil은 페이스북에서 Redux 이후에 새롭게 발표한 상태 관리 라이브러리로, 오직 리액트와 사용가능합니다. Recoil은 atom에서 selector, 그리고 selector에서 React component로 데이터가 흐르는 데이터 흐름 크래프(data-flow graph)을 통해 상태 관리 기능을 제공합니다.
Atom
Atom은 상태 유닛입니다. Atom은 업데이트 가능하며, 구독가능합니다. Atom이 업데이트되면 해당 atom에 구독한 컴포넌트는 새로운 상태로 리렌더링됩니다. Atom은 'atom' 함수로 생성할 수 있습니다.
const userState = atom({
key: 'userState',
default: {
name: 'name',
age: 0
}
});
Atom은 unique한 key 값을 가지며, 이 key 값은 디버깅 등 여러 기능들에서 사용합니다. default값 또한 가질 수 있습니다.
컴포넌트에서 atom을 읽거나 쓰려면, 'useRecoilState' 훅을 사용합니다.
const UserInfo = () => {
const [user, setUser] = useRecoilState(userState);
return (
<div>
<span>{user.name}</span>
<span>{user.age}</span>
</div>
)
}
Selectors
Selector는 순수함수로, atom 혹은 다른 selector를 input으로 받습니다. input으로 받는 selector나 atom이 업데이트된다면 해당 selector도 다시 실행됩니다. 컴포넌트는 atom과 마찬가지로 selecotr를 구독할 수 있으며, selector가 변경되면 리렌더링 됩니다.
Selector 또한 'selector' 함수로 생성하며 key 값과, get 메서드를 갖는 객체를 인자로 가집니다.
const userLabelState = selector({
key: 'userLabelState',
get: ({get}) => {
const user = get(userState);
return `${user.name} is ${user.age} years old`
}
});
selector 인자로 주어지는 get 메서드는 첫번째 인자로 get 콜백함수를 받는데 get 콜백함수로 다른 atom 혹은 selector 값을 가져올 수 있습니다. 이렇게 가져와진 atom이나 selector에 구독을 하게되어 의존 관계를 형성합니다.
Selector는 'useRecoilValue' 훅을 사용하여 읽을 수 있습니다.
const UserLabel = () => {
const userString = useRecoilValue(userLabelState);
return (
<h1>{userString}</h1>
)
}
비동기 데이터 쿼리
Selector는 비동기적으로도 실행될 수 있으며 동기와 비동기 코드가 어색함 없이 함께 사용될 수 있다는 점이 Recoil의 장점이라고 합니다. 비동기 Selector를 사용하기 위해선 단순히 get 콜백함수에서 값을 감싸는 Promise를 반환하면됩니다. 다만, 말씀드린 것처럼 selector는 순수함수이기때문에 'idempotent'한 연산에 대해서만 사용해야 합니다. 즉, 항상 같은 값을 반환하는 함수에만 사용해야 합니다. 따라서 read-only DB 쿼리에서 사용하면 좋습니다. 만약 변할 수 있는 데이터를 가져오기 위해선 Query Refresh를 사용할 수 있습니다. 혹은 다른 사이드 이펙트를 위해선 Atom Effects나 Recoil Sync Library를 사용할 수 있습니다.
마치며
이번 글에서는 Recoil에 대해서 알아보았습니다. 사실 아직 Recoil을 실제로 프로젝트에서 사용하기 이전이라, 장단점이나 Best Practice에 대해서는 알지 못합니다. 앞으로 프로젝트에서 사용해보며 배운점이 있다면 다시 글을 작성해보도록 하겠습니다.