Recoil
2024. 12. 27. 23:05

 

이전까지 로그인 상태와 같은 여러 컴포넌트에서 필요한 state를 사용하기 위해, 여러 컴포넌트를 거쳐서 작업을 했었다.

전역적으로 사용될 state가 많아 질수록, 컴포넌트의 props 값도 많아져 코드 가독성도 떨어지고 작업량이 늘어났다.

이러한 문제를 해결할 수 있는 여러가지 도구 중 [ Recoil, ContextApi, Redux, ... ], Recoil을 우선 사용해보기로 결정했다.

여기저기 글에서는 Redux가 러닝커브가 높다고 하여, 다른 것부터 사용해보고 이해력이 생겼을 때 더 고민해보기로 했다.

ContextApi는 성능에 악영향을 주는 문제가 있다고 한다.

Recoil은 React를 개발한 Facebook이 개발한 전역 상태 관리 도구이기도 하고 사용법도 간단하며, ContextApi의 성능 문제에 대한 문제도 없다는 글이 있어 우선 사용해보기로 하였다.

 

리코일을 쓴다면?

1. props drilling 해결

2. 리렌더 관련 성능향상

3. 최소한의 상태 보존

우선 porps drilling 부터 확인해보자.

최상단 컴포넌트과 최하단의 컴포넌트 사이의 단계가 많고 데이터를 연쇄적으로 넘겨 처리해야한다면,

해당 데이터를 사용하지 않는 컴포넌트들도, props를 받아 넘겨야 하는 추가적인 코드가 발생한다.

function App() {
  const [status, setS] = useState(false);
  return (<A status={status}/>);
};

function A({status}) {
    return(<B status={status}/>);
};

function B({status}) {
    return (<C status={status}/>);
};

function C({status}) {
    return (<div>{status}</div>);
};

 

하지만 Recoil의 Atom을 사용하면, 컴포넌트 사이에서 연쇄적으로 데이터를 전달할 필요 없이 필요한 컴포넌트에서 직접 상태를 사용한다.

function App() {
  return (
    <RecoilRoot>
      <A />
    </RecoilRoot>
  );
};

function A() {
  const [login, setLogin] = useRecoilState(isLoginAtom);
  return <B/>;
};

function B() {
  return <C/>;
};

function C() {
  const isLogin = useRecoilValue(isLoginAtom);
  return (<div></div>);
};

RecoilRoot안에서 전역적으로 사용될 Atom(State)을 필요한 컴포넌트에서 각각 사용하는 것을 볼 수 있다.


리렌더링은?

Recoil 공식 문서에 따르면, Atom(state)이 사용된 컴포넌트만 리렌더링이 일어난다고 설명한다.

아래의 상황을 보자.

D 컴포넌트에서 사용하던 값을 B 컴포넌트에서도 필요하게 되었다고 생각해보자.

최하단 컴포넌트에서 값을 사용하는데, A와 C 컴포넌트가 리렌더링 된다면 억울할 것이다.

import React from "react";
import { atom, useRecoilState, RecoilRoot } from "recoil";

const stateAtom = atom({
    key: "stateAtom",
    default: 0,
  });

  const D = () => {
    const [state] = useRecoilState(stateAtom);
    
    console.log("D rendered");

    return (
      <div>
        <h3>Component D</h3>
        <p>Value: {state}</p>
      </div>
    );
  }

  const C = () => {
    const [state, setState] = useRecoilState(stateAtom);
  
    console.log("C rendered");
  
    return (
      <div>
        <h3>Component C</h3>
        <p>Value: {state}</p>
        <button onClick={() => setState((prev) => prev + 1)}>Increment from C</button>
      </div>
    );
  };
  
  const B = () => {
    console.log("B rendered");
    return (
      <div>
        <h2>Component B</h2>
        <C />
      </div>
    );
  };
  
  const A = () => {
    console.log("A rendered");
  
    return (
      <div>
        <h1>Component A</h1>
        <B />
        <D />
      </div>
    );
  };

