React 성능 최적화: 실전 가이드

React 애플리케이션이 느려지는 이유와 이를 해결하는 방법들을 알아보겠습니다.

🚀 주요 최적화 기법

1. React.memo 활용

const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
  return (
    <div>
      {/* 복잡한 렌더링 로직 */}
    </div>
  );
});

// props가 같으면 리렌더링하지 않음

2. useMemo와 useCallback

function ProductList({ products, category }) {
  // 비싼 계산 결과를 메모화
  const filteredProducts = useMemo(() => {
    return products.filter(p => p.category === category);
  }, [products, category]);

  // 함수를 메모화해서 자식 컴포넌트 리렌더링 방지
  const handleUpdate = useCallback((id) => {
    // 업데이트 로직
  }, []);

  return (
    <div>
      {filteredProducts.map(product => (
        <ProductItem 
          key={product.id} 
          product={product}
          onUpdate={handleUpdate}
        />
      ))}
    </div>
  );
}

3. 가상화(Virtualization)

import { FixedSizeList as List } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );

  return (
    <List
      height={600}
      itemCount={items.length}
      itemSize={35}
    >
      {Row}
    </List>
  );
}

🔍 성능 문제 진단

React DevTools Profiler 사용

  1. Profiler 탭 열기
  2. 녹화 시작 후 문제되는 동작 수행
  3. Flamegraph 분석해서 느린 컴포넌트 찾기

Performance API 활용

function usePerformanceMonitor(componentName) {
  useEffect(() => {
    performance.mark(`${componentName}-start`);
    
    return () => {
      performance.mark(`${componentName}-end`);
      performance.measure(
        componentName,
        `${componentName}-start`,
        `${componentName}-end`
      );
    };
  });
}

⚡ 렌더링 최적화

1. 조건부 렌더링 최적화

// ❌ 나쁜 예
function App() {
  const [showModal, setShowModal] = useState(false);
  return (
    <div>
      {/* 항상 Modal 컴포넌트가 마운트됨 */}
      <Modal show={showModal} />
    </div>
  );
}

// ✅ 좋은 예
function App() {
  const [showModal, setShowModal] = useState(false);
  return (
    <div>
      {/* 필요할 때만 Modal 컴포넌트 마운트 */}
      {showModal && <Modal />}
    </div>
  );
}

2. 키 최적화

// ❌ 나쁜 예
items.map((item, index) => (
  <Item key={index} data={item} />
))

// ✅ 좋은 예
items.map((item) => (
  <Item key={item.id} data={item} />
))

📊 번들 크기 최적화

코드 스플리팅

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

Tree Shaking

// ❌ 전체 라이브러리 import
import _ from 'lodash';

// ✅ 필요한 함수만 import
import debounce from 'lodash/debounce';

🎯 핵심 원칙

  1. 측정 먼저: 추측하지 말고 측정하기
  2. 병목지점 찾기: 가장 느린 부분부터 최적화
  3. 사용자 경험: 실제 사용자가 느끼는 성능 개선
  4. 점진적 개선: 작은 개선사항들의 누적 효과

성능 최적화는 끝이 없는 여정입니다. 사용자 경험을 항상 염두에 두고 최적화하세요! 🚀