생명주기(Lifecycle)라는 거대한 개념이 제시되자, 팀은 가장 시급하고 중요한 문제부터 해결하기로 했다. 바로 "컴포넌트가 화면에 나타난 직후, 무언가 하고 싶다"는 개발자들의 근본적인 요구사항이었다.
이 요구는 다양한 시나리오에서 발생했다.
<NewsNotifier>처럼, 화면에 나타난 순간부터 주기적인 작업을 시작해야 할 때.<UserProfile>처럼, 일단 기본적인 틀을 먼저 보여준 뒤, 상세 프로필 정보를 서버에 비동기적으로 요청해야 할 때.<Modal>처럼, 화면에 나타난 직후 특정 입력 필드에 자동으로 포커스를 맞춰주고 싶을 때.- jQuery 플러그인 같은 외부 라이브러리를 특정 DOM 요소에 적용해야 할 때.
이 모든 작업은 공통점을 가지고 있었다.
첫째, 이것들은 부작용(Side Effects)이다. render 메서드의 순수성을 해치는, 외부 세계와의 상호작용이다.
둘째, 이 작업들은 반드시 실제 DOM이 생성된 이후에 실행되어야 한다. DOM이 없는데 포커스를 맞출 수도, jQuery 플러그인을 붙일 수도 없기 때문이다.
조던 워크는 이 명확한 요구사항을 해결하기 위해, 생명주기 목록에서 가장 중요한 메서드 중 하나를 꺼내 들었다.
componentDidMount()
이 이름은 그 역할을 명확하게 설명하고 있었다.
- component: 이 메서드는 컴포넌트 인스턴스에 속한다.
- Did: 과거형 시제. 어떤 일이 ‘일어난 후’를 의미한다.
- Mount: ‘마운트하다’, 즉 컴포넌트가 생성한 버추얼 DOM이 실제 DOM에 성공적으로 삽입되어 화면에 나타나는 과정을 의미한다.
종합하면, componentDidMount는 "컴포넌트가 실제 DOM에 마운트된 직후, 리액트가 호출해주는 약속된 갈고리(Hook)"였다.
맷은 이 새로운 메서드를 사용하여 자신의 <NewsNotifier> 컴포넌트를 수정했다.
var NewsNotifier = React.createClass({
getInitialState: function () {
return { hasNewNotification: false };
},
// 컴포넌트가 실제 DOM에 성공적으로 렌더링된 '직후'에 호출된다.
componentDidMount: function () {
console.log('NewsNotifier가 방금 화면에 나타났습니다!');
// 이제 이곳에서 안전하게 타이머를 시작할 수 있다.
// this.timerID는 나중에 타이머를 정리하기 위해 인스턴스에 저장한다.
this.timerID = setInterval(this.checkForNotifications, 5000);
},
checkForNotifications: function () {
// ... 서버에 새로운 소식이 있는지 확인하는 로직 ...
// 만약 새 소식이 있다면, this.setState({ hasNewNotification: true }) 호출
},
render: function () {
// ... 이전과 동일한 render 코드 ...
},
});
코드는 아름답게 분리되었다.
render는 오직 현재 상태를 보고 무엇을 그릴지만 순수하게 정의한다.componentDidMount는 컴포넌트의 렌더링 결과가 실제 DOM에 반영된 것을 확인한 후, 타이머를 설정하는 ‘부작용’을 책임진다.
이제 render가 몇 번 호출되든 상관없었다. componentDidMount는 컴포넌트의 생애 동안 단 한 번, 성공적으로 화면에 나타난 그 결정적인 순간에만 실행될 것이기 때문이다.
이 메서드의 도입은 리액트의 세계와 외부 세계(브라우저 API, 서버, 다른 라이브러리)를 잇는 공식적인 첫 번째 다리가 되었다. 개발자들은 더 이상 부작용을 일으키는 코드를 어디에 둬야 할지 고민하며 편법을 쓰지 않아도 되었다.
리액트는 componentDidMount라는 안전하고 예측 가능한 공간을 제공함으로써, 개발자들이 안심하고 외부 세계와 소통할 수 있는 길을 열어주었다. 이것은 리액트가 단순한 렌더링 엔진을 넘어, 완전한 애플리케이션을 구축할 수 있는 견고한 프레임워크로 진화하는 중요한 한 걸음이었다.


