[React] Recoil 정리

nemo·2022년 6월 23일
4

React

목록 보기
23/28

Recoil은 페이스북이 2020년 5월에 소개한 React 전용으로 나온 상태 관리 라이브러리다. Recoil을 통해 전역 상태를 관리하면 코드가 굉장히 간결해진다.

context API는 전역 상태를 전달할 때 객체 형태의 value를 사용한다. 따라서 객체 안의 값이 하나라도 변경되면 provider로 감싼 모든 하위 컴포넌트들이 리렌더링한다는 단점이 있다.
Recoil의 경우 각각의 전역 상태에 대한 atom이 생성되고 해당 상태를 구독하는 구성 요소만 리렌더링 된다. 따라서 불필요한 리렌더링을 방지할 수 있다.

이전 전역 상태관리 라이브러리 문제점

Redux의 경우 Flux 패턴을 사용하여 안정적으로 상태 관리가 가능하다. 그럼에도 Redux에 대한 불만이 꾸준히 나오는 데에는 이유가 있을 것이다.

  • React 전용 라이브러리가 아니다.
  • 초기 세팅(boilerplate)이 요구된다.
  • 비동기 데이터를 사용하려면 미들웨어 설치 등 추가적인 라이브러리 설치가 필요하다.

Recoil은 이러한 단점을 보완한 라이브러리이다.

Recoil 장점

  • Recoil은 전역 상태 관리를 컴포넌트 내부의 상태 관리처럼 간단한 get/set 인터페이스로 사용할 수 있도록 boilerplate-free API를 제공한다.
  • Concurrent Mode를 비롯한 다양한 React 기능들과 호환이 가능하다.
  • 상태 정의에 대한 코드 분할이 가능하다.
  • 전역 상태를 사용하고 있는 컴포넌트는 수정 없이 변경된 상태로 교체할 수 있다.
  • 컴포넌트 수정 없이 동기식/비동기식 전환이 가능하다.
  • 전역 상태의 경우 애플리케이션이 변경되더라도 그대로 유지된다.

Concurrent Mode (동시성 모드)
흐름이 여러개가 존재하는 경우를 의미한다. 즉, 리액트에서 알아서 렌더링 동작의 우선순위를 정하여 적절한 때에 렌더링을 해주는 것이다.


주요 개념

data-flow graph

상태 데이터가 atoms -> selectors -> 컴포넌트 순서로 흐르는 것을 말한다.

Atoms

Atoms는 상태 단위이며, 업데이트와 참조가 가능하다.
atom이 업데이트되면 각각의 컴포넌트들은 새로운 값을 반영하여 리렌더링 된다.
atoms는 컴포넌트 내부의 상태 대신 사용될 수 있다.
동일한 atom이 여러 컴포넌트에서 사용되는 경우 그 컴포넌트들은 상태를 공유한다.

Atoms는 atom 함수를 사용해 생성한다.
atom의 키 값은 전역적으로 고유해야 한다.

const fontSizeState = atom({
  key: 'fontSizeState', // 고유한 키 값
  default: 14,
});

등록된 전역 상태를 사용하기 위해서는 useRecoilState(키값)를 사용하면 된다. useState()와 사용법이 비슷하나 차이점이 있다면 내부 상태가 아닌 전역 상태라는 것이다.

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}

버튼을 클릭하면 폰트 사이즈가 1 증가하며, 해당 전역 상태를 사용하고 있는 다른 컴포넌트에서도 같이 변경된다.

function Text() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return <p style={{fontSize}}>This text will increase in size too.</p>;
}

selectors

selector는 전역 상태를 기반으로 어떤 계산을 통해 새로운 값을 내뱉는 순수함수다.
atom이나 다른 selector를 통해 입력을 받을 수 있다.
상위의 atom이나 다른 selector가 업데이트되면 하위의 selector 함수도 다시 실행된다.
컴포넌트들은 위에서 atom을 통해 전역 값을 사용했던 것처럼 selector를 사용할 수 있다.

기본적이고 최소한의 상태들은 atom에 저장해두고, selector에 명시한 함수를 통해 상태 값을 변경하면 된다. 따라서 이전 상태 관리 라이브러리들이 기존 상태 값을 보존하기 위해 했던 짓을 안 해도 된다.

컴포넌트의 관점으로 봤을 때 atom과 selector는 동일한 인터페이스를 가지므로 대체하여 사용할 수 있는 것이다.

get은 상태를 계산할 함수가 담겨져 있다. get으로 전달되는 매개변수를 통해 atom이나 다른 selector에 접근할 수 있다. (종속 관계 생성)

const fontSizeLabelState = selector({
  key: 'fontSizeLabelState', // 고유한 키 값
  get: ({get}) => {
    const fontSize = get(fontSizeState); 
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

위 예시에서 selector는 fontSizeState라는 atom에 의존성을 갖는다.

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>

      <button onClick={setFontSize(fontSize + 1)} style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

selector는 useRecoilValue(키값)을 통해 사용할 수 있다.
버튼을 클릭하면 폰트 사이즈도 증가하는 동시에 fontSizeLabel도 업데이트 되어 컴포넌트에 반영된다.


사용하기

Recoil은 webpack, Rollup과 같은 번들러와도 문제없이 호환된다.

설치

npm install recoil

주의할 점

Recoil은 빌드 후 ES5 문법으로 변환되지 않기 때문에 Recoil을 ES5와 같이 사용하는 것은 지원하지 않는다.
특히, Recoil은 ES6 문법인 Map과 Set 타입에 의존하기 때문에 polyfill을 통해 ES5 환경에서 사용하는 것은 성능상 문제를 야기할 수 있다.
Babel로 컴파일 후 preset @babel/preset-env을 이용하는 방법도 있지만 이 또한 문제가 발생할 수 있다고 한다.



참고
https://recoiljs.org/docs/introduction/motivation/

2개의 댓글

comment-user-thumbnail
2023년 6월 2일

좋은 글 잘 보고갑니다 .!!

답글 달기
comment-user-thumbnail
2023년 9월 6일

잘 읽었습니다

답글 달기