설명을 이해하기 전 필수 개념 2가지
- Render: DOM Tree를 구성하기 위해 각 element의 스타일 속성을 계산하는 과정
- Paint: 실제 스크린에 Layout을 표시하고 업데이트하는 과정
useEffect는 컴포넌트들이 render 와 paint 된 후 실행된다. 비동기적(asynchronous)으로 실행된다.
paint 된 후 실행되기 때문에, useEffect 내부에 DOM에 영향을 주는 코드가 있을 경우 사용자 입장에서는 화면의 깜빡임을 보게된다. 이렇게 되면 불편한 사용자 경험으로 이어질 수 있다.

import { useEffect, useState } from "react";
function App() {
const [age, setAge] = useState(0);
const [name, setName] = useState("");
useEffect(() => {
setAge(25);
setName("찬민");
}, []);
return (
<>
<div className="App">{`그의 이름은 ${name} 이며, 나이는 ${age}살 입니다.`}</div>
</>
);
}
export default App;

useEffect를 사용한 위 코드는 다음 순서대로 동작한다.
painteffect 내부의 setNumber, setName 호출 물론 이렇게 간단한 DOM 구조라면 금방 렌더링할 수 있겠지만, 화면이 복잡해지면 체감할 수 있을 정도로 렌더링 시간이 증가하게 된다.
useLayoutEffect 훅은 바로 이런 문제를 해결하기 위해 등장한 훅이다
useLayoutEffect 는 컴포넌트들이 render 된 후 실행되며, 그 이후에 paint 가 된다. 이 작업은 동기적(synchronous) 으로 실행된다. paint 가 되기전에 실행되기 때문에 DOM 을 조작하는 코드가 존재하더라도 사용자는 깜빡임을 경험하지 않는다.

화면에 DOM을 그리기 전에 Effect를 수행
import { useLayoutEffect, useState } from "react";
function App() {
const [age, setAge] = useState(0);
const [name, setName] = useState("");
useLayoutEffect(() => {
setAge(25);
setName("찬민");
}, []);
return (
<>
<div className="App">{`그의 이름은 ${name} 이며, 나이는 ${age}살 입니다.`}</div>
</>
);
}
export default App;

useLayoutEffect를 사용한 위 코드는 다음 순서대로 동작한다.
useLayoutEffect 내부의 setNumber, setName 호출
< div >그의 이름은 찬민이며, 나이는 25살 입니다.< /div> 를 paint
useLayoutEffect 는 동기적으로 실행되고 내부의 코드가 모두 실행된 후 painting 작업을 거친다. 따라서 로직이 복잡할 경우 사용자가 레이아웃을 보는데까지 시간이 오래걸린다는 단점이 있어, 기본적으로는 항상 useEffect 만을 사용하는 것을 권장한다고 한다. 구체적인 예시로는
등의 작업은 항상 useEffect 를 사용하되,
const Test = (): JSX.Element => {
const [value, setValue] = useState(0);
useLayoutEffect(() => {
if (value === 0) {
setValue(10 + Math.random() * 200);
}
}, [value]);
console.log('render', value);
return (
<button onClick={() => setValue(0)}>
value: {value}
</button>
);
};
화면이 깜빡거리는 상황일 때, 예를들어 위와같이 state 가 존재하며, 해당 state 이 조건에 따라 첫 painting 시 다르게 렌더링 되어야 할 때는 useEffect 사용 시 처음에 0이 보여지고 이후에 re-rendering 되며 화면이 깜빡거려지기 때문에 useLayoutEffect 를 사용하는 것이 바람직하다고 한다.
useEffect의 이펙트는 DOM이 화면에 그려진 이후에 호출된다.
useLayoutEffect의 이펙트는 DOM이 화면에 그려지기 전에 호출된다.
따라서 렌더링할 상태가 이펙트 내에서 초기화되어야 할 경우, 사용자 경험을 위해 useLayoutEffect를 활용하자!