1월에 진행했었던 메인 프로젝트의 상태관리 라이브러리로 Recoil을 선택하여 사용했었다. 그때는 시간적 여유가 부족해 필요한 개념만 구글링해서 휘뚜루마뚜루(?) 프로젝트에 적용시키느라 제대로 Recoil에 대한 정리를 못한 것 같아 간단하게 개념을 정리해보려고 한다. 메인프로젝트 코드를 다시 봤을 때 Recoil 관련 함수들의 사용법이 가물가물해 위기를 느껴,,,^^7
최근에 Redux, Redux toolkit에 관한 개념을 공부한 후 Recoil을 보니 새삼 Recoil 사용법이 정말 간단하다는게 느껴졌다. 아직 두 개 다 익숙하지 않지만 개념을 조금 공부해보니 Redux은 러닝커브도 높은 편이고 관련 라이브러리도 많아 다른 라이브러리들에 비해 복잡해보이지만, 가장 오래된 상태관리 라이브러리 답게 팬층도 두텁고 아직까지 사용하는 곳이 많다는 느낌이 들었다. 반면에 RTK은 Redux에 비해 확실히 간단하고 가벼운 느낌이 들었다. 그래도 여전히 Recoil이 가장 러닝커브가 낮고 간단한 것 같지만(어제 구글링해보니 토스에서 Recoil을 사용한다는 것 같다). zustand도 나중에 배워볼까 싶다!
해당 포스트는 Recoil을 사용하여 유저 로그인 정보를 전역 상태로 관리하는 기능에 대해 기록한 것이다. 다시 봐도 추억이 새록새록ㅠㅠ
https://bbeeyaks-moment.tistory.com/227
Recoil
먼저 npm으로 Recoil 패키지를 설치한다.
npm install recoil
RecoilRoot
recoil 상태를 사용하는 컴포넌트는 부모 트리 어딘가에 나타나는 RecoilRoot 가 필요하다. 루트 컴포넌트가 RecoilRoot를 넣기에 가장 좋은 장소다.
모든 컴포넌트에서 전역 상태를 사용하기 위해 index.tsx 안에서<RecoilRoot>로 <App>을 감싸줘야 한다.
<RecoilRoot>
<App />
</RecoilRoot>
Atom
Recoil에서 제공하는 데이터 상태의 단위이며, 업데이트가 가능하다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트들이 리렌더링 되는 결과가 발생할 것이다. redux에서 쓰이는 store 와 유사한 개념이라고 한다.
Hook
- useRecoilState() : 상태값을 가져오고 수정할 수 있다.
- useRecoilValue() : 상태값을 가져오기만 한다.(read only)
- useSetRecoilState() : 상태값을 수정만 할 수 있다. (write only) -> 평상시 useState 사용하는 것처럼 하면 됨
- useResetRecoilState() : 상태값을 초기값으로 리셋한다.
atom에 대한 개념은 이전에 메인프로젝트 진행 당시 숙지했으니 공홈 링크를 첨부해놓는 것으로 대신한다.
[예시]
https://recoiljs.org/ko/docs/basic-tutorial/atoms
Selector
atom의 state를 가져와서, 새로운 state를 만들어 반환한다. 파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다(기존 state를 이용할 뿐, 원본을 바꾸지 않는다 <- 마치 Redux의 reducer처럼). atom처럼 컴포넌트가 이를 구독할 수 있다. RTK 비동기 로직과 같은 사이드이펙트를 thunk를 사용하여 처리하는 느낌과 비슷하게 사용되는 것 같다.
selector에 대한 개념은 처음 공부하는 것이므로 공식 홈페이지에 있는 예시를 보며 이해해보자.
[예시]
하단 코드는 todoList 관련 atom이고,
const todoListState = atom({
key: 'todoListState',
default: [],
});
전체 todo 리스트에서 일부 기준에 따라 특정 항목이 필터링 된 새 리스트(예: 이미 완료된 항목 필터링)를 생성되어 파생되는 atom이다.
필터링 된 todo 리스트를 구현하기 위해서 우리는 atom에 저장될 수 있는 필터 기준을 선택해야 한다. 필터 옵션은 "Show All", "Show Completed"과 "Show Uncompleted"가 있다고 한다(기본값: "Show All").
const todoListFilterState = atom({
key: 'todoListFilterState',
default: 'Show All',
});
todoListFilterState와 todoListState를 사용해서 우리는 필터링 된 리스트를 파생하는 filteredTodoListState selector를 구성할 수 있다.
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;
}
},
});
filteredTodoListState는 내부적으로 2개의 의존성 todoListFilterState와 todoListState을 추적한다. 그래서 둘 중 하나라도 변하면 filteredTodoListState는 재 실행된다.
하단의 글은 공홈에 있는 글인데, 사실 잘 이해가 되지 않는다... 그래도 훗날 이해할 수 있을까 싶어 가져왔다.
컴포넌트 관점에서 보면 selector는 atom을 읽을 때 사용하는 같은 훅을 사용해서 읽을 수 있다. 그러나 특정한 훅은 쓰기 가능 상태 (즉, useRecoilState())에서만 작동하는 점을 유의해야 한다. 모든 atom은 쓰기 가능 상태지만 selector는 일부만 쓰기 가능한 상태(get과 set 속성을 둘 다 가지고 있는 selector)로 간주된다. 이 주제에 대해서 더 많은 정보를 보고 싶다면 Core Concepts 페이지를 보면 된다.
이제 필터링된 투두 리스트를 출력하는 로직을 살펴보자.
하단의 코드는 필터링된 투두 리스트를 출력하는 컴포넌트이고,
function TodoList() {
// changed from todoListState to filteredTodoListState
const todoList = useRecoilValue(filteredTodoListState);
return (
<>
<TodoListStats />
<TodoListFilters />
<TodoItemCreator />
{todoList.map((todoItem) => (
<TodoItem item={todoItem} key={todoItem.id} />
))}
</>
);
}
filter를 변경하는 컴포넌트 로직이다.
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>
</>
);
}
https://recoiljs.org/ko/docs/basic-tutorial/selectors
Recoil의 기본 개념에 대해 간단히 살펴보았다. 이전 프로젝트에서는 전역적으로 사용되었던 데이터를 저장하고 가져오는 용도로만 사용하여 깊게 공부해보지 못한채로 사용했는데, 공식 홈페이지를 한 번 쭉 읽어보니 개념이 조금 잡혔고 selector라는 새로운 개념도 알 수 있었다. Redux와 같은 상태관리 라이브러리다보니 흐름이 비슷하다는 생각이 들었고, Redux 개념을 공부한 뒤에 Recoil을 공부하니 훨씬 이해가 잘되는 것 같다.
공홈에는 설명이 적어 Recoil에 대해 더 자세히 알아보기 위해 구글링하다가 너무 좋은 글을 발견했다. 이보다 깔끔하게 정리할 수는 없겠다 싶은 느낌의 블로그였다. 위의 예시에서는 아주 간단하게 selector의 개념만 정리했고, 예시 코드도 get 뿐이 없는데, 아래 블로그를 참고하면 selector에 대해 더 자세하게 알 수 있다. 예시 코드에 set 활용법도 있고 비동기 로직에 대한 내용도 있으니!! 나중에 Recoil 사용할 때 무적권!! 참고해가며 해야겠다!
메인 프로젝트에서 custom useAxios를 만들어 API 통신 코드를 일관성있게 사용할 수 있도록 하였는데, 위 글에 나와있는 것처럼 비동기 관련 로직을 selector에서 처리해주는 방법도 한 번 써보고 싶다. 예시에는 get 요청만 나와있는데, post나 patch 등의 요청도 가능할 것 같다.
나중에 토이 프로젝트를 하게 되면 RTK던 Recoil이던 제대로 한 번 활용해보고 싶다는 생각이 들었다. 상태 관리 라이브러리와 꼭 친해지고 싶다! 기필코 친해지리라.
selector를 사용할 때 '캐싱'이라는 기능으로 인해 발생하는 문제(?)도 있다고 하니 추후에 참고해야겠다.
묵혀놨던 상태관리 라이브러리 개념 공부하기 완료다! 아직 개념만 공부한 터라 직접 사용해볼 때 여러가지 시행착오를 겪을 게 눈에 훤하지만 오늘 작성했던 글과 무한구글링을 통해 이겨내리라 믿는다.
'온라인 강의(유데미, 인프런 등) > React 완벽 가이드(유데미)' 카테고리의 다른 글
[react] 절대경로/상대경로 & React에서 import시 절대경로 사용하기 (0) | 2023.03.07 |
---|---|
[react & css] position 개념 간단하게 정리하기 (0) | 2023.03.07 |
[react & RTK] React Toolkit 비동기 작업 처리하기(createAsyncThunk) (0) | 2023.03.04 |
[react & RTK] Redux Toolkit 개념 및 예시 간단하게 살펴보기 (0) | 2023.03.03 |
[react & Redux] Redux 개념 및 예시 간단하게 알아보기 (0) | 2023.03.03 |