Community코딩 & 개발github.com

nickcrew/react-performance-optimization

React 성능 최적화 패턴 – 메모이제이션, 코드 분할, 효율적인 렌더링 전략을 사용합니다. 느린 React 애플리케이션을 최적화하거나 번들 크기를 줄이거나 대용량 데이터셋을 사용하는 사용자 경험을 개선할 때 사용하세요.

react-performance-optimization란 무엇인가요?

이 스킬은 React 애플리케이션의 성능 병목 현상을 진단하고 해결하기 위한 다양한 최적화 패턴을 제공합니다. 메모이제이션(useMemo, useCallback, React.memo)과 코드 분할(React.lazy, Suspense, 동적 import)을 비롯해 가상화(react-window, react-virtualized) 및 상태 관리 최적화(상태 끌어올리기, 컨텍스트 분할, 불변성 유지) 같은 고급 렌더링 전략을 다룹니다. 또한 React DevTools Profiler를 활용한 성능 측정 방법, 번들 크기 분석도 포함되어 있어 개발자가 구체적인 문제를 식별하고 해결할 수 있도록 도와줍니다. Next.js, CRA, Vite 등 다양한 React 기반 프레임워크에서 바로 적용 가능하며, 각 패턴은 실제 사용 사례와 함께 설명되어 실무에 쉽게 활용할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
Part ofReact
npx skills add https://github.com/nickcrew/claude-ctx-plugin/tree/main/skills/react-performance-optimization

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

react-performance-optimization은(는) 무엇을 하나요?

React 성능 최적화 패턴 – 메모이제이션, 코드 분할, 효율적인 렌더링 전략을 사용합니다. 느린 React 애플리케이션을 최적화하거나 번들 크기를 줄이거나 대용량 데이터셋을 사용하는 사용자 경험을 개선할 때 사용하세요.

개요

이 스킬은 React 컴포넌트 렌더링 성능을 개선하기 위한 입증된 패턴과 모범 사례를 제공합니다. 주요 최적화 기법은 다음과 같습니다:

  • 메모이제이션: 불필요한 재렌더링 방지 (React.memo, useMemo, useCallback)
  • 코드 분할: 초기 번들 크기 줄이기 (React.lazy, Suspense, 동적 import())
  • 가상화: 큰 목록/테이블에 필요한 DOM 노드만 렌더링 (react-window, react-virtualized)
  • 상태 관리 최적화: 리렌더링을 최소화하는 상태 구조화 (상태 끌어올리기, 컨텍스트 분할)
  • 렌더링 사이클 이해: useEffect, useLayoutEffect, 동시성 기능 (startTransition, useDeferredValue)
  • 프로파일링 도구: React DevTools, 번들 분석기 (webpack-bundle-analyzer, vite-plugin-inspect)

시작하기

성능 최적화를 적용하기 전에 프로파일링으로 병목 지점을 파악하세요. 이 스킬의 접근 방식은 측정(measurement), 최적화(optimization), 검증(verification)의 반복이며, 성급한 최적화를 지양하고 데이터 기반 의사 결정을 강조합니다.

설치

이 스킬은 특정 패키지 설치가 필요하지 않습니다. 필요한 경우 권장 라이브러리들을 설치하세요:

# 가상화 (필요 시)
npm install react-window react-virtualized-auto-sizer

# 번들 분석 (필요 시)
npm install --save-dev vite-plugin-inspect rollup-plugin-visualizer

사용법

React DevTools Profiler로 컴포넌트 트리의 렌더링 시간을 측정합니다:

import { Profiler } from 'react'

function onRenderCallback(
  id, // 발생한 커밋의 프로파일 트리 ID
  phase, // "mount" 또는 "update"
  actualDuration, // 커밋에 소요된 실제 시간
  baseDuration, // 하위 항목이 메모이제이션되지 않은 경우 예상 시간
  startTime, // React가 이 커밋을 렌더링하기 시작한 시간
  commitTime, // 이 커밋이 커밋된 시간
  interactions // 이 업데이트에 속한 상호작용 집합
) {
  if (actualDuration > 16) { // 60fps = ~16ms
    console.warn(`느린 렌더링 감지: ${id} (${actualDuration.toFixed(2)}ms)`)
  }
}

