생명주기(Life Cycle)
React 컴포넌트는 태어나서(생성), 살아가고(렌더링), 죽는(소멸) 과정을 거치는데, 이걸 생명주기라고 부른다. Mounting(탄생), Updating(삶), Unmounting(죽음) 세 단계로 나뉜다.


클래스 컴포넌트에서는 componentDidMount, componentDidUpdate, componentWillUnmount 같은 생명주기 메서드를 사용할 수 있고, 함수 컴포넌트에서는 useEffect Hook을 사용하여 비슷한 작업을 수행할 수 있다.
class Component의 생명 주기 메서드는 생략하고 useEffect hook에 대해서 알아보자
useEffect()가 무엇이냐?
useEffect()는 React한테 "야, 이 컴포넌트 렌더링될 때마다(혹은 조건 따라) 내가 시키는 특별한 작업 좀 해!" 라고 하는 Hook이다. 컴포넌트 화면에 나오고 나서 "이제 뭐 하지?" 하면, useEffect가 "이거 해!" 하는 거다.
쉽게 말하면, useEffect는 화면에 보이는 거 말고, 컴포넌트 생명 주기에 맞춰 해야 하는 부수 효과(side effects) 처리하는 담당자다. 데이터 가져오기, 타이머 설정, 외부 API 호출 같은 거.
useEffect를 쓰는 방법은 다음과 같다.
import { useEffect } from 'react';
useEffect(() => {
// 부수 효과를 수행하는 코드
}, [의존성 배열]);
useEffect()는 두 개 인자를 받는다. 첫 번째는 실행할 함수, 두 번째는 의존성 배열이다. 이 배열에 따라 useEffect 실행 시점이 정해진다!
위에서 말했듯이 useEffect()는 3가지의 함수를 처리하는 경우를 혼자 처리할 수 있다.
그럼 어떻게 혼자 처리할 수 있을까?
useEffect를 어떻게 작성하냐에 따라서 특정 시점에 useEffect를 호출할 수 있다.
예제를 살펴보면서 경우를 하나씩 알아보자.
import './App.css';
import { useEffect, useState } from 'react';
function App() {
const [showCounter, setShowCounter] = useState(false);
return (
<>
{showCounter && <Counter />}
<br />
<button className="show-btn" onClick={() => setShowCounter(!showCounter)}>
Show?
</button>
</>
);
}
function Counter() {
const [counter, setCounter] = useState(1);
const [counter2, setCounter2] = useState(100);
useEffect(() => {
console.log('컴포넌트가 마운트됨!');
}, []);
useEffect(() => {
console.log('리렌더링됨!');
});
useEffect(() => {
console.log('counter2 값이 변경됨');
}, [counter2]);
useEffect(() => {
return () => {
console.log('컴포넌트가 언마운트됨!');
};
}, []);
return (
<section>
<div>
<div>counter : {counter}</div>
<button onClick={() => setCounter(counter + 1)}>증가</button>
</div>
<div>
<div>counter : {counter2}</div>
<button onClick={() => setCounter2(counter2 - 1)}>감소</button>
</div>
</section>
);
}
export default App;
// 1. 컴포넌트가 최초 렌더링 되는 경우에 실행되는 useEffect 함수를 구현(마운트)
useEffect(() => {
console.log('컴포넌트가 마운트됨!');
}, []);
두 번째 인자에 빈 배열 [] 넣으면, 처음 화면에 그려질 때 딱 한 번만 실행돼. 초기 설정할 때 좋다.
// 2. 컴포넌트가 리렌더링 되는 경우에 실행되는 useEffect 함수를 구현 (업데이트)
useEffect(() => {
console.log('리렌더링됨!');
});
두 번째 인자를 비워두면, 처음 렌더링될 때랑 이후에 다시 렌더링될 때마다 계속 실행된다.
// 3. counter2 값이 변경되는 경우에 실행되는 useEffect 함수를 구현 (특정 값 업데이트)
useEffect(() => {
console.log('counter2 값이 변경됨');
}, [counter2]);
두 번째 인자에 [counter2] 넣으면, counter2 값 바뀔 때만 실행된다. 특정 값 변화에 반응해야 할 때 쓴다.
// 4. 클린업 함수를 작성 (언마운트)
useEffect(() => {
return () => {
console.log('컴포넌트가 언마운트됨!');
};
}, []);
useEffect 안에서 함수 return 하면 그게 클린업 함수다. 컴포넌트 사라지기 직전에 호출돼서, 남은 작업을 정리해 준다. 두 번째 인자에 빈 배열 [] 넣어서 언마운트될 때 딱 한 번 실행한다.
컴포넌트 생명 주기는 컴포넌트가 처음 렌더링되는 Mounting, 업데이트되는 Updating, 그리고 화면에서 사라지는 Unmounting의 세 단계로 이루어진다!
함수 컴포넌트에서는 useEffect Hook을 통해 이러한 생명 주기와 관련된 부수 효과를 처리할 수 있다. useEffect의 두 번째 인자인 의존성 배열을 어떻게 설정하느냐에 따라 실행 시점이 결정된다!
useEffect 활용
useEffect(() => { /* ... */ }, []); - 빈 배열을 의존성으로 전달하여 처음 렌더링될 때 한 번만 실행한다.useEffect(() => { /* ... */ }); - 두 번째 인자를 생략하여 매 렌더링마다 실행한다.useEffect(() => { /* ... */ }, [특정값]); - 특정 값이 변경될 때만 실행한다.useEffect(() => { return () => { /* ... */ }; }, []); - 클린업 함수를 반환하여 컴포넌트가 사라질 때 정리 작업을 수행한다.