props 드릴링 Context 개념 3단계 사용법 언제 쓰나 상태관리 대안 📝 문제풀이
◀ 이전 📋 목차 CH 11 ▶
🌐 CHAPTER 10 · 중급

Context — props 없이 값을 공유하기

깊은 곳에 있는 자식에게 값을 주려고 중간 컴포넌트들이 필요도 없는 props를 계속 넘기던 번거로움, 이제 끝낼 수 있어요. 이번 장은 props 드릴링 문제 → Context로 해결하는 흐름을 아주 천천히 따라갑니다.

🎯 이 장을 끝내면
🪜
문제 상황
props 드릴링 — 값 하나 넘기려다 지치는 이유
먼저 "왜 불편한가"부터 몸으로 느껴봐요.

지금까지 데이터는 부모 → 자식 한 방향으로 props를 통해 내려줬어요(CH 05). 그런데 만약 데이터를 아주 깊은 곳에 있는 자식이 필요로 한다면 어떻게 될까요? 중간에 있는 컴포넌트들은 자기는 그 값을 쓰지도 않으면서, 오직 아래로 전달만 하려고 props를 계속 받아 넘겨야 해요.

이렇게 중간 컴포넌트들이 필요도 없는 props를 단지 전달만 하려고 줄줄이 넘기는 현상props 드릴링(props drilling)이라고 불러요. "드릴로 층층이 뚫고 내려간다"는 느낌의 이름이에요.

🏠
Appuser 값 보유
여기서 user라는 값을 만들었어요.
Page그냥 전달
Page는 user를 안 쓰는데도 받아서 아래로 넘겨요.
Layout그냥 전달
Layout도 안 쓰는데도 또 받아서 넘겨요.
Avataruser 실제 사용
드디어 여기서 user를 써요. 진짜 필요한 곳은 여기뿐!
📦 택배 비유로 보면 AppAvatar에게 소포(user)를 보내고 싶어요. 그런데 직접 못 주고 Page → Layout을 거쳐야 해요. Page와 Layout은 소포 내용물이 필요 없는데도 "나 몰라, 그냥 아래로 전달만 할게" 하며 계속 손에서 손으로 넘겨야 하죠. 전달 단계가 3~4겹만 돼도 코드가 지저분해지고, 중간에 props 이름 하나 바꾸면 여러 파일을 다 고쳐야 해요.
🧭
핵심 통증. "중간 컴포넌트가 쓰지도 않는 props를 전달만 하려고 계속 받아 넘긴다" — 이게 props 드릴링이고, 이 번거로움을 없애려는 게 바로 Context예요.
🌐
Context 개념
중간 단계를 건너뛰는 "전역 통로"
위에서 아래로 층층이 넘기지 말고, 통로 하나 뚫어두자!

Context(컨텍스트)는 여러 컴포넌트가 함께 쓰는 값을 "전역 통로"로 만들어 두는 리액트 기능이에요. 통로에 값을 한 번 올려두면, 중간 단계를 일일이 거치지 않고 필요한 자식이 통로에서 바로 꺼내 쓸 수 있어요.

🏠
App (통로에 올림)
공유할 값을 Context에 넣어요
🌐
Context 통로
중간 단계 건너뜀
🙆
Avatar (바로 꺼냄)
필요한 자식이 직접 읽어요
🆚 props 드릴링 vs Context🟪 리액트 props 드릴링 — App → Page → Layout → Avatar, 한 층씩 손에서 손으로 넘김. 중간 컴포넌트가 전부 관여.
Context — App이 통로에 값을 올려두면, Avatar가 Page·Layout을 건너뛰고 통로에서 바로 읽음. 중간 컴포넌트는 아무것도 몰라도 됨. 비유하면 층층이 계단으로 물건 나르기(드릴링) vs. 건물 전체에 깔린 수도관에서 필요한 층이 수도꼭지만 열어 쓰기(Context).
💡
한 줄 정리. Context = "여러 곳에서 공유하는 값을 전역 통로에 올려두고, 중간을 건너뛰어 바로 꺼내 쓰는 방법."
🧩
3단계 사용법
만들고 · 감싸고 · 꺼내기🟪 리액트
딱 세 조각이면 돼요. 하나씩 볼게요.
1
① 통로 만들기 — createContext
createContext(기본값)로 Context 객체를 하나 만들어요. 이게 "전역 통로"예요. 괄호 안의 값은 Provider로 감싸지 않았을 때 쓰이는 기본값이에요.
2
② 통로에 값 올리고 감싸기 — <Context.Provider>
<ThemeContext.Provider value={공유값}>로 자식들을 감싸요. value에 넣은 값이 통로에 흐르고, 이 Provider 안쪽에 있는 모든 자손이 그 값을 읽을 수 있게 돼요.
3
③ 자식에서 꺼내 읽기 — useContext
값이 필요한 자식에서 const v = useContext(ThemeContext)라고 쓰면, 통로에 흐르던 value 값을 바로 꺼내 써요. props로 안 받아도 돼요!

이제 세 단계를 하나의 예제로 이어볼게요. "테마(다크/라이트)" 값을 공유하는 흔한 예예요.

import { createContext, useContext } from "react";

// ① 통로 만들기 (기본값 "light")
const ThemeContext = createContext("light");

