HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🚀
개발 노트
/
🎙️
Recoil
🎙️

Recoil

 
notion image
 

Recoil의 핵심 컨셉

  • 오직 React만을 위해 React처럼
  • React 내부상태만 이용
  • 작은 Atom 단위로 관리
  • 순수함수 Selector
  • Re-Render 최소화
  • 데이터 흐름을 따라서
  • 곧 새로운 React 기능과 호환성
  • TypeScript를 기본적으로 지원
 

초기 설계 시 고려할 점

  • 꼭 전역으로 관리되어야만 하는가?
    • 간단한 건 Hooks와 Props로 충분
  • 여러 상태값을 사용 용도에 따라 분류
    • UI 전용 상태 / 폼 데이터 처리 상태 / 서버 데이터 조회용 상태
  • 어떤 데이터를 캐싱하고, 실시간으로 반영되야 하는지 파악
  • 데이터가 어느 시점에서 변경되고 어떤 부분에 영향을 주는지 예측
 
 

Atoms

컴포넌트가 구독할 수 있는 상태의 단위.
동일한 atom이 여러 컴포넌트에서 사용되는 경우 모든 컴포넌트는 상태를 공유한다.
atom의 키값은 전역적으로 고유해야 한다. 기본값도 갖는다.
const todoListState = atom({ key: 'todoListState', default: [], });
Hooks
  • useRecoilState : 읽기, 쓰기
  • useRecoilValue : 읽기 전용
  • useSetRecoilState : 쓰기 전용
function TodoItem({item}) { const [todoList, setTodoList] = useRecoilState(todoListState); const index = todoList.findIndex((listItem) => listItem === item); const editItemText = ({target: {value}}) => { const newList = replaceItemAtIndex(todoList, index, { ...item, text: value, }); setTodoList(newList); }; const toggleItemCompletion = () => { const newList = replaceItemAtIndex(todoList, index, { ...item, isComplete: !item.isComplete, }); setTodoList(newList); }; const deleteItem = () => { const newList = removeItemAtIndex(todoList, index); setTodoList(newList); }; return ( <div> <input type="text" value={item.text} onChange={editItemText} /> <input type="checkbox" checked={item.isComplete} onChange={toggleItemCompletion} /> <button onClick={deleteItem}>X</button> </div> ); } function replaceItemAtIndex(arr, index, newValue) { return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)]; } function removeItemAtIndex(arr, index) { return [...arr.slice(0, index), ...arr.slice(index + 1)]; }
 

인상깊은 코드

replaceItemAtIndex, removeItemAtIndex
배열의 요소를 업데이트(수정, 삭제)하는 로직을 함수로 추상화한 것이 흥미롭다.
이 로직은 자주 사용된다. 유틸 함수로 두어서 전역적으로 사용해도 좋을 것 같다.
 
 

Selectors

Selectors는 상태를 기반으로 하는 파생 데이터를 계산하는 데 사용된다.
최소한의 상태 집합만 atoms에 저장하고 다른 모든 파생 데이터는
selectors에 명시한 함수를 통해 효율적으로 계산할 수 있다.
 
Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수다.
상위의 atoms 또는 selectors가 업데이트되면 하위의 selector 함수도 다시 실행된다.
컴포넌트들은 selectors를 atoms처럼 구독할 수 있으며,
selectors가 변경되면 컴포넌트들도 다시 렌더링된다.
 
Selectors는 어떤 컴포넌트가 자신을 필요로하는지,
또 자신은 어떤 상태에 의존하는지를 추적한다.
컴포넌트의 관점에서 보면
selectors와 atoms는 동일한 인터페이스를 가지므로 서로 대체할 수 있다.
 
useRecoilValue를 사용해 읽을 수 있다. 읽기 전용.
모든 atom은 쓰기 가능 상태지만
selector는 일부만 쓰기 가능한 상태(get과 set 속성을 둘 다 가지고 있는 selector)로 간주된다.
const todoListState = atom({ key: 'todoListState', default: [], }); const todoListFilterState = atom({ key: 'todoListFilterState', default: 'Show All', })
const filteredTodoListState = selector({ key: 'filteredTodoListState', get: ({ get }) => { const filter = get(todoListFilterState); const list = get(todoListState); switch (filter) { case 'Show Completed': return list.filter((item) => item.isComplete); case 'Show Uncompleted': return list.filter((item) => !item.isComplete); default: return list; } }, });
 
function TodoList() { const todoList = useRecoilValue(filteredTodoListState); return ( {todoList.map((todoItem) => ( <TodoItem item={todoItem} key={todoItem.id} /> ))} ); }
 
