티스토리 뷰

🔎 useContext

리액트로 만들어진 웹은 여러 개의 컴포넌트들로 이루어져 있습니다.

리액트에서의 일반적인 데이터 흐름은 부모 컴포넌트에서 자식 컴포넌트로 prop로 데이터를 전달합니다.

하지만 이렇게 부모에서 자식으로 또 다시 자식으로 props를 전달하다가 보면 depth는 굉장히 깊어지고

이에 따라 그 데이터를 추적하기 매우 어려워진다는 치명적인 단점이 생기게 됩니다.

 

또 그리고 전역적으로 사용하는 정보(ex. 사용자 정보, 테마, 언어 등)를 props로 단계별로 일일이 전달해야 한다면 깊이가 깊어지는 것는 물론이고 코드의 복잡도, 변경 시에 데이터를 추적하는 것이 매우 힘들 것입니다.

 

그래서 리액트에서는 이러한 문제를 해결하기 위해 Context API를 제공하고 있습니다.

 

❓ Context

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

Context를 사용하면 일일이 props로 전달해 주지 않아도 해당 데이터를 가지고 있는 상위 컴포넌트가 하위 컴포넌트들에게 "필요한 사람~?" 하고 알려줄 수 있습니다.

하위 컴포넌트들은 트리 어디에 위치하고 있든 "나 그 데이터 사용할래" 하면 해당 값에 접근할 수 있습니다.

그래서 사용자 정보, 테마 등과 같이 수많은 컴포넌트들에서 사용할 전역적인 데이터를 전달하기에 굉장히 편리합니다.

 

context를 사용하면 중간 컴포넌트를 거치지 않아도 어떤 자식 컴포넌트더라도 상위 컴포넌트가 가지고 있는 전역 데이터를 useContext를 통해 사용할 수 있습니다.

 

❓ 아니 그러면 이제 props는 쓸모가 없는건가 → Context는 꼭 필요할 때만..

Context를 사용하면 컴포넌트를 재사용하기 어려워질 수 있습니다.

Context의 주된 목적은 다양한 레벨에 있는 많은 컴포넌트들에게 전역적인 데이터를 전달하기 위함입니다.

React 공식 문서에는 Context를 사용하는 이유가 단순히 Prop drilling을 피하기 위한 목적이라면 Component Composition(컴포넌트 합성)이 더 간단한 해결책이라고 제시하고 있습니다.

 

📌 useContext 예제

Home 컴포넌트에는 Page라는 하위 컴포넌트가 있고

다시 또 Page 컴포넌트에는 3개의 하위 컴포넌트 Header, Content, Footer 가 존재합니다.

 

📄 Home.js 파일

import {useState} from 'react';
import Page from './Page';

function ContextHome() {
  const [isDark, setIsDark] = useState(false);
  return <Page isDark={isDark} setIsDark={setIsDark} />;
}
export default ContextHome;

 

📄 Page.js 파일

import Content from './Content';
import Footer from './Footer';
import Header from './Header';

function Page({isDark, setIsDark}) {
  return (
    <div className="page">
      <Header isDark={isDark} />
      <Content isDark={isDark} />
      <Footer isDark={isDark} setIsDark={setIsDark} />
    </div>
  );
}
export default Page;

 

📄 Header.js 파일

function Header({isDark}) {
  return (
    <header
      className="header"
      style={{
        backgroundColor: isDark ? 'black' : 'lightgray',
        color: isDark ? 'white' : 'black',
      }}
    >
      <h1>Welcome React!</h1>
    </header>
  );
}
export default Header;

 

📄 Content.js 파일

function Content({isDark}) {
  return (
    <div
      className="content"
      style={{
        backgroundColor: isDark ? 'black' : 'white',
        color: isDark ? 'white' : 'black',
      }}
    >
      <p>좋은 하루 되세요</p>
    </div>
  );
}
export default Content;

 

📄 Footer.js 파일

function Footer({isDark, setIsDark}) {
  const toggleTheme = () => {
    setIsDark(!isDark);
  };

  return (
    <footer
      className="footer"
      style={{
        backgroundColor: isDark ? 'black' : 'lightgray',
      }}
    >
      <button className="button" onClick={toggleTheme}>
        Dark Mode
      </button>
    </footer>
  );
}
export default Footer;

