프로그래밍/ReactJS

🚀 React 개발자 필수 개념: "상태 끌어올리기" 완전 정복

shimdh 2025. 9. 23. 09:20
728x90

안녕하세요, React 개발자 여러분! 오늘 함께 알아볼 주제는 바로 React에서 여러 컴포넌트 간에 공유되는 상태를 관리하는 핵심 개념, "상태 끌어올리기(Lifting state up)" 입니다. 이 개념을 제대로 이해하고 활용하는 것은 복잡한 React 애플리케이션을 효율적으로 구축하고 유지보수하는 데 있어 필수적인 역량이라고 할 수 있습니다.


상태 끌어올리기란 무엇인가요?

우리는 많은 애플리케이션에서 여러 컴포넌트가 동일한 데이터에 접근하고 조작해야 하는 시나리오를 마주하게 됩니다. 각 컴포넌트에서 별도의 상태를 독립적으로 유지하는 대신, "상태 끌어올리기" 는 이러한 공유 상태를 모든 컴포넌트의 "공통 조상 컴포넌트"로 이동시키는 것을 의미합니다.

이러한 접근 방식은 단순히 코드를 옮기는 것을 넘어, 더 나은 코드 구성과 일관성을 촉진하며, 무엇보다 중요한 데이터 불일치 문제를 방지하는 데 크게 기여합니다.


728x90

왜 상태를 끌어올려야 할까요?

상태 끌어올리기는 다음과 같은 여러 가지 중요한 이점을 제공합니다.

1. 중앙 집중식 데이터 관리

상태를 한 곳, 즉 부모 컴포넌트에 유지함으로써 데이터 관리와 디버깅이 훨씬 용이해집니다. 애플리케이션의 복잡도가 증가할수록 이 이점은 더욱 두드러집니다. 데이터의 흐름을 한눈에 파악하고, 어디서 데이터가 변경되고 있는지 추적하기 쉬워집니다.

2. 컴포넌트 전반의 일관성

여러 자식 컴포넌트가 동일한 데이터에 의존할 때, 상태 끌어올리기는 모든 컴포넌트가 데이터 변경 사항을 일관되게 반영하도록 보장합니다. 이는 사용자 경험(UX) 측면에서 매우 중요합니다. 예를 들어, 한 곳에서 데이터를 변경했을 때 다른 관련 UI 요소들이 즉시 업데이트되지 않는다면 사용자에게 혼란을 줄 수 있습니다.

3. 단순화된 컴포넌트 구조

상태를 부모 컴포넌트로 끌어올리면, 여러 계층의 컴포넌트를 통해 props를 전달해야 하는 이른바 'prop drilling'을 피할 수 있습니다. 이는 코드의 복잡성을 줄이고 가독성을 향상시키는 데 기여합니다. 특히 대규모 애플리케이션에서 코드 유지보수성을 크게 높여줍니다.


실질적인 예시: 온도 변환기

상태 끌어올리기가 어떻게 작동하는지 이해하기 위해 구체적인 예시를 살펴보겠습니다. 섭씨 온도를 입력하는 TemperatureInput 컴포넌트와 이 값을 화씨로 변환하여 표시하는 TemperatureDisplay 컴포넌트라는 두 개의 형제 컴포넌트를 가진 애플리케이션을 상상해 봅시다.

상태 끌어올리기 없는 초기 설정

만약 각 형제 컴포넌트가 자체적으로 온도를 독립적으로 관리한다면 어떤 문제가 발생할까요?

function TemperatureInput() {
    const [celsius, setCelsius] = useState(0); // 섭씨 온도 상태 독립 관리

    return (
        <input
            type="number"
            value={celsius}
            onChange={(e) => setCelsius(e.target.value)}
        />
    );
}

function TemperatureDisplay() {
    const [fahrenheit, setFahrenheit] = useState(32); // 화씨 온도 상태 독립 관리 (입력과 독립적)
    return <h2>{fahrenheit} °F</h2>;
}

