리액트의 렌더링 사이클과 useLayoutEffect 및 **useEffect**의 실행 시점은 리액트의 성능 최적화와 관련된 중요한 개념입니다. 이 부분을 이해하면 리액트의 렌더링 흐름을 더 잘 관리하고, 컴포넌트가 언제 업데이트되고, 언제 DOM에 반영되는지 정확히 알 수 있습니다.
1. 리액트의 렌더링 사이클 개요
리액트에서 상태(state)나 props가 변경되면, 그에 따라 컴포넌트는 다시 렌더링됩니다. 이때 리액트의 렌더링 사이클은 크게 두 가지 주요 단계를 거칩니다:
1. Render (렌더링 단계):
• 이 단계에서는 리액트가 가상 DOM을 생성하고, 새로운 Virtual DOM과 이전 Virtual DOM을 비교하여 변경 사항을 찾는 작업이 수행됩니다. 이 과정에서는 브라우저의 실제 DOM을 아직 건드리지 않습니다.
2. Commit (커밋 단계):
• 변경된 사항이 확인되면, 리액트는 실제 DOM에 변경된 내용을 적용합니다. 이 단계에서는 브라우저가 레이아웃을 계산하고, 화면을 다시 그립니다(리플로우와 리페인트). 이 과정이 완료된 후 브라우저에 최종적으로 사용자에게 보이는 화면이 표시됩니다.
이 렌더링 과정에서 각기 다른 시점에 리액트의 훅(Hooks)이 실행됩니다. 특히, useEffect와 useLayoutEffect는 렌더링 사이클의 다른 부분에서 실행되므로, 올바르게 이해하고 적절한 상황에 사용해야 합니다.
2. useEffect와 useLayoutEffect의 차이
useEffect
• 언제 실행되는가?
• useEffect는 DOM 업데이트 후에 실행됩니다. 즉, 컴포넌트의 렌더링이 완료된 후, 화면에 변경 사항이 적용된 이후에 호출됩니다.
• 브라우저가 사용자에게 화면을 그린 뒤에 비동기 작업이나, 추가적인 사이드 이펙트를 처리하는 데 적합합니다.
• 어떤 작업에 적합한가?
• 비동기 작업 (API 호출, 데이터 페칭)
• DOM에 직접적인 영향을 주지 않는 작업 (로깅, 이벤트 리스너 추가 등)
• 리렌더링을 유발하지 않는 작업
예시:
useEffect(() => {
console.log("컴포넌트가 화면에 렌더링된 후 실행됨");
fetch('https://api.example.com/data') // 비동기 데이터 로드
.then(response => response.json())
.then(data => setData(data));
return () => {
console.log("컴포넌트가 언마운트될 때 실행됨");
};
}, []); // 빈 배열이므로 컴포넌트가 처음 마운트될 때만 실행됨
• 여기서 useEffect는 DOM이 완전히 업데이트된 후에 실행되므로, 화면에 변화가 발생한 이후 비동기 작업이나 DOM과 직접 상관없는 로직을 처리하기에 적합합니다.
useLayoutEffect
• 언제 실행되는가?
• useLayoutEffect는 DOM 업데이트 직후, 브라우저가 화면을 그리기 전에 실행됩니다. 이 말은 DOM이 업데이트되었지만, 아직 브라우저에서 페인팅(그리기) 작업이 수행되기 전에 코드가 실행된다는 의미입니다.
• 따라서 useLayoutEffect는 DOM이 변경된 직후에 동기적으로 실행되며, 이 작업이 완료되기 전까지 브라우저는 화면을 그리지 않습니다. 이는 레이아웃에 영향을 미치는 작업을 처리할 때 유용합니다.
• 어떤 작업에 적합한가?
• DOM 요소를 즉시 수정하거나, 레이아웃이나 스타일을 동기적으로 적용해야 할 때
• 화면이 그려지기 전에 특정 DOM 조작이 필요할 때 (레이아웃을 변경하는 작업 등)
• 크기나 위치 계산과 같은 동기적인 레이아웃 작업
예시:
useLayoutEffect(() => {
const box = document.getElementById('box');
const boxHeight = box.getBoundingClientRect().height;
if (boxHeight > 200) {
box.style.backgroundColor = 'red'; // 레이아웃 변경
}
}, []);
• 여기서 useLayoutEffect는 DOM이 변경되자마자 바로 실행되며, 브라우저가 화면에 반영하기 전에 레이아웃 수정 작업을 동기적으로 처리합니다. 이는 UI의 크기나 스타일이 정확하게 적용되도록 보장합니다.
3. 리액트 렌더링 사이클에서 두 훅의 실행 순서
이미지에서 설명한 렌더링 과정에서의 실행 순서를 보면:
1. Render 단계: 리액트는 JSX로부터 새로운 VDOM을 생성하고, diffing을 통해 이전 VDOM과 비교합니다. 이 단계에서는 아직 실제 DOM이 변경되지 않았습니다.
2. Commit 단계: 리액트가 변경 사항을 실제 DOM에 반영합니다. 이때, DOM Mutation이 발생하고, DOM이 브라우저에 의해 업데이트됩니다.
3. useLayoutEffect 실행:
• DOM 변경 사항이 발생한 직후, 브라우저가 레이아웃을 계산하기 전에 useLayoutEffect가 실행됩니다.
• 이 단계에서 레이아웃을 변경하거나, DOM에 대한 동기적인 처리가 필요할 경우 작업을 수행할 수 있습니다.
4. 레이아웃과 페인트:
• 브라우저가 새로운 레이아웃을 계산하고, 화면을 다시 그립니다. 즉, 화면에 변화가 실제로 나타나는 시점입니다.
5. useEffect 실행:
• 페인트가 끝난 후에, useEffect가 실행됩니다. 이때는 화면에 모든 것이 표시된 상태이므로, 비동기 작업이나 렌더링에 직접적인 영향을 주지 않는 작업을 처리할 수 있습니다.
4. 언제 useLayoutEffect와 useEffect를 사용해야 할까?
• useEffect를 사용할 때:
• 성능 최적화를 위해, 가능하면 useEffect를 사용하는 것이 좋습니다.
• 화면이 업데이트된 이후에 실행되므로, 브라우저의 렌더링에 영향을 주지 않습니다. 비동기 작업이나, 화면에 영향을 미치지 않는 작업을 처리하기에 적합합니다.
• useLayoutEffect를 사용할 때:
• 레이아웃 조작이나 DOM 요소의 크기/위치 조정 등, 브라우저가 화면을 그리기 전에 반드시 처리해야 하는 작업이 있을 때 사용합니다.
• DOM을 동기적으로 변경하거나, 레이아웃 계산에 영향을 미치는 작업이 필요할 때 적합합니다. 하지만 이 훅을 남용하면 성능에 영향을 미칠 수 있으므로 필요한 경우에만 사용해야 합니다.
5. 렌더링 최적화 전략
1. 가능하면 useEffect 사용:
• 대부분의 사이드 이펙트는 useEffect로 처리하는 것이 성능에 더 유리합니다. 이는 브라우저의 렌더링 이후에 실행되기 때문에, UI가 깜빡이는 현상을 방지할 수 있습니다.
2. useLayoutEffect는 필요한 경우에만 사용:
• DOM 조작이 즉시 필요하거나, 레이아웃에 영향을 주는 작업이 필요할 때만 사용해야 합니다. useLayoutEffect는 동기적으로 실행되기 때문에 렌더링 성능에 영향을 줄 수 있습니다.
요약:
• **useEffect**는 렌더링이 완료된 후 실행되며, 주로 비동기 작업이나 렌더링 이후에 처리해야 할 작업을 수행합니다.
• **useLayoutEffect**는 DOM 변경 직후, 브라우저가 레이아웃을 계산하기 전에 실행되며, 레이아웃이나 DOM을 동기적으로 조작해야 할 때 사용됩니다.
• 렌더링 성능 최적화를 위해, 기본적으로 **useEffect**를 사용하고, 즉시 DOM 변경이 필요할 때만 **useLayoutEffect**를 사용하는 것이 좋습니다.
'React' 카테고리의 다른 글
[React] Context Api (1) | 2024.09.21 |
---|---|
[React] VDOM의 원리 (1) | 2024.09.21 |
[React] 직접적인 Dom조작은 피해라 (1) | 2024.09.15 |
[React] 커스텀 훅 만들기 (2) | 2024.09.15 |
[React] useRef 와 useEffect의 차이점 (0) | 2024.09.14 |