3 분 소요


1. useContext 개요

1.1 react context 등장 배경

일반적으로 부모컴포넌트 → 자식 컴포넌트로 데이터를 전달해 줄 때 props를 사용하였다. 하지만, 이 방식으로 데이터를 전달했을 때 prop drilling 현상이 일어난다는 문제점이 있다.

prop drilling의 문제점

  1. 깊이가 너무 깊어지면 prop이 어떤 컴포넌트로부터 왔는지 파악이 어려워진다.
  2. 어떤 컴포넌트에서 오류가 발생할 경우 추적이 힘들어지니 대처가 늦을 수 밖에 없다.

    자료 : https://www.copycat.dev/blog/react-context/

따라서 이러한 문제를 해결하기 위해 react context API가 등장하게 되었다. useContext hook을 통해 쉽게 전역 데이터를 관리할 수 있다.


1.2 context API 필수 개념

useContext Hook은 Context 객체를 받아 해당 Context의 현재 값을 반환한다. 이 Hook을 사용하면 Context의 Provider에서 제공한 값을 자식 컴포넌트에서 바로 사용할 수 있다.

  • createContext : context 생성
  • Consumer : context 변화 감지
  • Provider : context 전달(to 하위 컴포넌트)


1.3 범위 상태

context 는 문맥, 맥락이라는 의미이다.

  • 아래 사진에서 A라는 컴포넌트 하위에 있는 컴포넌트들은 동일한 맥락에서 대화를 하고 있다.
  • useContext를 root 경로에 두었을 때, 비로소 전역상태라고 할 수 있다.
  • 예시를 들면, 각 나라마다 다른 언어를 사용한다고 했을 때 나라의 꼭대기에 context를 잡아주면 하위에 있는 모든 국민들은 동일한 언어(데이터)를 사용할 수 있는 것이다.



2. useContext 구현하기

Context를 사용해서 isDark라는 데이터를 모든 하위 컴포넌트들에게 props를 사용하지 않고 공유해보도록하자.

2.1 Context 생성

  • children?
    • children은 해당 컴포넌트의 여는 태그와 닫는 태그 사이에 포함된 모든 요소나 컴포넌트를 의미한다.
    • 아래 코드에서 children은 ThemeContextProvider 컴포넌트로 감싸진 모든 하위 컴포넌트를 의미한다.
    • children props를 사용해서 ThemeContextProvider로 감싸지는 모든 자식 컴포넌트들이 value 값을 공유할 수 있다.
// src/context/ThemeContext.jsx

// (1) import
import { createContext, useState } from "react";

// (2) Context 생성
export const ThemeContext = createContext(null);

// (3) Provider 생성
export const ThemeContextProvider = ({ children }) => {
  // (4) props drilling으로 공유되는 useState를 옮겨주자
  const [isDark, setIsDark] = useState(false);

  return (
    <ThemeContext.Provider value={{ isDark, setIsDark }}>
      {children}
    </ThemeContext.Provider>
  );
};


2.2 Context 제공 (Provider)

// index.jsx
import ReactDOM from "react-dom/client";
import App from "./App";
import { ThemeContextProvider } from "./context/ThemeContext"; // (1) 만든 Context import

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  // (2) 만든 Provider 로 감싸기
  // (3) value엔 전달하고자하는 데이터 넣기
  <ThemeContextProvider>
    <App />
  </ThemeContextProvider>
);


2.3 useContext를 통한 값 사용

// components/pages/MainPage.jsx
import { ThemeContext } from "context/ThemeContext";
import { useContext } from "react";
import styled, { css } from "styled-components";

const MainPage = () => {
  // useContext Hook으로 Context로 전달한 정보 받아오기
  const { isDark, setIsDark } = useContext(ThemeContext);

  const toggleTheme = () => {
    setIsDark((prev) => !prev);
  };

  return (
    <MainLayout $isDark={isDark}>
      <p>안녕</p>
      <p>Hello</p>
      <button onClick={toggleTheme}>
        {isDark ? "라이트 모드" : "다크모드"}
      </button>
    </MainLayout>
  );
};

export default MainPage;

// const MainLayout = styled.main`
//   height: 100vh;
//   background-color: ${(props) => (props.isDark ? '#000000' : '#ffffff')};
// `;
const MainLayout = styled.main`
  height: 100vh;
  ${(props) =>
    props.$isDark
      ? css`
          background-color: black;
        `
      : css`
          background-color: white;
        `}
`;


3. 커스텀 훅으로 Context 사용하기

3.1 커스텀 훅 만들기

여러 컴포넌트에서 Context 값을 사용하려면 매번 useContext를 호출해야한다. 커스텀 훅을 만들면 Context를 좀 더 간편하게 사용할 수 있다.

아래는 AuthContext를 사용하여 인증 상태를 관리하는 커스텀 훅 useAuth를 만드는 예시이다.

// src/context/AuthContext.jsx
import { getProfile } from "api/auth";
import { createContext, useContext, useEffect, useState } from "react";

// 1. AuthContext 생성
const AuthContext = createContext();

// 2. AuthContext Provider
export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const isSignIn = !!user; // 로그인 상태 확인

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await getProfile();
        setUser(response.data.member);
      } catch (error) {
        setUser(null);
      } finally {
        setLoading(false);
      }
    };
    fetchUser();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <AuthContext.Provider value={{ user, setUser, isSignIn }}>
      {children}
    </AuthContext.Provider>
  );
};

// 3. useAuth 커스텀 훅 생성
export const useAuth = () => useContext(AuthContext);


3.2 커스텀 훅 사용하기

생성한 useAuth 훅을 호출하여 생성한 user, setUser, isSignIn 을 사용할 수 있다.

const { user, login, logout, isSignIn } = useAuth(); 와 같이 사용할 수 있다.

import { useAuth } from "context/AuthContext";
import { Navigate, Outlet, useLocation } from "react-router-dom";

const ProtectedRoute = () => {
  const { isSignIn } = useAuth(); // 커스텀 훅 사용하기
  const location = useLocation();

  if (!isSignIn) {
    return <Navigate to="/sign-in" state={{ from: location }} replace />;
  }

  return <Outlet />;
};

export default ProtectedRoute;



3. 주의사항

재사용성

Context를 사용하면 컴포넌트를 재사용하기 어려워질 수 있다. 필요할 때만 사용하는 것이 좋다.


렌더링 문제

  • useContext를 사용할 때, Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 된다.
  • 즉, value 부분을 항상 신경써줘야 한다. → 따라서 메모이제이션이 필요하다.



3. 참조

  • https://www.youtube.com/watch?v=JQ_lksQFgNw


태그:

카테고리:

업데이트:

댓글남기기