React (Context API와 useContext)

Jeonghun·2023년 5월 26일
3

React

목록 보기
9/21


React Context API와 useContext에 대해

이번 시간에는 React에서 흔히 사용하는 Context API와 useContext hook에 대해 알아보겠다. 많은 사람들이 Context API는 React에서 전역 상태 관리를 위해 사용하는 방법 이라고 생각할 수 있지만, 이는 정확하게 말하면 틀린 말이다. Context API는 React에서 발생하는 문제점인 'Props drilling'을 해결하기 위한 수단이다. 즉, 컴포넌트의 depth가 깊어질 경우 props를 편리하게 넘겨주기 위해 사용하는 도구이며, 이를 이용해 전역 상태를 관리할 수 있다. 이 때, useContext라는 hook을 사용하여 더 간편하게 전역 상태를 관리할 수 있게된다.

- Context API

🤔 Context API는 뭘까?
React의 Context API는 전역적으로 사용할 수 있는 데이터를 관리할 수 있게 해주는 API이다. 예를 들어 로그인 정보, 테마 설정 등의 전역적으로 사용되는 정보를 관리하는데 사용될 수 있다.

- Provider와 Consumer

Context API를 사용하기 위해서는, Provider와 Consumer에 대한 이해가 필요하다.

  • Provider: Context를 생성하고, 이를 하위 컴포넌트들에게 제공하는 컴포넌트를 말한다. 전역적으로 관리할 상태를 value prop으로 넘겨주면, 하위 컴포넌트들이 이 상태를 사용할 수 있게 된다.
  • Consumer: Provider에서 제공하는 상태를 사용하는 컴포넌트를 말한다. Provider의 상태를 구독하고, 상태가 변경될 때마다 재렌더링 된다.

📌 Context API 사용 예시

import React, { createContext } from 'react'; // context api를 사용하기 위해 import

const MyContext = createContext('defaultValue'); // createContext()를 이용해 context 생성

// App 컴포넌트
function App() {
  return (
    <MyContext.Provider value="myValue"> // provider를 사용한 context 제공
      <Child />
    </MyContext.Provider>
  );
}

// Child 컴포넌트
function Child() {
  return (
    <MyContext.Consumer> // consumer를 사용하여 provider의 value값을 사용함
      {(value) => <div>{value}</div>} // Consumer를 사용할 시, 함수를 렌더링 해야한다.
    </MyContext.Consumer>
  );
}

위 예제 코드를 살펴보면 App 컴포넌트에서 createContext 함수를 사용하여 Context를 생성하고, 이를 MyContext.Provider 컴포넌트를 통해 제공하고 있다. Child 컴포넌트에서는 MyContext.Consumer 컴포넌트를 사용하여 Provider의 value를 사용한다. 이때, Consumer 컴포넌트 내에서는 코드에서와 같이 함수를 리턴한다.

- useContext

위에서 알아본 Consumer를 사용한 방법도 물론 좋다. 하지만, 이와같은 방법은 조금 번거롭게 느껴질 수 있는데, 이를 더 간편하게 사용할 수 있도록 React에서는 'useContext'라는 hook을 제공한다.

🤔 useContext, 넌 누구니?
useContext는 Context의 value를 사용할 수 있게 해주는 React 기본 내장 Hook이다. useContext의 인자로 Context 객체를 넘겨주면, 해당 Context의 현재 value를 반환한다. useContext는 Consumer를 사용하는 것보다 간편하고, 섹시한 코드를 작성할 수 있도록 해준다.

📌 useContext 사용 예시

import React, { createContext, useContext } from 'react';

const MyContext = createContext('defaultValue'); // createContext로 컨텍스트를 만들고

// App 컴포넌트
function App() {
  return (
    <MyContext.Provider value="myValue"> // provider로 컨텍스트를 제공한다.
      <Child />
    </MyContext.Provider>
  );
}

// Child 컴포넌트
function Child() {
  const value = useContext(MyContext); // useContext를 사용하여 value값을 가져온다.
  return <div>{value}</div>;
}

위 예제 코드에서는 useContext(MyContext)를 사용하여 Provider의 value를 받아오고 있다. 위와 같이 useContext를 사용하면 Consumer를 사용하는 것보다 더 간결하고 직관적인 코드를 작성할 수 있다.

