HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
프로그래머스 프론트엔드 데브코스 2기
프로그래머스 프론트엔드 데브코스 2기
/
기동2팀
기동2팀
/
📃
회의록 문서
/
🥇
Recoil 도입하기
🥇

Recoil 도입하기

 

Recoil을 도입한 이유

contextAPI에 비해서 무엇이 좋은가? 어떤 이유로 Recoil을 도입하게 되었는가?
  • contextAPI가 가진 단점
    • 렌더링 문제
    • 코드가 너무 많아진다.
    • 등등
동기 | Recoil
호환성 및 단순함을 이유로 외부의 글로벌 상태관리 라이브러리보다는 React 자체에 내장된 상태 관리 기능을 사용하는 것이 가장 좋다. 그러나 React는 다음과 같은 한계가 있다. 컴포넌트의 상태는 공통된 상위요소까지 끌어올려야만 공유될 수 있으며, 이 과정에서 거대한 트리가 다시 렌더링되는 효과를 야기하기도 한다. Context는 단일 값만 저장할 수 있으며, 자체 소비자(consumer)를 가지는 여러 값들의 집합을 담을 수는 없다.
동기 | Recoil
https://recoiljs.org/ko/docs/introduction/motivation
동기 | Recoil

장점

  • React에 최적화된 상태관리 라이브러리
  • 난이도가 쉬움
  • Re-Render 최소화
  • TypeScript를 기본적으로 지원 (의존 모듈 설치x)
 
한편으로, Recoil은 비동기적인 데이터를 처리하는 데 다소 문제가 있음을 발견하였다. 이 때문에 Recoil은 동기적인 전역 상태의 관리에 한정해 사용하고, 비동기적인 전역 상태의 관리는 useSWR을 사용할 계획이다.
useSWR에 관해서는 아래를 참고해주시기 바란다.
🌈
useSWR 도입 논의
 

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

Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수다.
상위의 atoms 또는 selectors가 업데이트되면 하위의 selector 함수도 다시 실행된다.
컴포넌트들은 selectors를 atoms처럼 구독할 수 있으며,
selectors가 변경되면 컴포넌트들도 다시 렌더링된다.
 
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 상태 관리 라이브러리?

기타 영상

[B1] 상태관리, 이제 Recoil 하세요.
상태관리, 이제 Recoil 하세요. - 김현태 | MASS ADOPTION발표자료: https://drive.google.com/file/d/1XzROX52XAeDFDYOfqgKOQ9ypDJkBBzal/view?usp=sharing
[B1] 상태관리, 이제 Recoil 하세요.
https://www.youtube.com/watch?v=0-UaleJZOw8
[B1] 상태관리, 이제 Recoil 하세요.