icon안동민 개발노트

useContext 훅으로 데이터 공유하기


 React의 Context API와 useContext 훅은 컴포넌트 트리 전체에 걸쳐 데이터를 효과적으로 공유할 수 있게 해주는 강력한 도구입니다.

 이를 통해 props drilling 문제를 해결하고 전역 상태를 관리할 수 있습니다.

Context API의 개념

 Context는 React 컴포넌트 트리 안에서 전역적으로 데이터를 공유할 수 있는 방법을 제공합니다.

 이는 사용자 인증 정보, 테마, 언어 설정 등과 같이 애플리케이션의 여러 부분에서 필요한 데이터를 관리하는 데 유용합니다.

Context 생성 및 Provider 설정

 Context를 생성하고 Provider를 설정하는 방법은 다음과 같습니다.

import React, { createContext, useState } from 'react';
 
// Context 생성
export const ThemeContext = createContext();
 
// Provider 컴포넌트
export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
 
  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };
 
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

 이 예제에서는 테마 관련 상태와 함수를 포함하는 Context를 생성하고, Provider 컴포넌트를 통해 하위 컴포넌트에 이 Context를 제공합니다.

useContext 훅 사용하기

 useContext 훅을 사용하여 Consumer 컴포넌트에서 Context 값을 쉽게 사용할 수 있습니다.

import React, { useContext } from 'react';
import { ThemeContext } from './ThemeProvider';
 
function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);
 
  return (
    <button 
      onClick={toggleTheme}
      style={{ 
        background: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#333' : '#fff'
      }}
    >
      Toggle Theme
    </button>
  );
}

 이 컴포넌트는 ThemeContext에서 제공하는 theme 값과 toggleTheme 함수를 사용하여 테마를 변경할 수 있는 버튼을 렌더링합니다.

여러 Context 조합하기

 여러 개의 Context를 사용해야 할 때는 다음과 같이 중첩하여 사용할 수 있습니다.

import { ThemeProvider } from './ThemeContext';
import { UserProvider } from './UserContext';
 
function App() {
  return (
    <ThemeProvider>
      <UserProvider>
        <MainContent />
      </UserProvider>
    </ThemeProvider>
  );
}

 그리고 Consumer 컴포넌트에서는 필요한 Context를 각각 useContext로 가져와 사용할 수 있습니다.

import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
import { UserContext } from './UserContext';
 
function UserProfile() {
  const { theme } = useContext(ThemeContext);
  const { user } = useContext(UserContext);
 
  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333' }}>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

Context 사용 시 성능 고려사항

 Context를 사용할 때는 다음과 같은 성능 관련 사항을 고려해야 합니다.

  1. 불필요한 리렌더링 : Context 값이 변경되면 해당 Context를 사용하는 모든 컴포넌트가 리렌더링됩니다. 따라서 자주 변경되는 값과 그렇지 않은 값을 분리하여 관리하는 것이 좋습니다.
const FastChangeContext = createContext();
const SlowChangeContext = createContext();
 
function Provider({ children }) {
  const [fastChangingValue, setFastChangingValue] = useState(0);
  const [slowChangingValue, setSlowChangingValue] = useState(0);
 
  return (
    <SlowChangeContext.Provider value={slowChangingValue}>
      <FastChangeContext.Provider value={fastChangingValue}>
        {children}
      </FastChangeContext.Provider>
    </SlowChangeContext.Provider>
  );
}
  1. 메모이제이션 활용 : React.memo, useMemo, useCallback 등을 사용하여 불필요한 리렌더링을 방지할 수 있습니다.
const MemoizedChild = React.memo(function Child({ text }) {
  const theme = useContext(ThemeContext);
  return <div style={{ color: theme.color }}>{text}</div>;
});

Context 사용 시 주의점

  1.  남용 주의 : 모든 전역 상태를 Context로 관리하려고 하면 애플리케이션의 구조가 복잡해질 수 있습니다. 정말 필요한 경우에만 Context를 사용하세요.

  2.  기본값 설정 : createContext에 기본값을 제공하면, Provider 없이 Context를 사용할 때 발생할 수 있는 오류를 방지할 수 있습니다.

const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });
  1.  동적 Context 생성 주의 : 렌더링 중에 새로운 Context 객체를 생성하지 마세요. 이는 의도치 않은 리렌더링을 유발할 수 있습니다.

  2.  Provider 최적화 : Provider의 value prop에 객체를 직접 전달하면 불필요한 리렌더링이 발생할 수 있습니다. 대신 상태를 분리하거나 useMemo를 사용하세요.

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
 
  const contextValue = useMemo(() => ({
    theme,
    toggleTheme: () => setTheme(t => t === 'light' ? 'dark' : 'light')
  }), [theme]);
 
  return (
    <ThemeContext.Provider value={contextValue}>
      {children}
    </ThemeContext.Provider>
  );
}

 Context API와 useContext 훅은 React 애플리케이션에서 전역 상태를 관리하고 컴포넌트 간 데이터 공유를 용이하게 하는 강력한 도구입니다.

 이를 통해 props drilling 문제를 해결하고 애플리케이션의 구조를 더 깔끔하게 유지할 수 있습니다.

 그러나 Context를 사용할 때는 성능 영향을 고려해야 하며, 불필요한 리렌더링을 방지하기 위해 적절한 최적화 기법을 적용해야 합니다.

 또한, Context의 남용은 애플리케이션의 구조를 복잡하게 만들 수 있으므로, 전역적으로 공유해야 하는 데이터에 대해서만 Context를 사용하는 것이 좋습니다.

 적절히 사용된 Context는 React 애플리케이션의 상태 관리를 크게 개선할 수 있으며, 특히 중간 규모 이상의 프로젝트에서 그 가치를 발휘합니다.

 더 복잡한 상태 관리가 필요한 경우 Redux나 MobX 같은 전용 상태 관리 라이브러리를 고려할 수 있지만, 많은 경우 Context API만으로도 충분한 솔루션을 제공할 수 있습니다.