useEffect 올바르게 사용하기
💡 useEffect란 무엇인가?
useEffect
란, 함수형 컴포넌트에서의side-effects
활용을 위한Hooks
다.
근데 도대체 이게 무슨 말일까…?? 😅
우선 이전까지 내가 피상적으로만 이해했던 **useEffect**
의 개념은, 컴포넌트가 render
됐을 때, 또는 특정 state
에 변화가 생겼을 때 원하는 함수 및 로직을 실행하고싶을 때 활용하는 것이었다. 하지만 이렇게만 이해하고 useEffect를 사용하니, dependencies
관련 문제에도 직면하고, 옳지 못한 방향으로 useEffect
를 남발하는 지경에 이르기 까지 했다. 따라서 이번 기회에 useEffect
에 대해 제대로 정리하여 좀 더 효율적으로 React를 활용해보고자 한다.
🤔 side-effects
side-effects
란, 특정 함수가 컴포넌트의 output 자체에 목적을 두는 것이 아닌 특정 기능을 수행하기 위해 기능하는 것을 의미한다. 즉, setTimeout()
, fetch request
, DOM을 직접적으로 조작하는 것 등, 명확한 return
값 반환을 목적으로 하지 않는 함수 또는 계산을 뜻한다고 할 수 있다.
아래의 예시를 확인해보자
1 | const Profile = ({ name }) => { |
이 코드는 올바르게 짜여진 코드라고 할 수 있을까?
그렇지 않다. 위에서 설명했듯이 document.title
의 값을 바꿔주는 기능은 side-effect
인데, 함수형 컴포넌트 내에서 저렇게 코드를 써버리면 컴포넌트가 렌더링 될 때마다 document.title
이 재정의 될 것이다.
우리가 바라는 것은 name
의 값이 변경되었을 때만 document.title
을 변경하는 것이기 때문에 이를 **useEffect**
를 통해 핸들링 해줘야 하는 것이다.
1 | // 이게 바르게 된 코드 및 useEffect, side-effect의 활용법이라고 할 수 있다. |
🥑 useEffect의 기본 형태
**useEffect**
는 아래와 같이 기본적으로 두 개의 인자로 이루어진다.
**callback**
⇒ 콜백 함수는 기본적으로 side-effect를 위한 로직을 담은 함수다. 이전 포스트에서도 설명했듯이, useEffect 내에
setState()
함수가 담기는 것은 권장되지 않는다.**dependencies**
⇒
useEffect
내의callback
발동을 위한 하나의 조건이라고 생각하면 된다. 만약 의존성 배열(dependencies)가 비어있다면, 이는 “컴포넌트가 렌더링 됐을 때 한 번만 실행한다” 라는 것을 의미한다. 하지만 이외에 의존성 배열 내에state
또는props
가 담겨있으면, 해당 값들이 변경됐을 때 callback을 실행하겠다는 것을 의미한다.
🍼 의존성 배열 (dependencies array)
위서에서 설명했듯, 의존성 배열에 무엇이 담겨 있느냐에 따라
useEffect
내의callback
실행 여부가 결정된다. 그렇다면 의존성 배열에는 어떤 형태들이 존재할까?
의존성 배열 자체가 선언되지 않은 경우
⇒ 컴포넌트가 렌더링 될 때마다
callback
이 실행되도록 한다. (리렌더링 포함)빈 배열만 선언된 경우
⇒ 컴포넌트가 최초 렌더링 됐을 때, 딱 한 번만
callback
이 실행된다.props 또는 state가 선언된 경우
⇒ 해당
props
또는state
가 변경되었을 경우에만callback
이 실행된다.
🧬 컴포넌트 생명주기 (Component Lifecycle)
useEffect를 위한 컴포넌트 생명 주기는 크게 두 가지로 정리될 수 있다.
Component Did Mount
,Component Did Update
다.
componentDidMount
와 ComponentDidUpdate
등은 모던 리액트만을 학습한 사람들에게는 낯선 단어일 수 있다. 이는 이전에 자주 사용되던 class형 컴포넌트에서 활용되던 개념이기 때문이다.
쉽게 설명하자면 componentDidMount
는 말 그대로 컴포넌트가 렌더링 됐을 때를 의미하며, componentDidUpdate
는 컴포넌트가 업데이트 됐을 때를 의미한다.
componentDidMount
위에서 설명했듯, 이는 useEffect에서 빈 배열만 의존성 배열에 선언됐을 경우를 의미한다.
componentDidUpdate
이는 useEffect에서 의존성 배열에 특정 props 또는 state가 선언됐을 경우를 의미한다.
🧹 Cleanup Function
Cleanup Function이란, 컴포넌트가
unmount
되기 전 또는 업데이트 되기 직전에 특정한 작업 및 기능을 수행할 수 있도록 도와준다.
또한, 메모리 누수를 방지해주며 원치 않는 결과가 도출되지 않도록 사전에 차단해주는 역할을 한다.
기본 형태
1 | useEffect(() => { |
우리가 위와 같이 useEffect 내에 return
으로 함수를 정의하면, useEffect는 이 함수를 cleanup
함수로 인식한다.
cleanup function의 작동 순서
image from Dmitri Pavlutin’s post.
- 컴포넌트가 최초 렌더링 됐을 때는
cleanup function
이 동작하지 않는다. - 그 이후의 렌더링 시에, 이전
side-effect
값을 참조하여 이를 기반으로 최신화된side-effect
를 실행시킨다. - 컴포넌트가 언마운트 될 때
cleanup function
이 마지막으로 실행되며 이는 최신화된cleanup function
이다.
cleanup function이 왜 필요한가?
아래의 코드 예시를 보면, 해당 useEffect에는 cleanup function이 선언되어있지 않다. 이 경우에는 이전에 활용되었던 모든 message props들이 2초마다 찍히게 된다.
1 | import { useEffect } from 'react'; |
우리가 바라는 것은 최신화된 message
props
만을 console.log()로 찍어내는 것이다. 이 때문에 우리는 cleanup function을 사용하는 것이다.
1 | import { useEffect } from 'react'; |
이렇게 하면 우리는 이전에 사용됐던 message
props
를 제거하고, 최신화된 props
만 찍어낼 수 있다.
포스팅에 참고한 자료
A Simple Explanation of React.useEffect()
useEffect 올바르게 사용하기