공부하자

[React] 라이프 사이클과 Hook 본문

React

[React] 라이프 사이클과 Hook

dev_riley 2023. 2. 8. 01:22

리액트를 사용해 프로젝트를 하다보면 라이프 사이클에 대해 알고 적절히 활용을 해야합니다.

그래서 이 글에서는 라이프 사이클, 라이프 사이클 메서드, 그리고 많이 사용되는 Hook에 대해 정리를 해보았습니다.


라이프 사이클이란 무엇인가?

모든 리액트 컴포넌트는 라이프 사이클이 존재합니다.

컴포넌트는 생성(mounting) => 업데이트(updating) => 제거(unmounting)의 생명주기를 갖는데, 이 생명주기를 활용해서 필요한 작업들을 적절한 시기에 지정해줘야 불필요한 업데이트를 방지할 수 있습니다.

 

[ React 17이상 버전 생명주기 ]

Life Cycle Method

출처 : http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

 

React Lifecycle Methods diagram

Fully interactive and accessible React Lifecycle Methods diagram.

projects.wojtekmaj.pl

리액트의 클래스 컴포넌트는 라이프 사이클 메서드를 활용하고, 함수형 컴포넌트는 Hook을 사용합니다.

생명 주기 메서드 

생명 주기 메서드는 컴포넌트가 브라우저 상에서 나타나고, 업데이트되고, 사라지게 될 때 호출되는 메서드입니다.

생명 주기 메서드는 총 9가지가 있고, 이 메서드들은 서드파티 라이브러리를 사용하거나, DOM에 직접 접근해야하는 상황에서 유리합니다.

 

[ 레거시 생명 주기 메서드]

17버전 이전에 사용하던 메서드인데 17버전 이후 deprecated된 메서드들이 있습니다. 여전히 사용이 가능하지만, 새로 작성하는 코드에 대해서는 사용하지 않을 것을 권장한다고 공식 문서에 나와있습니다.

  • componentWillMount()
  • componentWillUpdate()
  • componentWillRecivedProps()

라이프 사이클은 총 3가지 Mount, Update, Unmount 카테고리로 나뉩니다.

Mount

DOM이 생성되고 웹 브라우저상에 나타나는 것

[ 마운트시 호출 되는 메서드 ]

  • constructor : 컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드
  • getDerivedStateFromProps : props에 있는 값을 state에 넣을 때 사용하는 메서드
  • render : 준비한 UI를 렌더링하는 메서드
  • componentDidMount : 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드

[ 마운트 과정 ]

컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출됩니다.

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount

Update

컴포넌트는 다음 4가지 경우에 업데이트됩니다.

  1. props가 바뀔 때
  2. state가 바뀔 때
  3. 부모 컴포넌트가 렌더링될 때
  4. this.forceUpdate로 강제로 렌더링 트리거할 때

[ 업데이트시 호출되는 메서드 ]

  • getDerivedStateFromProps : 앞서 mount 과정에서도 호출되고, props 변화에 따라 state 값에도 변화를 주고 싶을 때 사용
  • shouldComponentUpdate : 컴포넌트가 리렌더링을 해야 할지 말아야 할지를 결정, true를 반환하면 다음 라이프사이클 메서드를 계속 실행, false를 반환하면 작업을 중지(리렌더링X)합니다.
  • render : 컴포넌트를 리렌더링 한다.
  • getSnapshotBeforeUpdate : 컴포넌트 변화를 DOM에 반영하기 바로 직전에 호출
  • componentDidUpdate : 컴포넌트의 업데이트 작업이 다 끝난 후 호출

[ 업데이트 과정 ]

props 또는 state가 변경되면 갱신이 발생하고, 컴포넌트가 다시 렌더링될 때 다음과 같은 순서대로 호출됩니다.

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

Unmount

mount의 반대 과정, 즉 컴포넌트를 DOM에서 제거하는 것

언마운트시 호출되는 메서드

  • componentWillUnmount : 컴포넌트가 웹 브라우저 상에서 사라지기 전에 호출

자주 사용되는 메서드 정리

[ Mounting ] render()

redner() 메서드는 클래스 컴포넌트에서 반드시 구현되어야 하는 유일한 메서드입니다. 이 메서드가 호출되면 this.props와 this.state의 값을 활용하여 아래의 것 중 하나를 반환해야 합니다.

  • React elemnet
  • 배열과 Fragment
  • Protal
  • 문자열과 숫자
  • Boolean이나 null