🤔 useContext를 쓰면 뭐가 좋은거지?
useContext를 사용하여 상태관리를 할 경우 아래와 같은 장점이 있다.

  • 전역 상태 관리를 간편하게 할 수 있다.
  • Provider와 Consumer를 직접 사용하는 것보다 더 간결하고 직관적인 코드를 작성할 수 있다.
  • 여러 Context를 사용하는 경우에도 간편하게 상태를 사용할 수 있다.

아직 잘 모르겠다고? 아래 예시 코드를 통해 더 복잡한 상황에서의 사용법을 알아보자.

📌 useContext 사용 예시(2)

import React, { useState, createContext } from 'react';

// Context 생성
const UserContext = createContext();
const ThemeContext = createContext();

// Provider 컴포넌트
// UserProvider와 ThemeProvider, 두 개의 Provider 컴포넌트를 만들고,
export function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  
  return (
    // 각 컴포넌트는 각각의 state와 state 변경 함수를 value prop으로 넘겨준다.
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

먼저, createContext를 이용하여 UserContext와 ThemeContext 두 개의 Context를 생성하였다. 그리고 이를 이용하여 UserProvider와 ThemeProvider 두 개의 Provider 컴포넌트를 만든다. 각각의 Provider 컴포넌트는 각각의 상태와 상태 변경 함수를 value prop으로 넘겨준다. 이제 위에서 만든 Provider 컴포넌트를 사용하여 'App' 컴포넌트에서 전역 상태를 제공해보자.

// App.jsx

import React from 'react';
import { UserProvider, ThemeProvider } from './Providers';
import MyComponent from './MyComponent';

function App() {
  return (
    <UserProvider>
      <ThemeProvider>
        <MyComponent />
      </ThemeProvider>
    </UserProvider>
  );
}

export default App;

그리고, MyComponent 컴포넌트에서 useContext를 사용하여 UserContext와 ThemeContext의 상태를 사용해보자.

// MyComponent.jsx

import React, { useContext } from 'react';
import { UserContext, ThemeContext } from './Providers';

function MyComponent() {
  // useContext를 사용하여 각 provier 컨텍스트에서 state를 가져온다.
  const { user, setUser } = useContext(UserContext);
  const { theme, setTheme } = useContext(ThemeContext);

  const handleLogin = () => {
    setUser({ name: 'John', age: 30 });
  };

  const handleLogout = () => {
    setUser(null);
  };

  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };

  return (
    <div>
      <button onClick={handleLogin}>Login</button>
      <button onClick={handleLogout}>Logout</button>
      <button onClick={toggleTheme}>Toggle theme</button>
      <p>User: {user ? user.name : 'Not logged in'}</p>
      <p>Theme: {theme}</p>
    </div>
  );
}

export default MyComponent;

MyComponent 컴포넌트에서는 로그인, 로그아웃, 테마 변경 기능을 구현하였다. useContext를 이용하여 UserProvider와 ThemeProvider로부터 상태와 상태 변경 함수를 가져와서 사용하고 있다.

이처럼, useContext는 여러 개의 전역 상태를 편리하게 관리할 수 있게 해주며, 코드를 더욱 간결하고 명확하게 작성할 수 있도록 해준다.

- useState vs useContext

자, 이제 useContext가 전역 상태를 관리하는데 좋다는걸 알게되었다. 그럼, 같은 상태 관리 hook인 'useState'와는 어떤점이 다른가? 아래는 'useState'와 'useContext'의 대표적인 차이점이다.

  • useState: 컴포넌트 내부의 상태를 관리한다. 컴포넌트가 리렌더링 될 때마다 해당 상태는 유지된다.
  • useContext: 전역 상태를 관리한다. 여러 컴포넌트 간에 같은 상태를 공유해야 하는 경우 사용한다.

따라서, 컴포넌트 내부에서만 사용되는 상태(예를 들어, form의 입력 값 저장, 로딩 상태, 모달창 열림/닫힘 상태 등)는 useState를 사용하여 관리하고, 여러 컴포넌트에서 공유되어야 하는 상태(로그인한 유저 정보, 테마 설정, 에플리케이션 전체에서 출력되는 메세지나 알림 등)는 useContext를 사용하여 관리할 수 있다.


Context API와 useContext hook은 React에서 전역 상태 관리를 위해 제공하는 강력한 도구이다. 이를 잘 활용하면 전역 상태를 효과적으로 관리하고, 코드를 더 간결하고 직관적으로 작성할 수 있다. 이 두 가지 기능을 잘 이해하고 사용할 수 있도록 해야겠다.

profile
안녕하세요, 프론트엔드 개발자 임정훈입니다.

0개의 댓글