Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions .claude/commands/optimize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# Performance Optimization Analysis Command

Analyze the blog codebase from a performance optimization perspective and provide improvement recommendations as a senior frontend developer.

## Input

$ARGUMENTS: Analysis target (optional)
- Specific file path: `src/app/post/[id]/page.tsx`
- Specific directory: `src/app/_components/`
- Analysis type: `bundle`, `rendering`, `data-fetching`, `images`
- If empty: Full codebase analysis

## Reference Skill

**Always analyze based on rules from `.claude/skills/react-best-practices/SKILL.md`.**

Individual rule details: `.claude/skills/react-best-practices/rules/*.md`

## Analysis Process

### 1. Priority 1: Eliminating Waterfalls (CRITICAL)

**Related rules:** `async-*`

```bash
# Promise.all usage
grep -r "Promise.all" src/ --include="*.tsx" --include="*.ts"

# Sequential await patterns (potential Waterfall) - async-parallel violation
grep -rn "await" src/ --include="*.tsx" | head -30

# Suspense boundary usage - async-suspense-boundaries
grep -r "<Suspense" src/ --include="*.tsx"
```

**Checklist:**
- [ ] `async-parallel`: Use Promise.all() for independent operations
- [ ] `async-defer-await`: Move await to actual usage point
- [ ] `async-suspense-boundaries`: Stream content with Suspense

### 2. Priority 2: Bundle Size Optimization (CRITICAL)

**Related rules:** `bundle-*`

```bash
# Barrel import anti-pattern - bundle-barrel-imports violation
grep -r "export \* from" src/ --include="index.ts"
grep -r "from \".*/index\"" src/ --include="*.tsx"

# Dynamic import usage - bundle-dynamic-imports
grep -r "dynamic(" src/ --include="*.tsx"
grep -r "import(" src/ --include="*.tsx" --include="*.ts"

# Client component ratio
grep -r "use client" src/ --include="*.tsx" | wc -l
```

**Checklist:**
- [ ] `bundle-barrel-imports`: Import directly, avoid barrel files
- [ ] `bundle-dynamic-imports`: Use next/dynamic for heavy components
- [ ] `bundle-defer-third-party`: Load analytics/logging after hydration
- [ ] `bundle-preload`: Preload on hover/focus for perceived speed

### 3. Priority 3: Server-Side Performance (HIGH)

**Related rules:** `server-*`

```bash
# React.cache() application - server-cache-react
grep -r "import { cache }" src/app/api/
grep -rn "export const" src/app/api/dato/ --include="*.ts"

# Minimize data passed to client - server-serialization
grep -r "PostType\|PostWithoutMarkdownType" src/ --include="*.tsx"

# Parallel fetching - server-parallel-fetching
grep -r "Promise.all" src/app/ --include="*.tsx"
```

**Checklist:**
- [ ] `server-cache-react`: Apply React.cache() to all API functions
- [ ] `server-serialization`: Minimize data passed to client components
- [ ] `server-parallel-fetching`: Restructure components for parallel fetching

### 4. Priority 4: Client-Side Data Fetching (MEDIUM-HIGH)

**Related rules:** `client-*`

```bash
# SWR usage - client-swr-dedup
grep -r "useSWR\|from \"swr\"" src/

# Global event listener duplication - client-event-listeners
grep -r "addEventListener" src/ --include="*.tsx"
grep -r "removeEventListener" src/ --include="*.tsx"
```

**Checklist:**
- [ ] `client-swr-dedup`: Use SWR for automatic request deduplication
- [ ] `client-event-listeners`: Deduplicate global event listeners

### 5. Priority 5: Re-render Optimization (MEDIUM)

**Related rules:** `rerender-*`

```bash
# Inline function/object patterns (cause re-renders)
grep -r "onClick={() =>" src/ --include="*.tsx" | head -20
grep -r "style={{" src/ --include="*.tsx" | head -20

# useCallback/useMemo usage
grep -r "useCallback\|useMemo" src/ --include="*.tsx"

# useEffect dependencies - rerender-dependencies
grep -r "useEffect" src/ --include="*.tsx" -A 3 | head -40
```

**Checklist:**
- [ ] `rerender-memo`: Extract expensive work into memoized components
- [ ] `rerender-dependencies`: Use primitive dependencies in effects
- [ ] `rerender-functional-setstate`: Use functional setState for stable callbacks
- [ ] `rerender-lazy-state-init`: Pass function to useState for expensive initialization

### 6. Priority 6: Rendering Performance (MEDIUM)

**Related rules:** `rendering-*`

```bash
# Conditional rendering pattern - rendering-conditional-render
grep -r "&& <" src/ --include="*.tsx" | head -20

# content-visibility usage - rendering-content-visibility
grep -r "content-visibility" src/ --include="*.scss"

# Static JSX hoisting - rendering-hoist-jsx
grep -r "const.*=.*<" src/ --include="*.tsx" | head -20
```

**Checklist:**
- [ ] `rendering-conditional-render`: Use ternary instead of &&
- [ ] `rendering-content-visibility`: Use content-visibility for long lists
- [ ] `rendering-hoist-jsx`: Extract static JSX outside components

### 7. Priority 7: JavaScript Performance (LOW-MEDIUM)

**Related rules:** `js-*`

```bash
# RegExp in loops - js-hoist-regexp
grep -rn "new RegExp" src/ --include="*.ts" --include="*.tsx"

# Array method chaining - js-combine-iterations
grep -r "\.filter.*\.map\|\.map.*\.filter" src/ --include="*.tsx"
```

**Checklist:**
- [ ] `js-hoist-regexp`: Hoist RegExp creation outside loops
- [ ] `js-combine-iterations`: Combine multiple filter/map into one loop
- [ ] `js-set-map-lookups`: Use Set/Map for O(1) lookups

