안녕하세요! 오늘은 제가 실제 프로젝트를 진행하면서 겪었던 React 성능 최적화 경험을 공유해보려고 합니다. 특히 대규모 데이터를 다루는 대시보드 프로젝트에서 발생했던 성능 이슈들을 처리하면서 알게된 것들에 대해 적어보겠습니다..
처음에는 별 생각 없이 개발했던 대시보드 프로젝트였는데, 데이터가 쌓이고 기능이 추가되면서 점점 느려지기 시작했습니다. 특히 다음과 같은 문제들이 발생했습니다:
아무리 좋은 기능을 만들어도 사용자가 답답함을 느낀다면 소용없겠죠? 그래서 본격적으로 성능 최적화를 시작했습니다.
가장 먼저 눈에 띈 문제는 불필요한 리렌더링이었습니다. React Developer Tools로 확인해보니 부모 컴포넌트가 업데이트될 때마다 모든 자식 컴포넌트들이 덩달아 리렌더링되고 있었습니다.
// 🔴 Before: 매번 새로운 함수가 생성되어 자식 컴포넌트가 리렌더링됨
function ParentComponent() {
const handleClick = () => {
// 뭔가를 처리
}
return <ChildComponent onClick={handleClick} />
}
// 🟢 After: useCallback을 사용해 함수를 메모이제이션
function ParentComponent() {
const handleClick = useCallback(() => {
// 뭔가를 처리
}, [])
return <ChildComponent onClick={handleClick} />
}
특히 차트 컴포넌트에서는 React.memo를 적용하는 것만으로도 눈에 띄는 성능 향상이 있었어요.

대시보드의 특성상 많은 데이터를 처리해야 했는데, 이 부분에서도 병목이 있었습니다.
// 🔴 Before: 렌더링할 때마다 매번 계산
function Dashboard() {
const processData = (data) => {
// 엄청 복잡한 계산...
return result;
}
const chartData = processData(rawData);
return <Chart data={chartData} />
}
// 🟢 After: useMemo로 필요할 때만 계산
function Dashboard() {
const chartData = useMemo(() => {
// 엄청 복잡한 계산...
return result;
}, [rawData]) // rawData가 변경될 때만 재계산
return <Chart data={chartData} />
}
처음에는 모든 코드를 한 번에 불러오고 있었는데, 이게 초기 로딩 시간을 늘리는 주범이었습니다. React.lazy와 Suspense를 활용해서 필요한 컴포넌트만 동적으로 불러오도록 변경했습니다.
// 🎉 필요한 페이지만 동적으로 불러오기
const DashboardPage = React.lazy(() => import('./pages/Dashboard'));
const AnalyticsPage = React.lazy(() => import('./pages/Analytics'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/analytics" element={<AnalyticsPage />} />
</Routes>
</Suspense>
);
}
이러한 최적화 작업을 진행한 후의 변화가 정말 놀라웠어요:
성능 최적화는 한 번에 끝나는 게 아니라 지속적으로 신경 써야 하는 부분인 것 같아요. 새로운 기능을 추가할 때마다 성능에 미치는 영향을 고려하고, 정기적으로 성능을 체크하는 습관을 들이는 게 중요하다고 느꼈습니다.
다음에는 Next.js를 활용한 서버 사이드 렌더링 최적화 경험도 공유해보도록 하겠습니다!