[React] 전역상태와 지역상태(전역상태관리)

Props Drilling

 리액트를 다루다보면 상태(state)를 어떤식으로 사용할지에 대해 많이 고민하게 된다. 어떤 값들을 state로 두어야할지부터 시작해서 어디다가 두어야할지까지 항상 염두해야 한다. 기본적으로는 부모 컴포넌트에 상태를 만들고 props로 내려서 자식 컴포넌트에서 사용한다. 깊이가 얼마가 되든 props로 내려주기만 하면 하위 컴포넌트에서 사용할 수 있다. 그런데 그러다보면 props drilling이 일어나는데, 전혀 필요없는 state인데도 밑으로 내려줘야 하는 이유 때문에 props로 받아오는 과정이다.

 물론 리액트가 기본적으로 제공하는 기능이며, 일반적으로 드릴링해서 사용하기는 한다. 허나, 만약에 컴포넌트의 깊이가 5개 이상, 10개 이상, 또는 더 많아지게 된다면? props 지옥에 빠진다. 최상위 컴포넌트에 있는 상태를 마리아나 해구에서 사용하기 위해 구멍을 계속 하는 상황에 직면한다.

 아래로 구멍을 계속 뚫다보면 '지금 대체 뭐하는 짓이지?'라는 생각이 들 수 있다. 개발자는 무언가 반복되는 상황이 싫다. 반복은 내가 하는게 아니라 컴퓨터가 해야 한다!

전역상태가 뭐야?

 전역변수(Global variable)와 비슷한 뉘앙스로, 전역상태(Global state)라고 부른다. 상태의 위치가 어디던 간에 어디서든 사용할 수 있는 상태가 전역상태이다. 반대로 지역상태는 어느 한 구역에서만 사용되는 상태라고 보면 되겠다(기본적인 state의 개념이라고 보면 됨). 전역상태는 어떠한 상태를 리액트 코드 전역에서 사용하기 위한 상태 관리의 개념이다.

Context API

 리액트에서는 굳이 삽으로 구멍을 파지 않아도 다른 컴포넌트로 상태를 전달할 수 있는 도구를 가지고 있다. 특정 컴포넌트가 상태를 Provide하여 context로 저장되고, 다른 컴포넌트에서 이 context를 사용해 전역 상태로 쓸 수 있다.

  • 상태를 제공하는 컴포넌트
// Provider Component
import { createContext, useMemo, useState } from 'react'

export const LoginContext = createContext({ login: false, setLogin: () => {} })

const ProviderComponent = () => {
  const [login, setLogin] = useState(false);
  const memo = useMemo(() => ({ login, setLogin }), [login, setLogin]);
  // useMemo를 사용하는 이유는 해당 객체가 매번 리렌더링 되지 않게 하기 위함
  return (
    <LoginContext.Provider value={memo}>
      <PassThrough />
      <div>{login ? 'logged in' : 'logged out'}</div>
    </LoginContext.Provider>
  );
);

export default ProviderComponent;
  • 거쳐가는 자식 컴포넌트
// PassThrough Component
import ConsumerComponent from './ConsumerComponent';

export default const PassThrough = () => {
  return <ConsumerComponent />;
};
// 거쳐가는 컴포넌트에서 props를 받거나 내려주지 않음
  • 상태를 받는 컴포넌트
// ConsumerComponent
import { useContext } from 'react';
import { LoginContext } from '/ProviderComponent';

export default const ConsumerComponent = () => {
  const { login, setLogin } = useContext(LoginContext);
  
  const handleLogin = () => {
    setLogin((prev) => !prev)
  }
  return (
    <div className="login-button-wrapper">
      <button onClick={handleLogin}>{login ? "로그아웃" : "로그인"}</button>
    <div>
  );
};

Provider와 Consumer 컴포넌트는 서로 props를 주고받지 않고 있지만 정상적으로 상태를 받아와서 사용할 수 있다!

Redux 같은 라이브러리를 사용하는 것도 좋겠지만, 전역상태만을 컨트롤하기위함이라면 굳이 복잡하게 라이브러리까지 갈 필요없이 context 훅을 사용하면 된다.

  • 주의할 점: 상태를 받는 컴포넌트가 자손 컴포넌트가 아닐 경우에는 Provider로 제공된 상태 값을 불러올 수 없습니다.

좋은 웹페이지 즐겨찾기