side effect가 뭘까? 다들 잘 알듯이, 부작용이라는 뜻이다.
현실에서와 프로그래밍에서의 부작용의 뜻이 약간 다른데, 프로그래밍에선 말 그대로 내가 의도치 않은 부수적인 작용이 일어나는 것을 말한다.
예를 들자면,
let count = 0
function greetWithSideEffect(name) { // Input
count = count + 1 // Side Effect!
return `${name}님 안녕하세요!` // Output
}
greetWithSideEffect() 라는 함수는 이름을 받아 인삿말을 리턴하는 함수이다.
하지만 이 함수는 단순히 input과 output만 존재하는 함수가 아니다. 실행하는 중간에 함수 스코프 외부에 있는 count 변수의 값을 변경하는 프로세스가 섞여있다. 이는 함수의 결과값 이외의 다른 상태를 변경시키는 행위에 해당하므로 Side Effect 가 있다고 할 수 있는 것이다.
이러한 예시 말고도, 우리에게 아주 친숙한 예로 console.log 등이 있다!
console.log 함수를 사용하면 값을 계산해서 리턴하는 게 아니라 콘솔 창에 문자열을 출력하게 되는데, 이 자체가 외부 상태를 변경해서 문자열을 출력하는 것이다.
이렇게 함수 내부에서 함수 외부에 있는 값 또는 상태를 변경하는 행위를 'side effect'라고 한다.
하지만 이 side effect가 단어 뜻 그대로 나쁘기만한 순수 쓰레기같은 친구인가 하면~~~
그건 또 아니다.
useEffect 는 리액트 컴포넌트 함수 안에서 이 side effect를 실행하고 싶을 때 사용하는 함수이기 때문이다.
DOM 노드를 직접 변경하기도 하며,
브라우저에 데이터를 저장할 수도 있고,
네트워크 통신 요청를 보내는 것처럼 말이다.
상기하였듯, 주로 리액트 외부에 있는 데이터나 상태를 변경할 때 사용하는데,
간단한 예시를 보면 이해에 좀 더 좋을 것 같다.
페이지 내 데이터 변경
useEffect(() => {
document.title = title;
}, [title]);
네트워크 요청
useEffect(() => {
fetch('https://alpha.bravo/delta')
.then((res) => res.json())
.then((result) => setData(result));
}, [])
데이터 저장
useEffect(() => {
localStorage.setItem('Echo', Echo);
}, [Echo]);
타이머
useEffect(() => {
const tikTok = setInterval(() => {
setSecond((prevSec) => prevSec + 1);
}, 1000);
return () => {
clearInterval(tikTok);
}
}, []);
useEffect는 보통 '동기화'에 쓰면 유용한 경우가 많다. 여기서 동기화란, 컴포넌트 내부의 데이터와 그 외부에 있는 데이터를 일치시키는 것을 의미한다.
이게 뭔 개소리요 싶으실텐데, 어떤 의미인지 이번에도 간단한 예시를 가져왔다.
import { useEffect, useState } from 'react';
const TITLE = 'Untitled';
export default function ChangeTitle() {
const [title, setTitle] = useState(Title);
const titleChangeHandler = (e) => {
const inputText = e.target.value;
setTitle(inputText);
};
const titleClearHandler = () => {
setTitle(TITLE);
};
useEffect(() => {
document.title = title;
}, [title]);
return (
<div>
<input value={title} onChange={titleChangeHandler} />
<button onClick={titleClearHandler}>RESET</button>
</div>
);
}
상단의 ChangeTitle컴포넌트는 입력 값에 따라 페이지 타이틀을 바꾸는 컴포넌트이다.
만약 이 코드를 핸들러 함수만 사용해서 처리를 하려 했다면, 핸들러마다
document.title = inputText or TITLE;
을 포함하는 코드가 있었어야 할 것이다.
하지만 우리는 useEffect를 사용함으로써 document.title이라는 side effect를 다루는 부분만 따로 처리할 수 있었고,
document.title 을 변경하는 코드를 신경 쓰지 않고 사용할 수 있어 편리해 질 수 있겠다!
즉, useEffect 를 사용했을 때 반복되는 코드를 줄이고, 동작을 쉽게 예측할 수 있는 코드를 작성할 수 있게 되는 것이다.