리액트의 생명주기, 상태 관리, 그리고 데이터 흐름

리액트는 단순히 화면을 그리는 도구를 넘어, 데이터의 변화를 효율적으로 감지하고 UI를 동적으로 업데이트하는 강력한 메커니즘을 가지고 있다. 오늘은 리액트 개발의 핵심인 컴포넌트 생명주기부터 데이터 관리의 두 축인 State와 Props, 그리고 컴포넌트 간의 통신 방식까지 상세히 정리해 본다.


1. 리액트의 핵심: 컴포넌트의 생명주기와 렌더링

리액트 컴포넌트는 브라우저에 나타나고(Mount), 업데이트되고(Update), 사라지는(Unmount) 특정 순서에 따라 호출되는 생명주기 메서드를 가진다.

  • 렌더링 과정: 리액트는 리액트 요소를 이용해 메모리상에 가상 DOM(Virtual DOM)을 생성한다. 이후 데이터 변경이 발생하면 이전 가상 DOM과 새로운 가상 DOM을 비교하여, 실제 변경된 부분만 실제 DOM에 반영한다.

  • render 메서드: 클래스 컴포넌트의 필수 메서드로, 단 하나의 리액트 요소를 반환하며 화면에 무엇을 그릴지 결정하는 설계도 역할을 한다.


2. 상태(State) vs 속성(Props): 데이터 관리의 두 축

리액트에서 데이터를 다루는 방식은 ‘누가 소유하느냐’에 따라 두 가지로 나뉜다.

  • 상태(State): 컴포넌트 내부에서 관리하며 사용자의 상호작용(타이핑, 클릭)이나 API 응답에 따라 동적으로 변할 수 있는 데이터이다.

  • 속성(Props): 부모 컴포넌트로부터 전달받는 데이터로, 컴포넌트 입장에서는 수정할 수 없는 읽기 전용(Immutable) 데이터이다.

  • 핵심 차이: Props는 정적인 설정값을 전달할 때, State는 변화하는 동적인 정보를 관리할 때 사용된다.


3. setState를 통한 상태 업데이트와 이벤트 핸들링

리액트에서 상태를 직접 변경(this.state.data = ...)하는 것은 엄격히 금지된다. 반드시 this.setState()를 사용해야 리액트가 변화를 감지하고 화면을 다시 그린다.

  • 이벤트 처리: 리액트는 브라우저의 기본 이벤트를 합성 이벤트(Synthetic Event)로 감싸서 처리한다. 예를 들어 입력창의 값이 바뀔 때 onChange 이벤트를 통해 setState를 호출하면 컴포넌트의 상태가 최신화된다.

  • 비동기적 갱신: setState는 성능 최적화를 위해 여러 상태 변경 요청을 일괄 처리(Batching)하므로, 호출 직후 상태가 즉각 반영되지 않을 수 있음을 염두에 두어야 한다.


4. 컴포넌트 간의 데이터 흐름: 하향식 전파와 역방향 통신

리액트의 데이터 흐름은 기본적으로 부모에서 자식으로 흐르는 하향식(Top-down) 단방향 흐름이다.

  • 하향식 흐름: 데이터(Props)는 부모에서 자식으로만 전달되어 데이터의 출처를 명확히 한다.

  • 역방향 통신(콜백 함수): 자식 컴포넌트(예: 입력창)의 사건을 부모(예: 목록)에게 알려야 할 때는 부모가 함수를 Props로 자식에게 전달한다. 자식은 이벤트 발생 시 이 함수를 호출(Callback)하여 부모의 상태를 변경시키고, 결과적으로 전체 UI가 다시 렌더링되도록 한다.


5. JSX: 더 직관적인 UI 작성 방식

기존 React.createElement 방식은 복잡한 UI를 표현하기에 가독성이 매우 떨어진다. 이를 보완하기 위해 등장한 것이 바로 JSX(JavaScript XML)이다.

  • 직관적인 구조: HTML과 유사한 구문을 자바스크립트 내부에서 사용하여 코드의 가독성을 획기적으로 높였다.

  • 선언적 프로그래밍: “화면이 어떻게 보여야 한다”는 결과값에 집중할 수 있게 해주어, 개발자가 일일이 DOM을 조작하는 수고를 덜어주고 비즈니스 로직에 더 집중하게 돕는다.


리액트 개발의 본질은 “상태를 어느 컴포넌트에 둘 것인가?” 그리고 “데이터를 어떻게 전달하고 소통할 것인가?”를 설계하는 데 있다. 이러한 구조적 이해는 프로젝트가 커질수록 유지보수가 쉽고 견고한 애플리케이션을 만드는 밑바탕이 된다. 데이터의 흐름을 장악하고 생명주기를 적절히 활용하는 능력이야말로 숙련된 리액트 개발자로 가는 지름길이다.

댓글 남기기