mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
docs(ko-KR): complete missing sections in code-reviewer and planner translations
- code-reviewer: add code examples (deep nesting, useEffect deps, key props, N+1 queries), Project-Specific Guidelines section, cost-awareness check - planner: add Worked Example (Stripe Subscriptions) and Red Flags sections
This commit is contained in:
@@ -42,6 +42,23 @@ model: sonnet
|
|||||||
- **취약한 의존성** — 알려진 취약점이 있는 패키지
|
- **취약한 의존성** — 알려진 취약점이 있는 패키지
|
||||||
- **로그에 비밀 노출** — 민감한 데이터 로깅 (토큰, 비밀번호, PII)
|
- **로그에 비밀 노출** — 민감한 데이터 로깅 (토큰, 비밀번호, PII)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: 문자열 연결을 통한 SQL 인젝션
|
||||||
|
const query = `SELECT * FROM users WHERE id = ${userId}`;
|
||||||
|
|
||||||
|
// GOOD: 매개변수화된 쿼리
|
||||||
|
const query = `SELECT * FROM users WHERE id = $1`;
|
||||||
|
const result = await db.query(query, [userId]);
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: 소독 없이 사용자 HTML 렌더링
|
||||||
|
// 항상 DOMPurify.sanitize() 또는 동등한 것으로 사용자 콘텐츠 소독
|
||||||
|
|
||||||
|
// GOOD: 텍스트 콘텐츠 사용 또는 소독
|
||||||
|
<div>{userComment}</div>
|
||||||
|
```
|
||||||
|
|
||||||
### 코드 품질 (HIGH)
|
### 코드 품질 (HIGH)
|
||||||
|
|
||||||
- **큰 함수** (50줄 초과) — 작고 집중된 함수로 분리
|
- **큰 함수** (50줄 초과) — 작고 집중된 함수로 분리
|
||||||
@@ -53,6 +70,31 @@ model: sonnet
|
|||||||
- **테스트 누락** — 테스트 커버리지 없는 새 코드 경로
|
- **테스트 누락** — 테스트 커버리지 없는 새 코드 경로
|
||||||
- **죽은 코드** — 주석 처리된 코드, 사용되지 않는 import, 도달 불가능한 분기
|
- **죽은 코드** — 주석 처리된 코드, 사용되지 않는 import, 도달 불가능한 분기
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: 깊은 중첩 + 변이
|
||||||
|
function processUsers(users) {
|
||||||
|
if (users) {
|
||||||
|
for (const user of users) {
|
||||||
|
if (user.active) {
|
||||||
|
if (user.email) {
|
||||||
|
user.verified = true; // 변이!
|
||||||
|
results.push(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD: 조기 반환 + 불변성 + 플랫
|
||||||
|
function processUsers(users) {
|
||||||
|
if (!users) return [];
|
||||||
|
return users
|
||||||
|
.filter(user => user.active && user.email)
|
||||||
|
.map(user => ({ ...user, verified: true }));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### React/Next.js 패턴 (HIGH)
|
### React/Next.js 패턴 (HIGH)
|
||||||
|
|
||||||
React/Next.js 코드 리뷰 시 추가 확인:
|
React/Next.js 코드 리뷰 시 추가 확인:
|
||||||
@@ -66,6 +108,26 @@ React/Next.js 코드 리뷰 시 추가 확인:
|
|||||||
- **로딩/에러 상태 누락** — 폴백 UI 없는 데이터 페칭
|
- **로딩/에러 상태 누락** — 폴백 UI 없는 데이터 페칭
|
||||||
- **오래된 클로저** — 오래된 상태 값을 캡처하는 이벤트 핸들러
|
- **오래된 클로저** — 오래된 상태 값을 캡처하는 이벤트 핸들러
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// BAD: 의존성 누락, 오래된 클로저
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData(userId);
|
||||||
|
}, []); // userId가 deps에서 누락
|
||||||
|
|
||||||
|
// GOOD: 완전한 의존성
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData(userId);
|
||||||
|
}, [userId]);
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// BAD: 재정렬 가능한 목록에서 인덱스를 key로 사용
|
||||||
|
{items.map((item, i) => <ListItem key={i} item={item} />)}
|
||||||
|
|
||||||
|
// GOOD: 안정적인 고유 key
|
||||||
|
{items.map(item => <ListItem key={item.id} item={item} />)}
|
||||||
|
```
|
||||||
|
|
||||||
### Node.js/Backend 패턴 (HIGH)
|
### Node.js/Backend 패턴 (HIGH)
|
||||||
|
|
||||||
백엔드 코드 리뷰 시:
|
백엔드 코드 리뷰 시:
|
||||||
@@ -78,6 +140,22 @@ React/Next.js 코드 리뷰 시 추가 확인:
|
|||||||
- **에러 메시지 누출** — 클라이언트에 내부 에러 세부사항 전송
|
- **에러 메시지 누출** — 클라이언트에 내부 에러 세부사항 전송
|
||||||
- **CORS 설정 누락** — 의도하지 않은 오리진에서 접근 가능한 API
|
- **CORS 설정 누락** — 의도하지 않은 오리진에서 접근 가능한 API
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: N+1 쿼리 패턴
|
||||||
|
const users = await db.query('SELECT * FROM users');
|
||||||
|
for (const user of users) {
|
||||||
|
user.posts = await db.query('SELECT * FROM posts WHERE user_id = $1', [user.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD: JOIN 또는 배치를 사용한 단일 쿼리
|
||||||
|
const usersWithPosts = await db.query(`
|
||||||
|
SELECT u.*, json_agg(p.*) as posts
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN posts p ON p.user_id = u.id
|
||||||
|
GROUP BY u.id
|
||||||
|
`);
|
||||||
|
```
|
||||||
|
|
||||||
### 성능 (MEDIUM)
|
### 성능 (MEDIUM)
|
||||||
|
|
||||||
- **비효율적 알고리즘** — O(n log n) 또는 O(n)이 가능한데 O(n²)
|
- **비효율적 알고리즘** — O(n log n) 또는 O(n)이 가능한데 O(n²)
|
||||||
@@ -132,7 +210,20 @@ Fix: 환경 변수로 이동하고 .gitignore/.env.example에 추가
|
|||||||
- **경고**: HIGH 이슈만 (주의하여 merge 가능)
|
- **경고**: HIGH 이슈만 (주의하여 merge 가능)
|
||||||
- **차단**: CRITICAL 이슈 발견 — merge 전에 반드시 수정
|
- **차단**: CRITICAL 이슈 발견 — merge 전에 반드시 수정
|
||||||
|
|
||||||
## AI 생성 코드 리뷰 부록
|
## 프로젝트별 가이드라인
|
||||||
|
|
||||||
|
가능한 경우, `CLAUDE.md` 또는 프로젝트 규칙의 프로젝트별 컨벤션도 확인:
|
||||||
|
|
||||||
|
- 파일 크기 제한 (예: 일반적으로 200-400줄, 최대 800줄)
|
||||||
|
- 이모지 정책 (많은 프로젝트가 코드에서 이모지 사용 금지)
|
||||||
|
- 불변성 요구사항 (변이 대신 spread 연산자)
|
||||||
|
- 데이터베이스 정책 (RLS, 마이그레이션 패턴)
|
||||||
|
- 에러 처리 패턴 (커스텀 에러 클래스, 에러 바운더리)
|
||||||
|
- 상태 관리 컨벤션 (Zustand, Redux, Context)
|
||||||
|
|
||||||
|
프로젝트의 확립된 패턴에 맞게 리뷰를 조정하세요. 확신이 없을 때는 코드베이스의 나머지 부분이 하는 방식에 맞추세요.
|
||||||
|
|
||||||
|
## v1.8 AI 생성 코드 리뷰 부록
|
||||||
|
|
||||||
AI 생성 변경사항 리뷰 시 우선순위:
|
AI 생성 변경사항 리뷰 시 우선순위:
|
||||||
|
|
||||||
@@ -140,3 +231,7 @@ AI 생성 변경사항 리뷰 시 우선순위:
|
|||||||
2. 보안 가정 및 신뢰 경계
|
2. 보안 가정 및 신뢰 경계
|
||||||
3. 숨겨진 결합 또는 의도치 않은 아키텍처 드리프트
|
3. 숨겨진 결합 또는 의도치 않은 아키텍처 드리프트
|
||||||
4. 불필요한 모델 비용 유발 복잡성
|
4. 불필요한 모델 비용 유발 복잡성
|
||||||
|
|
||||||
|
비용 인식 체크:
|
||||||
|
- 명확한 추론 필요 없이 더 비싼 모델로 에스컬레이션하는 워크플로우를 플래그하세요.
|
||||||
|
- 결정론적 리팩토링에는 저비용 티어를 기본으로 사용하도록 권장하세요.
|
||||||
|
|||||||
@@ -95,6 +95,85 @@ model: opus
|
|||||||
6. **점진적으로** — 각 단계가 검증 가능해야 함
|
6. **점진적으로** — 각 단계가 검증 가능해야 함
|
||||||
7. **결정 문서화** — 무엇만이 아닌 왜를 설명
|
7. **결정 문서화** — 무엇만이 아닌 왜를 설명
|
||||||
|
|
||||||
|
## 실전 예제: Stripe 구독 추가
|
||||||
|
|
||||||
|
기대되는 상세 수준을 보여주는 완전한 계획입니다:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 구현 계획: Stripe 구독 결제
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
무료/프로/엔터프라이즈 티어의 구독 결제를 추가합니다. 사용자는 Stripe Checkout을
|
||||||
|
통해 업그레이드하고, 웹훅 이벤트가 구독 상태를 동기화합니다.
|
||||||
|
|
||||||
|
## 요구사항
|
||||||
|
- 세 가지 티어: Free (기본), Pro ($29/월), Enterprise ($99/월)
|
||||||
|
- 결제 흐름을 위한 Stripe Checkout
|
||||||
|
- 구독 라이프사이클 이벤트를 위한 웹훅 핸들러
|
||||||
|
- 구독 티어 기반 기능 게이팅
|
||||||
|
|
||||||
|
## 아키텍처 변경사항
|
||||||
|
- 새 테이블: `subscriptions` (user_id, stripe_customer_id, stripe_subscription_id, status, tier)
|
||||||
|
- 새 API 라우트: `app/api/checkout/route.ts` — Stripe Checkout 세션 생성
|
||||||
|
- 새 API 라우트: `app/api/webhooks/stripe/route.ts` — Stripe 이벤트 처리
|
||||||
|
- 새 미들웨어: 게이트된 기능에 대한 구독 티어 확인
|
||||||
|
- 새 컴포넌트: `PricingTable` — 업그레이드 버튼이 있는 티어 표시
|
||||||
|
|
||||||
|
## 구현 단계
|
||||||
|
|
||||||
|
### Phase 1: 데이터베이스 & 백엔드 (2개 파일)
|
||||||
|
1. **구독 마이그레이션 생성** (File: supabase/migrations/004_subscriptions.sql)
|
||||||
|
- Action: RLS 정책과 함께 CREATE TABLE subscriptions
|
||||||
|
- Why: 결제 상태를 서버 측에 저장, 클라이언트를 절대 신뢰하지 않음
|
||||||
|
- Dependencies: 없음
|
||||||
|
- Risk: Low
|
||||||
|
|
||||||
|
2. **Stripe 웹훅 핸들러 생성** (File: src/app/api/webhooks/stripe/route.ts)
|
||||||
|
- Action: checkout.session.completed, customer.subscription.updated,
|
||||||
|
customer.subscription.deleted 이벤트 처리
|
||||||
|
- Why: 구독 상태를 Stripe와 동기화 유지
|
||||||
|
- Dependencies: 단계 1 (subscriptions 테이블 필요)
|
||||||
|
- Risk: High — 웹훅 서명 검증이 중요
|
||||||
|
|
||||||
|
### Phase 2: 체크아웃 흐름 (2개 파일)
|
||||||
|
3. **체크아웃 API 라우트 생성** (File: src/app/api/checkout/route.ts)
|
||||||
|
- Action: price_id와 success/cancel URL로 Stripe Checkout 세션 생성
|
||||||
|
- Why: 서버 측 세션 생성으로 가격 변조 방지
|
||||||
|
- Dependencies: 단계 1
|
||||||
|
- Risk: Medium — 사용자 인증 여부를 반드시 검증해야 함
|
||||||
|
|
||||||
|
4. **가격 페이지 구축** (File: src/components/PricingTable.tsx)
|
||||||
|
- Action: 기능 비교와 업그레이드 버튼이 있는 세 가지 티어 표시
|
||||||
|
- Why: 사용자 대면 업그레이드 흐름
|
||||||
|
- Dependencies: 단계 3
|
||||||
|
- Risk: Low
|
||||||
|
|
||||||
|
### Phase 3: 기능 게이팅 (1개 파일)
|
||||||
|
5. **티어 기반 미들웨어 추가** (File: src/middleware.ts)
|
||||||
|
- Action: 보호된 라우트에서 구독 티어 확인, 무료 사용자 리다이렉트
|
||||||
|
- Why: 서버 측에서 티어 제한 강제
|
||||||
|
- Dependencies: 단계 1-2 (구독 데이터 필요)
|
||||||
|
- Risk: Medium — 엣지 케이스 처리 필요 (expired, past_due)
|
||||||
|
|
||||||
|
## 테스트 전략
|
||||||
|
- 단위 테스트: 웹훅 이벤트 파싱, 티어 확인 로직
|
||||||
|
- 통합 테스트: 체크아웃 세션 생성, 웹훅 처리
|
||||||
|
- E2E 테스트: 전체 업그레이드 흐름 (Stripe 테스트 모드)
|
||||||
|
|
||||||
|
## 위험 및 완화
|
||||||
|
- **위험**: 웹훅 이벤트가 순서 없이 도착
|
||||||
|
- 완화: 이벤트 타임스탬프 사용, 멱등 업데이트
|
||||||
|
- **위험**: 사용자가 업그레이드했지만 웹훅 실패
|
||||||
|
- 완화: 폴백으로 Stripe 폴링, "처리 중" 상태 표시
|
||||||
|
|
||||||
|
## 성공 기준
|
||||||
|
- [ ] 사용자가 Stripe Checkout을 통해 Free에서 Pro로 업그레이드 가능
|
||||||
|
- [ ] 웹훅이 구독 상태를 정확히 동기화
|
||||||
|
- [ ] 무료 사용자가 Pro 기능에 접근 불가
|
||||||
|
- [ ] 다운그레이드/취소가 정상 작동
|
||||||
|
- [ ] 모든 테스트가 80% 이상 커버리지로 통과
|
||||||
|
```
|
||||||
|
|
||||||
## 리팩토링 계획 시
|
## 리팩토링 계획 시
|
||||||
|
|
||||||
1. 코드 스멜과 기술 부채 식별
|
1. 코드 스멜과 기술 부채 식별
|
||||||
@@ -114,6 +193,17 @@ model: opus
|
|||||||
|
|
||||||
각 Phase는 독립적으로 merge 가능해야 합니다. 모든 Phase가 완료되어야 작동하는 계획은 피하세요.
|
각 Phase는 독립적으로 merge 가능해야 합니다. 모든 Phase가 완료되어야 작동하는 계획은 피하세요.
|
||||||
|
|
||||||
---
|
## 확인해야 할 위험 신호
|
||||||
|
|
||||||
|
- 큰 함수 (50줄 초과)
|
||||||
|
- 깊은 중첩 (4단계 초과)
|
||||||
|
- 중복 코드
|
||||||
|
- 에러 처리 누락
|
||||||
|
- 하드코딩된 값
|
||||||
|
- 테스트 누락
|
||||||
|
- 성능 병목
|
||||||
|
- 테스트 전략 없는 계획
|
||||||
|
- 명확한 파일 경로 없는 단계
|
||||||
|
- 독립적으로 전달할 수 없는 Phase
|
||||||
|
|
||||||
**기억하세요**: 좋은 계획은 구체적이고, 실행 가능하며, 해피 패스와 엣지 케이스 모두를 고려합니다. 최고의 계획은 자신감 있고 점진적인 구현을 가능하게 합니다.
|
**기억하세요**: 좋은 계획은 구체적이고, 실행 가능하며, 해피 패스와 엣지 케이스 모두를 고려합니다. 최고의 계획은 자신감 있고 점진적인 구현을 가능하게 합니다.
|
||||||
|
|||||||
Reference in New Issue
Block a user