### 8. Image Optimization

```bash
# Next.js Image component usage
grep -r "from \"next/image\"" src/
grep -r "<Image" src/ -A 3 | head -30

# Plain img tag usage (warning)
grep -r "<img" src/ --include="*.tsx"
```

**Checklist:**
- [ ] Next/Image component usage
- [ ] priority attribute (LCP images)
- [ ] Appropriate sizes specification

## Analysis Result Template

```markdown
## Performance Analysis Report

### Executive Summary
- Overall Performance Score: [A/B/C/D/F]
- Key Bottlenecks:
- Expected Improvement:

---

### Priority 1: Eliminating Waterfalls (CRITICAL)

| Rule | Status | Location | Improvement |
|------|--------|----------|-------------|
| async-parallel | | | |
| async-defer-await | | | |
| async-suspense-boundaries | | | |

---

### Priority 2: Bundle Size Optimization (CRITICAL)

| Rule | Status | Location | Improvement |
|------|--------|----------|-------------|
| bundle-barrel-imports | | | |
| bundle-dynamic-imports | | | |
| bundle-defer-third-party | | | |

---

### Priority 3: Server-Side Performance (HIGH)

| Rule | Status | Location | Improvement |
|------|--------|----------|-------------|
| server-cache-react | | | |
| server-serialization | | | |
| server-parallel-fetching | | | |

---

### Priority 4-5: Client & Re-render (MEDIUM)

| Rule | Status | Location | Improvement |
|------|--------|----------|-------------|
| client-swr-dedup | | | |
| rerender-memo | | | |
| rerender-dependencies | | | |

---

### Improvement Roadmap by Priority

#### P0 - Critical (Immediate)
- [ ]

#### P1 - High (Within 1 week)
- [ ]

#### P2 - Medium (Within 2 weeks)
- [ ]

---

*Analyzed by Claude Code with react-best-practices skill*
```

## Reference Documents

- **React Best Practices**: `.claude/skills/react-best-practices/SKILL.md`
- **Individual Rule Details**: `.claude/skills/react-best-practices/rules/*.md`
- **Project Guidelines**: `CLAUDE.md`
76 changes: 74 additions & 2 deletions todolist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,78 @@
# React Best Practices 개선 Todo List

`posts/page.tsx` 컴포넌트 트리 분석 결과
## 전체 코드베이스 성능 분석 (2026-01-18)

**Overall Score: B+**

### P0 - Critical (즉시 적용)

- [ ] **React.cache() 전체 API 함수 적용** (`server-cache-react`)
- `src/app/api/dato/getPosts.ts` - cache 미적용
- `src/app/api/dato/getCategories.ts` - cache 미적용
- `src/app/api/dato/getPostIds.ts` - cache 미적용
- ✅ `src/app/api/dato/getPostById.ts` - 이미 적용됨
- 효과: 동일 렌더링 사이클 내 중복 API 요청 자동 제거

```typescript
// 적용 예시
import { cache } from "react";

const _getPosts = async <T>(query = GET_META_FIELDS): Promise<T> => {
// 기존 로직
};

export const getPosts = cache(_getPosts);
```

### P1 - High (1주 내 적용)

- [ ] **Shiki dynamic import 적용** (`bundle-dynamic-imports`)
- 파일: `src/app/post/[id]/Markdown/_components/Code.tsx`
- 문제: shiki는 번들 사이즈가 큰 라이브러리
- 개선: dynamic import로 코드 스플리팅

```typescript
const highlightCode = async (code: string, language: string) => {
const { codeToHtml } = await import("shiki");
return codeToHtml(code, { lang: language, theme: "github-dark" });
};
```

- [ ] **Barrel imports 제거** (`bundle-barrel-imports`)
- 파일: `src/types/index.ts`
- 문제: `export * from` 패턴이 tree-shaking 방해
- 개선: 직접 import 사용
- Before: `import { PostType } from "@/types"`
- After: `import { PostType } from "@/types/apiResponseType"`

### P2 - Medium (2주 내 적용)

- [ ] **Fuse.js lazy loading** (`bundle-conditional`)
- 파일: `src/app/_components/Search/index.tsx`
- 문제: 검색 기능이 항상 필요하지 않음
- 개선: Search 컴포넌트 진입 시점에 dynamic import 고려

- [ ] **content-visibility 적용** (`rendering-content-visibility`)
- 파일: Posts 컴포넌트 스타일
- 적용 시점: 포스트 목록이 많아질 경우

---

## 잘 적용된 부분 (전체 분석)

| 패턴 | 위치 | 상태 |
|------|------|------|
| Promise.all 병렬 페칭 | `SidebarWrapper/index.tsx:11` | ✅ |
| Suspense boundaries | `layout.tsx:122`, `Markdown/index.tsx:40` | ✅ |
| Third-party 지연 로딩 | `layout.tsx:69,81` (afterInteractive) | ✅ |
| 데이터 직렬화 최적화 | `PostWithoutMarkdownType`, `Pick<>` 사용 | ✅ |
| Next/Image 사용 | `Cards`, `MarkdownImage`, `Heading` | ✅ |
| useMemo 적용 | `Search/index.tsx:17` (Fuse 인스턴스) | ✅ |
| useDebounce 적용 | 검색 입력 최적화 | ✅ |

---

## `posts/page.tsx` 컴포넌트 트리 분석 결과

## 컴포넌트 트리 구조

Expand Down Expand Up @@ -84,4 +156,4 @@ posts/page.tsx (Server Component)

---

*Generated: 2026-01-17*
*Last Updated: 2026-01-18*