본문 바로가기
React

[React] 리액트의 렌더링 사이클(useLayoutEffect , useEffect)

by goblin- 2024. 9. 21.

리액트의 렌더링 사이클과 useLayoutEffect 및 **useEffect**의 실행 시점은 리액트의 성능 최적화와 관련된 중요한 개념입니다. 이 부분을 이해하면 리액트의 렌더링 흐름을 더 잘 관리하고, 컴포넌트가 언제 업데이트되고, 언제 DOM에 반영되는지 정확히 알 수 있습니다.

1. 리액트의 렌더링 사이클 개요

 

리액트에서 상태(state)나 props가 변경되면, 그에 따라 컴포넌트는 다시 렌더링됩니다. 이때 리액트의 렌더링 사이클은 크게 두 가지 주요 단계를 거칩니다:

 

1. Render (렌더링 단계):

이 단계에서는 리액트가 가상 DOM을 생성하고, 새로운 Virtual DOM과 이전 Virtual DOM을 비교하여 변경 사항을 찾는 작업이 수행됩니다. 이 과정에서는 브라우저의 실제 DOM을 아직 건드리지 않습니다.

2. Commit (커밋 단계):

변경된 사항이 확인되면, 리액트는 실제 DOM에 변경된 내용을 적용합니다. 이 단계에서는 브라우저가 레이아웃을 계산하고, 화면을 다시 그립니다(리플로우와 리페인트). 이 과정이 완료된 후 브라우저에 최종적으로 사용자에게 보이는 화면이 표시됩니다.

 

이 렌더링 과정에서 각기 다른 시점에 리액트의 훅(Hooks)이 실행됩니다. 특히, useEffectuseLayoutEffect는 렌더링 사이클의 다른 부분에서 실행되므로, 올바르게 이해하고 적절한 상황에 사용해야 합니다.

 

2. useEffect와 useLayoutEffect의 차이

 

useEffect

 

언제 실행되는가?

useEffectDOM 업데이트 후에 실행됩니다. 즉, 컴포넌트의 렌더링이 완료된 후, 화면에 변경 사항이 적용된 이후에 호출됩니다.

브라우저가 사용자에게 화면을 그린 뒤에 비동기 작업이나, 추가적인 사이드 이펙트를 처리하는 데 적합합니다.

어떤 작업에 적합한가?

비동기 작업 (API 호출, 데이터 페칭)

DOM에 직접적인 영향을 주지 않는 작업 (로깅, 이벤트 리스너 추가 등)

리렌더링을 유발하지 않는 작업

 

예시:

useEffect(() => {
  console.log("컴포넌트가 화면에 렌더링된 후 실행됨");
  fetch('https://api.example.com/data') // 비동기 데이터 로드
    .then(response => response.json())
    .then(data => setData(data));

  return () => {
    console.log("컴포넌트가 언마운트될 때 실행됨");
  };
}, []); // 빈 배열이므로 컴포넌트가 처음 마운트될 때만 실행됨

여기서 useEffectDOM이 완전히 업데이트된 후에 실행되므로, 화면에 변화가 발생한 이후 비동기 작업이나 DOM과 직접 상관없는 로직을 처리하기에 적합합니다.

 

useLayoutEffect

 

언제 실행되는가?

useLayoutEffectDOM 업데이트 직후, 브라우저가 화면을 그리기 전에 실행됩니다. 이 말은 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