Mount 동작을 구현하려면, useEffect 안에 콜백 함수를 작성한 뒤 deps에 빈 배열을 넣으면 됩니다. useEffect는 deps 배열에 있는 값이 변경될 때만 실행되기 때문에, 결국 해당 콜백 함수는 App 컴포넌트가 처음 마운트(Mount)될 때 한 번만 실행되고, 이후에는 다시 실행되지 않습니다. 따라서, 콜백 함수 안에 console.log("Mount")를 작성하고 브라우저 콘솔을 확인하면, “Mount”라는 메시지가 최초로 출력됩니다. 이후, 아무리 상태(state)의 값을 변경해도 “Mount” 메시지는 다시 출력되지 않는 것을 확인할 수 있습니다.
import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";
import { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
const [input, setInput] = useState("");
// 1. 마운트 : 탄생
useEffect(() => {
console.log("Mount");
}, []);
const onClickButton = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<input
type="text"
value={input}
onChange={(event) => {
setInput(event.target.value);
}}
/>
</section>
<section>
<Viewer count={count} />
</section>
<section>
<Controller onClickButton={onClickButton} />
</section>
</div>
);
}
export default App;

App 컴포넌트가 마운트(Mount)될 때 단 한 번만 실행해야 하는 코드가 있다면, useEffect를 호출하고 deps에 빈 배열을 전달하면 됩니다.
Update 동작을 구현하려면, useEffect를 호출하고 첫 번째 인수로 콜백 함수를 넣은 뒤, 두 번째 인수인 deps를 생략하면 됩니다. deps를 생략하면 콜백 함수는 Mount 시점에 한 번 실행된 후, App 컴포넌트가 리렌더링될 때마다, 즉 Update가 발생할 때마다 계속 실행됩니다.
예를 들어, 콜백 함수 안에 console.log("Update")를 작성한 뒤 브라우저 콘솔을 확인하면, 컴포넌트가 Mount될 때 “Mount”와 “Update”가 동시에 한 번 출력됩니다. 이후 버튼을 눌러 App 컴포넌트를 리렌더링하면, “Update” 메시지가 브라우저 콘솔에 출력되는 것을 확인할 수 있습니다.
import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";
import { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
const [input, setInput] = useState("");
// 1. Mount : 탄생
useEffect(() => {
console.log("Mount");
}, []);
// 2. Update : 변화, 리렌더링
useEffect(() => {
console.log("Update");
});
const onClickButton = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<input
type="text"
value={input}
onChange={(event) => {
setInput(event.target.value);
}}
/>
</section>
<section>
<Viewer count={count} />
</section>
<section>
<Controller onClickButton={onClickButton} />
</section>
</div>
);
}
export default App;

하지만 Mount 시점을 제외하고, App 컴포넌트가 Mount된 이후에 Update가 발생하는 순간에만 콜백 함수를 실행하고 싶다면, useRef를 사용하여 App 컴포넌트가 Mount되었는지 여부를 체크하는 변수를 만들어 활용하면 됩니다.
isMount라는 변수를 생성하고, useRef를 사용해 새로운 Reference 객체를 만들어 초기값으로 false를 설정합니다. 이는 아직 App 컴포넌트가 Mount되지 않았음을 의미합니다. 이후, 두 번째 useEffect 안에서 조건문을 사용해 isMount.current에 !(Not)을 붙여 현재 값이 false인지 확인합니다. 만약 false라면, 이는 컴포넌트가 아직 Mount되지 않은 상태임을 나타냅니다. 이 시점에서 isMount.current 값을 true로 변경해, 이제 컴포넌트가 Mount되었음을 표시합니다.
import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";
import { useState, useEffect, useRef } from "react";
function App() {
const [count, setCount] = useState(0);
const [input, setInput] = useState("");
const isMount = useRef(false);
// 1. Mount : 탄생
useEffect(() => {
console.log("Mount");
}, []);
// 2. Update : 변화, 리렌더링
useEffect(() => {
if (!isMount.current) {
isMount.current = true;
return;
}
console.log("Update");
});
const onClickButton = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<input
type="text"
value={input}
onChange={(event) => {
setInput(event.target.value);
}}
/>
</section>
<section>
<Viewer count={count} />
</section>
<section>
<Controller onClickButton={onClickButton} />
</section>
</div>
);
}
export default App;
위와 같이 설정하면, 콜백 함수는 App 컴포넌트가 최초로 Mount될 때 실행되지만, 조건문이 참(true)이기 때문에 current 값을 true로 변경한 뒤 바로 반환(return)되므로 조건문 아래의 코드는 실행되지 않습니다. 이후, App 컴포넌트가 리렌더링되어 다시 호출될 때가 되어서야 console.log("Update")가 출력됩니다.


따라서, 실제로 컴포넌트의 Update 단계에서만 코드를 실행하고 싶다면, useRef를 사용해 하나의 Reference 객체를 생성하고 이를 플래그로 활용하면 됩니다.
Unmount를 처리하기 위해 useEffect를 제어하려면, 화면에 나타났다 사라지는 컴포넌트를 추가해야 합니다. 이를 위해, count가 짝수일 때만 화면에 렌더링되는 컴포넌트를 하나 생성합니다.
const Even = () => {
return <div>짝수입니다.</div>;
};
export default Even;
///////////////////////////////////////////////////////
import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";
import Even from "./components/Even";
import { useState, useEffect, useRef } from "react";
function App() {
const [count, setCount] = useState(0);
const [input, setInput] = useState("");
const isMount = useRef(false);
// 1. Mount : 탄생
useEffect(() => {
console.log("Mount");
}, []);
// 2. Update : 변화, 리렌더링
useEffect(() => {
if (!isMount.current) {
isMount.current = true;
return;
}
console.log("Update");
});
const onClickButton = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<input
type="text"
value={input}
onChange={(event) => {
setInput(event.target.value);
}}
/>
</section>
<section>
<Viewer count={count} />
{count % 2 === 0 ? <Even /> : null}
</section>
<section>
<Controller onClickButton={onClickButton} />
</section>
</div>
);
}
export default App;
Even 컴포넌트에서 useEffect를 사용하여 Even 컴포넌트의 Unmount 시점을 제어할 수 있습니다. 이를 위해, useEffect의 첫 번째 인수로 콜백 함수를 작성하고, 두 번째 인수로 빈 배열을 전달합니다. 그런 다음, 콜백 함수 안에서 return을 사용해 새로운 화살표 함수(arrow function)를 반환하면 됩니다.
import { useEffect } from "react";
const Even = () => {
useEffect(() => {
return () => {
console.log("Unmount");
};
}, []);
return <div>짝수입니다.</div>;
};
export default Even;
useEffect의 콜백 함수가 반환하는 함수를 클린업 함수 또는 정리 함수라고 부르며, 이는 useEffect가 종료될 때 실행됩니다. 현재와 같이 deps를 빈 배열로 전달하면 useEffect는 언제 실행될까요? 컴포넌트가 Mount될 때 실행됩니다. 반대로, useEffect의 종료는 컴포넌트가 Unmount될 때 이루어지며, 이 시점에서 정리 함수가 호출됩니다.
