리액트는 단순히 화면을 그리는 도구를 넘어, 데이터의 변화를 효율적으로 감지하고 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을 조작하는 수고를 덜어주고 비즈니스 로직에 더 집중하게 돕는다.
리액트 개발의 본질은 “상태를 어느 컴포넌트에 둘 것인가?” 그리고 “데이터를 어떻게 전달하고 소통할 것인가?”를 설계하는 데 있다. 이러한 구조적 이해는 프로젝트가 커질수록 유지보수가 쉽고 견고한 애플리케이션을 만드는 밑바탕이 된다. 데이터의 흐름을 장악하고 생명주기를 적절히 활용하는 능력이야말로 숙련된 리액트 개발자로 가는 지름길이다.