본문 바로가기

cs/react

[React] Context API & Provider 구조

목차

1. Context란

2. Context 사용 방법

3. 사용시 주의 사항

4. Context 사용이 적합한 경우

5. Context 사용 방법(번외)


Context란

컴포넌트간의 데이터를 전달하는 방법으로, 전역적인 값 관리가 가능한 방법입니다.

중간 컴포넌트를 거쳐서 하위 컴포넌트에 props를 전달하지 않고 필요한 컴포넌트에게 전달할 수 있는 방법이라 props 전달 방식의 단점인 props drilling을 해결할 수 있습니다.

 

props drilling

상위 컴포넌트에서 하위 컴포넌트로 props를 전달하려고 할때 props가 필요없는 중간 컴포넌트를 통해 데이터를 연달아 전달해야하는 현상.

 

또한 Context는 일종의 데이터 보관소 역할을 합니다.

Context 역할 - 데이터 보관소


Context 사용 방법

(1) Context 생성

import {createContext} from "react";

const UserContext = creatContext(null);

데이터를 하위 컴포넌트에 전달하는 것이 주 목적이기 때문에 컴포넌트 바깥에 생성합니다.

=> 렌더링 될 때마다 다시 생성될 필요 없음.

(2) Provider로 값 공급

function App() {
	const user = {name = "Faker", age : 28};
    
    return (
    	<UserContext.Provider value={user}>
           <Champions/>
        </UserContext.Provider>
    )
}

contextProvider가 감싸는 모든 하위 컴포넌트는 props를 사용하지 않아도 value 속성으로 값을 전달할 수 있습니다.

(3) useContext로 값 읽기

function Champions() {
    const user = useContext(UserContext);
    
    return <div>{user.name}</div>
}

useContext(Context.Provider에서 내려온 값을 읽는 훅)은 읽기 전용이기 때문에 값을 변경하려면 Provider쪽의 state를 관리해야합니다.

Provider가 상위에 없는 경우 createContext의 기본값을 반환합니다. (1번에서 정의한 null)


추가 사용 예시

//App.jsx (최상위 컴포넌트)
import { useState, useRef, useReducer, useCallback, createContext } from "react";
...

export const TodoContext = createContext();

function App() {
  ...

  return (
    <>
      <div className="App">
        <Header />
        <TodoContext.Provider 
          value={{//전달할 값
            todos, onCreate, onUpdate, onDelete,
          }}
        >
          <Editor />
          <List />
        </TodoContext.Provider>
      </div>
    </>
  );
}

export default App;
//하위 컴포넌트 - Editor.jsx
import { useState, useRef, useContext } from "react";
import { TodoContext } from "../App"; 
//상위 컴포넌트에서 만든 TodoContext import

const Editor = () => {
  const { onCreate } = useContext(TodoContext); 
  //onCreate만 사용함 구조분해할당
  ...
  return (
    ...
  );
};

export default Editor;
//하위 컴포넌트 - List.jsx
import { useState, useMemo, useContext } from "react";
import { TodoContext } from "../App";

const List = () => {
  const { todos } = useContext(TodoContext);
  ...

  return (
    <div className="List">
      ...
      <div className="todos_wrapper">
        {filteredTodos.map((todo) => {
          return <TodoItem key={todo.id} {...todo} />; 
          //props로 넘겨주던 것들 삭제
        })}
      </div>
    </div>
  );
};

export default List;
//하위 컴포넌트 - TodoItem.jsx
import { memo, useContext } from "react";
import { TodoContext } from "../App";

const TodoItem = ({ id, isDone, content, date }) => {
  const { onUpdate, onDelete } = useContext(TodoContext);
...
  return (
    ...
  );
};

export default memo(TodoItem);


사용시 주의 사항

  • context 사용시 컴포넌트의 재사용이 어려울 수 있습니다.
  • 단순히 props drilling을 피하기 위함이라면 Component Composition(컴포넌트 합성)을 권장합니다.
  • context의 value가 변경되면 contextProvider 하위 모든 컴포넌트가 리렌더링 됩니다.
  • Provider가 중첩된다면 추적이 어려워집니다.
  • 필터링 상태, 검색어, input값 등과 같이 자주 바뀌는 값은 context 사용을 지양합니다.

주의 사항 중 context 사용시 컴포넌트의 재사용이 어려울 수 있다는 부분이 바로 이해가 되지 않아서 약간의 예시를 들어보겠습니다.

 

UserProfile이라는 컴포넌트는 useHeader라는 context에 있는 로그아웃 처리를 사용한다고 가정하겠습니다.

// UserProfile.jsx
import React from 'react';
import { useHeader } from '@/utils/headerUtils'; // Context에 종속됨