function App() {
  // ② Provider로 감싸고, value에 공유할 값을 올림
  return (
    <ThemeContext.Provider value="dark">
      <Page />
    </ThemeContext.Provider>
  );
}

// 중간 컴포넌트들은 theme를 몰라도 됨 (props 전달 불필요!)
function Page() {
  return <Layout />;
}
function Layout() {
  return <Avatar />;
}

// ③ 필요한 자식이 통로에서 바로 꺼내 읽음
function Avatar() {
  const theme = useContext(ThemeContext); // "dark"
  return <p>현재 테마: {theme}</p>;
}
🔎 세 조각의 역할 한눈에🟪 리액트 createContext(기본값) — 통로(Context 객체)를 만든다. (딱 한 번)
<Context.Provider value={값}> — 통로에 값을 흘려보내고, 그 값을 받을 자식들을 감싼다.
useContext(Context) — 자식에서 통로의 값을 꺼내 읽는 훅. 기억법: 만들고(create) → 감싸고(Provider) → 꺼낸다(useContext).
🪝
자식이 값을 읽을 때 쓰는 훅은 useContext 하나예요. const 값 = useContext(만든Context객체) 형태로 쓰고, 인자로는 createContext로 만든 그 객체를 넘겨요. 문자열 이름이 아니라 객체 자체를 넘긴다는 점에 주의!
언제 쓰나
Context가 잘 맞는 곳 & 조심할 곳
좋은 도구지만, 아무 데나 쓰면 오히려 독이 돼요.

Context는 "앱의 아주 많은 곳에서 필요로 하는 전역적인 값"에 잘 맞아요. 대표적인 예를 볼게요.

잘 맞는 예왜 Context가 어울리나
🎨 테마(다크/라이트)거의 모든 컴포넌트가 색·스타일을 위해 테마를 알아야 해서.
👤 로그인 사용자 정보헤더·프로필·설정 등 여러 곳에서 현재 사용자를 참조해서.
🌍 언어 설정(i18n)모든 텍스트가 선택된 언어를 알아야 번역돼서. (i18n = internationalization의 약자, "국제화")
⚠️ 남용 주의 — 모든 걸 Context로 하지 마세요 한두 단계만 내려가면 되는 값은 그냥 props로 넘기는 게 더 단순해요. 또 Provider의 value가 바뀌면 그 값을 읽는 모든 컴포넌트가 다시 렌더링돼요. 자주 바뀌는 값을 큰 통로에 몽땅 담으면 리렌더가 과하게 일어나 성능이 나빠질 수 있어요. 판단 기준: "정말 많은 곳에서 공유하고, 자주 안 바뀌는 전역 값"일 때만 Context. 아니면 props로 충분해요.
🔤
렌더링 / 리렌더링 — 리액트가 컴포넌트를 실행해 화면을 그려내는 것이 "렌더링"이고, 값이 바뀌어 화면을 다시 그리는 것이 "리렌더링(리렌더)"이에요. 리렌더가 너무 자주·너무 많이 일어나면 화면이 버벅일 수 있어서, 위처럼 넓은 Context에 자주 바뀌는 값을 담는 걸 조심하는 거예요.
🚨
흔한 오해. "props 드릴링이 조금이라도 보이면 무조건 Context" — 아니에요. 2~3단계 얕은 전달은 props가 오히려 읽기 쉽고, Context는 정말 넓게 공유되는 값에 아껴 쓰세요.
🗂️
상태관리 대안
Context vs Zustand · Redux
Context의 "위치"를 큰 그림에서 짚어요.

전역 값을 다루는 방법에는 Context 말고도 Zustand · Redux 같은 상태관리 라이브러리가 있어요(CH 01 생태계에서 잠깐 봤죠). Context는 리액트에 기본 내장되어 있고 가벼운 대신, 복잡하고 자주 바뀌는 대형 상태를 다루기엔 기능이 단출해요.

🪶
Context (내장·가벼움)
설치 필요 없음. 테마·로그인·언어처럼 자주 안 바뀌는 가벼운 전역 값에 딱. 작은 앱에 먼저 이걸로.
🏗️
Zustand / Redux (라이브러리)
따로 설치. 크고 복잡하고 자주 바뀌는 상태를 리렌더 최소화·디버깅 도구까지 갖춰 효율적으로 다뤄요.
📐 이렇게 나눠 생각하면 편해요 Context — 리액트 내장, 설치 불필요, 가벼운 전역 값(테마·로그인·언어)에 딱. 자주 안 바뀌는 값에 강함.
Zustand / Redux — 별도 라이브러리. 크고 복잡하고 자주 바뀌는 상태를 효율적으로(리렌더 최소화·디버깅 도구 등) 다룰 때 유리. 한 줄로: Context는 상태관리 라이브러리의 "가벼운 대안" 자리예요. 작은 앱엔 Context, 규모가 커지면 Zustand/Redux를 고민하면 됩니다.
🧭
지금 단계 결론. "전역 값 공유가 필요하면 먼저 Context로 충분한지 보고, 규모가 커져 벅차면 상태관리 라이브러리를 고려한다." 이 정도 감만 챙기면 돼요.
🧠 이 장 핵심 요약
📝
문제풀이 · 점검
배운 걸 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 네 가지 유형(객관식 · O/X · 빈칸 · 개념결과)을 섞어 두었어요.
CH 11 🪝 커스텀 훅 — 반복되는 로직을 나만의 훅으로 묶기