function App() {
  return (
    <RecoilRoot>
      <A />
    </RecoilRoot>
  )
}

다행이도, 버튼을 누르면 콘솔에 C rendered, D rendered가 순서대로 출력된다.

 

어떻게 최소한의 상태 보존을 해준다는건데?

결론은, Selector를 사용하면 쓸모없는 상태의 보존을 방지하는 장점을 가지게 한다.

더보기

왜 상태 값이 많으면 좋지 않을까?

 

여러 곳에서 동일한 데이터를 관리하면 데이터 불일치 문제가 발생할 수 있다.

예를 들어 섭씨와 화씨 온도를 각각 관리하는 경우, 한쪽만 업데이트하면 데이터가 일치하지 않게 된다. = 실수 가능성 증가

Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수(pure function)다.

기존의 상태 값들을 활용/계산해서 새로운 상태 값을 반환하고 이를 구독을 한 컴포넌트가 활용한다.

selectors의 반환 값이 변경되면 컴포넌트들도 다시 렌더링 된다.

 

selector 사용 예시를 들어보자.

당근 마켓에서 매너 온도를 통해 유저의 따뜻함 정도를 보여주고 있다.

당근 마켓이 미국 진출을 위해 온도를 화씨로 바꾸려고 한다. [ 사실은, 온도 대신 점수로 대체하긴 했다 ]

로컬라이징을 위해, 두가지 아톰(도씨, 화씨)을 가지고 활용할 수도 있지만, Selector를 활용해 도씨를 기반으로 화씨 값을 반환할 수 있다.

// 섭씨 온도 atom
const celsiusState = atom({
  key: 'celsiusState',
  default: 36.5,
});

// 섭씨를 화씨로 변환하는 selector
const fahrenheitState = selector({
  key: 'fahrenheitState',
  get: ({ get }) => {
    const celsius = get(celsiusState);
    const fahrenheit = (celsius * 9 / 5) + 32;
    return fahrenheit;
  },
});

어떠한 상태 값을 활용해 새로운 값을 반환할 지에 대한 부분은 개발자의 역량이 큰 것 같다.

새로운 전역 상태를 만들 때, 기존의 상태 값을 활용할 수 있을지 고민해보자.

 

Recoil 사용 시 주의할 점은?

전역 상태 관리라는 것은, 오히려 데이터 흐름을 파악하기 어렵게 만들 수도 있다.

프로젝트의 규모가 커지게 되고 여러 컴포넌트가 같은 상태를 바라보는 상황에서 상태에 관련한 문제가 발생했을 때, 어떠한 컴포넌트에서 이러한 문제를 발생 시키는지 파악하기 어려울 수도 있다.

 

메모리 누수 문제가 있다는 글이 있다.

https://medium.com/@clockclcok/recoil-%EC%9D%B4%EC%A0%9C%EB%8A%94-%EB%96%A0%EB%82%98-%EB%B3%B4%EB%82%BC-%EC%8B%9C%EA%B0%84%EC%9D%B4%EB%8B%A4-ff2c8674cdd5

 

Recoil, 이제는 떠나 보낼 시간이다

개요

medium.com

 

iOS 개발할 때는 Xcode 사이드바에서 CPU와 메모리 사용량을 실시간으로 체크해줘서, 편했는데 흠...

Visual Studio Code에서 개발을 하다보니, 메모리 누수가 있다는 사실을 글로 확인하게 되었다...

우선, 메모리 누수 확인은 앞으로도 하게 될 일이니, 메모리 누수 확인 방법부터 체크하고 진행하고 있는 프로젝트 확인부터 해야겠다.

 

'FE > React' 카테고리의 다른 글

React useRef vs useState  (0) 2025.02.13
react-hook-form, register 렌더링 분석  (0) 2025.01.19
Recoil 메모리 누수 확인하기  (2) 2025.01.11
npm vs yarn  (1) 2024.12.26
React 왜 쓸까?  (0) 2024.12.24