props 없이 깊은 컴포넌트까지 값을 전달하는 Context API.
Context로 전역 상태 공유
테마, 로그인 사용자, 언어 설정처럼 앱 곳곳에서 필요한 값을 props로 일일이 내려보내면 번거롭습니다. Context 는 이런 값을 트리 어디서든 꺼내 쓰게 해 줍니다.
학습 목표
- props drilling 문제가 무엇인지 안다.
createContext,Provider,useContext의 역할을 안다.- 테마 전환 예제를 직접 만들 수 있다.
- Context를 언제 쓰고 언제 피해야 하는지 판단할 수 있다.
props drilling 문제
중간 컴포넌트들은 값을 쓰지도 않으면서 그저 아래로 전달만 하는 경우가 생깁니다.
function App() {
const [theme, setTheme] = useState('light');
return <Page theme={theme} />;
}
function Page({ theme }) { // 안 쓰는데 받음
return <Toolbar theme={theme} />;
}
function Toolbar({ theme }) { // 또 안 쓰는데 받음
return <Button theme={theme} />;
}
function Button({ theme }) { // 여기서야 사용
return <button className={theme}>버튼</button>;
}
이렇게 props가 여러 층을 거쳐 전달되는 걸 props drilling 이라고 합니다. Context로 이 사슬을 끊을 수 있습니다.
createContext · Provider · useContext
3단계로 사용합니다.
import { createContext, useContext, useState } from 'react';
// 1) Context 생성 (기본값 지정 가능)
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
// 2) Provider로 감싸 값을 공급
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Button() {
// 3) useContext로 어디서든 꺼내 쓰기
const theme = useContext(ThemeContext);
return <button className={theme}>버튼</button>;
}
| 단계 | 함수 | 역할 |
|---|---|---|
| 생성 | createContext(기본값) | 통로를 만든다 |
| 공급 | <Context.Provider value={...}> | 값을 트리에 흘려보낸다 |
| 소비 | useContext(Context) | 가장 가까운 Provider 값을 읽는다 |
테마 전환 예제
값과 함께 변경 함수도 객체로 묶어 공급하면 어디서든 토글할 수 있습니다.
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggle = () => setTheme((t) => (t === 'light' ? 'dark' : 'light'));
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
function ThemeButton() {
const { theme, toggle } = useContext(ThemeContext);
return (
<button className={theme} onClick={toggle}>
현재 테마: {theme}
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemeButton />
</ThemeProvider>
);
}
💡 TIP — Provider 로직을
ThemeProvider컴포넌트로 따로 빼면App이 깔끔해지고 재사용도 쉬워집니다.
주의점
Context는 강력하지만 만능은 아닙니다.
- 리렌더 범위 — Provider의
value가 바뀌면 그 값을 쓰는 모든 소비자가 다시 렌더됩니다. 자주 바뀌는 큰 값은 분리하거나useMemo로 감싸세요.
// value가 매 렌더 새 객체 → 소비자 항상 리렌더
<ThemeContext.Provider value={{ theme, toggle }}>
// useMemo로 참조 안정화
const value = useMemo(() => ({ theme, toggle }), [theme]);
- 모든 걸 Context로 옮기지 말 것 — 한두 단계만 내려가는 props는 그냥 props가 낫습니다. Context는 "정말 전역적인" 값에만.
- Provider 밖에서 쓰면 기본값을 받습니다. 기본값을
null로 두고 커스텀 훅에서 검사하면 실수를 빨리 잡을 수 있습니다.
function useTheme() {
const ctx = useContext(ThemeContext);
if (ctx === null) throw new Error('ThemeProvider 안에서만 사용하세요');
return ctx;
}
⚠️ 주의 — Context는 "전역 상태 관리 도구"라기보다 "값 전달 통로"입니다. 복잡한 상태 로직은
useReducer와 함께 쓰거나 별도 상태 관리 라이브러리를 고려하세요.
요약
- props drilling: 중간 컴포넌트가 안 쓰는 props를 계속 전달하는 문제.
- Context는
createContext→Provider→useContext3단계로 쓴다. - 값과 변경 함수를 객체로 묶어 공급하면 전역 토글이 쉽다.
value가 바뀌면 모든 소비자가 리렌더 →useMemo로 안정화.- 한두 단계 전달은 그냥 props. 전역 값에만 Context.
연습문제
- 로그인한 사용자 정보(
{ name, role })를 공급하는UserContext를 만들고, 깊은 자식에서 이름을 표시하세요. - 위 테마 예제에
dark일 때 배경을 검게 만드는 CSS 클래스를 연결해 보세요. - Provider 밖에서
useContext를 호출하면 어떤 값이 나오는지 실험하고 설명하세요. value객체를useMemo로 감쌌을 때와 안 감쌌을 때 소비자 리렌더 차이를 확인하세요.
힌트 — 3번은
createContext의 기본값이 답입니다. 4번은 소비자에서console.log('render')로 확인.
💡 연습문제 풀이
불러오는 중…
댓글 0
“React.js” 강좌에 대한 댓글입니다.