리액트를 처음 배우기 시작하면, 코드가 언제 실행이 되는 건지, 렌더링은 왜 여러 번 일어나는 건지 이해하기가 어렵게 느껴지기도 합니다. 그래서 리액트 컴포넌트의 생성되고, 업데이트되고, 사라지기까지의 흐름을 정리해 보겠습니다.

(이미지 출처 - 멋쟁이사자처럼 웹 프론트엔드 부트캠프 15기, GD쌤 강의자료)
컴포넌트의 생명주기는 마운팅, 업데이팅, 언마운팅으로 세 부분으로 나눠볼 수 있습니다.
→ 처음에 이 설명을 듣고 무슨 말인지 잘 이해가 가지 않았습니다. 이 과정은 클래스 컴포넌트의 경우에 해당이 됩니다. 클래스 컴포넌트는 React.Component의 상속을 받아 만들어지는데, 그러므로 클래스 컴포넌트의 부모 클래스는 React.Component인 것입니다. super(props)를 호출한다는 것은 부모 클래스인 React.Component의 생성자를 먼저 실행하겠다는 뜻입니다. 이 과정을 통해 React는 this를 초기화하고, this.props를 사용할 수 있도록 준비합니다.
하지만, 많은 경우 constructor를 직접 만들지 않아도 됩니다. 왜냐하면, state를 props로부터 만들 필요가 없거나 기본값만 있으면 되는 경우가 대부분이기 때문입니다. 예를 들어,
class Counter extends React.Component {
state = {
count: 0,
};
}
위 예시처럼 클래스 필드 문법을 사용하면 리액트가 내부적으로 초기 state를 설정해주기 때문에, constructor를 직접 작성할 필요가 없어집니다.
그리고 중요한 점은 최근에 함수형 컴포넌트와 Hooks가 표준이 되면서, constructor를 직접 사용할 일은 점점 줄어들고 있다는 것입니다.
+) getDerivedStateFromProps는 부모로부터 받은 props 변화에 따라 state를 ‘동기화’해야 할 때 사용하는 생명주기 메서드입니다. 이 메서드는 언제 호출이 될까요?
즉, 렌더링 직전에 항상 호출될 수 있는 단계입니다.
그러므로 이 메서드는 side effect를 만들면 안 되고, 순수하게 값 계산만 해야 합니다.
그렇다면 side effect(부작용)란 무엇일까요?
- side effect는 “함수를 실행했을 때, 값 계산 말고 바깥에 영향을 주는 모든 행동”을 말합니다.
이 설명만으론 아직 좀 이해가 어렵습니다.
그렇다면, side effect가 있는 경우 혹은 해야하는 경우로 어떤 것이 있을까요?side effect가 있는 경우
- 외부 상태나 전역 값의 변경 (변수 재할당)
- 서버 통신
- DOM 조작
- 타이머, 이벤트 등록
- 로그 출력 (엄밀히 말하면 side effect이지만 디버깅 목적으로는 허용되는 경우가 많습니다)
side effect를 해야하는 곳
- componentDidMount
- componentDidUpdate
- componentWillUnmount
- 함수형 컴포넌트의 useEffect
getDerivedStateFromProps 메서드는 잘못 사용하면 state와 props의 역할이 뒤섞이기 때문에, 대부분의 경우에는 필요하지 않습니다.
render는 언제 호출될까요?
- 컴포넌트가 처음 마운트될 때
- state가 변경될 때
- props가 변경될 때
- 부모 컴포넌트가 다시 렌더링될 때
즉,
- 같은 props, 같은 state → 항상 같은 JSX 반환
- 외부 상태를 바꾸지 않음
- side effect를 만들지 않음
- 서버로부터 데이터 요청 (API 호출)
- DOM에 직접 접근해야 하는 초기 설정
- 타이머 설정
- 외부 라이브러리 초기화
- 이벤트 리스너 등록
대신
- React.memo
- useMemo
- useCallback
같은 도구로 같은 효과를 낸다.
게다가 잘못 사용하면 버그를 만들기 쉬워 보통은 PureComponent나 memo 기반 최적화를 사용하는 것이 권장됩니다.
업데이트 전·후의 DOM 차이를 알아야 하는 경우를 위해 이 단계가 필요한데, 어떤 경우가 이에 해당할까요?
- 스크롤 위치 유지
- DOM 크기 변화 감지 등...
- “화면이 실제로 바뀐 뒤” 실행되는 단계입니다.
- 최초 마운트 시에는 호출되지 않고, props와 state가 변경되었을 때 호출됩니다.
이 단계는 업데이트 이후의 side effect 처리 공간으로 주로 아래와 같은 작업을 수행합니다.
- props / state 변화에 따른 API 재요청
- DOM 조작
- 외부 라이브러리 업데이트
- 이 단계의 핵심 역할은 컴포넌트가 사용하던 외부 자원을 정리(clean up)하는 것입니다.
왜 정리가 필요할까요?
컴포넌트는 사라지지만,
- 타이머
- 이벤트 리스너
- 웹소켓 연결
- 외부 라이브러리
같은 것들은 자동으로 정리되지 않습니다.
정리를 하지 않으면
- 메모리 누수
- 의도치 않은 동작
- 존재하지 않는 컴포넌트에 대한 상태 업데이트
같은 문제가 생길 수 있기 때문에 정리가 필요합니다.
리액트에서 컴포넌트의 라이프사이클 메서드나 useEffect가 개발 환경에서 두 번 호출되는 현상은 버그가 아니라 의도된 동작입니다.
이는 Vite로 프로젝트를 생성했을 때 기본으로 적용되는 main.jsx의 때문이며, 개발 모드에서만 발생합니다.
<StrictMode>는 리액트 애플리케이션을 더 안전하게 만들기 위한 개발 도구로, 프로덕션 빌드에는 영향을 주지 않으며, 개발 중에만 잠재적인 문제를 미리 발견하도록 돕습니다.
리액트의 모든 컴포넌트는 순수 함수임을 가정하기 때문에 동일한 입력에 대해(props, state, context) 동일한 출력(JSX)을 반환해야 합니다.
이상 컴포넌트 생명주기(라이프 사이클)에 대한 정리를 마치겠습니다.