render() 함수는 순수해야 합니다. 즉, 컴포넌트의 state를 변경하지 않고, 호출될 때마다 동일한 결과를 반환해야 하며, 브라우저와 직접적으로 상호작용을 하지 않아야 합니다. 만약 브라우저와 직접적으로 상호작용해야 하는 경우가 생긴다면, 해당 작업을 componentDidMount()나 다른 생명주기 메서드 내에서 수행해야 합니다. reder()를 순수하게 유지해야 컴포넌트이 동작을 이해하기 쉽습니다.

 

[ Mounting ] constructor()

메서드를 바인딩하거나 state를 초기화하는 작업이 없다면, 해당 React 컴포넌트에는 생성자를 구현하지 않아도 됩니다.

React 컴포넌트의 생성자는 해당 컴포넌트가 마운트되기 전에 호출됩니다. React.component를 상속한 컴포넌트의 생성자를 구현할 때에는 다른 구문에 앞서 super(props)를 호출해야 합니다. 그렇지 않아면 this.props가 생성자 내에서 정의되지 않아 버그로 이어질 수 있습니다.

React에서 생성자는 보통 아래의 두가지 목적을 위해 사용됩니다.

  • this.state에 객체를 할당하여 지역 state를 초기화할 때
  • 인스턴스에 이벤트 처리 메서드를 바인딩할 때

constructor() 내부에서 setState()를 호출하면 안됩니다. 컴포넌트에 지역 state가 필요하다면 생성자 내에서 this.state에 초기 state 값을 할당하면 됩니다.