function UserProfile() {
    const { setHeader } = useHeader();

    useEffect(() => {
        // 이 컴포넌트가 마운트될 때, 헤더 설정을 Context에 의존하여 변경합니다.
        setHeader({
            title: "내 프로필",
            isLogoutBtn: true,
            onLogout: () => { console.log("로그아웃 처리..."); }
        });

        return () => {
            // 언마운트 시 헤더를 기본 상태로 되돌립니다.
            setHeader({});
        };
    }, [setHeader]);

    return <div>사용자 프로필 상세 정보</div>;
}

export default UserProfile;

 

문제점

  • UserProfile 컴포넌트는  useHeader라는 context에 종속 되어있기 때문에 해당 context를 제공하지 않는 레벨에서는 사용할 수 없습니다.
  • 이는 특정 context에 종속되어 context가 제공되지 않는 곳에서는 컴포넌트를 재사용할 수 없다는 문제점을 있습니다.

해결방법

  • 재사용이 중요한 범용적인 UI컴포넌트 (버튼, 입력 필드 등)에는 context를 피하고 props를 사용하는 것이 좋습니다.
  • 단순 props drilling을 피하기 위함이라면 Component Composition을 권장합니다.

Context 사용이 적합한 경우

context는 애플리케이션의 핵심 데이터, 테마 같이 전역적으로 필수적인, 자주 변하지 않는 전역값에 적합합니다.

 

  • 로그인한 사용자 정보
  • 테마 정보(light / dark)
  • 언어 설정
  • 글로벌 config (전역 환경 설정)
  • 인증 토큰

Context 사용 방법(번외)

import { ModalProvider } from "@/utils/modalUtils";
import { HeaderProvider } from "@/utils/headerUtils";

export default function CommonProvider({children}) {
   return (
     <ModalProvider>
       <HeaderProvider>{children}</HeaderProvider>
     </ModalProvider>
   )
}
import React, { createContext, useState, useContext, useEffect } from "react";

const HeaderContext = createContext();

export const HeaderProvider({children}) => {
    const [headerState, setHeaderState] = useState({
      title: "",
      onLogout = () => {},
      isLogoutBtn: false,
      onSearch = () => {},
      isSearchBtn: false,
      isSettingBtn: false,
      onSetting = () => {}
    })
    
    const setHeader = ({
    	title = "",
        onLogoutBtn = () => {},
        isLogoutBtn = false,
        onSearch = () => {},
        isSearchBtn = false,
        onSetting = () => {},
        isSettingBtn = false
    }) => {
    	setHeaderState({
        	title,
            onLogoutBtn,
            isLogoutBtn,
            onSearch,
            isSearchBtn,
            onSetting,
            isSettingBtn
        })
    }
    
    const value = { headerState, setHeader };

    return <HeaderContext.Provider value={value}>{children}</HeaderContext.Provider>;
}
import CommonProvider from "@/utils/CommonProvider";

function App() {
	return (
    	<>
          <CommonProvider>
            <Router>
              <RootRoutes/>
            </Router>
            <ModalAlert/>
          </CommonProvider>
        </>
    )
}
function MainComponent() {
	const {setHeader} = useHeader(); //context에 정의된 header정보 관리 함수 호출
    
    useEffect(() => {
       setHeader({
         isLogoutBtn: true,
         onLogout: () => {
           openAlert({
             messag: "로그아웃 하시겠습니까?",
             isCancelBtn: true,
             onConfirm: () => {
              logout();
             }
           })
         }
     })
    },[])
}

 

사용법 요약

  • Context의 데이터를 전달해주는 Provider를 컴포넌트화 해서 App.js에서 간략하게 표현할 수 있습니다.
  • HeaderProvider 컴포넌트가 받는 {children}은 HeaderProvider가 받는 하위 컴포넌트를 의미합니다.
  • CommonProvider에 HeaderContext를 전달하는 HeaderProvider를 하위에 정의했기 때문에, App.js에서 CommonProvider 하위의 모든 컴포넌트는 HeaderProvider에서 제공하는 header 정보를 사용할 수 있습니다.
  • 하위 컴포넌트인 MainComponent는 context에 저장된 header정보를 제어할 수 있게 됩니다.

참고

https://ko.legacy.reactjs.org/docs/context.html

 

Context – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

 

'cs > react' 카테고리의 다른 글

[React] Custom Hook  (0) 2025.12.02
[React] Redux Toolkit  (0) 2025.12.01
[React] 컴포넌트 생명주기  (0) 2025.11.28
[React] 리액트 훅  (0) 2025.11.27
[React] 가상 DOM과 재조정  (0) 2025.11.24