@su-record/vibe 0.4.5 โ 0.4.6
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/.claude/agents/simplifier.md +1 -1
- package/.claude/commands/vibe.analyze.md +1 -1
- package/.claude/commands/vibe.run.md +1 -1
- package/.claude/commands/vibe.spec.md +2 -2
- package/.claude/commands/vibe.verify.md +1 -1
- package/.claude/settings.local.json +3 -1
- package/README.md +4 -4
- package/bin/vibe +41 -13
- package/package.json +1 -1
- package/templates/hooks-template.json +1 -1
- package/.agent/rules/core/communication-guide.md +0 -104
- package/.agent/rules/core/development-philosophy.md +0 -53
- package/.agent/rules/core/quick-start.md +0 -121
- package/.agent/rules/languages/dart-flutter.md +0 -509
- package/.agent/rules/languages/go.md +0 -396
- package/.agent/rules/languages/java-spring.md +0 -586
- package/.agent/rules/languages/kotlin-android.md +0 -491
- package/.agent/rules/languages/python-django.md +0 -371
- package/.agent/rules/languages/python-fastapi.md +0 -386
- package/.agent/rules/languages/rust.md +0 -425
- package/.agent/rules/languages/swift-ios.md +0 -516
- package/.agent/rules/languages/typescript-nextjs.md +0 -441
- package/.agent/rules/languages/typescript-node.md +0 -375
- package/.agent/rules/languages/typescript-react-native.md +0 -446
- package/.agent/rules/languages/typescript-react.md +0 -525
- package/.agent/rules/languages/typescript-vue.md +0 -353
- package/.agent/rules/quality/bdd-contract-testing.md +0 -388
- package/.agent/rules/quality/checklist.md +0 -276
- package/.agent/rules/quality/testing-strategy.md +0 -437
- package/.agent/rules/standards/anti-patterns.md +0 -369
- package/.agent/rules/standards/code-structure.md +0 -291
- package/.agent/rules/standards/complexity-metrics.md +0 -312
- package/.agent/rules/standards/naming-conventions.md +0 -198
- package/.agent/rules/tools/mcp-hi-ai-guide.md +0 -665
- package/.agent/rules/tools/mcp-workflow.md +0 -51
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
# ๐ Python + FastAPI ํ์ง ๊ท์น
|
|
2
|
-
|
|
3
|
-
## ํต์ฌ ์์น (core์์ ์์)
|
|
4
|
-
|
|
5
|
-
```markdown
|
|
6
|
-
โ
๋จ์ผ ์ฑ
์ (SRP)
|
|
7
|
-
โ
์ค๋ณต ์ ๊ฑฐ (DRY)
|
|
8
|
-
โ
์ฌ์ฌ์ฉ์ฑ
|
|
9
|
-
โ
๋ฎ์ ๋ณต์ก๋
|
|
10
|
-
โ
ํจ์ โค 30์ค (๊ถ์ฅ), โค 50์ค (ํ์ฉ)
|
|
11
|
-
โ
์ค์ฒฉ โค 3๋จ๊ณ
|
|
12
|
-
โ
Cyclomatic complexity โค 10
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Python ํนํ ๊ท์น
|
|
16
|
-
|
|
17
|
-
### 1. ํ์
ํํธ 100% ํ์
|
|
18
|
-
|
|
19
|
-
```python
|
|
20
|
-
# โ ํ์
ํํธ ์์
|
|
21
|
-
def get_user(user_id):
|
|
22
|
-
return db.get(user_id)
|
|
23
|
-
|
|
24
|
-
# โ
์์ ํ ํ์
ํํธ
|
|
25
|
-
async def get_user(user_id: str, db: AsyncSession) -> User | None:
|
|
26
|
-
result = await db.execute(select(User).where(User.id == user_id))
|
|
27
|
-
return result.scalar_one_or_none()
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### 2. Pydantic์ผ๋ก Contract ์ ์
|
|
31
|
-
|
|
32
|
-
```python
|
|
33
|
-
from pydantic import BaseModel, Field, EmailStr, field_validator
|
|
34
|
-
|
|
35
|
-
class CreateUserRequest(BaseModel):
|
|
36
|
-
"""์ฌ์ฉ์ ์์ฑ ์์ฒญ ์คํค๋ง"""
|
|
37
|
-
email: EmailStr
|
|
38
|
-
username: str = Field(min_length=3, max_length=50)
|
|
39
|
-
password: str = Field(min_length=8)
|
|
40
|
-
age: int = Field(ge=0, le=150)
|
|
41
|
-
|
|
42
|
-
@field_validator("username")
|
|
43
|
-
def validate_username(cls, v: str) -> str:
|
|
44
|
-
if not v.isalnum():
|
|
45
|
-
raise ValueError("Username must be alphanumeric")
|
|
46
|
-
return v.lower()
|
|
47
|
-
|
|
48
|
-
class Config:
|
|
49
|
-
json_schema_extra = {
|
|
50
|
-
"example": {
|
|
51
|
-
"email": "user@example.com",
|
|
52
|
-
"username": "johndoe",
|
|
53
|
-
"password": "securepass123",
|
|
54
|
-
"age": 25,
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
class UserResponse(BaseModel):
|
|
59
|
-
"""์ฌ์ฉ์ ์๋ต ์คํค๋ง"""
|
|
60
|
-
id: str
|
|
61
|
-
email: str
|
|
62
|
-
username: str
|
|
63
|
-
created_at: datetime
|
|
64
|
-
|
|
65
|
-
class Config:
|
|
66
|
-
from_attributes = True # SQLAlchemy ํธํ
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 3. async/await ํจํด
|
|
70
|
-
|
|
71
|
-
```python
|
|
72
|
-
# โ
๋น๋๊ธฐ I/O (๋ฐ์ดํฐ๋ฒ ์ด์ค, API ํธ์ถ)
|
|
73
|
-
async def get_user_with_posts(
|
|
74
|
-
user_id: str,
|
|
75
|
-
db: AsyncSession
|
|
76
|
-
) -> tuple[User, list[Post]]:
|
|
77
|
-
# ๋ณ๋ ฌ ์คํ
|
|
78
|
-
user_task = db.execute(select(User).where(User.id == user_id))
|
|
79
|
-
posts_task = db.execute(select(Post).where(Post.user_id == user_id))
|
|
80
|
-
|
|
81
|
-
user_result, posts_result = await asyncio.gather(user_task, posts_task)
|
|
82
|
-
|
|
83
|
-
user = user_result.scalar_one_or_none()
|
|
84
|
-
posts = list(posts_result.scalars().all())
|
|
85
|
-
|
|
86
|
-
return user, posts
|
|
87
|
-
|
|
88
|
-
# โ ๋๊ธฐ ํจ์ (๋ธ๋กํน)
|
|
89
|
-
def get_user(user_id: str):
|
|
90
|
-
return requests.get(f"/users/{user_id}") # ๋ธ๋กํน!
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### 4. Early Return ์ ํธ
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
# โ ์ค์ฒฉ๋ if๋ฌธ
|
|
97
|
-
async def process_order(order_id: str, db: AsyncSession):
|
|
98
|
-
order = await get_order(order_id, db)
|
|
99
|
-
if order:
|
|
100
|
-
if order.is_valid:
|
|
101
|
-
if order.items:
|
|
102
|
-
if order.user.is_active:
|
|
103
|
-
return await process_items(order.items)
|
|
104
|
-
return None
|
|
105
|
-
|
|
106
|
-
# โ
Early return
|
|
107
|
-
async def process_order(order_id: str, db: AsyncSession) -> ProcessResult | None:
|
|
108
|
-
order = await get_order(order_id, db)
|
|
109
|
-
if not order:
|
|
110
|
-
return None
|
|
111
|
-
if not order.is_valid:
|
|
112
|
-
return None
|
|
113
|
-
if not order.items:
|
|
114
|
-
return None
|
|
115
|
-
if not order.user.is_active:
|
|
116
|
-
return None
|
|
117
|
-
|
|
118
|
-
return await process_items(order.items)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 5. Repository ํจํด (๋ฐ์ดํฐ ์ก์ธ์ค ๋ถ๋ฆฌ)
|
|
122
|
-
|
|
123
|
-
```python
|
|
124
|
-
# โ
Repository ๋ ์ด์ด
|
|
125
|
-
class UserRepository:
|
|
126
|
-
"""๋ฐ์ดํฐ ์ก์ธ์ค๋ง ๋ด๋น"""
|
|
127
|
-
|
|
128
|
-
def __init__(self, db: AsyncSession):
|
|
129
|
-
self.db = db
|
|
130
|
-
|
|
131
|
-
async def get_by_id(self, user_id: str) -> User | None:
|
|
132
|
-
result = await self.db.execute(
|
|
133
|
-
select(User).where(User.id == user_id)
|
|
134
|
-
)
|
|
135
|
-
return result.scalar_one_or_none()
|
|
136
|
-
|
|
137
|
-
async def create(self, user: User) -> User:
|
|
138
|
-
self.db.add(user)
|
|
139
|
-
await self.db.commit()
|
|
140
|
-
await self.db.refresh(user)
|
|
141
|
-
return user
|
|
142
|
-
|
|
143
|
-
async def get_by_email(self, email: str) -> User | None:
|
|
144
|
-
result = await self.db.execute(
|
|
145
|
-
select(User).where(User.email == email)
|
|
146
|
-
)
|
|
147
|
-
return result.scalar_one_or_none()
|
|
148
|
-
|
|
149
|
-
# โ
Service ๋ ์ด์ด (๋น์ฆ๋์ค ๋ก์ง)
|
|
150
|
-
class UserService:
|
|
151
|
-
"""๋น์ฆ๋์ค ๋ก์ง๋ง ๋ด๋น"""
|
|
152
|
-
|
|
153
|
-
def __init__(self, repository: UserRepository):
|
|
154
|
-
self.repository = repository
|
|
155
|
-
|
|
156
|
-
async def create_user(
|
|
157
|
-
self, request: CreateUserRequest
|
|
158
|
-
) -> UserResponse:
|
|
159
|
-
# ๋น์ฆ๋์ค ๊ท์น: ์ด๋ฉ์ผ ์ค๋ณต ์ฒดํฌ
|
|
160
|
-
existing = await self.repository.get_by_email(request.email)
|
|
161
|
-
if existing:
|
|
162
|
-
raise HTTPException(409, detail="Email already exists")
|
|
163
|
-
|
|
164
|
-
# ๋น์ฆ๋์ค ๊ท์น: ๋น๋ฐ๋ฒํธ ํด์ฑ
|
|
165
|
-
hashed_password = hash_password(request.password)
|
|
166
|
-
|
|
167
|
-
# ์์ฑ
|
|
168
|
-
user = User(
|
|
169
|
-
email=request.email,
|
|
170
|
-
username=request.username,
|
|
171
|
-
password_hash=hashed_password,
|
|
172
|
-
)
|
|
173
|
-
user = await self.repository.create(user)
|
|
174
|
-
|
|
175
|
-
return UserResponse.model_validate(user)
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### 6. ์์กด์ฑ ์ฃผ์
(FastAPI Depends)
|
|
179
|
-
|
|
180
|
-
```python
|
|
181
|
-
# app/core/deps.py
|
|
182
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
183
|
-
|
|
184
|
-
async def get_db() -> AsyncSession:
|
|
185
|
-
"""๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์
์์กด์ฑ"""
|
|
186
|
-
async with async_session_maker() as session:
|
|
187
|
-
yield session
|
|
188
|
-
|
|
189
|
-
async def get_current_user(
|
|
190
|
-
token: str = Depends(oauth2_scheme),
|
|
191
|
-
db: AsyncSession = Depends(get_db)
|
|
192
|
-
) -> User:
|
|
193
|
-
"""ํ์ฌ ์ฌ์ฉ์ ์์กด์ฑ"""
|
|
194
|
-
payload = decode_jwt(token)
|
|
195
|
-
user = await get_user_by_id(payload["sub"], db)
|
|
196
|
-
if not user:
|
|
197
|
-
raise HTTPException(401, detail="Invalid credentials")
|
|
198
|
-
return user
|
|
199
|
-
|
|
200
|
-
# app/api/v1/users.py
|
|
201
|
-
@router.get("/me", response_model=UserResponse)
|
|
202
|
-
async def get_current_user_profile(
|
|
203
|
-
current_user: User = Depends(get_current_user)
|
|
204
|
-
):
|
|
205
|
-
"""ํ์ฌ ์ฌ์ฉ์ ํ๋กํ ์กฐํ"""
|
|
206
|
-
return UserResponse.model_validate(current_user)
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### 7. ์๋ฌ ์ฒ๋ฆฌ ํ์ค
|
|
210
|
-
|
|
211
|
-
```python
|
|
212
|
-
from fastapi import HTTPException
|
|
213
|
-
|
|
214
|
-
# โ
๋ช
ํํ ์๋ฌ ๋ฉ์์ง
|
|
215
|
-
async def get_user(user_id: str, db: AsyncSession) -> User:
|
|
216
|
-
user = await db.get(User, user_id)
|
|
217
|
-
if not user:
|
|
218
|
-
raise HTTPException(
|
|
219
|
-
status_code=404,
|
|
220
|
-
detail=f"User {user_id} not found"
|
|
221
|
-
)
|
|
222
|
-
return user
|
|
223
|
-
|
|
224
|
-
# โ
์ปค์คํ
์์ธ
|
|
225
|
-
class UserNotFoundError(Exception):
|
|
226
|
-
def __init__(self, user_id: str):
|
|
227
|
-
self.user_id = user_id
|
|
228
|
-
super().__init__(f"User {user_id} not found")
|
|
229
|
-
|
|
230
|
-
# ์ ์ญ ์์ธ ํธ๋ค๋ฌ
|
|
231
|
-
@app.exception_handler(UserNotFoundError)
|
|
232
|
-
async def user_not_found_handler(request: Request, exc: UserNotFoundError):
|
|
233
|
-
return JSONResponse(
|
|
234
|
-
status_code=404,
|
|
235
|
-
content={"detail": str(exc)}
|
|
236
|
-
)
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### 8. SQLAlchemy 2.0 ์คํ์ผ
|
|
240
|
-
|
|
241
|
-
```python
|
|
242
|
-
from sqlalchemy import select, func
|
|
243
|
-
from sqlalchemy.orm import selectinload
|
|
244
|
-
|
|
245
|
-
# โ
2.0 ์คํ์ผ (async + select)
|
|
246
|
-
async def get_users_with_posts(db: AsyncSession) -> list[User]:
|
|
247
|
-
result = await db.execute(
|
|
248
|
-
select(User)
|
|
249
|
-
.options(selectinload(User.posts)) # Eager loading
|
|
250
|
-
.where(User.is_active == True)
|
|
251
|
-
.order_by(User.created_at.desc())
|
|
252
|
-
.limit(20)
|
|
253
|
-
)
|
|
254
|
-
return list(result.scalars().all())
|
|
255
|
-
|
|
256
|
-
# โ 1.x ์คํ์ผ (๋ ๊ฑฐ์)
|
|
257
|
-
def get_users():
|
|
258
|
-
return session.query(User).filter_by(is_active=True).all()
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### 9. Python ๊ด์ฉ๊ตฌ ํ์ฉ
|
|
262
|
-
|
|
263
|
-
```python
|
|
264
|
-
# โ
List comprehension
|
|
265
|
-
active_users = [u for u in users if u.is_active]
|
|
266
|
-
|
|
267
|
-
# โ
Dictionary comprehension
|
|
268
|
-
user_dict = {u.id: u.name for u in users}
|
|
269
|
-
|
|
270
|
-
# โ
Generator expression (๋ฉ๋ชจ๋ฆฌ ํจ์จ)
|
|
271
|
-
total = sum(u.age for u in users)
|
|
272
|
-
|
|
273
|
-
# โ
Context manager
|
|
274
|
-
async with db.begin():
|
|
275
|
-
user = User(...)
|
|
276
|
-
db.add(user)
|
|
277
|
-
# ์๋ commit/rollback
|
|
278
|
-
|
|
279
|
-
# โ
Dataclass (๊ฐ๋จํ ๋ฐ์ดํฐ ๊ตฌ์กฐ)
|
|
280
|
-
from dataclasses import dataclass
|
|
281
|
-
|
|
282
|
-
@dataclass(frozen=True) # Immutable
|
|
283
|
-
class Point:
|
|
284
|
-
x: float
|
|
285
|
-
y: float
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### 10. ๋ก๊น
ํ์ค
|
|
289
|
-
|
|
290
|
-
```python
|
|
291
|
-
import structlog
|
|
292
|
-
|
|
293
|
-
logger = structlog.get_logger()
|
|
294
|
-
|
|
295
|
-
# โ
๊ตฌ์กฐํ๋ ๋ก๊น
|
|
296
|
-
async def create_user(request: CreateUserRequest):
|
|
297
|
-
logger.info(
|
|
298
|
-
"user_creation_started",
|
|
299
|
-
email=request.email,
|
|
300
|
-
username=request.username
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
try:
|
|
304
|
-
user = await user_service.create(request)
|
|
305
|
-
logger.info(
|
|
306
|
-
"user_creation_succeeded",
|
|
307
|
-
user_id=user.id,
|
|
308
|
-
email=user.email
|
|
309
|
-
)
|
|
310
|
-
return user
|
|
311
|
-
except Exception as e:
|
|
312
|
-
logger.error(
|
|
313
|
-
"user_creation_failed",
|
|
314
|
-
email=request.email,
|
|
315
|
-
error=str(e),
|
|
316
|
-
exc_info=True
|
|
317
|
-
)
|
|
318
|
-
raise
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
## ์ํฐํจํด
|
|
322
|
-
|
|
323
|
-
```python
|
|
324
|
-
# โ any ํ์
|
|
325
|
-
def process_data(data: any): # ํ์
์์ ์ฑ ์์ค
|
|
326
|
-
return data
|
|
327
|
-
|
|
328
|
-
# โ ๋ธ๋กํน I/O in async ํจ์
|
|
329
|
-
async def bad_example():
|
|
330
|
-
data = requests.get("https://api.example.com") # ๋ธ๋กํน!
|
|
331
|
-
return data
|
|
332
|
-
|
|
333
|
-
# โ ์์ธ ๋ฌด์
|
|
334
|
-
try:
|
|
335
|
-
risky_operation()
|
|
336
|
-
except:
|
|
337
|
-
pass # ์ํ!
|
|
338
|
-
|
|
339
|
-
# โ Mutable default argument
|
|
340
|
-
def append_to_list(item, my_list=[]): # ๋ฒ๊ทธ!
|
|
341
|
-
my_list.append(item)
|
|
342
|
-
return my_list
|
|
343
|
-
|
|
344
|
-
# โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ
|
|
345
|
-
def append_to_list(item, my_list: list | None = None):
|
|
346
|
-
if my_list is None:
|
|
347
|
-
my_list = []
|
|
348
|
-
my_list.append(item)
|
|
349
|
-
return my_list
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
## ์ฝ๋ ํ์ง ๋๊ตฌ
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
# ํฌ๋งทํ
|
|
356
|
-
black .
|
|
357
|
-
isort .
|
|
358
|
-
|
|
359
|
-
# ๋ฆฐํ
|
|
360
|
-
flake8 .
|
|
361
|
-
ruff check .
|
|
362
|
-
|
|
363
|
-
# ํ์
์ฒดํฌ
|
|
364
|
-
mypy app/ --strict
|
|
365
|
-
|
|
366
|
-
# ํ
์คํธ
|
|
367
|
-
pytest tests/ -v --cov=app
|
|
368
|
-
|
|
369
|
-
# ๋ณด์ ์ฒดํฌ
|
|
370
|
-
bandit -r app/
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
## ์ฒดํฌ๋ฆฌ์คํธ
|
|
374
|
-
|
|
375
|
-
Python/FastAPI ์ฝ๋ ์์ฑ ์:
|
|
376
|
-
|
|
377
|
-
- [ ] ํ์
ํํธ 100% (ํจ์ ์๊ทธ๋์ฒ, ๋ณ์)
|
|
378
|
-
- [ ] Pydantic ์คํค๋ง๋ก Contract ์ ์
|
|
379
|
-
- [ ] async/await ์ฌ์ฉ (I/O ์์
)
|
|
380
|
-
- [ ] Early return ํจํด
|
|
381
|
-
- [ ] Repository + Service ๋ ์ด์ด ๋ถ๋ฆฌ
|
|
382
|
-
- [ ] ์์กด์ฑ ์ฃผ์
(Depends)
|
|
383
|
-
- [ ] ๋ช
ํํ ์๋ฌ ๋ฉ์์ง
|
|
384
|
-
- [ ] ๊ตฌ์กฐํ๋ ๋ก๊น
|
|
385
|
-
- [ ] ํจ์ โค 30์ค (SRP ์ค์)
|
|
386
|
-
- [ ] ๋ณต์ก๋ โค 10
|