[React] 전역 상태 관리 (2) - Context API

박하늘·2025년 2월 25일
0

React

목록 보기
10/15
post-thumbnail

⚛️ Context API 란?

  • React에서 컴포넌트 간 상태(state)를 공유할 때 사용하는 기능
  • 전역 상태 관리 방법 중 내부 관리 방법이며, Redux 같은 상태 관리 라이브러리를 사용하지 않고도 전역 상태를 쉽게 관리 가능



💡 Context API 사용법

1. createContext 로 전역 상태 만들기 - 전역상태 저장소

📁 countContext.jsx

import { createContext, useState } from "react";

// 1️⃣ Context 생성 (createContext) - 전역 상태를 생성
export const CounterContext = createContext();

// 2️⃣ Provider를 생성하여 상태를 전달
export const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);

  return (
    <CounterContext.Provider value={{ count, setCount }}>
      {children}
    </CounterContext.Provider>
  );
};
  • Context API 관련 파일을 담을 폴더를 하나 생성 해주고 그 안에 Context API 관련 코드 파일(countContext) 을 담아주기
    → Context API 를 사용 할 때는 Context API 를 담아둘 별도의 폴더를 생성하는 것이 좋다
  • { children }을 사용하는 이유는 Provider 내부에 있는 모든 하위 컴포넌트들(<APP> ・・・)이 해당 Provider에서 제공하는 상태를 사용할 수 있도록 하기 위해서입니다.

2. Provider 로 전역 상태 연결해 주기

📁 main.jsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { CounterProvider } from "./CounterContext";

ReactDOM.render(
  <CounterProvider>
    <App />
  </CounterProvider>,
  document.getElementById("root")
);
  • <App> 컴포넌트를 생성한 전역 상태로 감싸주기

3. useContext 로 전역 상태 가져오기

📁 App.jsx

import React, { useContext } from "react";
import { CounterContext } from "./CounterContext";

const Counter = () => {
  const { count, setCount } = useContext(CounterContext);

  return (
    <div>
      <h1>카운트: {count}</h1>
      <button onClick={() => setCount(count + 1)}>증가</button>
      <button onClick={() => setCount(count - 1)}>감소</button>
    </div>
  );
};

export default Counter;



💡 전역 상태를 내보내는 방법 ( 배열 VS 객체 )

[배열]

  • useState처럼 사용하기 편리한 방식
  • 상태 값과 변경 함수를 배열로 제공하여, 구조 분해 할당을 통해 사용
const [상태, 상태변경함수] = useState('초기 상태값');

// Context Provider 내부에서 전역 상태를 제공
value={[상태, 상태변경함수]};

// 전역 상태를 가져와 사용
const [상태, 상태변경함수] = use전역상태();

✅ 장점:

  • useState와 유사한 방식으로 사용 가능
  • 직관적인 상태 관리가 가능

❌ 단점:

  • 여러 개의 상태를 관리할 경우 불편
  • 특정 상태만 가져와 사용하기 어려움

[객체]

  • 여러 개의 상태 값을 객체로 묶어 관리
  • 필요한 값만 구조 분해 할당하여 사용할 수 있음
// Context Provider 내부에서 전역 상태를 객체 형태로 제공
value={{1,2,3,4,5 }};

// 특정 값만 가져와 사용
const {2,4 } = use전역상태();
const {1,5 } = use전역상태();
const {3 } = use전역상태();

✅ 장점:

  • 필요한 값만 선택적으로 가져올 수 있음
  • 여러 개의 상태를 한 번에 관리하기 편리함

❌ 단점:

  • 값이 많아질 경우 관리가 복잡해질 수 있음
  • 상태를 갱신할 때 불필요한 리렌더링이 발생할 가능성이 있음

✔️ 결론

배열 방식상태 값이 하나이거나 단순한 상태 변경이 필요할 때
객체 방식여러 개의 상태를 함께 관리하거나, 필요한 값만 선택적으로 가져오고 싶을 때




