필수 개념
- Render: DOM Tree 를 구성하기 위해 각 엘리먼트의 스타일 속성을 계산하는 과정
- Paint: 실제 스크린에 Layout을 표시하고 업데이트하는 과정 즉 시각적 변경 사항이 사용자에게 표시되는 것을 말함.
개념
1) useEffect
useEffect는 컴포넌트들이 render와 paint된 후 실행된다. 이는 **비동기적(asynchronous)**으로 실행된다. paint된 후 실행되기 때문에, useEffect내부에 DOM에 영향을 주는 코드가 있을 경우 사용자 입장에서는 화면의 깜빡임을 보게된다.
2) useLayoutEffect
useLayoutEffect는 컴포넌트들이 render된 후 실행되며, 그 이후에 paint가 되게 된다. 이 작업은 동기적(synchronous) 으로 실행되며, paint가 되기전에 실행되기 때문에 DOM을 조작하는 코드가 존재하더라도 사용자는 깜빡임을 경험하지 않게 된다.
useLayoutEffect와 useEffect의 차이점
위에서 보듯이 둘의 라이프 사이클을 다르다. 이 둘의 큰 차이점은 render와 paint의 실행되는 라이프 사이클이 다른 것이다.
React 컴포넌트의 라이프사이클
- 사용자는 앱과 state나 props의 구성 요소 변경과 상호 작용한다.
- React는 DOM(Document Object Model)을 업데이트한다.
- useLayoutEffect가 랜더된다.
- 시각적 변경 사항이 사용자에게 표시된다.(paint)
- useEffect가 랜더된다.
그러면 언제 useEffect를 쓰고 언제 useLayoutEffect 써야할까?
useLayoutEffect 는 동기적으로 실행되고 내부의 코드가 모두 실행된 후 painting 작업을 거치게 된다. 이말은 브라우저가 useLayoutEffect를 그리기 전에 실행되고 사용자가 시각적인 변화를 보기 전에 발생한다는 말이다.
1) useEffect를 사용해야 할 때
따라서 로직이 복잡할 경우 사용자가 레이아웃을 보는데까지 시간이 오래걸린다는 단점이 있어, 기본적으로는 useEffect을 사용하는 것을 권장한다. 구체적인 예시로는
- 데이터 fetch
- event handler
- state reset
등의 작업은 항상 useEffect 를 사용하되,
2) useLayoutEffect를 사용해야 할 때
DOM을 직접 업데이트하고 UI를 시각적으로 변경해야 하는 경우 useLayoutEffect. 더 매끄럽게 만들고 깜박임 문제를 개선해야 할 때 사용하는 것이 좋다.
useLayoutEffect 코드
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 를 사용하는 것이 바람직하다.
덧붙여,
React 18에서 useEffect를 사용하면 사용자가 버튼을 클릭하여 새 메시지를 추가하더라도 부드러운 스크롤을 얻을 수 있다. 이것이 npm 패키지를 최신 상태로 유지해야 하는 것을 말해준다.
둘 다 장점과 단점이 존재하기에 필요한 순간에 알아서 적절히 사용하면 될 듯하다. 또한, 위에서 말한 것처럼 npm 패키지와 다른 라이브러리를 최신으로 유지하여 깜박임이 보이지 않게 해야 할 것이다.