기술 블로그 – 2023년 10월 9일
1. 서론
리액트(React)는 웹 애플리케이션의 UI를 구축하기 위한 JavaScript 라이브러리입니다. 반응형 사용자 인터페이스를 쉽게 만들 수 있도록 해주지만, 복잡한 애플리케이션에서는 성능 저하가 발생할 수 있습니다. 본 글에서는 리액트의 성능 최적화 개념과 전략을 깊이 탐구하고, 실제 예제 코드를 통해 이를 이해할 수 있도록 돕겠습니다.
2. React의 최적화 개념
리액트의 성능 최적화는 주로 다음과 같은 개념을 통해 이루어집니다:
- 가상 DOM(Virtual DOM): 리액트는 업데이트가 발생할 때 실제 DOM 대신 가상 DOM을 사용하여 변화가 필요한 부분만을 업데이트하는 방식으로 성능을 최적화합니다.
- 불필요한 렌더링 방지: 리액트는 컴포넌트의 상태(state) 변화와 props 전파에 의해 자동으로 렌더링되지만, 이를 최적화하여 불필요한 렌더링을 줄이는 다양한 방법을 제공합니다.
- 메모이제이션(Memoization): 리액트에서는 메모이제이션 기법을 사용하여 이전 계산 결과를 저장하고, 같은 입력에 대해 이전 결과를 재사용함으로써 성능을 향상시킬 수 있습니다.
- 코드 분할(Code splitting): 서버에서 필요한 코드만 로드하여 초기 로딩 시간을 줄이는 방법입니다.
3. React의 성능 최적화 전략
3.1. PureComponent와 React.memo
React.PureComponent
는 상태와 props가 바뀌지 않은 경우 렌더링을 방지합니다. 이를 통해 불필요한 렌더링을 줄일 수 있습니다. 아래의 예를 통해 살펴보겠습니다.
import React, { PureComponent } from 'react';
class Counter extends PureComponent {
render() {
return (
{this.props.count}
);
}
}
React.memo
는 함수형 컴포넌트에 대한 최적화입니다. 해당 컴포넌트에 전달되는 props가 변화하지 않는 경우, 리렌더링을 방지합니다. 다음 예제를 확인하세요.
import React from 'react';
const MemoizedCounter = React.memo(({ count, increment }) => {
return (
{count}
);
});
3.2. useCallback과 useMemo
함수형 컴포넌트에서는 useCallback
과 useMemo
훅을 사용하여 성능을 최적화할 수 있습니다.
useCallback
훅은 함수를 메모이제이션하여 불필요한 생성과 렌더링을 방지합니다.
useMemo
는 계산된 값을 메모이제이션하여 필요할 때만 다시 계산하도록 합니다.
import React, { useState, useCallback, useMemo } from 'react';
const CounterApp = () => {
const [count, setCount] = useState(0);
const [additionalCount, setAdditionalCount] = useState(0);
const incrementCount = useCallback(() => {
setCount(c => c + 1);
}, []);
const computedValue = useMemo(() => {
return count * 2; // expensive calculation
}, [count]);
return (
Count: {count}
Computed Value: {computedValue}
);
}
3.3. React.lazy와 Suspense
React.lazy
와 Suspense
를 통해 코드 분할을 구현할 수 있습니다. 다음은 이를 활용한 예제입니다.
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const App = () => {
return (
Hello World!
Loading... }>
);
}
3.4. shouldComponentUpdate와 React.memo
클래스형 컴포넌트에서는 shouldComponentUpdate
메서드를 사용하여 컴포넌트가 렌더링할지 여부를 결정할 수 있습니다.
아래 예제를 통해 이를 살펴보겠습니다.
import React, { Component } from 'react';
class MyComponent extends Component {
shouldComponentUpdate(nextProps) {
return this.props.value !== nextProps.value;
}
render() {
return {this.props.value};
}
}
4. 성능 측정
리액트의 성능을 측정하는 것은 최적화의 첫 번째 단계입니다.
React DevTools의 Profiler를 사용하여 컴포넌트의 렌더링 시간을 측정하고, 성능 병목 지점을 찾아 적절한 최적화 전략을 적용할 수 있습니다.
import React, { Profiler } from 'react';
const onRender = (
id, // 프리 프로파일링을 위한 구분자
phase, // 'mount' 또는 'update'
actualDuration, // 실제 렌더링 시간
baseDuration, // 기본 렌더링 시간
startTime, // 시작 시간
commitTime, // 커밋 시간
interactions // 해당 렌더링에 관련된 상호작용 집합
) => {
console.log(`${id}를 ${phase}하는데 ${actualDuration}ms가 걸렸습니다.`);
};
const App = () => (
);
5. 결론
리액트의 성능 최적화는 웹 애플리케이션의 사용자 경험을 개선하는 데 매우 중요합니다.
다양한 최적화 전략을 적절히 활용하고, 이들을 적용하여 애플리케이션의 성능을 극대화할 수 있습니다.
최적화를 위한 지속적인 모니터링과 성능 측정을 통해 애플리케이션의 성능을 꾸준히 관리하는 것이 중요합니다.
마지막으로, 성능을 최적화하는 과정은 비즈니스 요구 사항과 사용자 경험 향상을 위한 필수적인 과정이라는 점을 명심해야 합니다.