fe-perf
SKILL.md
FE Performance Optimization
$ARGUMENTS로 전달된 대상의 성능을 분석하고 최적화한다.
분석 절차
- 대상 파악: 파일 또는 "bundle" 키워드에 따라 분석 범위를 결정한다
- 코드 분석: 성능 안티패턴을 식별한다
- 최적화 제안: 우선순위별로 개선안을 제시한다
- 적용: 승인된 최적화를 적용한다
성능 최적화 카테고리
1. 렌더링 최적화
불필요한 리렌더링 방지
// Bad — 매 렌더마다 새 객체 생성
function Parent() {
return <Child style={{ color: "red" }} data={[1, 2, 3]} />;
}
// Good — 안정적 참조
const style = { color: "red" };
const data = [1, 2, 3];
function Parent() {
return <Child style={style} data={data} />;
}
React.memo 적절한 사용
// 비용이 큰 컴포넌트에만 적용
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
return items.map((item) => <ExpensiveItem key={item.id} item={item} />);
});
useMemo / useCallback
// 비용이 큰 계산에 useMemo
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// memo된 자식에 전달하는 콜백에 useCallback
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);
2. 코드 분할 & Lazy Loading
라우트 기반 코드 분할
Next.js App Router는 자동으로 라우트별 코드 분할을 적용한다. 추가 최적화:
// 무거운 컴포넌트 lazy loading
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
loading: () => <Skeleton className="h-[400px]" />,
ssr: false, // 클라이언트 전용
});
조건부 import
// Bad — 항상 로드
import { PDFViewer } from "react-pdf";
// Good — 필요할 때만 로드
const PDFViewer = dynamic(() =>
import("react-pdf").then((mod) => ({ default: mod.PDFViewer }))
);
3. 번들 사이즈 최적화
트리쉐이킹 확인
// Bad — 전체 라이브러리 import
import _ from "lodash";
_.debounce(fn, 300);
// Good — 개별 함수 import
import debounce from "lodash/debounce";
debounce(fn, 300);
// Best — 작은 대안 사용
import { useDebouncedCallback } from "use-debounce";
번들 분석
# Vite 번들 분석
npx vite-bundle-visualizer
# Next.js 번들 분석
ANALYZE=true next build # @next/bundle-analyzer 필요
무거운 의존성 교체 가이드
| 무거운 라이브러리 | 가벼운 대안 |
|---|---|
moment (300KB) |
date-fns (트리쉐이킹) 또는 dayjs (2KB) |
lodash (70KB) |
개별 import 또는 ES 네이티브 메서드 |
uuid (10KB) |
crypto.randomUUID() (네이티브) |
axios (30KB) |
fetch (네이티브) |
classnames (2KB) |
clsx (1KB) 또는 cn() |
4. 이미지 최적화
// Next.js Image 컴포넌트 사용
import Image from "next/image";
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // LCP 이미지에 priority
placeholder="blur" // 블러 플레이스홀더
sizes="(max-width: 768px) 100vw, 50vw" // 반응형 크기
/>
// 아이콘 — SVG inline 또는 sprite
import { LucideIcon } from "lucide-react"; // 트리쉐이킹 지원
5. Server Component 최적화
// Server Component에서 데이터 가져오기 (제로 번들)
async function ProductList() {
const products = await db.product.findMany();
return products.map((p) => <ProductCard key={p.id} product={p} />);
}
// 병렬 데이터 페칭
async function Dashboard() {
const [users, orders, stats] = await Promise.all([
getUsers(),
getOrders(),
getStats(),
]);
// ...
}
6. 리스트 가상화
// 100+ 아이템 리스트에 가상화 적용
import { useVirtualizer } from "@tanstack/react-virtual";
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} className="h-[500px] overflow-auto">
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
height: virtualItem.size,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<ListItem item={items[virtualItem.index]} />
</div>
))}
</div>
</div>
);
}
7. 캐싱 전략
// React Query 캐싱
const { data } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5분간 fresh
gcTime: 30 * 60 * 1000, // 30분간 캐시 유지
});
// Next.js fetch 캐싱
const data = await fetch("/api/data", {
next: { revalidate: 3600 }, // 1시간 ISR
});
// Next.js unstable_cache
import { unstable_cache } from "next/cache";
const getCachedData = unstable_cache(
async () => db.query(),
["cache-key"],
{ revalidate: 3600 }
);
8. Web Vitals 체크포인트
| 지표 | 목표 | 최적화 포인트 |
|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 히어로 이미지 priority, 폰트 preload |
| FID/INP (Interaction to Next Paint) | < 200ms | 무거운 연산 분리, useDeferredValue |
| CLS (Cumulative Layout Shift) | < 0.1 | 이미지 width/height 지정, 스켈레톤 |
| TTFB (Time to First Byte) | < 800ms | SSG/ISR, 에지 캐싱 |
리포트 형식
# Performance Audit: [대상]
## 요약
- 주요 이슈: N개
- 예상 개선 효과: [번들 -NKB, 렌더링 -Nms 등]
## High Impact (우선 적용)
### [P1] 이슈 제목
- **영향**: [번들 사이즈 / 렌더링 / 로딩 속도]
- **현재**: 설명
- **개선안**: 코드
## Medium Impact
...
## Low Impact
...
실행 규칙
- 인자가 없으면 사용자에게 분석 대상을 질문한다
- "bundle" 인자 시
package.json, build 설정을 분석한다 - 과도한 최적화를 경계한다 (측정 가능한 이슈에 집중)
- 최적화 전후 차이를 구체적 수치로 설명한다
Weekly Installs
2
Repository
ingpdw/pdw-fe-dev-toolFirst Seen
Feb 7, 2026
Security Audits
Installed on
mcpjam2
openhands2
replit2
junie2
windsurf2
zencoder2