performance-optimization
39
总安装量
40
周安装量
#5332
全站排名
安装命令
npx skills add https://github.com/supercent-io/skills-template --skill performance-optimization
Agent 安装分布
opencode
33
codex
30
gemini-cli
28
github-copilot
23
antigravity
20
Skill 文档
Performance Optimization
When to use this skill
- ë린 íì´ì§ ë¡ë: Lighthouse ì ì ë®ì
- ë린 ë ëë§: ì¬ì©ì ì¸í°ëì ì§ì°
- í° ë²ë¤ í¬ê¸°: ë¤ì´ë¡ë ìê° ì¦ê°
- ë린 쿼리: ë°ì´í°ë² ì´ì¤ ë³ëª©
Instructions
Step 1: ì±ë¥ 측ì
Lighthouse (Chrome DevTools):
# CLI
npm install -g lighthouse
lighthouse https://example.com --view
# CIìì ìëí
lighthouse https://example.com --output=json --output-path=./report.json
Web Vitals 측ì (React):
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
// Google Analytics, Datadog ë±ì¼ë¡ ì ì¡
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
Step 2: React ìµì í
React.memo (ë¶íìí 리ë ëë§ ë°©ì§):
// â ëì ì: ë¶ëª¨ê° 리ë ëë§ë ëë§ë¤ ììë 리ë ëë§
function ExpensiveComponent({ data }: { data: Data }) {
return <div>{/* ë³µì¡í ë ëë§ */}</div>;
}
// â
ì¢ì ì: props ë³ê²½ ììë§ ë¦¬ë ëë§
const ExpensiveComponent = React.memo(({ data }: { data: Data }) => {
return <div>{/* ë³µì¡í ë ëë§ */}</div>;
});
useMemo & useCallback:
function ProductList({ products, category }: Props) {
// â
íí°ë§ ê²°ê³¼ ë©ëª¨ì´ì ì´ì
const filteredProducts = useMemo(() => {
return products.filter(p => p.category === category);
}, [products, category]);
// â
ì½ë°± ë©ëª¨ì´ì ì´ì
const handleAddToCart = useCallback((id: string) => {
addToCart(id);
}, []);
return (
<div>
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} onAdd={handleAddToCart} />
))}
</div>
);
}
Lazy Loading & Code Splitting:
import { lazy, Suspense } from 'react';
// â
Route-based code splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// â
Component-based lazy loading
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<HeavyChart data={data} />
</Suspense>
</div>
);
}
Step 3: ë²ë¤ í¬ê¸° ìµì í
Webpack Bundle Analyzer:
npm install --save-dev webpack-bundle-analyzer
# package.json
{
"scripts": {
"analyze": "webpack-bundle-analyzer build/stats.json"
}
}
Tree Shaking (ì¬ì©íì§ ìë ì½ë ì ê±°):
// â ëì ì: ì ì²´ ë¼ì´ë¸ë¬ë¦¬ ìí¬í¸
import _ from 'lodash';
// â
ì¢ì ì: íìí ê²ë§ ìí¬í¸
import debounce from 'lodash/debounce';
Dynamic Imports:
// â
íìí ëë§ ë¡ë
button.addEventListener('click', async () => {
const { default: Chart } = await import('chart.js');
new Chart(ctx, config);
});
Step 4: ì´ë¯¸ì§ ìµì í
Next.js Image ì»´í¬ëí¸:
import Image from 'next/image';
function ProductImage() {
return (
<Image
src="/product.jpg"
alt="Product"
width={500}
height={500}
priority // LCP ì´ë¯¸ì§ì¸ ê²½ì°
placeholder="blur" // ë¸ë¬ íë ì´ì¤íë
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}
WebP í¬ë§· ì¬ì©:
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="Fallback">
</picture>
Step 5: ë°ì´í°ë² ì´ì¤ 쿼리 ìµì í
N+1 쿼리 문ì í´ê²°:
// â ëì ì: N+1 queries
const posts = await db.post.findMany();
for (const post of posts) {
const author = await db.user.findUnique({ where: { id: post.authorId } });
// 101ë² ì¿¼ë¦¬ (1 + 100)
}
// â
ì¢ì ì: JOIN ëë include
const posts = await db.post.findMany({
include: {
author: true
}
});
// 1ë² ì¿¼ë¦¬
ì¸ë±ì¤ ì¶ê°:
-- ë린 쿼리 ìë³
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- ì¸ë±ì¤ ì¶ê°
CREATE INDEX idx_users_email ON users(email);
-- ë³µí© ì¸ë±ì¤
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
ìºì± (Redis):
async function getUserProfile(userId: string) {
// 1. ìºì íì¸
const cached = await redis.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// 2. DB ì¡°í
const user = await db.user.findUnique({ where: { id: userId } });
// 3. ìºì ì ì¥ (1ìê°)
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
Output format
ì±ë¥ ìµì í ì²´í¬ë¦¬ì¤í¸
## Frontend
- [ ] React.memoë¡ ë¶íìí 리ë ëë§ ë°©ì§
- [ ] useMemo/useCallback ì ì í ì¬ì©
- [ ] Lazy loading & Code splitting
- [ ] ì´ë¯¸ì§ ìµì í (WebP, lazy loading)
- [ ] ë²ë¤ í¬ê¸° ë¶ì ë° ê°ì
## Backend
- [ ] N+1 쿼리 ì ê±°
- [ ] ë°ì´í°ë² ì´ì¤ ì¸ë±ì¤ ì¶ê°
- [ ] Redis ìºì±
- [ ] API Response ìì¶ (gzip)
- [ ] CDN ì¬ì©
## 측ì
- [ ] Lighthouse ì ì 90+
- [ ] LCP < 2.5s
- [ ] FID < 100ms
- [ ] CLS < 0.1
Constraints
íì ê·ì¹ (MUST)
- 측ì 먼ì : ì¶ì¸¡íì§ ë§ê³ íë¡íì¼ë§
- ì ì§ì ê°ì : í ë²ì íëì© ìµì í
- ì±ë¥ 모ëí°ë§: ì§ìì ì¼ë¡ ì¶ì
ê¸ì§ ì¬í (MUST NOT)
- 조기 ìµì í: ë³ëª©ì´ ìëë° ìµì ííì§ ìì
- ê°ë ì± í¬ì: ì±ë¥ì ìí´ ì½ë를 ë³µì¡íê² ë§ë¤ì§ ìì
Best practices
- 80/20 ë²ì¹: 20% ë ¸ë ¥ì¼ë¡ 80% ê°ì
- ì¬ì©ì ì¤ì¬: ì¤ì ì¬ì©ì ê²½í ê°ì ì ì§ì¤
- ìëí: CIìì ì±ë¥ íê· í ì¤í¸
References
Metadata
ë²ì
- íì¬ ë²ì : 1.0.0
- ìµì¢ ì ë°ì´í¸: 2025-01-01
- í¸í íë«í¼: Claude, ChatGPT, Gemini
ê´ë ¨ ì¤í¬
íê·¸
#performance #optimization #React #caching #lazy-loading #web-vitals #code-quality