생명주기(lifecycle)란?
컴포넌트가 페이지에 렌더링되기 전 준비 과정부터 페이지에서 사라지는 순간까지를 생명주기라고 한다.
모든 리액트 컴포넌트는 생명주기를 가진다. 클래스 컴포넌트에서는 생명주기 메서드를 사용함으로써 컴포넌트가 처음 생성(mount)되어 렌더링될 때나, 업데이트 전후, 또는 제거(소멸, unmount) 시기에 해야 되는 작업들을 각각 처리할 수 있다.
자주 사용되는 생명주기 메서드에는 ⭐ 표시를 달았다.
> 마운트(Mount)
DOM이 생성되고 웹 브라우저 상에 나타나는 것을 말한다. 이때 호출되는 메서드는 다음과 같다.
> 업데이트(Update)
💡 업데이트를 발생시키는 요인
1. 자신이 전달받은 props가 변경될 때
2. 자신의 state가 바뀔 때 (setState를 통해 업데이트될 때)
3. 부모 컴포넌트가 리렌더링될 때
4. forceUpdate 함수가 실행될 때
컴포넌트는 이렇게 네 가지 경우에 업데이트가 일어난다. 이때 호출되는 메서드는 다음과 같다.
- static getDerivedStateFromProps
- shouldComponentUpdate
- render⭐
- getSnapshotBeforeUpdate
- componentDidUpdate⭐
> 언마운트(Unmount)
컴포넌트를 DOM에서 제거하는 것을 말한다. 이때 호출되는 메서드는 다음과 같다.
생명주기 메서드
생명주기 메서드의 종류는 오류 처리 메서드(static getDerivedStateFromError, componentDidCatch)
를 포함해 총 열 가지가 있다. 메서드의 이름에 Will 접두사가 있으면 어떤 작업을 하기 전에 실행되고, Did 접두사가 있으면 어떤 작업을 하고난 후에 실행된다.
> render() ⭐
클래스 컴포넌트 및 생명주기 메서드에서 유일한 필수 메서드로, 컴포넌트를 렌더링한다.
리액트 엘리먼트나 배열, Fragment, 문자열 및 숫자, 또는 Boolean, null 중 하나를 반환해야 한다.
컴포넌트의 state를 변화시키거나 브라우저와 상호작용해 DOM 정보를 가져와야 하는 경우 componentDidMount 등 다른 생명주기 메서드에서 처리해야 하며, render() 함수 자체는 순수해야 한다.
또한 리렌더링 여부를 결정하는 shouldComponentUpdate가 false를 반환하면 render()는 호출되지 않는다.
> constructor ⭐
컴포넌트의 생성자 메서드로, 컴포넌트가 마운트되기 전 가장 먼저 호출되는 메서드이다. 해당 컴포넌트에 초기 state를 정하거나 이벤트 처리 메서드를 바인딩하는 작업이 없다면 생성자를 구현하지 않아도 된다.
constructor(props) {
super(props);
// this.setState()는 호출하면 ❌
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
> getDerivedStateFromProps
props로 받아온 것을 state에 넣어주고 싶을 때 사용하는데, 컴포넌트가 최초 마운트될 때와 업데이트될 때마다 render()를 호출하기 직전에 호출된다. 공식 문서에서는 여러 다른 대안을 권장하고 있고, 사용례가 드물다. (시간의 흐름에 따라 변하는 props에 state가 의존하는 경우)
단순히 props 변화에 대응한 부수 효과를 발생시키고자 한다면 componentDidUpdate를 사용하면 된다.
> componentDidMount ⭐
컴포넌트가 마운트되고 트리에 삽입되어 최초 렌더링이 일어난 직후 호출된다. 메서드 호출 시점엔 컴포넌트가 화면에 나타나 있는 상태다. DOM을 사용해야 하는 작업들이나 외부 라이브러리 연동, 네트워크 요청(axios, fetch 등) 같은 비동기 작업 및 이벤트 등록, 데이터 구독을 보내기 적절한 위치이다. 구독 해제 작업은 componentWillUnmount에서 반드시 수행해주어야 한다.
> shouldComponentUpdate
props 또는 state를 변경했을 때 리렌더링 여부를 결정한다. 성능 최적화를 위해 사용되는 메서드이다. 반드시 true 또는 false를 반환해야 하며, 기본값은 true이므로 매번 변화 시마다 리렌더링을 수행하게 된다. false를 반환할 경우 이후 메서드인 render와 componentDidUpdate는 호출되지 않는다. 또한 이 메서드는 최초 렌더링 시기나 forceUpdate()가 사용될 경우 호출되지 않는다.
함수 컴포넌트에서는 React.memo를 사용해 shouldComponentUpdate의 역할을 대신해 성능 최적화를 할 수 있다.
> getSnapshotBeforeUpdate
render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출된다. 컴포넌트에 변화가 일어나기 직전의 DOM 상태를 가져와서, 특정 값을 반환하면 componentDidUpdate 메서드에서 세 번째 파라미터인 snapshot 값으로 전달받아와서 사용할 수 있다. 업데이트 직전 값을 참고해야할 때 활용할 수 있다. (스크롤바 위치 유지 등)
함수 컴포넌트 + Hooks를 사용할 때는 getSnapshotBeforeUpdate를 대체할 수 있는 기능이 아직 없다.
> componentDidUpdate ⭐
화면에 원하는 변화가 갱신되어 리렌더링이 완료된 직후 호출된다. prevProps, prevState를 사용해 컴포넌트가 이전에 가졌던 데이터에 접근하거나, getSnapshotBeforeUpdate에서 반환한 값이 있다면 snapshot 값을 조회할 수 있다.
업데이트가 끝난 시기이므로 DOM 관련 조작을 하거나 이전과 현재의 props를 비교해 변화가 있을 경우 네트워크 요청을 보내는 작업을 이 시기에 하면 된다.
> componentWillUnmount ⭐
컴포넌트가 마운트 해제되어 DOM에서 제거되기 직전에 호출된다. 타이머 제거나 네트워크 요청 취소, componentDidMount에서 생성된 구독 해제 등의 정리 작업을 수행해야 한다.
> getDerivedStateFromError
자손 컴포넌트에서 오류가 발생했을 때 호출된다. 이 메서드는 매개변수로 오류를 전달받고, 갱신된 state 값을 반드시 반환해야 한다. 렌더 단계에서 호출되므로 부수 효과를 발생시키면 안된다.
렌더링 중 에러 발생 시 대체 UI 렌더링 제어가 필요할 때 사용할 수 있다.
> componentDidCatch
자손 컴포넌트에서 오류가 발생했을 때 호출된다. 발생한 오류, 오류를 발생시킨 컴포넌트 정보를 포함한 객체 이렇게 2개의 매개변수를 전달받는다. 커밋 단계에서 호출되므로 부수 효과를 발생시켜도 된다.
오류 로그 기록 등이 필요할 때 사용할 수 있다.
함수 컴포넌트에서 생명주기
클래스 컴포넌트는 길어지는 코드 길이와 중복 코드의 발생, 그리고 가독성 문제 등이 있었다. 이로 인해 함수 컴포넌트로의 변화가 나타났고, 현재는 함수 컴포넌트를 사용하도록 권장되고 있다.
그런데 함수 컴포넌트에서는 기존의 생명주기 메서드를 사용할 수 없다. 그래서 해당 기능을 사용하기 위해 Hooks가 2019년 6월에 정식 출시되었다. use 키워드를 앞에 붙여, 함수 컴포넌트에서도 클래스 컴포넌트의 기능을 낚아채서(hooking) 사용한다고 해서 리액트 Hooks라고 부른다.
> useEffect
useEffect는 생명주기 메서드 중 componentDidMount, componentDidUpdate, componentWillUnmount의 역할을 모두 수행할 수 있다. 인자로 콜백 함수와 Deps( Dependency Array; 의존성 배열)을 전달 받는다.
컴포넌트가 최초 렌더링이 되면(componentDidMount) 첫 번째 인자 콜백을 실행하고,
리렌더링이 될 때(componentDidUpdate) 의존성 배열의 요소를 확인하고 바뀐 게 있을 때만 콜백을 실행한다.
그리고 콜백 내에서 return한 clean up 함수는 컴포넌트가 제거될 때(componentWillUnmount) 동작한다.
useEffect(() => {
// componentDidMount, componentDidUpdate
// to do...
return () => {
// componentWillUnmount
// clean up...
};
}, []);
> React.memo
클래스 컴포넌트에서는 컴포넌트의 리렌더링을 방지할 때 shouldComponentUpdate 메서드를 사용하면 되지만,
함수 컴포넌트에서는 대신 React.memo 함수를 사용한다.
부모 컴포넌트로부터 전달받는 props가 바뀌지 않으면 리렌더링 되지 않도록 설정해서
자식 컴포넌트의 리렌더링 성능을 최적화해줄 수 있다.
사용 방법은 컴포넌트를 만들고 나서 감싸주면 된다.
import React from 'react';
const Component = (props) => {
return <div>contents</div>;
};
export default React.memo(Component);
Reference >
- https://ko.reactjs.org/docs/react-component.html
- 리액트를 다루는 기술 p.172-188, 194-199, 293