본문 바로가기
FE/React

[Recoil] recoil을 사용하여 전역 상태 관리 하기

by suhyeon chae 2023. 1. 8.

이번 포스팅은 Recoil을 사용하여 상태관리를 하는 내용입니다. 공식 문서 예시로 설명하겠습니다.

 

1. Recoil 이란?

https://recoiljs.org/ko/docs/introduction/installation

 

설치 | Recoil

NPM

recoiljs.org

React를 만든 회사 즉, Facebook에서 만든 상태 관리 라이브러리 입니다.

 

흔히 알고 있는 Redux같은 라이브러리라고 생각하시면 됩니다. 저도 그렇지만 Redux가 러닝커브가 높아서 쉽게 적용하기가 어려운 편이라고 생각하시는 분들이 많은데, Redux 보다 훨씬 쉬운 Recoil에 대해서 알아보겠습니다.

 

2. Recoil을 사용하는 이유

Recoil을 사용하지 않으면 부모 -> 자식 컴포넌트로의 데이터는 쉽게 전달할 수 있지만

 

자식 -> 부모 컴포넌트로의 데이터 전달이나 부모-자식 관계가 아닌 컴포넌트들 사이에서의 데이터 전달은 쉽지 않죠.

A -> B 로의 데이터 전달을 해야한다고 한다면, 최악의 경우에는 Header에서부터 쭉 내려줘야하는 경우죠. 이렇게 되면 코드도 굉장히 지저분해집니다.  이럴 경우에 전역으로 상태관리를 해서 필요한 컴포넌트에서 import 받아서 사용하는 것이 훨씬 편리할 것 이라고 생각됩니다

 

3. Recoil을 적용하는 곳

대표적으로는 로그인, 검색 쪽인 것 같습니다.

1. 로그인을 했을 경우 페이지 이동을 해도 로그인 유저 정보는 계속 남아있어야하기 때문이죠.

2. 검색인 경우도  검색어를 검색창에 입력하고, 검색어가 검색 결과 페이지까지 전달이 되어야 검색 결과 페이지에서 검색어로 필터링을 해서 보여줄 수 있습니다.

 

4. Recoil 사용법

먼저 라이브러리를 설치 합니다.

 

- npm인 경우

npm install recoil
npm install @types/recoil //typescript 사용하는 경우에만

 

-  yarn인 경우

yarn add recoil
yarn add @types/recoil // typescript 사용하는 경우에만

 

설치가 됐다면 package.json > dependenciesrecoil @types/recoil이 잘 들어가있는지 확인합니다.

 

 

설치가 잘 됐다면 index.js에서  RecoilRoot로 감싸줘야 합니다.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "@/App";
import { RecoilRoot } from "recoil";

const root = ReactDOM.createRoot(
    document.getElementById("root") as HTMLElement
);
root.render(
    <React.StrictMode>
        <RecoilRoot>
            <App />
        </RecoilRoot>
    </React.StrictMode>
);

사용하기 위한 기본적인 설정을 마쳤습니다. 이제 본격적으로 사용법으로 들어가겠습니다.


<Atom>

Recoil을 사용할 때 가장 중요한 개념은 Atom입니다. åtom에는 우리가 사용할 상태(state)를 담습니다.

먼저 프로젝트에다가 global이라는 디렉토리를 생성하고 fontSizeState.tsx를 생성하겠습니다.

(타입 스크립트를 기반으로 설명하겠습니다. 그리고 나중에 hook 사용할 때를 대비하여 ts가 아닌 tsx로 생성하겠습니다.)

 

src > global > fontSizeState.tsx 

 

- 공식문서에 있는 예제입니다.

export const fontSizeState = atom({
  key: 'fontSizeState', // 중복되면 안되는 값
  default: 14, // 초기값 (여기서는 폰트 사이즈 초기값이 14로 초기화)
});

여기서 atom을 recoil에서 import 받아서 사용하겠습니다. 전역으로 상태관리할 변수는 useState를 바로사용하지 못합니다.

꼭 atom을 통해야만 합니다. 

 

key : 중복되지 않는 값

default : 초기 값

 

위 처럼 설정해주시면 됩니다. key는 atom끼리 겹치지 않게 해야합니다. 따라서 보통은 변수명을 그대로 key로 적습니다.

atom에 타입을 설정해줄 수도 있습니다 

export const fontSizeState = atom<number>({
  key: 'fontSizeState',
  default: 14,
});

또는 

type fontSize = number;

export const fontSizeState = atom<fontSize>({
  key: 'fontSizeState',
  default: 14,
});

 

다음과 같이 atom을 통해 fontSize의 초기값 설정을 마쳤으면 FontButton.tsx 파일을 만들겠습니다.

 

src > components > FontButton.tsx 

import {useRecoilState} from 'recoil';
import {fontSizeState} from '@/global/fontSizeState'

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

useRecoilState(atom)값을 넣어주면 사용할 수 있습니다. 

사용 문법은 useState와 동일한데 초기값을 atom값을 넣어주는 것만 다릅니다. fontSize 초기값은 14일 것입니다.

 

 

- 만약에 setter함수(setFontSize)가 필요하지 않다면 아래 예시처럼 useRecoilValue를 사용하여 value값만 뺄 수 있습니다.

import {useRecoilValue} from 'recoil';
import {fontSizeState} from '@/global/fontSizeState'

const fontSize = useRecoilValue(fontSizeState);

 

 

- 만약에 setter함수만 필요하다면 아래 예시처럼 useSetRecoilState를 사용하여 setter함수만 뺄 수 있습니다.

import {useSetRecoilState} from 'recoil';
import {fontSizeState} from '@/global/fontSizeState'

const setFontSize = useSetRecoilState(fontSizeState);

다시 본격적으로 코드로 와서,

import {useRecoilState} from 'recoil';
import {fontSizeState} from '@/global/fontSizeState'

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

+ 버튼을 누르면 fontSize가 1씩 증가한다는 코드인데요, fontSize는 FontButton 컴포넌트에서 1이 증가가 되었지만, 

fontSize는 Recoil이 관리하는 상태 변수이기 때문에 어느 컴포넌트에서나 증가된 fontSize 값을 랜더링 할 수 있습니다.

 

확인하기 위해서 src > src > components > Text.tsx 생성합니다.

import {useRecoilState} from 'recoil';
import {fontSizeState} from '@/global/fontSizeState'

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

 Text 컴포넌트는 fontSize 값을 변경하는 어떠한 로직이 들어가있지 않음에도 불구하고 , FontButton  컴포넌트에서 증가시킨 값이 랜더링 될 것입니다!