이 설정에서는 사용자가 TemperatureInput에 값을 입력하더라도 TemperatureDisplay는 자체적인 독립 상태를 가지고 있기 때문에 그 변화에 반응할 방법이 없습니다. 결과적으로 두 컴포넌트 간의 데이터 동기화 문제가 발생하며, 이는 사용자가 원하는 동작이 아닙니다.

상태 끌어올리기 구현

이 문제를 해결하기 위해, 우리는 온도 상태를 공통 부모 컴포넌트인 App으로 끌어올려 두 자식 컴포넌트가 공유할 수 있도록 할 수 있습니다.

function App() {
    // 공유 온도 상태를 App 컴포넌트에서 관리
    const [temperature, setTemperature] = useState({ celsius: 0 });

    // 섭씨 온도 변경을 처리하는 함수
    const handleCelsiusChange = (value) => {
        setTemperature({ celsius: value });
    };

    return (
        <div>
            {/* TemperatureInput에 현재 섭씨 온도와 변경 함수를 props로 전달 */}
            <TemperatureInput
                celsius={temperature.celsius}
                onCelsiusChange={handleCelsiusChange}
            />
            {/* TemperatureDisplay에 변환된 화씨 온도를 props로 전달 */}
            <TemperatureDisplay fahrenheit={(temperature.celsius * 9/5) + 32} />
        </div>
    );
}

function TemperatureInput({ celsius, onCelsiusChange }) {
    // 부모로부터 받은 celsius 값과 변경 함수를 사용
    return (
        <input
            type="number"
            value={celsius}
            onChange={(e) => onCelsiusChange(e.target.value)}
        />
    );
}

function TemperatureDisplay({ fahrenheit }) {
    // 부모로부터 받은 fahrenheit 값을 사용
    return <h2>{fahrenheit.toFixed(2)} °F</h2>;
}

이 예시에서 App 컴포넌트는 공유 온도 상태를 유지합니다. handleCelsiusChange 함수는 사용자가 TemperatureInput과 상호작용할 때 중앙 온도 상태를 업데이트합니다. 중요한 것은 두 자식 컴포넌트(TemperatureInput, TemperatureDisplay) 모두 부모(App)로부터 필요한 데이터를 props로 받는다는 점입니다.

이러한 구조는 한 컴포넌트에서 이루어진 변경 사항이 해당 데이터에 의존하는 다른 모든 컴포넌트에 즉시 반영되도록 보장합니다. 이는 동적인 UI를 구축하는 데 필수적이며, 애플리케이션의 데이터 흐름을 예측 가능하고 관리하기 쉽게 만듭니다.


결론

상태 끌어올리기는 공유 정보에 접근해야 하는 React 컴포넌트 간의 효과적인 통신에 매우 중요한 개념입니다. 이러한 방식으로 애플리케이션을 구조화함으로써 얻을 수 있는 이점은 다음과 같습니다.

  • 유지보수성 향상: 모든 관련 로직이 단일 위치에 있어 디버깅 프로세스가 단순화되고, 코드 수정이 용이해집니다.
  • UI 전반의 일관된 동작 보장: 데이터 불일치로 인한 예기치 않은 UI 동작을 방지하고 사용자 경험을 개선합니다.
  • 코드 가독성 및 재사용성 증가: 컴포넌트 간의 의존성을 명확하게 하고, 각각의 컴포넌트가 하나의 명확한 책임을 가지도록 돕습니다.

상태를 끌어올리는 방법과 시기를 이해하는 것은 React를 사용하여 복잡한 애플리케이션을 효과적으로 구축하는 능력을 크게 향상시킬 것입니다. 이 개념을 마스터하는 것은 React 개발자로서의 역량을 강화하는 중요한 단계이니, 여러분의 프로젝트에 적극적으로 적용해 보시길 권장합니다!

728x90