1. React Profiler
React Profiler는 React Developer Tools의 일부로 제공되며, 애플리케이션 렌더링 성능을 측정하는 데 도움을 준다.
성능을 측정하고자 하는 부분을 녹화한 후 중지를 누르면 컴포넌트별 렌더링 여부와 렌더링 시간 등을 볼 수 있다.
Profiler 사용법
import { Profiler } from 'react';
function App() {
return (
<div>
<Profiler id="Navigation" onRender={onRenderCallback}>
<Navigation {...props} />
</Profiler>
<Main {...props} />
</div>
);
}
onRender 콜백 함수
onRender 콜백 함수는 Profiler가 렌더링이 완료될 때마다 호출되며, 이 함수는 렌더링된 컴포넌트의 성능 데이터를 제공한다.
onRender 함수는 다음과 같은 입력값을 받는다.
- id: 렌더링된 프로파일 트리의 식별자.
- phase: 현재 렌더링이 "mount"(처음 렌더링)인지 "update"(업데이트 중)인지
- actualDuration: 렌더링이 완료되기까지 소요된 실제 시간
- baseDuration: 컴포넌트 트리를 렌더링하는 데 예상되는 시간(메모리 최적화된 예상 시간)
- startTime: React가 업데이트를 렌더링하기 시작한 시간
- commitTime: React가 이 업데이트를 커밋한 시간
- interactions: 이 업데이트와 관련된 상호작용 집합
2. React.lazy
React.lazy는 Lazy Loading을 통해 애플리케이션을 여러 개의 작은 번들로 나누어 코드 스플리팅을 간편하게 구현할 수 있게 해주는 함수이다. 이를 사용하면 웹 페이지가 처음 로드될 때 필요하지 않은 리소스를 미리 불러오지 않으며, 사용자가 특정 기능이나 페이지를 요청할 때 해당 코드만 불러온다. 그 결과, 초기 로딩 시간이 단축되고, 불필요한 코드가 로드되지 않아 성능이 개선된다.
Suspense
React.lazy를 사용하여 컴포넌트를 동적으로 불러올 때, 컴포넌트를 로드하는 동안 잠깐의 지연이 발생할 수 있다.
이때 사용자는 잠깐 비어 있는 화면을 보게 되는데, Suspense는 이 화면을 채워준다.
Suspense는 로딩 중인 컴포넌트를 대신해 로딩 상태를 보여주고, 컴포넌트가 로드되면 해당 컴포넌트를 렌더링한다.
import {Suspense, lazy} from "react";
import {BrowserRouter as Router, Routes, Route} from "react-router-dom";
const Home = lazy(()=> import('./routes/Home'));
const About =lazy(()=> import('./routes/About'));
const App = () =>(
<Router>
<Suspense fallback ={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About/>}/>
</Routes>
</Suspense>
</Router>
);
- 이때, lazy를 적용할 컴포넌트는 반드시 default로 export된 컴포넌트이어야 한다.
3. Debounce & Throttle
아래와 같은 코드에서 사용자에게 입력값을 받는다면 입력값이 변경될 때마다 onChange 이벤트가 발생한다.
이로 인해 매번 상태 업데이트와 함께 리렌더링이 이루어져 성능이 저하된다.
import { useState } from 'react';
const MainPage = () => {
const [state, setState] = useState('');
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setState(e.target.value);
};
console.log(state);
return <input onChange={onChange} />;
};
export default MainPage;
이러한 리렌더링을 최적화하기 위해서는 입력이 끝난 후에 상태를 업데이트하는 방법이 있고, 일정 주기로 업데이트하는 방법이 있다.
이러한 기술을 해주는 게 Debounce와 Throttle이다. 둘 다 함수의 연속적인 실행을 제한하는 목적을 가지고 있다.
Debounce
Debounce는 마지막 이벤트 이후 특정 시간 동안 추가 이벤트가 발생하지 않을 때만 함수를 실행한다.
Debounce는 Trailing과 Leading 두 종류가 있다.
1. Raw events over time
실제 발생한 이벤트
2. Trailing Debounce
마지막 이벤트 이후 일정 시간(delay)이 지나면 함수 실행
3. Leading Debounce
최초 이벤트에 대해서만 함수를 실행하고 지정한 기간의 타이머 작동, 이후 이벤트는 제한
지정한 기간 이내에 다시 이벤트가 발생하면 타이머 초기화
4. Trailing and Leading Debounce
Trailing과 Leading Debounce의 혼합
Debounce 예시
import { useState } from 'react';
const MainPage = () => {
const debounce = (fn, delay) => {
let timeout;
return (...args) => {
let result;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
result = fn(...args);
}, delay);
return result;
};
};
const [state, setState] = useState('');
const onChange = (e) => {
setState(e.target.value);
};
console.log(state);
const debouncedOnChange = debounce(onChange, 500); // 500ms 지연
return <input onChange={debouncedOnChange} />;
};
export default MainPage;
- Debounce를 활용하여 입력값이 500ms 동안 변경되지 않을 때만 상태를 업데이트
Throttle
Throttle는 일정 시간 간격마다 한 번만 함수를 실행한다.
이벤트가 여러 번 트리거 되는 경우에도 함수는 정기적으로 호출된다.
(이 이미지 이해하신 분 구합니다...😥)
Throttle 예시
const MainPage = () => {
const [isThrottled, setIsThrottled] = useState(false);
const handleClick = () => {
if (!isThrottled) {
setIsThrottled(true);
setTimeout(() => {
setIsThrottled(false);
}, 1000); // 1000ms 간격으로만 클릭 처리
}
};
return (
<div>
<button onClick={handleClick}>Click</button>
</div>
);
};
export default MainPage;
- Throttle를 활용하여 버튼을 클릭할 때 일정 시간 간격으로만 클릭 이벤트를 처리
Debounce와 Throttle 차이점
Debounce | Throttle | |
발생 시점 | 입력이 멈춘 후, 일정 시간동안 대기한 후 실행 | 이벤트 발생 후, 일정 시간 간격으로 실행 |
주요 목적 | 짧은 시간 동안 발생하는 연속 이벤트를 하나로 묶음 | 이벤트를 일정 주기로 제한하여 과도한 실행 방지 |
사용 예시 | 검색어 자동완성, 사용자 입력 처리 등 | 스크롤, 리사이징 등 연속적인 이벤트 처리 |
lodash 라이브러리
import { debounce } from 'lodash';
...
const debouncedOnChange = debounce(onChange, 500);
import { throttle } from "lodash";
...
const throttleSearch = throttle((value) => setSearch(value), 1000);
lodash를 사용하면 debounce와 throttle를 훨씬 간편하게 구현할 수 있다❗️
'리액트 심화 스터디' 카테고리의 다른 글
🏄♀️ 리심스 7주차 - Concurrent Mode (1) | 2024.12.11 |
---|---|
[Week 7] Concurrent Mode, Suspense (2) | 2024.12.10 |
🏄♀️ 리심스 6주차 - 리액트 성능 최적화 (2) | 2024.12.03 |
🏄♀️ 리심스 5주차 - 리액트 서버 컴포넌트 (2) | 2024.11.26 |
[Week 5] SSR과 RSC (2) | 2024.11.26 |