React 단방향 데이터 흐름과 State 끌어올리기(state Lifting)

Kingmo·2022년 4월 2일

React는 데이터가 부모에서 자식으로만 흐르는 단방향 데이터 흐름(One-way Data Flow)을 따른다.

이 구조는 데이터의 흐름을 예측하기 쉽게 만들어 주지만, 반대로 자식 컴포넌트에서 발생한 이벤트로 다른 형제 컴포넌트나 부모의 UI를 변경해야 할 때 제약이 생긴다.

이를 해결하기 위한 React의 기본 설계 패턴이 State 끌어올리기(Lifting State Up)이다.

단방향 데이터 흐름의 장점과 한계

  • 장점: 데이터는 항상 상위(부모)에서 하위(자식)로 props를 통해 전달된다. 버그가 발생했을 때 데이터가 어디서 변경되었는지 추적하기 쉽고 컴포넌트 간 결합도가 낮아진다.
  • 한계: 자식 컴포넌트 입장에서는 내려받은 props가 부모의 상태값(state)인지 단순 하드코딩된 값인지 알 수 없으며, 자식이 부모의 상태를 직접 수정할 수도 없다.

만약 두 개 이상의 자식 컴포넌트가 동일한 데이터를 공유하거나 동기화해야 한다면, 데이터의 소유권을 어디에 두어야 할지 문제가 발생한다.

State 끌어올리기(Lifting State Up)란?

동일한 상태를 공유해야 하는 여러 컴포넌트가 있다면, 그 상태를 공통의 가장 가까운 부모 컴포넌트로 끌어올려 관리하는 패턴이다.

부모 컴포넌트는 상태값(state)과 함께 상태를 변경하는 상태 변경 함수(setState)를 자식에게 props로 전달한다. 자식 컴포넌트는 이벤트 발생 시 전달받은 함수를 호출하여 부모의 상태를 변경하도록 요청(역방향 통신)한다.

코드 예시

1. 자식 컴포넌트들

// ChildA.jsx (상태를 화면에 보여주는 컴포넌트)
export function CounterViewer({ count }) {
  return <div>현재 카운트: {count}</div>;
}

// ChildB.jsx (상태 변경 이벤트를 발생시키는 컴포넌트)
export function CounterButton({ onIncrease }) {
  return <button onClick={onIncrease}>1 증가</button>;
}

2. 부모 컴포넌트 (상태 소유 및 제어)

// Parent.jsx
import { useState } from "react";
import { CounterViewer } from "./ChildA";
import { CounterButton } from "./ChildB";

export default function Parent() {
  // 공통으로 공유할 상태를 부모에서 선언
  const [count, setCount] = useState(0);

  const handleIncrease = () => {
    setCount((prev) => prev + 1);
  };

  return (
    <div>
      {/* 한 자식에게는 상태값을 읽기 전용으로 전달 */}
      <CounterViewer count={count} />

      {/* 다른 자식에게는 상태를 변경할 수 있는 함수를 전달 */}
      <CounterButton onIncrease={handleIncrease} />
    </div>
  );
}

주의할 점: Props Drilling

State 끌어올리기는 컴포넌트 계층이 얕을 때는 직관적이고 깔끔하다.

하지만 컴포넌트 트리가 깊어지면 상태와 핸들러 함수를 중간 컴포넌트들이 단순히 전달만 하는 Props Drilling 문제가 발생한다.

  • 상태를 실제로 쓰지 않는 중간 컴포넌트들까지 불필요한 props를 전달받아야 한다.
  • 컴포넌트 간 결합도가 높아져 리팩토링이 어려워진다.

이러한 깊은 구조에서는 상태 끌어올리기 대신 Context API나 전역 상태 관리 라이브러리를 도입해 상태를 전역 저장소에서 직접 주입받는 방식을 고려해야 한다.

profile
Developer

0개의 댓글