constructor(props) {
  super(props);
  // 여기서 this.setState()를 호출하면 안 됩니다!
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

생성자는 this.state를 직접 할당할 수 있는 유일한 곳입니다. 그 외의 메서드에서는 this.setState()를 사용해야 합니다.

 

!! 주의 !!

state에 props를 복사하면 안됩니다!! 이는 불필요한 작업이며 버그를 발생시킵니다. 가장 흔히 하는 실수 중 하나이니 주의해야합니다.

constructor(props) {
 super(props);
 // 이렇게 하지 마세요!
 this.state = { color: props.color };
}

[ Mounting ] componentDidMount()

componentDidMount()는 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후에 호출됩니다.

DOM 노드가 있어야 하는 초기화 작업은 이 메서드에서 이루어지면 안됩니다. 외부에서 데이터를 불러와야 한다면, 네트워크 요청을 보내기 적절한 위치입니다.

이 메서드는 데이터 구독을 설정하기 좋은 위치입니다. 데이터 구독이 이루어졌다면, componentWillUnmount()에서 구독 해제 작업을 반드시 수행해야 합니다.

 

[ Updating ] comonentDidUpdate()

componentDidUpdate()는 갱신이 일어난 직후에 호출됩니다. 이 메서드는 최초 렌더링에서는 호출되지 않습니다.

컴포넌트가 갱신되었을 때 DOM을 조작하기 위하여 이 메서드를 활용하면 좋습니다. 또한, 이전과 현재의 props를 비교하여 네트워크 요청을 보내는 작업도 이 메서드에서 이루어지면 됩니다.

componentDidUpdate(prevProps) {
  // 전형적인 사용 사례 (props 비교를 잊지 마세요)
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

[ Unmounting ] componentWillUnmount()

componentWillUnmount()는 컴포넌트가 마운트 해제되어 제거되기 직전에 호출됩니다. 이 메서드 내에서 타이머 제거, 네트워크 요청 취소, componentDidMount() 내에서 생성된 구독 해제 등 필요한 모든 정리 작업을 수행합니다.

이제 컴포넌트는 다시 렌더링되지 않으므로 이 메서드 내에서 setState()를 호출하면 안됩니다. 컴포넌트 인스턴스가 마운트 해제되고 나면, 절대로 다시 마운트되지 않습니다.

React Hook이란?

리액트에서 16.8버전에 새로 도입되었으며, 기존에 사용하던 class를 이용한 코드를 작성할 필요없이, state와 여러 React 기능을 사용할 수 있도록 만든 라이브러리입니다. 함수형 컴포넌트에서 state와 생명 주기 기능을 연동할 수 있게 해주는 함수입니다.

React Hook을 도입한 이유

1. 컴포넌트에서 상태관련 로직을 사용할 때 레이어 변화없이 재사용할 수 있게 합니다.

2. 라이프 사이클 메서드 기반이 아닌 로직 기반으로 나눌 수 있어서 컴포넌트를 함수 단위로 잘게 쪼갤 수 있습니다.

React Hook 사용 규칙

1. 최상위에서만 Hook을 호출해야한다. 반복문, 조건문, 중첩함수 내에서 Hook을 호출하면 안된다. 이 규칙을 따름으로써 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것을 보장할 수 있습니다.

2. 리액트 함수 컴포넌트에서만 Hook을 호출해야 합니다. 일반 JS함수에서는 호출해서는 안됩니다.

자주 사용하는 리액트 Hook

1. useState()

가장 많이 사용되는 Hook 중에 하나이고, 함수형 컴포넌트에서 상태를 관리할 때 사용합니다. 

현재 상태를 나타내는 state값과 이 값의 상태를 변경하는 setState값을 한쌍으로 제공합니다.([state이름, setter이름]) 또한 초기값을 설정할 수 있으며, 이 초기값은 첫 렌더링 때 한 번 사용됩니다.

const [ state, setState ] = useState(initialState);

2. useEffect()

리액트 컴포넌트가 렌더링 될때마다 특정 작업을 실행할 수 있도록 하는 Hook입니다.

컴포넌트가 mount, update, unmount될 때 작업을 처리할 수 있습니다. 쉽게 말해 useEffect는 클래스에서 사용하는 생명 주기 메서드의 각 단계의 메서드인 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것이라 생각하면 됩니다.

useEffect(() => {}) // 렌더링이 될 때마다 수행된다. 3가지 메서드 모두 해당
useEffect(() => {}, []) // 화면이 렌더링 된 이후에만 수행된다. componentDidMount
useEffect(() => {}, [값]) // 화면이 렌더링 된 이후에 수행되고, '값'이 변경되었을 경우에 해당 메서드가 수행된다. componentDidUpdate
useEffect(() => {return(() => func())}) // clean-up함수를 리턴해, Unmount될 때 정리해야할 함수들을 처리할 수도 있다. componentWillUnmount

3. useContext()

리액트에서 제공하는 context API를 통해 context에 데이터를 저장하고, 이 데이터가 필요한 컴포넌트에서 useContext를 사용해 데이터를 불러와 사용할 수 있습니다. 참고로 클래스 컴포넌트에서는 consumer로 데이터를 불러와 사용할 수 있습니다.

const value = useContext(MyContext);

4. useReducer()

useState의 대체 함수로 컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리할 수 있습니다. 하윗값이 복잡한 정적 로직을 만들거나, 다음 state가 이전 state에 의존적인 경우 보통 useState 대신 useReducer를 사용합니다.

const [state, dispatch] = useReducer(reducer, initialArg, init);

5. useRef()

특정 DOM에 접근하여 DOM 조작을 가능하게 하는 Hook입니다. 특정 요소를 선택해야하는 상황에서 useRef함수를 주로 사용합니다.

 

6. useMemo()

함수형 컴포넌트에서 최적화를 할 때 사용하는 Hook으로, 연산을 최소화해줍니다. useMemo()는 메모이제이션된 값을 반환하는데, 어떤 의존값(deps)이 변경되었을 때만 전달된 함수(fn)가 실행되 이 실행값이 반환됩니다.

const memoizedValue = useMemo(() => fn, [deps]);

7. useCallback()

useMemo와 마찬가지고 리액트에서 최적화를 할 때 사용하는 Hook으로, 불필요한 렌더링을 방지해줍니다. useCallback()은 메모이제이션된 함수를 반환하는데, 어떤 의존값(deps)이 변경되었을 때만 전달된 함수(fn)가 반환됩니다.

useCallback을 사용하지 않으면 함수가 재생성돼 값은 같지만 참조값이 바뀌는 함수가 생겨 리렌더링이 되는데, 이때 useCallback을 사용해 메모이제이션된 함수를 반환하게 되면 불필요한 리렌더링을 줄일 수 있습니다.

const memoizedCallback = useCallback(fn, [deps])

출처 : https://ko.reactjs.org/docs/react-component.html#legacy-lifecycle-methods

 

Comments