Footer에 있는 버튼을 누르게 되면 다크모드/라이트모드가 되도록 코드를 작성했습니다.

실행화면

위 코드에서 눈여겨 볼 점은 Home 컴포넌트에서 isDark라는 state는 우리 웹에 전체적인 테마와 관련된 정보를 담고 있기 때문에 전역적이라고도 할 수 있습니다.

우리 웹이 굉장히 컸다면 이 isDark라는 state를 모든 컴포넌트에게 전달해줘야 할지도 모릅니다.

 

또 눈여겨 볼 점은 Home 컴포넌트에서 Page 컴포넌트로 isDark와 setIsDark를 props로 넘겨주었는데

실질적으로 Page 컴포넌트에서는 isDark라는 state를 사용하지는 않고 다시 또 그 하위 컴포넌트들에게 전달만 했습니다.

그렇기 때문에 Page 컴포넌트는 isDark라는 정보를 필요로 하지 않는 중간 컴포넌트로 볼 수 있습니다.

 

위 코드에서는 컴포넌트가 4개뿐이라 중간 컴포넌트가 있는 것이 그렇게 복잡해 보이지 않을 수 있지만

중간 컴포넌트가 페이지 1개 뿐만 아니라 10개 20개 된다고 한다면 굉장히,,,,네.. 그렇습니다.

 

그래서 이번에는 props를 사용하지 않고 하위 컴포넌트들에게 isDark와 setIsDark를 공유해 보겠습니다.

그러면 중간 컴포넌트였던 Page 컴포넌트는 isDark를 전혀 몰라도 됩니다.

 

먼저 전역으로 담아놓고 사용할 저장소(store)를 하나 선언해 줍니다.

여기에 데이터를 담아놓고 전역에서 사용하려고 합니다.

 

📄 Context/ThemeContext.js 파일

import { createContext } from 'react';

export const ThemeContext = createContext(); // 저장소 하나

 

그리고 이제 최상위 컴포넌트에서 이 값을 가지고 있다면 이제 useContext를 통해 어디에서든 하위 컴포넌트가 해당 저장소에 들어있는 값을 사용할 수 있게 됩니다.

 

📄 Home.js 파일

import { useState } from 'react';
import Page from './Page';
import { ThemeContext } from './Context/ThemeContext';

function ContextHome() {
  const [isDark, setIsDark] = useState(false);
  return(
    <ThemeContext.Provider value={{isDark, setIsDark}}>
      <Page />
    </ThemeContext.Provider>
  );
}
export default ContextHome;

이제 Home 컴포넌트에서 좀전에 선언해둔 하나의 저장소인 ThemeContext를 import 해와서 Proivder와 함께 최상위 엘리먼트로 작성해 줍니다.

또한 이때 인자로 있는 value는 우리가 만든 저장소에 담아놓을 데이터라고 생각하면 됩니다.

추가로 현재 value를 배열 형태로 넘겨주었기 때문에 하위 컴포넌트 어디에서는 구조분해 할당으로 isDark와 setIsDark를 얼마든지 접근할 수 있게 됩니다.

즉, props 없이 얼마든지 값을 사용할 수 있다는 말과 같습니다.

 

📄 Page.js 파일

import Content from './Content';
import Footer from './Footer';
import Header from './Header';

function Page() {
  return (
    <div className="page">
      <Header />
      <Content />
      <Footer />
    </div>
  );
}
export default Page;

isDark를 몰라도 되었었던 중간 컴포넌트인 Page에도 깔끔하게 코드가 정리된 것을 확인할 수 있습니다.

 

📄 Header.js 파일

import {useContext} from 'react';
import {ThemeContext} from './Context/ThemeContext';

function Header() {
  const {isDark} = useContext(ThemeContext);

  return (
    <header
      className="header"
      style={{
        backgroundColor: isDark ? 'black' : 'lightgray',
        color: isDark ? 'white' : 'black',
      }}
    >
      <h1>Welcome React!</h1>
    </header>
  );
}
export default Header;

Header 컴포넌트 역시 props로 받는 값 없이 useContext를 통해 isDark에 접근하는 것을 확인할 수 있습니다. 

Content와 Footer도 동일하게 useContext를 사용해서 전역 상태에 접근해서 사용하면 됩니다.

728x90
LIST
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함