모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

13강 / 전체 30강

상태 끌어올리기

5분 읽기 조회 5

상태 끌어올리기 패턴으로 형제 컴포넌트 간 상태를 공유하는 방법과 컴포넌트 합성(Composition) 패턴의 실전 활용을 배웁니다.

🤝 왜 상태를 끌어올려야 하는가

두 컴포넌트가 동일한 데이터를 공유해야 할 때 어떻게 해야 할까요? 리액트에서 데이터는 항상 위에서 아래로(부모 → 자식) 흐릅니다. 형제 컴포넌트끼리 직접 데이터를 주고받을 수 없습니다. 이 문제를 해결하는 패턴이 상태 끌어올리기(Lifting State Up)입니다.

핵심 아이디어: 두 컴포넌트가 공유해야 하는 상태를 가장 가까운 공통 부모로 이동시킵니다.

📌 문제 상황 — 온도 변환기

섭씨와 화씨 입력창이 있고, 하나를 바꾸면 다른 것도 자동으로 업데이트됩니다:

// ❌ 각자 state를 가지면 동기화 불가
function CelsiusInput() {
  const [value, setValue] = useState('')
  return <input value={value} onChange={(e) => setValue(e.target.value)} />
}

function FahrenheitInput() {
  const [value, setValue] = useState('')  // 완전히 별개의 상태!
  return <input value={value} onChange={(e) => setValue(e.target.value)} />
}

이렇게 만들면 두 입력창의 값이 서로 연동되지 않습니다. 해결책은 상태를 부모로 끌어올리는 것입니다.

✅ 해결 — 상태를 공통 부모로 이동

// 자식 컴포넌트들: 값과 콜백을 Props로 받음
function TemperatureInput({
  label,
  value,
  onChange,
}: {
  label: string
  value: string
  onChange: (val: string) => void
}) {
  return (
    <label className="block">
      <span className="text-sm font-medium">{label}</span>
      <input
        type="number"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className="mt-1 block w-full border rounded px-3 py-2"
      />
    </label>
  )
}

// 부모: 상태를 가지고 두 자식에 내려줌
function TemperatureConverter() {
  const [celsius, setCelsius] = useState('')

  const fahrenheit = celsius !== ''
    ? String(Math.round(parseFloat(celsius) * 9 / 5 + 32))
    : ''

  function handleCelsiusChange(val: string) {
    setCelsius(val)
  }

  function handleFahrenheitChange(val: string) {
    const f = parseFloat(val)
    if (!isNaN(f)) {
      setCelsius(String(Math.round((f - 32) * 5 / 9)))
    } else {
      setCelsius('')
    }
  }

  return (
    <div className="space-y-4 p-6 max-w-sm">
      <h2 className="text-xl font-bold">온도 변환기</h2>
      <TemperatureInput label="섭씨 (°C)" value={celsius} onChange={handleCelsiusChange} />
      <TemperatureInput label="화씨 (°F)" value={fahrenheit} onChange={handleFahrenheitChange} />
    </div>
  )
}

이제 섭씨 입력창을 바꾸면 부모의 celsius state가 업데이트되고, fahrenheit 값이 자동으로 계산되어 두 번째 입력창에 반영됩니다.

🧩 컴포넌트 합성 (Composition)

상태 끌어올리기와 함께 중요한 패턴이 합성(Composition)입니다. children이나 render props를 활용해 컴포넌트를 유연하게 조합합니다.

// 레이아웃 컴포넌트 — children으로 내용을 유연하게 받음
function Panel({
  title,
  children,
  action,
}: {
  title: string
  children: React.ReactNode
  action?: React.ReactNode  // 헤더 오른쪽에 올 요소 (선택)
}) {
  return (
    <div className="rounded-2xl border border-gray-100 overflow-hidden">
      <div className="flex items-center justify-between px-6 py-4 border-b bg-gray-50">
        <h3 className="font-bold text-gray-900">{title}</h3>
        {action}
      </div>
      <div className="p-6">{children}</div>
    </div>
  )
}

// 사용: 완전히 다른 내용을 같은 Panel에 담을 수 있음
function Dashboard() {
  return (
    <div className="space-y-4">
      <Panel title="최근 주문" action={<button className="text-sm text-indigo-600">전체 보기</button>}>
        <OrderList />
      </Panel>

      <Panel title="알림" action={<span className="text-xs bg-red-100 text-red-600 px-2 py-0.5 rounded-full">3개 미읽음</span>}>
        <NotificationList />
      </Panel>
    </div>
  )
}

🗂️ 언제 상태를 끌어올릴까

항상 가장 가까운 공통 부모까지만 올립니다. 불필요하게 높이 올리면 prop drilling(깊은 곳까지 props를 전달하는 것)이 생깁니다. 규칙:

  • 형제 컴포넌트가 같은 데이터를 필요로 할 때 → 부모로 끌어올리기
  • 한 컴포넌트만 사용하는 상태 → 그 컴포넌트 안에 유지
  • 많은 컴포넌트가 공유해야 할 때 → Context(뒤에서 다룸)

✅ 정리

  • 리액트 데이터는 위→아래 단방향. 형제 컴포넌트는 직접 통신 불가.
  • 공유 상태는 가장 가까운 공통 부모로 끌어올립니다.
  • 자식은 값(Props)과 콜백(함수 Props)을 받아 표시하고 변경 요청만 합니다.
  • children과 선택적 Props(action?)로 유연한 합성 컴포넌트를 만듭니다.

관련 주제

  • lifting state up
  • 공통 부모 컴포넌트
  • 콜백으로 상태 변경 전달
  • 형제 컴포넌트 상태 공유
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 리액트 입문 — 컴포넌트로 만드는 웹
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

리액트 입문 — 컴포넌트로 만드는 웹

이 강의가 속한 시리즈는 총 30강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...