useEffect 올바르게 사용하기


React 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
2
3
4
5
const Profile = ({ name }) => {
const message = `안녕하세요 ${name}님!`;
document.title = `${name}님의 프로필`;
return <h3>{message}</h3>;
};

이 코드는 올바르게 짜여진 코드라고 할 수 있을까?

그렇지 않다. 위에서 설명했듯이 document.title의 값을 바꿔주는 기능은 side-effect인데, 함수형 컴포넌트 내에서 저렇게 코드를 써버리면 컴포넌트가 렌더링 될 때마다 document.title이 재정의 될 것이다.

우리가 바라는 것은 name의 값이 변경되었을 때만 document.title을 변경하는 것이기 때문에 이를 **useEffect**를 통해 핸들링 해줘야 하는 것이다.

1
2
3
4
5
6
7
8
// 이게 바르게 된 코드 및 useEffect, side-effect의 활용법이라고 할 수 있다.
const Profile = ({ name }) => {
const message = `안녕하세요 ${name}님!`;
useEffect(() => {
document.title = `${name}님의 프로필`;
}, [name]);
return <h3>{message}</h3>;
};

🥑 useEffect의 기본 형태

**useEffect**는 아래와 같이 기본적으로 두 개의 인자로 이루어진다.

useEffect 기본 형태

  1. **callback**

    ⇒ 콜백 함수는 기본적으로 side-effect를 위한 로직을 담은 함수다. 이전 포스트에서도 설명했듯이, useEffect 내에 setState()함수가 담기는 것은 권장되지 않는다.

  2. **dependencies**

    useEffect 내의 callback 발동을 위한 하나의 조건이라고 생각하면 된다. 만약 의존성 배열(dependencies)가 비어있다면, 이는 “컴포넌트가 렌더링 됐을 때 한 번만 실행한다” 라는 것을 의미한다. 하지만 이외에 의존성 배열 내에 state 또는 props가 담겨있으면, 해당 값들이 변경됐을 때 callback을 실행하겠다는 것을 의미한다.

    image from Dmitri Pavlutin’s post.


🍼 의존성 배열 (dependencies array)

위서에서 설명했듯, 의존성 배열에 무엇이 담겨 있느냐에 따라 useEffect 내의 callback 실행 여부가 결정된다. 그렇다면 의존성 배열에는 어떤 형태들이 존재할까?

  1. 의존성 배열 자체가 선언되지 않은 경우

    ⇒ 컴포넌트가 렌더링 될 때마다 callback이 실행되도록 한다. (리렌더링 포함)

  2. 빈 배열만 선언된 경우

    ⇒ 컴포넌트가 최초 렌더링 됐을 때, 딱 한 번만 callback이 실행된다.

  3. props 또는 state가 선언된 경우

    ⇒ 해당 props 또는 state가 변경되었을 경우에만 callback이 실행된다.


🧬 컴포넌트 생명주기 (Component Lifecycle)

컴포넌트 생명주기

useEffect를 위한 컴포넌트 생명 주기는 크게 두 가지로 정리될 수 있다.
Component Did Mount, Component Did Update다.

componentDidMountComponentDidUpdate 등은 모던 리액트만을 학습한 사람들에게는 낯선 단어일 수 있다. 이는 이전에 자주 사용되던 class형 컴포넌트에서 활용되던 개념이기 때문이다.

쉽게 설명하자면 componentDidMount는 말 그대로 컴포넌트가 렌더링 됐을 때를 의미하며, componentDidUpdate는 컴포넌트가 업데이트 됐을 때를 의미한다.

  1. componentDidMount

    위에서 설명했듯, 이는 useEffect에서 빈 배열만 의존성 배열에 선언됐을 경우를 의미한다.

  2. componentDidUpdate

    이는 useEffect에서 의존성 배열에 특정 props 또는 state가 선언됐을 경우를 의미한다.


🧹 Cleanup Function

Cleanup Function이란, 컴포넌트가 unmount되기 전 또는 업데이트 되기 직전에 특정한 작업 및 기능을 수행할 수 있도록 도와준다.
또한, 메모리 누수를 방지해주며 원치 않는 결과가 도출되지 않도록 사전에 차단해주는 역할을 한다.

기본 형태

1
2
3
4
5
6
useEffect(() => {
// side-effect logic
return () => {
cleanup;
};
}, [input]);

우리가 위와 같이 useEffect 내에 return으로 함수를 정의하면, useEffect는 이 함수를 cleanup 함수로 인식한다.

cleanup function의 작동 순서

image from Dmitri Pavlutin’s post.

image from Dmitri Pavlutin’s post.

  1. 컴포넌트가 최초 렌더링 됐을 때는 cleanup function이 동작하지 않는다.
  2. 그 이후의 렌더링 시에, 이전 side-effect값을 참조하여 이를 기반으로 최신화된 side-effect를 실행시킨다.
  3. 컴포넌트가 언마운트 될 때 cleanup function이 마지막으로 실행되며 이는 최신화된 cleanup function이다.

cleanup function이 왜 필요한가?

아래의 코드 예시를 보면, 해당 useEffect에는 cleanup function이 선언되어있지 않다. 이 경우에는 이전에 활용되었던 모든 message props들이 2초마다 찍히게 된다.

1
2
3
4
5
6
7
8
import { useEffect } from 'react';
function RepeatMessage({ message }) {
useEffect(() => {
setInterval(() => {
console.log(message);
}, 2000);
}, [message]);
return <div>I'm logging to console "{message}"</div>;

restless-wildflower-c0cfw

우리가 바라는 것은 최신화된 message props만을 console.log()로 찍어내는 것이다. 이 때문에 우리는 cleanup function을 사용하는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
import { useEffect } from 'react';
function RepeatMessage({ message }) {
useEffect(() => {
const id = setInterval(() => {
console.log(message);
}, 2000);
return () => {
clearInterval(id);
};
}, [message]);
return <div>I'm logging to console "{message}"</div>;
}

이렇게 하면 우리는 이전에 사용됐던 message props를 제거하고, 최신화된 props만 찍어낼 수 있다.

코드샌드박스 예시


포스팅에 참고한 자료

A Simple Explanation of React.useEffect()


useEffect 올바르게 사용하기

https://hoonjoo-park.github.io/react/3.useEffect/

Author

Hoonjoo

Posted on

2022-02-03

Updated on

2022-02-07

Licensed under

Comments