@kood/claude-code 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +255 -149
- package/package.json +1 -1
- package/templates/.claude/agents/researcher.md +8 -1
- package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
- package/templates/.claude/scripts/deploy/build-run.sh +36 -0
- package/templates/.claude/scripts/deploy/deploy-check.sh +38 -0
- package/templates/.claude/scripts/git/git-all.sh +57 -0
- package/templates/.claude/scripts/git/git-clean-check.sh +31 -0
- package/templates/.claude/scripts/git/git-commit.sh +51 -0
- package/templates/.claude/scripts/git/git-info.sh +34 -0
- package/templates/.claude/scripts/git/git-push.sh +50 -0
- package/templates/.claude/scripts/lint/lint-check.sh +56 -0
- package/templates/.claude/scripts/lint/lint-file.sh +41 -0
- package/templates/.claude/scripts/pm/pm-detect.sh +25 -0
- package/templates/.claude/scripts/pm/pm-run.sh +41 -0
- package/templates/.claude/scripts/version/version-bump.sh +54 -0
- package/templates/.claude/scripts/version/version-find.sh +49 -0
- package/templates/.claude/skills/docs-fetch/SKILL.md +5 -4
- package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
- package/templates/.claude/skills/project-optimizer/SKILL.md +374 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
- package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
- package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
- package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
- package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
- package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
- package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
- package/templates/.claude/skills/sql-optimizer/SKILL.md +437 -0
- package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +93 -27
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
- package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
- package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +570 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
- package/templates/tanstack-start/docs/architecture.md +238 -167
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
- package/templates/tauri/CLAUDE.md +189 -0
- package/templates/tauri/docs/guides/distribution.md +261 -0
- package/templates/tauri/docs/guides/getting-started.md +302 -0
- package/templates/tauri/docs/guides/mobile.md +288 -0
- package/templates/tauri/docs/library/tauri/index.md +510 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Optimize Linter and Formatter Configuration
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Consistent code style, faster feedback loop
|
|
5
|
+
tags: dx, lint, format, eslint, prettier, biome, ruff
|
|
6
|
+
languages: all
|
|
7
|
+
related: [dx-type-safety, code-complexity]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 린터/포매터 최적 설정
|
|
11
|
+
|
|
12
|
+
프로젝트에 맞는 린터/포매터를 설정하여 일관된 코드 스타일과 빠른 피드백을 보장합니다.
|
|
13
|
+
|
|
14
|
+
**언어별 권장 도구 (2026):**
|
|
15
|
+
|
|
16
|
+
| 언어 | 린터 | 포매터 | 비고 |
|
|
17
|
+
|------|------|--------|------|
|
|
18
|
+
| **JS/TS** | ESLint 9 (flat config) | Prettier 또는 Biome | Biome = lint+format 올인원 |
|
|
19
|
+
| **Python** | Ruff | Ruff (format) | flake8+isort+black 100x 빠른 대체 |
|
|
20
|
+
| **Go** | go vet + staticcheck | gofmt (내장) | |
|
|
21
|
+
| **Rust** | clippy | rustfmt (내장) | |
|
|
22
|
+
| **Java** | SpotBugs / Error Prone | google-java-format | |
|
|
23
|
+
|
|
24
|
+
**빠른 피드백을 위한 설정:**
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
// lint-staged (Git 커밋 시 변경 파일만)
|
|
28
|
+
{
|
|
29
|
+
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
|
|
30
|
+
"*.py": ["ruff check --fix", "ruff format"],
|
|
31
|
+
"*.go": ["gofmt -w"],
|
|
32
|
+
"*.rs": ["rustfmt"]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**원칙:** 전체 프로젝트 린트는 CI에서, 로컬은 변경 파일만 (lint-staged).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ensure Test Coverage on Critical Paths
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Confidence in refactoring, catches regressions
|
|
5
|
+
tags: dx, test, coverage, critical-path
|
|
6
|
+
languages: all
|
|
7
|
+
related: [code-error-handling, dx-ci-speed]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 핵심 로직 테스트 커버리지 확보
|
|
11
|
+
|
|
12
|
+
100% 커버리지보다 핵심 비즈니스 로직의 높은 커버리지가 중요합니다.
|
|
13
|
+
|
|
14
|
+
**우선순위:**
|
|
15
|
+
|
|
16
|
+
| 우선순위 | 대상 | 이유 |
|
|
17
|
+
|---------|------|------|
|
|
18
|
+
| **1** | 비즈니스 로직/도메인 | 변경 빈도 높음, 버그 영향 큼 |
|
|
19
|
+
| **2** | 데이터 변환/파싱 | 엣지 케이스 많음 |
|
|
20
|
+
| **3** | API 엔드포인트 | 외부 계약 |
|
|
21
|
+
| **4** | 에러 처리 경로 | 간과하기 쉬움 |
|
|
22
|
+
| LOW | UI 컴포넌트 렌더링 | 자주 변경, ROI 낮음 |
|
|
23
|
+
| LOW | 외부 서비스 호출 | Mock 필요, 깨지기 쉬움 |
|
|
24
|
+
|
|
25
|
+
**테스트 도구 (2026):**
|
|
26
|
+
|
|
27
|
+
| 언어 | 도구 | 특징 |
|
|
28
|
+
|------|------|------|
|
|
29
|
+
| **JS/TS** | Vitest | Vite 통합, ESM 네이티브, 빠름 |
|
|
30
|
+
| **Python** | pytest | 풍부한 에코시스템 |
|
|
31
|
+
| **Go** | testing (내장) | `go test -cover` |
|
|
32
|
+
| **Rust** | cargo test (내장) | `cargo tarpaulin` (커버리지) |
|
|
33
|
+
|
|
34
|
+
**목표:** 핵심 로직 80%+, 전체 60%+ (프로젝트 성격에 따라 조정).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Maximize Type System Usage
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Catches bugs at compile time, better IDE support
|
|
5
|
+
tags: dx, type, safety, strict, generics
|
|
6
|
+
languages: [ts, go, rust, java, csharp]
|
|
7
|
+
related: [code-error-handling, code-naming]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 타입 시스템 최대 활용
|
|
11
|
+
|
|
12
|
+
언어의 타입 시스템을 최대한 활용하여 런타임 에러를 컴파일 타임에 잡습니다.
|
|
13
|
+
|
|
14
|
+
**언어별 strict 설정:**
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
// TypeScript - tsconfig.json
|
|
18
|
+
{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true } }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```toml
|
|
22
|
+
# Rust - Clippy strict
|
|
23
|
+
[lints.clippy]
|
|
24
|
+
pedantic = "warn"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# Python - mypy strict
|
|
29
|
+
[tool.mypy]
|
|
30
|
+
strict = true
|
|
31
|
+
disallow_any_generics = true
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```go
|
|
35
|
+
// Go - go vet + staticcheck
|
|
36
|
+
// go vet ./...
|
|
37
|
+
// staticcheck ./...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**패턴:**
|
|
41
|
+
|
|
42
|
+
| 패턴 | 효과 | 언어 |
|
|
43
|
+
|------|------|------|
|
|
44
|
+
| **Discriminated union** | 상태별 안전한 분기 | TS, Rust (enum), Python (Literal) |
|
|
45
|
+
| **Branded type** | 같은 원시형 혼동 방지 | TS (`type UserId = string & { __brand: 'UserId' }`) |
|
|
46
|
+
| **Exhaustive check** | switch에서 누락 분기 감지 | TS (`never`), Rust (match), Go (exhaustive) |
|
|
47
|
+
| **Generic constraint** | 타입 파라미터 제한 | 모든 정적 타입 언어 |
|
|
48
|
+
|
|
49
|
+
**원칙:** `any`/`object`/`interface{}` 사용 최소화. 가능한 가장 구체적인 타입 사용.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Batch Queries to Eliminate N+1
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 100x fewer queries, 10x faster response
|
|
5
|
+
tags: io, database, n+1, batch, query
|
|
6
|
+
languages: all
|
|
7
|
+
related: [concurrency-parallel, io-cache-layer]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## N+1 쿼리 제거
|
|
11
|
+
|
|
12
|
+
목록 조회 후 각 항목마다 추가 쿼리를 실행하는 N+1 패턴을 배치/조인으로 전환합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (N+1):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# 100명 유저 = 101 쿼리
|
|
18
|
+
users = db.query("SELECT * FROM users")
|
|
19
|
+
for user in users:
|
|
20
|
+
orders = db.query(f"SELECT * FROM orders WHERE user_id = {user.id}")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// Prisma N+1
|
|
25
|
+
const users = await prisma.user.findMany()
|
|
26
|
+
for (const user of users) {
|
|
27
|
+
const orders = await prisma.order.findMany({ where: { userId: user.id } })
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**✅ 올바른 예시 (배치/조인):**
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
# 2 쿼리
|
|
35
|
+
users = db.query("SELECT * FROM users")
|
|
36
|
+
user_ids = [u.id for u in users]
|
|
37
|
+
orders = db.query("SELECT * FROM orders WHERE user_id = ANY($1)", user_ids)
|
|
38
|
+
|
|
39
|
+
# SQLAlchemy - eager loading
|
|
40
|
+
users = session.query(User).options(selectinload(User.orders)).all()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Prisma - include
|
|
45
|
+
const users = await prisma.user.findMany({
|
|
46
|
+
include: { orders: true }
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```go
|
|
51
|
+
// GORM - Preload
|
|
52
|
+
var users []User
|
|
53
|
+
db.Preload("Orders").Find(&users)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# ActiveRecord - includes
|
|
58
|
+
users = User.includes(:orders).all
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```java
|
|
62
|
+
// JPA - EntityGraph
|
|
63
|
+
@EntityGraph(attributePaths = {"orders"})
|
|
64
|
+
List<User> findAll();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**감지 방법:** 쿼리 로그에서 반복되는 유사 쿼리 패턴 확인. ORM 쿼리 카운터 활용.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Cache Layer for Repeated Reads
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 10-100x latency reduction for cached data
|
|
5
|
+
tags: io, cache, lru, ttl, redis, in-memory
|
|
6
|
+
languages: all
|
|
7
|
+
related: [io-batch-queries, memory-bounded-cache]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 반복 읽기에 캐시 레이어 추가
|
|
11
|
+
|
|
12
|
+
동일 데이터를 반복 조회하는 경우 인메모리/외부 캐시를 추가하여 DB/API 부하를 줄입니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (매번 DB 조회):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
def get_user(user_id: str) -> User:
|
|
18
|
+
return db.query("SELECT * FROM users WHERE id = $1", user_id)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**✅ 올바른 예시 (캐시 레이어):**
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# Python (functools.lru_cache)
|
|
25
|
+
from functools import lru_cache
|
|
26
|
+
|
|
27
|
+
@lru_cache(maxsize=1000)
|
|
28
|
+
def get_config(key: str) -> str:
|
|
29
|
+
return db.query("SELECT value FROM config WHERE key = $1", key)
|
|
30
|
+
|
|
31
|
+
# TTL 있는 캐시 (cachetools)
|
|
32
|
+
from cachetools import TTLCache
|
|
33
|
+
cache = TTLCache(maxsize=1000, ttl=300) # 5분
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Node.js (lru-cache)
|
|
38
|
+
import { LRUCache } from 'lru-cache'
|
|
39
|
+
const cache = new LRUCache<string, User>({ max: 1000, ttl: 5 * 60 * 1000 })
|
|
40
|
+
|
|
41
|
+
async function getUser(id: string): Promise<User> {
|
|
42
|
+
const cached = cache.get(id)
|
|
43
|
+
if (cached) return cached
|
|
44
|
+
const user = await db.user.findUnique({ where: { id } })
|
|
45
|
+
if (user) cache.set(id, user)
|
|
46
|
+
return user
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```go
|
|
51
|
+
// Go (groupcache / ristretto)
|
|
52
|
+
import "github.com/dgraph-io/ristretto"
|
|
53
|
+
cache, _ := ristretto.NewCache(&ristretto.Config{
|
|
54
|
+
NumCounters: 1e7, MaxCost: 1 << 30, BufferItems: 64,
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// Rust (moka)
|
|
60
|
+
use moka::future::Cache;
|
|
61
|
+
let cache: Cache<String, User> = Cache::builder()
|
|
62
|
+
.max_capacity(1000)
|
|
63
|
+
.time_to_live(Duration::from_secs(300))
|
|
64
|
+
.build();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**캐시 전략:** 읽기 빈도 높고, 쓰기 빈도 낮은 데이터에 적용. TTL로 신선도 보장.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Reuse Connections with Pooling
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Eliminates connection overhead, 5-20x throughput
|
|
5
|
+
tags: io, connection, pool, keep-alive, reuse
|
|
6
|
+
languages: all
|
|
7
|
+
related: [concurrency-pool, memory-pool-reuse]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 커넥션 풀/재사용
|
|
11
|
+
|
|
12
|
+
매 요청마다 새 커넥션을 생성하지 않고 풀에서 재사용합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (매번 새 커넥션):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
def get_user(user_id):
|
|
18
|
+
conn = psycopg2.connect(DSN) # 매번 TCP 핸드셰이크 + TLS + 인증
|
|
19
|
+
cursor = conn.cursor()
|
|
20
|
+
cursor.execute("SELECT ...", (user_id,))
|
|
21
|
+
result = cursor.fetchone()
|
|
22
|
+
conn.close()
|
|
23
|
+
return result
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**✅ 올바른 예시 (커넥션 풀):**
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
# Python (SQLAlchemy)
|
|
30
|
+
engine = create_engine(DSN, pool_size=10, max_overflow=20, pool_recycle=3600)
|
|
31
|
+
|
|
32
|
+
# Python (asyncpg)
|
|
33
|
+
pool = await asyncpg.create_pool(DSN, min_size=5, max_size=20)
|
|
34
|
+
async with pool.acquire() as conn:
|
|
35
|
+
result = await conn.fetchrow("SELECT ...", user_id)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Prisma (자동 풀링)
|
|
40
|
+
// datasource db { url = "..." } → connection_limit 설정
|
|
41
|
+
// schema.prisma: url = "...?connection_limit=10"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```go
|
|
45
|
+
// Go (database/sql - 내장 풀)
|
|
46
|
+
db, _ := sql.Open("postgres", dsn)
|
|
47
|
+
db.SetMaxOpenConns(25)
|
|
48
|
+
db.SetMaxIdleConns(10)
|
|
49
|
+
db.SetConnMaxLifetime(5 * time.Minute)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```rust
|
|
53
|
+
// Rust (deadpool-postgres)
|
|
54
|
+
let pool = Pool::builder(manager).max_size(16).build().unwrap();
|
|
55
|
+
let client = pool.get().await?;
|
|
56
|
+
let rows = client.query("SELECT ...", &[&user_id]).await?;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```java
|
|
60
|
+
// Java (HikariCP)
|
|
61
|
+
HikariConfig config = new HikariConfig();
|
|
62
|
+
config.setMaximumPoolSize(10);
|
|
63
|
+
config.setMinimumIdle(5);
|
|
64
|
+
config.setConnectionTimeout(30000);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**HTTP 커넥션 재사용:** `Keep-Alive` 헤더, HTTP/2 멀티플렉싱, HTTP 클라이언트 인스턴스 재사용.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Serialize Only Required Fields
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: 30-70% payload reduction, faster serialization
|
|
5
|
+
tags: io, serialization, payload, network, select
|
|
6
|
+
languages: all
|
|
7
|
+
related: [io-batch-queries, memory-large-data]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 필요한 필드만 직렬화/전송
|
|
11
|
+
|
|
12
|
+
전체 객체 대신 실제 사용하는 필드만 선택하여 직렬화 비용과 네트워크 전송량을 줄입니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (전체 객체 반환):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# 50개 필드 전부 전송, 클라이언트는 3개만 사용
|
|
18
|
+
@app.get("/users")
|
|
19
|
+
def list_users():
|
|
20
|
+
return db.query(User).all() # password_hash 포함 위험
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// 전체 객체 반환
|
|
25
|
+
return await prisma.user.findMany() // 50개 필드
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**✅ 올바른 예시 (필요 필드만):**
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# DTO/스키마로 필드 제한
|
|
32
|
+
@app.get("/users")
|
|
33
|
+
def list_users():
|
|
34
|
+
users = db.query(User.id, User.name, User.email).all()
|
|
35
|
+
return [UserResponse(id=u.id, name=u.name, email=u.email) for u in users]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Prisma select
|
|
40
|
+
const users = await prisma.user.findMany({
|
|
41
|
+
select: { id: true, name: true, email: true }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- SQL
|
|
47
|
+
SELECT id, name, email FROM users; -- ✅
|
|
48
|
+
SELECT * FROM users; -- ❌
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```go
|
|
52
|
+
// Go - json 태그로 제어
|
|
53
|
+
type UserResponse struct {
|
|
54
|
+
ID string `json:"id"`
|
|
55
|
+
Name string `json:"name"`
|
|
56
|
+
Email string `json:"email"`
|
|
57
|
+
// PasswordHash 필드 없음 → 자동 제외
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**적용 범위:** API 응답, SSR 데이터, 캐시 저장, 메시지 큐 페이로드.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stream Large Data Instead of Loading All
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Constant memory usage, no OOM on large datasets
|
|
5
|
+
tags: io, streaming, iterator, generator, memory
|
|
6
|
+
languages: all
|
|
7
|
+
related: [memory-large-data, concurrency-pipeline]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 대용량 데이터 스트리밍 처리
|
|
11
|
+
|
|
12
|
+
전체 데이터를 메모리에 적재하지 않고 스트림/이터레이터/제너레이터로 청크 단위 처리합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (전체 로드):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# 100만 행 전체 메모리 적재
|
|
18
|
+
rows = db.execute("SELECT * FROM big_table").fetchall()
|
|
19
|
+
for row in rows:
|
|
20
|
+
process(row)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**✅ 올바른 예시 (스트리밍):**
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
# Python - 서버 사이드 커서
|
|
27
|
+
cursor = db.execute("SELECT * FROM big_table")
|
|
28
|
+
while batch := cursor.fetchmany(1000):
|
|
29
|
+
for row in batch:
|
|
30
|
+
process(row)
|
|
31
|
+
|
|
32
|
+
# 파일 스트리밍
|
|
33
|
+
with open("huge.csv") as f:
|
|
34
|
+
for line in f: # 한 줄씩 (메모리 일정)
|
|
35
|
+
process(line)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Node.js - 스트림
|
|
40
|
+
import { createReadStream } from 'fs'
|
|
41
|
+
import { pipeline } from 'stream/promises'
|
|
42
|
+
|
|
43
|
+
await pipeline(
|
|
44
|
+
createReadStream('huge.csv'),
|
|
45
|
+
new Transform({ transform(chunk, enc, cb) { /* 청크 처리 */ cb() } }),
|
|
46
|
+
createWriteStream('output.csv')
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```go
|
|
51
|
+
// Go - Scanner (파일), rows.Next() (DB)
|
|
52
|
+
scanner := bufio.NewScanner(file)
|
|
53
|
+
for scanner.Scan() {
|
|
54
|
+
process(scanner.Text())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
rows, _ := db.Query("SELECT * FROM big_table")
|
|
58
|
+
defer rows.Close()
|
|
59
|
+
for rows.Next() {
|
|
60
|
+
var item Item
|
|
61
|
+
rows.Scan(&item.ID, &item.Name)
|
|
62
|
+
process(item)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```rust
|
|
67
|
+
// Rust - Iterator (lazy)
|
|
68
|
+
use std::io::{BufRead, BufReader};
|
|
69
|
+
let reader = BufReader::new(file);
|
|
70
|
+
for line in reader.lines() {
|
|
71
|
+
process(line?);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**적용 기준:** 10MB+ 파일, 10K+ DB 행, API 페이지네이션.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Bound Cache Size with LRU/TTL
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Prevents unbounded memory growth
|
|
5
|
+
tags: memory, cache, lru, ttl, bounded
|
|
6
|
+
languages: all
|
|
7
|
+
related: [io-cache-layer, concurrency-pool]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 캐시에 크기 제한 설정
|
|
11
|
+
|
|
12
|
+
무제한 캐시는 시간이 지남에 따라 메모리를 소진합니다. LRU(최소 최근 사용) + TTL(유효 시간)로 제한합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (무제한 캐시):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
cache = {} # 무제한 성장
|
|
18
|
+
def get_user(id):
|
|
19
|
+
if id not in cache:
|
|
20
|
+
cache[id] = db.get_user(id)
|
|
21
|
+
return cache[id]
|
|
22
|
+
# 100만 유저 → 100만 항목, OOM
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**✅ 올바른 예시 (제한된 캐시):**
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# Python
|
|
29
|
+
from cachetools import TTLCache, LRUCache
|
|
30
|
+
cache = TTLCache(maxsize=10000, ttl=300) # 최대 1만, 5분 TTL
|
|
31
|
+
|
|
32
|
+
# functools (크기만 제한)
|
|
33
|
+
@lru_cache(maxsize=1000)
|
|
34
|
+
def get_config(key): ...
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Node.js
|
|
39
|
+
import { LRUCache } from 'lru-cache'
|
|
40
|
+
const cache = new LRUCache<string, User>({
|
|
41
|
+
max: 10000, // 최대 항목 수
|
|
42
|
+
ttl: 5 * 60 * 1000, // 5분
|
|
43
|
+
maxSize: 50 * 1024 * 1024, // 50MB (선택)
|
|
44
|
+
sizeCalculation: (v) => JSON.stringify(v).length,
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```go
|
|
49
|
+
// Go (ristretto)
|
|
50
|
+
cache, _ := ristretto.NewCache(&ristretto.Config{
|
|
51
|
+
NumCounters: 1e5, // 키 추적 수
|
|
52
|
+
MaxCost: 1 << 26, // 64MB
|
|
53
|
+
BufferItems: 64,
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```rust
|
|
58
|
+
// Rust (moka)
|
|
59
|
+
let cache: Cache<String, User> = Cache::builder()
|
|
60
|
+
.max_capacity(10_000)
|
|
61
|
+
.time_to_live(Duration::from_secs(300))
|
|
62
|
+
.build();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**원칙:** 캐시 = max 크기 + 만료 정책. 둘 다 없으면 메모리 릭과 같음.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Paginate or Stream Large Datasets
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Constant memory usage regardless of data size
|
|
5
|
+
tags: memory, pagination, streaming, large-data
|
|
6
|
+
languages: all
|
|
7
|
+
related: [io-stream, concurrency-pipeline]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 대용량 데이터 페이지네이션/스트리밍
|
|
11
|
+
|
|
12
|
+
전체 데이터를 한번에 메모리에 적재하지 않고 페이지 단위 또는 스트림으로 처리합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시:**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
all_users = db.query("SELECT * FROM users").fetchall() # 100만 행 = OOM
|
|
18
|
+
response = jsonify(all_users)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**✅ 올바른 예시:**
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# 커서 기반 페이지네이션
|
|
25
|
+
@app.get("/users")
|
|
26
|
+
def list_users(cursor: str = None, limit: int = 50):
|
|
27
|
+
query = db.query(User).order_by(User.id)
|
|
28
|
+
if cursor:
|
|
29
|
+
query = query.filter(User.id > cursor)
|
|
30
|
+
users = query.limit(limit).all()
|
|
31
|
+
next_cursor = users[-1].id if users else None
|
|
32
|
+
return {"data": users, "next_cursor": next_cursor}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// 커서 기반 (Prisma)
|
|
37
|
+
const users = await prisma.user.findMany({
|
|
38
|
+
take: 50,
|
|
39
|
+
skip: cursor ? 1 : 0,
|
|
40
|
+
cursor: cursor ? { id: cursor } : undefined,
|
|
41
|
+
orderBy: { id: 'asc' },
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```go
|
|
46
|
+
// Go - 청크 처리
|
|
47
|
+
const batchSize = 1000
|
|
48
|
+
var lastID int64
|
|
49
|
+
for {
|
|
50
|
+
var batch []User
|
|
51
|
+
db.Where("id > ?", lastID).Order("id").Limit(batchSize).Find(&batch)
|
|
52
|
+
if len(batch) == 0 { break }
|
|
53
|
+
processBatch(batch)
|
|
54
|
+
lastID = batch[len(batch)-1].ID
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**페이지네이션 유형:**
|
|
59
|
+
|
|
60
|
+
| 유형 | 장점 | 단점 |
|
|
61
|
+
|------|------|------|
|
|
62
|
+
| **Offset** | 간단 구현 | 대량 데이터 시 느림, 중복/누락 |
|
|
63
|
+
| **Cursor** | 일관성, 빠름 | 임의 페이지 접근 불가 |
|
|
64
|
+
| **Keyset** | 가장 빠름 | 정렬 키 필요 |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Lazy Initialize Expensive Resources
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Faster startup, lower baseline memory
|
|
5
|
+
tags: memory, lazy, initialization, startup
|
|
6
|
+
languages: all
|
|
7
|
+
related: [concurrency-defer-await, build-code-split]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 비싼 초기화 지연 실행
|
|
11
|
+
|
|
12
|
+
애플리케이션 시작 시 모든 리소스를 초기화하지 않고, 실제 사용 시점에 지연 초기화합니다.
|
|
13
|
+
|
|
14
|
+
**❌ 잘못된 예시 (즉시 초기화):**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# 모듈 import 시 즉시 무거운 ML 모델 로드
|
|
18
|
+
import tensorflow as tf
|
|
19
|
+
model = tf.keras.models.load_model("huge_model.h5") # 5초, 2GB RAM
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// 모듈 로드 시 즉시 초기화
|
|
24
|
+
const heavyConfig = JSON.parse(fs.readFileSync('config.json', 'utf8'))
|
|
25
|
+
const searchIndex = buildSearchIndex(allDocuments) // 3초
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**✅ 올바른 예시 (지연 초기화):**
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# Python - 필요 시점에 로드
|
|
32
|
+
_model = None
|
|
33
|
+
def get_model():
|
|
34
|
+
global _model
|
|
35
|
+
if _model is None:
|
|
36
|
+
import tensorflow as tf
|
|
37
|
+
_model = tf.keras.models.load_model("huge_model.h5")
|
|
38
|
+
return _model
|
|
39
|
+
|
|
40
|
+
# Python 3.8+ functools.cached_property
|
|
41
|
+
class Service:
|
|
42
|
+
@cached_property
|
|
43
|
+
def model(self):
|
|
44
|
+
return load_heavy_model()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// JS/TS - getter 또는 lazy 패턴
|
|
49
|
+
let _searchIndex: SearchIndex | null = null
|
|
50
|
+
function getSearchIndex(): SearchIndex {
|
|
51
|
+
if (!_searchIndex) {
|
|
52
|
+
_searchIndex = buildSearchIndex(allDocuments)
|
|
53
|
+
}
|
|
54
|
+
return _searchIndex
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// Rust - LazyLock (std) 또는 once_cell
|
|
60
|
+
use std::sync::LazyLock;
|
|
61
|
+
static CONFIG: LazyLock<Config> = LazyLock::new(|| {
|
|
62
|
+
load_config().expect("config load failed")
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```go
|
|
67
|
+
// Go - sync.Once
|
|
68
|
+
var (
|
|
69
|
+
instance *Service
|
|
70
|
+
once sync.Once
|
|
71
|
+
)
|
|
72
|
+
func GetService() *Service {
|
|
73
|
+
once.Do(func() { instance = newService() })
|
|
74
|
+
return instance
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**적용 대상:** ML 모델, 검색 인덱스, 대용량 설정, 외부 서비스 커넥션.
|