Front-end/Recoil

Recoil 정리 1편 - Recoil시작하기 (atom, selector)

아지송아지 2022. 3. 17. 12:31

안녕하세요

오늘은 상태 관리툴인 Recoil에 대해 알아보겠습니다.

 

상태 관리


상태 관리 툴을 사용하면 state값들을 props로 계속 타고들어가지 않아 코드가 깔끔해집니다.

프로젝트의 규모가 커질수록 상태 관리 또한 필요성이 커지게 됩니다.

 

많은 프론트엔드 개발자들이 Redux, MobX, Recoil 등 여러가지 기술로 상태 관리를 하고 있는데요.

Redux의 경우 전 세계적으로 많이 사용되지만 만족도는 현저히 떨어집니다.

action, reducer, selector 등 사용하기 까다롭습니다.

 

https://2020.stateofjs.com/en-US/technologies/datalayer/
https://2020.stateofjs.com/en-US/technologies/datalayer/

실제로 redux는 사용률은 1위이지만 만족도는 5위에 위치하고 있습니다.

어떤 조사 결과에 따르면 3년 안에 없어질 1순위가 redux였습니다.

저 또한 프로젝트를 진행하며 Redux를 사용할 때마다 매번 세팅하는 것이 귀찮았고, 어떻게 하면 이 작업을 줄일 수 있을까 고민하였습니다.

그러던 중 오늘 소개해드릴 Recoil을 만나게 되었습니다.

 

 

 

 

Recoil? Recoil!!


 

Recoil은 만들어진지 2년밖에 안되었지만 최근 개발자들 사이에서 큰 인기를 끌고 있습니다.

수많은 상태관리툴중 왜 우리는 Recoil을 사용해야 하고 다른 개발자들이 찾는 걸까요?

 

 

https://www.npmtrends.com/mobx-vs-recoil-vs-redux

 

특징을 살펴보겠습니다.

 

1.Made by Facebook

Recoil은 Facebook에서 만들었습니다.

React를 위해 만들어진 상태 관리 라이브러리이기 때문에 React처럼 작동을 하며 기존의 React를 사용하였던 개발자라면 쉽게 접근할 수 있습니다.

 

예시 코드입니다.

useState와 유사하게 생겼으며 [get, set] 기능 또한 동일합니다.

 

 

// useState
const [id, setId] = useState(0);

// recoil
const [count, setCount] = useRecoilState(countState);

 

 

2. 간단하고 강력하다.

store, action, reducer 등 여러 가지를 신경 쓸 일 없이 초기 세팅이 직관적이고 간단합니다.

또한 비동기 데이터 흐름을 위한 내장 솔루션까지 제공합니다.

앱 전체의 모든 상태 변경을 관찰하여 지속성, 라우팅, 디버깅을 구현하는데 용이합니다.

 

 

시작


npm으로 Recoil 패키지를 설치합니다.

npm install recoil

yarn

yarn add recoil

 

 

RecoilRoot

recoil을 사용하기 위해서는 사용하고자하는 부모 컴포넌트에다 <RecoilRoot>를 사용해야합니다.

보통 전역적으로 사용하기 때문에 루트 컴포넌트에 넣는게 좋습니다.

// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { RecoilRoot } from 'recoil';

ReactDOM.render(
  <React.StrictMode>
    <RecoilRoot>
      <App />
    </RecoilRoot>
  </React.StrictMode>,
  document.getElementById('root')
);

 

 

Atom

atom은 redux의 store와 같은 개념입니다.

데이터 상태의 단위이며, 업데이트와 구독이 가능합니다.

atom의 값이 바뀌면 구독하고있는 장소는 모두 새로운 값으로 리렌더링됩니다.

여러개 생성이 가능합니다.

 

import { atom } from "recoil";

export default atom({
    key: 'countState',
    default: 0,
});

atom은 key와 default값을 설정해주어야합니다.

 

* key

atom을 식별하는데 필요한 고유한 문자열입니다.

프로젝트 전체에서 다른 atom, selector에 대해 고유해야합니다.

 

* default

초기값을 설정해줍니다.

 

 

Selector

atom에서는 불가능한 비동기 처리와 복잡한 로직을 구현할 수 있습니다.

공식문서에는 아래와 같이 정의되어 있습니다.

Selector는 파생된 상태(derived state)의 일부를 나타낸다.
파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다.

순수 함수란, 함수에 동일한 인자가 주어졌을 때 항상 같은 값을 리턴하는 함수입니다. (외부의 영향은 받지 않아야 합니다.)

그럼 파생된 상태와 selector는 어떤 것인지 코드를 통해 알아보겠습니다.

 

 

import { atom } from "recoil";

export default atom({
    key: 'countState',
    default: 0,
});
import { DefaultValue, selector } from "recoil";
import countState from "../atom/countState";

export default selector({
    key: "countSelector",
    get: ({get}): number => {
        const count = get(countState);
        return count + 1;
    },
    set: ({set, get}, newCount)=>{
        return set(countState, newCount + 10)
    }
})

selector는 총 세 가지 값을 가집니다.

 

* key

atom의 key와 동일하며 프로젝트 전체에서 고유한 문자열을 가져야 합니다.

 

* get

파생된 상태를 반환하는 곳입니다.

get(countState) 처럼 countState를 get하고 있으면 countState가 바뀔 때마다 새로운 값을 리턴해줍니다.

get()을 여러번 사용 가능하고 그중 하나라도 변하게 되면 리렌더링됩니다.

    get: ({get}): number => {
        // countState를 구독하고 있습니다.
        // countState가 바뀔 때마다 1증가 시켜서 반환합니다.
        const count = get(countState);
        return count + 1;
    },

 

 

* set

set 없이 get만 제공되면 selector는 read-only 한 상태이지만 set을 제공하면 쓰기 가능한 상태를 반환합니다. 

(useRecoilState에서 다시 다루겠습니다.)

 

set은 selector의 값을 수정하는 것이 아닌  수정 가능한 atom의 값을 바꿔줍니다.

    set: ({set, get}, newCount)=>{
    	// 설명을 위한 코드로,
        // 현재 count는 사용하고 있지 않습니다.
    	const count = get(countState);
        
        return set(countState, newCount + 10)
    }

set안에서의 get()은 구독하지 않습니다. 단순히 atom이나 selector의 값을 찾는데 사용합니다.

 

set()은 두가지 매개변수를 받습니다. 

첫 번째는 Recoil의 상태 (위 코드에서는 countState)

두 번째는 어떤 값으로 바뀔 건지 즉 새로운 값을 넣습니다.

 

만약 전달받은 새롭운 값이 업데이트 함수나 초기화 액션이라면 DefaulValue 일 수도 있습니다.

(useResetRecoilState에서 다시 한번 다룰 예정이니 지금은 넘어가셔도 괜찮습니다.)

 

비동기 Selector 

const myQuery = selector({
  key: 'MyQuery',
  get: async ({get}) => {
    return await myAsyncQuery(get(queryParamState));
  },
});

atom을 직접 사용할 때 비동기 통신을 하는게 아닌 위 코드와 같이 seletor 안에서 비동기 통신 후 값을 사용할 수 있습니다.

 

 

 

지금까지 recoil의 기초와 atom, selector에 대해 알아보았습니다.

다음 시간에는 useRecoilState와 useRecoilValue 등 직접 사용해 보겠습니다.

 

 

참고 : https://recoiljs.org/ko/