당신은 리액트를 쓰면서 useEffect를 얼마나 사용하는가?
useEffect는 React 컴포넌트에서 side effect를 처리하는데 너무나도 중요하게 사용되는 훅이다.
useEffect에 대해서 알아보려면 side effect에 대한 이야기부터 차근차근 밟아가야 한다.
Side Effect란?
간단하게 한국어로 번역하면 부수효과라는 뜻으로 함수나 표현식의 실행 결과 외에 발생하는 영향을 말한다.
우리가 일반적으로 프로그래밍을 할 때에는 함수나 표현식의 결과값을 반환하는 것을 기대한다. 하지만 예상한 결과 값 외에도 외부 상태를 변경하거나 다른 부작용을 발생시키는 경우가 있다. 이런 외부 상태의 변경이나 부작용을 Side Effect라고 부른다. 보통 비동기적으로 처리되는 효과들을 Side Effect라고 부른다고 생각하면 편하다.
Side Effect는 다음과 같은 형태로 나타날 수 있다.
- 데이터 요청: 백엔드 서버에서 데이터를 가져오거나 전송 할 때 발생
- DOM을 직접 조작: 리액트에서 HTML 요소를 직접 생성 또는 수정 또는 삭제할 때 발생
- 이벤트 핸들링: 사용자 입력에 반응하여 동작을 수행 할 때 발생
- 타이머 설정: setTimeout, setInterval 같은 타이밍 함수를 사용할 때 발생
Functional Component 이전에 Class Component를 사용 할 때에는 ComponentDidUpdate() 함수 같은 Life Cycle Hook들을 활용하여 Side Effect를 처리했다. 리액트 16 버전 이후로는 Functional로 작성하기 때문에 useEffect 훅을 사용하여 해결할 수 있다.
useEffect, 어떻게 사용 하는 건데?
리액트 class 생명주기 메서드와 익숙한가? useEffect Hook은 componentDidMount, ComponentDidUpdate, ComponentWillMount가 모두 합쳐진 것과 같은 효과를 낸다.
useEffect 훅은 컴포넌트가 렌더링될 때마다 useEffect 내에 있는 콜백 함수를 실행하거나, 컴포넌트의 상태나 속성이 바뀔 때마다 특정 작업을 수행할 수 있게 해 준다.
사용 법은 다음과 같다.
useEffect(() => {
// 특정 작업 수행
// ...
return () => {
// Cleanup 작업 수행
// ...
};
}, [dependency]);
먼저 useEffect는 useEffect(콜백함수, dependency)로 구성되어 있다.
위 코드에서 () => {//특정 작업 수행} 부분에서는 useEffect가 수행할 작업을 작성한다. 기본적으로 useEffect를 포함하는 컴포넌트가 렌더링 될 때 실행 된다.
return () => { // Cleanup 작업 수행} 부분은 Cleanup 함수를 반환하는 부분으로 useEffect를 포함하는 컴포넌트의 생명주기가 끝날 때 또는 다음 useEffect 콜백 함수 호출 전에 실행되는 Cleanup 작업을 정의하는 데 사용된다. 일반적으로 Unmounting 단계와 같다고 생각하면 좋다.
[dependency] 부분은 말 그대로 useEffect의 의존성 배열이다. 이 의존성 배열 안에 특정 상태(useState로 만든 변수) 또는 속성을 넣고 의존성들이 변경될 때마다 useEffect가 실행되도록 지정하는 역할을 한다. 의존성 배열은 빈 배열로라도 존재해야 하고, 빈 배열일 경우 컴포넌트가 첫 렌더링 될 때 한 번만 콜백 함수가 동작한다.
이렇게 useEffect를 사용하면 컴포넌트의 생명주기에 따른 작업을 처리하고, 상태와 속성의 변화에 따른 작업을 해낼 수 있다.
useEffect가 필요한 경우
간단하게 useEffect가 필요한 경우에 대해 알아보자.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
// 컴포넌트가 렌더링될 때마다 데이터 가져오기
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
setData(data);
};
// 컴포넌트 렌더링 시 데이터 가져오기
fetchData();
return (
<div>
<p>Count: {count}</p>
<p>Data: {data}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
export default Example;
위와 같은 코드가 있을 때 fecthData 함수는 컴포넌트가 렌더링 될 때마다 데이터를 가져와서 data 상태를 업데이트 하도록 되어있다. 이런 방식으로 작성하면 컴포넌트가 렌더링 될 때마다 데이터를 가져오기 때문에 잘못된 코드라고 볼 수 있다.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
// 컴포넌트가 렌더링될 때마다 데이터 가져오기
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
setData(data);
};
fetchData();
}, []);
return (
<div>
<p>Count: {count}</p>
<p>Data: {data}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
export default Example;
반면 위의 코드를 살펴보자. useEffect를 사용하여 데이터 가져오기 작업을 처리한다. 의존성 배열에 빈 배열을 전달하기 때문에 Example 컴포넌트가 처음 렌더링 될 때만 데이터를 가져오도록 한다.
정리
useEffect의 동작 방식으로 정리 해보자.
- 컴포넌트가 렌더링 될 때 useEffect 훅 내에 콜백 함수가 실행된다.
- 콜백 함수 내에 있는 작업(sideEffect)이 수행된다.
- 컴포넌트가 unMount 될 때 또는 다음 useEffect가 실행될 때 Cleanup 함수가 실행된다. Cleanup 함수는 대개 이전 useEffect에서 생성한 작업들을 정리하는 역할을 한다.
- React는 다음 렌더링시에 useEffect 호출을 스케줄링하기 위해 현재 훅을 저장한다.
- 컴포넌트가 다시 렌더링 될 때 이전 렌더링에서 저장한 useEffect 호출과 비교하여 의존성 배열의 값이 바뀌었는지 확인한다.
- 의존성 배열 값이 변경되었다면 Cleanup 함수가 실행된 후에 다시 useEffect 훅 내의 함수가 호출된다. 변경되지 않았다면 useEffect 콜백 함수 실행은 넘어간다.
다음 공식문서를 보면 더욱더 useEffect에 대해 탐구할 수 있다.
https://react.dev/reference/react/useEffect
'프론트엔드 개발 > React' 카테고리의 다른 글
[알고 쓰자] React 작동 원리 (0) | 2023.03.08 |
---|---|
[React] 기본 Hooks (0) | 2022.07.11 |
[React] Higher Order Component (0) | 2022.07.11 |
[React] Styled Components (0) | 2022.07.06 |
[React] Router - JSX로 라우팅 이동하기 (0) | 2022.07.05 |