[React]전역 상태관리란?<useContext를 이용한 전역 상태관리>

·

3 min read

특정 변수나 배열, 객체 등 어떤 자료구조든지 웹 페이지에서 계속해서 업데이트되면서 동적인 상호작용을 가능캐 하는 것은 전부 상태관리를 통해 이루어진다. React의 상태관리의 단초는 useState이다. State들은 props를 통해 위아래로 연결되며 서로 다른 파일에 있더라도 특정한 상태에 대한 제어를 할 수 있게 된다.

하지만 페이지의 규모가 커짐에 따라 컴포넌트 트리(컴포넌트 간 위계를 도식화한 것)는 더욱더 복잡하고 커지게 된다. 운용해야 할 상태 역시 많아질 수 있는데, 한 컴포넌트에서 다른 컴포넌트로 전달하는 props도 빼곡해지고, 경우에 따라 중간에서 쓰이지 않는 상태가 자식에서 쓰인다는 이유로 여러 컴포넌트를 불필요하게 경유하게 된다. 몇 단계만 걸쳐 내려가도 이 문제로 골치를 겪은 적이 한번씩은 있을 것이다. 이야말로 '상태관리를 위한 상태관리' 아닌가!

이러한 문제점에서 해결책으로 등장한 개념인 '전역 상태 관리'는, state를 props로 전달하는 방식이 아니라, 특정 컴포넌트 트리 전반에서 공유할 수 있게 한다.

전역 상태관리를 지원하는 api에는, react에 내장된 useContext 훅을 포함하여 redux, recoil, zustand, zotai, react-query 등 다양한 서드파티 라이브러리들까지 존재한다.

이 포스트에는 useContext를 활용하여 간단한 상태관리를 구현해보고자 한다.


구현은 크게 2단계로 이루어진다.

  1. context 만들기

  2. context 사용하기

Context 만들기

일반적으로 context를 정의하는 부분은 파일을 따로 만들어서 관리한다.

여기 article-context.js에서 우리가 관리할 자료형을 정의하고, context를 구현해보자.

type Article = { //for typescript
  id: string;
  time: number;
};

const dummyArticleList: Article[] = [
  {
    id: "article1",
    time: 130,
  },
  {
    id: "article2",
    time: 40,
  },
  {
    id: "article3",
    time: 75,
  }
];

Article이라는 객체는 id와 time 필드를 가지며, dummy list는 리스트의 초기값이다.

우리는 Article의 배열을 관리하고자 한다.

export const articleContext = createContext();
  • createContext는 context객체를 생성한다.

  • context객체 자체는 정보를 가지고 있지 않고, 어떤 state에 대한 context인지를 나타낸다.

  • 다른 파일에서 이 articleContext를 사용해야 하니 export 키워드를 달아주자.

export const ArticleProvider = () => {
  const [articleState, setArticleState] = useState(dummyArticleList);

  return (
    <articleContext.Provider value={articleState}>
      {children}
    </articleContext.Provider>
  );
};
  • articleState는 상태관리의 대상이므로 useState에 등록시켜준다.

  • ArticleProvider란, 하나의 wrapper로서 이 context를 사용하는 다른 컴포넌트를 감싸기 위한 컴포넌트다.

context객체가 가지고 있는 context.Provider는, 감싸고 있는 children이 그 깊이에 상관없이 context에 접근할 수 있게 한다. 그 원형은

<articleContext.Provider value={initialValue}>
   <Page />
<articleContext.Provider>

과 같은데, context를 선언하는 파일을 분리시키면서 wrapper 컴포넌트를 리턴하게 한다.

  • useState는 함수 밖에서 호출이 안되므로 component 안에서 선언시켜준다.

article-context.js 전문

//article-context.js 
import { createContext, useState } from "react";

export type Article = { //for typescript
  id: string;
  elapsedTime: number;
};

const dummyArticleList: Article[] = [
  {
    id: "article1",
    elapsedTime: 130,
  },
  {
    id: "article2",
    elapsedTime: 40,
  },
  {
    id: "article3",
    elapsedTime: 75,
  },
];

export const articleContext = createContext();

export const ArticleProvider = () => {
  const [articleState, setArticleState] = useState(dummyArticleList);

  return (
    <articleContext.Provider value={articleState}>
      {children}
    </articleContext.Provider>
  );
};

Context 사용하기

//App.js
<ArticleProvider>
    <Home />
</ArticleProvider>

우선 context가 필요한 최상단 루트를 Provider로 감싸준다.

import { useContext } from "react";
import { articleContext } from "@/context/article-context";

const Home = () => {
   const articles = useContext(articleContext);

   console.log(articles[0].id) //article1

   ...
}
  • context를 사용하고자 하는 컴포넌트에서는 미리 생성한 context를 import해야 한다.

  • useContext(articleContext)로 value를 받아와 다음과 같이 간단히 사용가능하다.

문제점

💡
article의 업데이트는 어떻게 할까?

Home에서는 article를 업데이트하는 setArticle함수가 현재 없다.

Article를 업데이트하는 함수 역시 context를 통해 같이 전달해보도록 하자.

export const ArticleProvider = () => {
  const [articleState, setArticleState] = useState(dummyArticleList);

  const addArticle = (id) => { 
    const newArticle = { id: id, elapsedTime: 0 };
    const newState = [...articleState, newArticle];
    setArticleState(newState);
  };

  const removeArticle = (id) => {
    const newState = articleState.filter((item)=>{
      if(item.id != id) return true
    });
    setArticleState(newState);
  };

  return (
    <articleContext.Provider value={{articles: articleState, addArticle: addArticle, removeArticle: removeArticle}}>
      {children}
    </articleContext.Provider>
  );
};

addArticle, removeArticle 함수를 구현해 value에 동봉해주었다.

이는 이제 Home에서 다음과 같이 사용가능하다.

const ctx_article = useContext(articleContext);
const articles = ctx_article.articles;
const addArticle = ctx_article.addArticle;
const removeArticle = ctx_article.removeArticle;

참고자료

React 공식문서 - createContext

React 공식문서 - useContext

edited by 정정환