React Hook: useEffect란?
useEffect의 의미와 사용 방법
사이드 이펙트 구현은 useEffect 훅 함수가 만들어진 이유다. 앞선 글에서도 설명했지만, useEffect를 다룰 때 중요한 개념이니 다시 한번 짚고 넘어가도록 하자. 리액트에서 사이드 이펙트란 '리액트 컴포넌트를 화면에 1차로 렌더링 한 후, 비동기식으로 처리되는 부수적인 효과'다. 이렇게 한 줄로 설명하면 이해가 잘 안 가려나? 쉽게 말해, 일단 화면에 렌더링 할 수 있는 거 다 하고 나중에 비동기식으로 추가, 변동되는 데이터를 처리하는 걸 보고 사이드 이펙트라고 한다. useEffect는 이 사이드 이펙트를 함수형 컴포넌트에서 구현하기 위해 고안된 기능이다. 리액트 공식 홈페이지에선 이를 클래스형 컴포넌트의 생명주기 메소드와 비교해서 설명한다.
React의 class 생명주기 메소드에 친숙하다면, useEffect Hook을 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋습니다.
위 예시에서 나온 메소드들의 공통점을 알겠는가? 바로 렌더링이 완료된 이후 호출되는 메소드들이라는 것이다. 이렇듯, useEffect는 데이터 가져오기, 구독 설정하기, 수동으로 React 컴포넌트 DOM 수정하기 등, 기존 컴포넌트들을 렌더링 한 이후 어떤 일을 수행할지 설정하는 함수다. 예시를 통해 살펴보자.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const onClickHandler = () => {
setCount(count+1);
}
useEffect(() => {
console.log("count:", count)
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={onClickHandler}>
Click me
</button>
</div>
);
}
해당 코드는 지난번 useState를 설명할 때 사용했던 코드다. 'Click me'버튼 한번 클릭할 때마다 count변수가 1씩 커지고, count값이 변할 때마다 useEffect를 통해 console.log가 실행될 것이다. 이제 useEffect 부분을 한번 더 자세히 살펴보자.
useEffect(() => {
console.log("count:", count)
}, [count])
우리는 여기서 useEffec의 구조를 살펴볼 수 있다. useEffect의 구조는 useEffect(effect, deps)로, deps에 useEffect가 구동될 조건이 되는 변수, effect에 deps에 들어간 변수에 변화가 생길 때 호출될 함수를 넣는다.
위 예시에 대입해보자. 예시 코드에서 effect에 해당하는 값은 console.log("count:", count)다. 그렇다면 이 함수는 언제 호출될까? 바로 deps에 해당하는 값인 count 변수에 변화가 생길 경우다. deps는 dependencys의 줄임말로 "의존"이라는 뜻에 걸맞게 useEffect는 이 deps값의 변화의 의존해 effect가 호출될지 안될지 판단한다. 이 deps는 배열 안에 선언한다. useEffect는 기본적으로 가장 처음 렌더링 될 때 한번, 그 뒤 deps 값에 변화가 생겼을 때 한 번씩 실행된다.
// 1. deps를 안넣은 경우
useEffect(() => {
console.log("count:", count)
})
// 2. deps로 빈 배열을 넣은 경우
useEffect(() => {
console.log("count:", count)
}, [])
// 3. deps로 인자가 있는 배열을 넣은 경우
useEffect(() => {
console.log("count:", count)
}, [count])
그렇다면 이 세 가지 useEffect 함수는 어떤 차이가 있을까? 첫 번째, deps값이 없는 경우를 생각해보자. deps값이 없다면 해당 함수는 새로 렌더링이 될 때마다 조건 상관없이 호출된다. 두 번째, deps로 빈 배열을 넣은 경우에는 페이지가 가장 처음 렌더링 될 때, 단 한 번만 실행된다. 마지막 세 번째로 deps로 인자가 있는 배열을 넣은 경우에는 해당 값들에 변화가 생길 때마다 useEffect함수를 수행해준다.
Clean-up 함수
useEffect기능을 공부하면 가끔 useEffect안에서 함수를 return 하는 경우를 볼 수 있다. 이렇게 useEffect에서 return 되는 함수를 정리(Clean-up) 함수라고 한다. 말 그대로 useEffect를 정리하여 컴포넌트 상태를 초기화하는 역할을 한다고 보면 된다. 클래스형 컴포넌트의 생명주기 메소드와 비교해보자면 componentWillUnmount와 비슷한 기능을 담당한다고 보면 된다. 정리 함수는 다음과 같은 실행 과정을 거친다.
- 컴포넌트 마운트 시, useEffect 실행(단, 이때는 정리 함수는 실행되지 않음)
- 의존 값 변경 시, 해당 useEffect의 정리 함수 호출
- 정리 함수 호출이 끝나면 effect 실행
- 컴포넌트 언마운트 시, useEffect의 정리 함수 호출
보다 직관적인 이해를 위해 실제 예시를 살펴보도록 하자.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const onClickHandler = () => {
setCount(count+1);
}
useEffect(() => {
console.log("count:", count)
return () => {
console.log("cleanUp:", count)
}
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={onClickHandler}>
Click me
</button>
</div>
);
}
위 코드와 그림을 보자. 코드는 앞서 사용하던 예시 코드를 정리 함수를 눈으로 파악하기 쉽도록 조금 수정한 코드다. 아래 사진은 해당 코드를 기반으로 count 변수가 3이 될 때까지 버튼을 클릭한 후 캡처한 사진이다.
사진의 1번 로그는 페이지가 맨 처음 렌더링 될 때 useEffect가 실행된 것이다. 이를 통해 가장 처음 렌더링 될 때 실행되는 useEffect는 정리 함수가 실행되지 않는다는 것을 알 수 있다.
이제 'Click me' 버튼을 클릭해보자. 정리 함수는 useEffect의 effect에 해당하는 함수가 실행되기 전에 먼저 실행된다고 했다. 이는 2번 로그에서 확인할 수 있다. 해당 출력은 정리 함수를 통해 출력된 것으로 count에 1이 추가되기 전에 실행되어 1이 아닌 0을 출력하고 있다. 이 직후 effect가 실행되어 수정된 count가 적용된 1이 출력된다. 그리고 4번~7번은 2,3번 상황의 반복이다.
이제 정리 함수의 실행 순서, 구현 방법은 알았다. 그래서 이걸 왜 쓰는 것일까? 만일 컴포넌트가 마운트 될 때, 이벤트 리스너(이벤트가 발생했을 때, 이를 감지하고 그에 따른 기능을 수행할 수 있게 하는 것)를 통해 이벤트를 추가했다면 컴포넌트 언마운트 시, 해당 이벤트를 삭제해줘야 한다. 그렇지 않다면 컴포넌트가 리렌더링 될 때마다 새로운 이벤트 리스너가 핸들러에 바인딩되고, 이는 메모리 누수로 이어질 수 있다.
즉, 정리 함수는 보통 예시에서 살펴본 DOM에 추가한 이벤트 리스너를 컴포넌트 언마운트 시 제거할 때, 외부 데이터 구독을 설정할 때 등의 경우에 사용해 메모리 누수를 막는 역할을 한다.
useEffect 활용하면 보다 적은 연산과 적은 렌더링을 통한 쾌적한 react 앱을 만들 수 있다. 이 또한 적재적소에 활용하도록 하자.
참고
https://ko.reactjs.org/docs/hooks-effect.html
Using the Effect Hook – React
A JavaScript library for building user interfaces
ko.reactjs.org
https://basemenks.tistory.com/224
react 에서 cleaunup 의 실행과정
아래 코드를 살펴봅시다. useEffect의 의존성(dependency)으로 count을가졌습니다. useEffect(() => { console.log('count1', count); return () => { console.log('count1 CleanUp', count); }; }, [count]);..
basemenks.tistory.com
https://goddaehee.tistory.com/308
[React] 7. React hooks - useEffect란?
7. React hooks - useEffect란? 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ React hook 중 useEffect에 대한 내용 ] 입니다. : ) https://ko.reactjs.org/docs/hooks-effect.html 1. useEffect 훅이란? -..
goddaehee.tistory.com