<Profiler id="Navigation" onRender={onRenderCallback}>
  <Navigation />
</Profiler>

핵심 패턴

1. 메모이제이션

React.memo를 사용하여 prop이 변경되지 않은 경우 컴포넌트의 재렌더링을 방지하세요:

const ExpensiveList = React.memo(function ExpensiveList({ items, onSelect }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={() => onSelect(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  )
})

useCallback과 useMemo로 props 안정성을 유지하세요:

function SearchResults({ query }) {
  const filteredItems = useMemo(
    () => items.filter(item => item.name.includes(query)),
    [query]
  )

  const handleSelect = useCallback((id) => {
    console.log('선택됨:', id)
  }, []) // 의존성이 없으므로 안정적인 참조 유지

  return <ExpensiveList items={filteredItems} onSelect={handleSelect} />
}

2. 코드 분할

라우트 레벨에서 코드를 분할하여 초기 다운로드 크기를 줄이세요:

import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))

export default function App() {
  return (
    <Suspense fallback={<div>로딩 중...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  )
}

3. 가상화

큰 목록(수백/수천 개 항목)에는 react-window를 사용하세요:

import { FixedSizeList as List } from 'react-window'
import AutoSizer from 'react-virtualized-auto-sizer'

function VirtualizedList({ items }) {
  return (
    <AutoSizer>
      {({ height, width }) => (
        <List
          height={height}
          itemCount={items.length}
          itemSize={50} // 각 행의 높이 (픽셀)
          width={width}
        >
          {({ index, style }) => (
            <div style={style}>
              {items[index].name}
            </div>
          )}
        </List>
      )}
    </AutoSizer>
  )
}

4. 동시성 기능

React 18의 동시성 기능으로 urgent 업데이트의 응답성을 유지하세요:

import { startTransition, useDeferredValue } from 'react'

function SearchPage() {
  const [query, setQuery] = useState('')
  const deferredQuery = useDeferredValue(query)
  const isStale = query !== deferredQuery

  // 느린 필터는 지연된 값 사용
  const searchResults = useMemo(
    () => filterItems(allItems, deferredQuery),
    [deferredQuery]
  )

  return (
    <>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      <Suspense fallback={<div>검색 중...</div>}>
        <SearchResults results={searchResults} />
      </Suspense>
    </>
  )
}

5. 배열 키 최적화

리스트를 렌더링할 때는 안정적이고 고유한 key prop을 사용하세요. 인덱스를 키로 사용하지 마세요:

// 좋지 않음 (인덱스 사용)
{items.map((item, index) => <Item key={index} item={item} />)}

// 좋음 (고유 ID 사용)
{items.map(item => <Item key={item.id} item={item} />)}

성능 프로파일링

최적화를 적용하기 전과 후에 성능을 측정하세요:

  1. Chrome DevTools Performance 탭: 프레임 드롭 감지
  2. React DevTools Profiler: 개별 컴포넌트 렌더링 시간 확인
  3. 번들 분석기: 코드 중복 및 거대한 의존성 식별
    • Vite: vite-plugin-inspect 또는 rollup-plugin-visualizer
    • CRA/Webpack: webpack-bundle-analyzer

모범 사례 요약

  • 측정 후 최적화: 가정하지 말고 프로파일러로 확인하세요.
  • 컴포넌트 분할하기: 자주 변경되는 상태를 별도의 작은 컴포넌트로 분리하세요.
  • 컨텍스트 주의: 컨텍스트 값이 변경되면 모든 소비자가 리렌더링됩니다. 필요할 때만 분할하세요.
  • 불변성 유지: 배열은 concat/spread, 객체는 spread를 사용하여 예기치 않은 리렌더링을 방지하세요.
  • 프레임워크 기능 활용: Next.js 이미지 최적화, 스트리밍 SSR, 부분 Pre-rendering을 사용하세요.

면책사항

이 스킬은 코딩 에이전트를 위한 가이드입니다. 일부 최적화는 복잡성을 증가시킬 수 있습니다(예: 메모이제이션은 가비지 컬렉션 압력을 증가시킴). 측정 가능한 이점이 있을 때만 적용하세요.

관련 스킬