필터를 변경하려면 우리는 TodoListFilter컴포넌트를 구현해야 한다.
function TodoListFilters() { const [filter, setFilter] = useRecoilState(todoListFilterState); const updateFilter = ({target: {value}}) => { setFilter(value); }; return ( <> Filter: <select value={filter} onChange={updateFilter}> <option value="Show All">All</option> <option value="Show Completed">Completed</option> <option value="Show Uncompleted">Uncompleted</option> </select> </> ); }
통계 표시하기
const todoListStatsState = selector({ key: 'todoListStatsState', get: ({get}) => { const todoList = get(todoListState); const totalNum = todoList.length; const totalCompletedNum = todoList.filter((item) => item.isComplete).length; const totalUncompletedNum = totalNum - totalCompletedNum; const percentCompleted = totalNum === 0 ? 0 : totalCompletedNum / totalNum; return { totalNum, totalCompletedNum, totalUncompletedNum, percentCompleted, }; }, });
function TodoListStats() { const { totalNum, totalCompletedNum, totalUncompletedNum, percentCompleted, } = useRecoilValue(todoListStatsState); const formattedPercentCompleted = Math.round(percentCompleted * 100); return ( <ul> <li>Total items: {totalNum}</li> <li>Items completed: {totalCompletedNum}</li> <li>Items not completed: {totalUncompletedNum}</li> <li>Percent completed: {formattedPercentCompleted}</li> </ul> ); }
 
atoms와 selectors의 용도를 잘 구분해서 사용하는 것이 핵심일 것 같다.
다음 사실을 기억하자.
최소한의 상태 집합만 atoms에 저장하고, 다른 모든 파생 데이터는 selectors에 명시한 함수를 통해 계산한다. Selectors는 자신이 의존하는 atoms나 selectors의 상태가 변경되면 재실행된다.
 

기타

  • atoms, selector의 키 값은 이름과 동일하게 사용한다.
  • 공식문서에서는 atoms, selectors에 접미사로 state를 붙인다.
  • 통계(statistics)를 보통 stats라는 약어로 표현한다.
 

참고자료

공식 문서

Recoil
Brief Introduction into Recoil Recoil은 React처럼 작동하고 생각합니다. 앱에 추가하여 빠르고 유연한 공유되는 상태를 사용해 보세요. 파생 데이터와 비동기 쿼리는 순수 함수와 효율적인 구독으로 관리됩니다. 코드 분할을 손상시키지 않고 앱 전체의 모든 상태 변경을 관찰하여 지속성, 라우팅, 시간 이동 디버깅 또는 실행 취소를 구현합니다.
Recoil
https://recoiljs.org/ko/
Recoil

입문

Recoil - 또 다른 React 상태 관리 라이브러리?
원문: Sveta Slepner https://medium.com/swlh/recoil-another-react-state-management-library-97fc979a8d2b 많은 React 상태 관리 라이브러리들이 있고, 가끔 새로운 라이브러리가 등장한다. 그러나 페이스북에서 직접 상태 관리 솔루션을 소개하는 것은 흔하지 않다. 이 라이브러리가 어떤 장점이 있고 새로운 점이 있는지, 그리고 앞으로 시간을 투자할 가치가 있는지 알아보자.
Recoil - 또 다른 React 상태 관리 라이브러리?
https://ui.toast.com/weekly-pick/ko_20200616
Recoil - 또 다른 React 상태 관리 라이브러리?

기타

리코일(Recoil) 기초
1. Flexible shared state React에서 공유된 상태(shared state)를 사용하기 위해 Context Provider를 사용한다. 그러나 Provider가 점점 많아지면 층층이 쌓이면 사이에 걸쳐있는 불필요한 컴포넌트들까지 리렌더링이 발생한다. Recoil은 이를 해결하기 위해서 React state와는 수직적인 방향으로 상태를 관리 한다. (수직적이라는 것은 위 그림에서 보듯이 React 상태 트리가 바닥면이고 Recoil의 상태는 위에서 아래로 수직으로 내려오는 모양을 말하는 것이다.
리코일(Recoil) 기초
https://ttum.tistory.com/36
리코일(Recoil) 기초
[번역] 리액트 상태 관리의 새로운 흐름
리액트 애플리케이션이 커지고 복잡해지면서 전역에서 공유되는 상태를 어떻게 관리하는지가 중요해지고 있습니다. 보통의 경우에는 꼭 필요할 때만 전역 상태를 관리하는 것을 권장하곤 합니다. 근본적인 문제를 이해하면 "새로운 흐름"의 상태 관리 접근 방식이 가지고 있는 트레이드오프에 대해서 평가하는 데 도움이 될 것입니다. 모든 상황에 있어서, 밑부분부터...
[번역] 리액트 상태 관리의 새로운 흐름
https://medium.com/@yujso66/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%9D%90%EB%A6%84-6e5ed0022e39
[번역] 리액트 상태 관리의 새로운 흐름