🔮 예제

: Context API를 이용하여 상태 관리하기 !

1 📁 counterContext.jsx

import { useContext } from "react"
import { useState } from "react"
import { createContext } from "react"

const counterContext = createContext()

export const CounterProvider = ({children}) => {
    const [counter, setCounter] = useState(0)
    return(
        <counterContext.Provider value={{counter, setCounter}}>
            {children}
        </counterContext.Provider>
    )
}

export function useCounter() {
    return useContext(counterContext)
}

2 📁 main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import { CounterProvider } from "./context/counterContext.jsx";


ReactDOM.createRoot(document.getElementById("root")).render(
<CounterProvider>
<App />
</CounterProvider>);

3 📁 App.jsx

import styled from "styled-components";
import { useState } from "react";
import { CounterProvider, useCounter } from "./context/counterContext";

const Component = styled.div`
  font-weight: 700; border: 3px solid blue; border-radius: 10px;
  flex-grow: 1; line-height: 30px;
  text-align: center; padding: 10px; margin: 10px;
  > button {
    margin-left: 10px;
  }
`;

const Container = styled.div`
  display: flex;
  width: 100%;
  justify-content: center;
`;

export default function App() {
  console.log("App");
  return (
    <Container>
      <Component>
        App
        <Container>
          <Child1  />
          <Child2 />
        </Container>
      </Component>
    </Container>
  );
}

function Child1() {
  console.log("Child1");
  return (
    <Component>
      Child1
      <Container>
        <Child3/>
        <Child4 />
      </Container>
    </Component>
  );
}

function Child2() {
  console.log("Child2");
  return (
    <Component>
      Child2
      <Container>
        <Child5 />
        <Child6 />
      </Container>
    </Component>
  );
}

function Child3() {
  const { counter } = useCounter()
  console.log("Child3");
  return <Component>Child3 : {counter} </Component>;
}

function Child4() {
  console.log("Child4");
  return <Component>Child4</Component>;
}

function Child5() {
  console.log("Child5");
  return <Component>Child5</Component>;
}

function Child6() {
  const { setCounter } = useCounter()
  console.log("Child6");
  return (
    <Component>
      Child6
      <button
        onClick={() => {
          setCounter(prev => prev + 1);
        }}
      >
        +
      </button>
    </Component>
  );
}
  • Mount전체 컴포넌트 rendering
  • + 버튼 클릭 시 해당 상태가 포함된 <Child3>, <Child6> 만 Update 되며 rerendering



🔀 Context API 장단점

❇️ 장점

  • Props Drilling 해결 : 부모 → 자식 → 손자로 props를 계속 전달할 필요 없이 필요한 컴포넌트에서 바로 데이터 접근 가능
  • 전역 상태 관리가 간편함 : Redux, MobX 같은 외부 라이브러리 없이 React 내장 기능만으로 전역 상태를 관리할 수 있음.
  • 재사용성 향상: 여러 컴포넌트에서 동일한 상태를 쉽게 공유 가능.
    예를 들어, 다크 모드, 다국어 설정, 사용자 로그인 정보 등을 모든 컴포넌트에서 쉽게 사용 가능.

❎ 단점

  • 모든 컴포넌트가 리렌더링됨 → 최적화 필요
  • 상태가 복잡해지면 Redux 같은 라이브러리가 더 적합
    ✔️ 해결 방법: useMemo, useReducer를 함께 사용하면 성능 최적화 가능

✅ 결론: 언제 Context API를 사용할까?

소규모 프로젝트 & 간단한 상태 관리
✔ 전역 상태를 많이 변경하지 않는 경우 (ex: 테마, 언어 설정, 사용자 로그인 정보 등)
✔ Redux 같은 라이브러리를 쓰기엔 너무 무거울 때

➡ 결론: Context API는 간단한 전역 상태 공유에는 적합하지만, 상태가 복잡해지면 Redux 같은 라이브러리가 더 효율적

0개의 댓글