feat: Nuxt 코드 리뷰 항목 및 공통 스킬 수량 업데이트

This commit is contained in:
gil
2026-05-03 19:23:17 +09:00
parent b75a733d4a
commit eebbbea6af
4 changed files with 237 additions and 2 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -72,6 +72,7 @@
| `conventional-commit` | 커밋 메시지 컨벤션 | ✅ |
| `project-init` | 프로젝트 초기 설정 | ✅ |
| `vue-component-review` | 기존 Vue 컴포넌트 리뷰 | ✅ |
| `nuxt-code-review` | 브랜치/PR 단위 근거 기반 코드 리뷰 | ✅ |
---
@@ -80,5 +81,5 @@
| 구분 | 전체 | 구현 완료 | 미구현 | 검토 중 |
| --- | --- | --- | --- | --- |
| Phase별 Skill | 15 | 6 | 8 | 1 |
| 공통 Skill | 3 | 3 | 0 | 0 |
| **합계** | **18** | **9** | **8** | **1** |
| 공통 Skill | 4 | 4 | 0 | 0 |
| **합계** | **19** | **10** | **8** | **1** |

View File

@@ -0,0 +1,139 @@
---
name: nuxt-code-review
description: |
브랜치/PR 단위 코드 리뷰를 수행합니다. git diff 기반으로 변경된 코드 전체를
팀 컨벤션과 평가 기준에 따라 근거 기반(evidence-based)으로 리뷰합니다.
"코드 리뷰해줘", "PR 리뷰", "브랜치 리뷰", "변경사항 검토", "diff 리뷰" 등을
요청하면 트리거됩니다. (단일 .vue 파일 심층 리뷰는 vue-component-review 스킬을 사용하세요.)
---
# Nuxt 코드 리뷰 (브랜치/PR 단위)
이 스킬은 현재 브랜치의 전체 변경사항을 대상으로 근거 기반 코드 리뷰를 수행합니다.
모든 코멘트는 `references/evaluation-criteria.md`의 기준 ID를 인용하여 주관적 의견이 아닌
팀 컨벤션에 근거합니다.
## 핵심 원칙
1. **근거 인용 필수**: 모든 코멘트는 평가 기준 ID(예: `VUE-002`)를 반드시 인용한다. 기준 없는 의견은 코멘트로 작성하지 않는다.
2. **수정 제안 필수**: "이 코드가 잘못됐다"만으로는 부족하다. 구체적인 수정 방법(before/after)을 함께 제시한다.
3. **리플렉션으로 오탐 제거**: 최종 출력 전 모든 코멘트를 재검증하여 컨텍스트를 오해한 오탐을 제거한다.
## 작업 순서
### Phase 1: 컨텍스트 수집
1. 베이스 브랜치를 확인한다. 사용자가 명시하지 않으면 `main`으로 가정하고 알린다.
2. 변경 범위를 파악한다:
```bash
git diff <base>...HEAD --stat
git log <base>...HEAD --oneline
```
3. 프로젝트 전용 컨벤션 파일이 있으면 읽는다 (우선순위 높음):
- `.claude/project/conventions.md`
- `docs/adr.md` 또는 `docs/adr.yaml`
- `docs/code-convention.yaml`
4. `references/evaluation-criteria.md`를 로드하여 평가 기준을 준비한다.
5. 변경 의도를 1줄로 요약한다 (커밋 메시지, PR 설명, 파일 목록에서 추론).
6. **대규모 diff 처리**: 변경 파일이 30개 초과 또는 변경 라인이 2000줄 초과이면, 리뷰할 디렉토리나 파일 타입을 사용자에게 물어 범위를 축소한다.
### Phase 2: 구조화된 코드 리뷰
1. `git diff <base>...HEAD`로 전체 diff를 읽는다.
2. 아래 파일은 리뷰에서 제외한다:
- `.nuxt/`, `node_modules/`, `.output/` 내 파일
- 락 파일 (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`)
- 자동 생성 파일 (`*.generated.ts`, `*.d.ts` 중 빌드 산출물)
3. 파일별로 해당하는 평가 기준을 대조하며 검토한다.
4. 각 발견사항을 우선순위에 따라 분류한다:
- **[p1] 반드시 수정**: 버그, 런타임 에러, 보안 취약점, 팀 표준 위반 중 기능에 영향을 주는 것
- **[p2] 강력 권장**: 컨벤션 위반, 타입 누락, 유지보수 부담을 주는 패턴
- **[p3] 권장**: 코드를 더 좋게 만들지만 위반은 아닌 개선사항
- **[p4] 사소**: 스타일 지적, 네이밍 선호, 사소한 가독성
5. 크로스파일 이슈도 점검한다:
- 변경된 파일 간 패턴 불일치
- 타입 변경 후 사용처가 업데이트되지 않은 경우
- import/export 정합성
### Phase 3: 리뷰 리플렉션 (자기 검증)
모든 코멘트를 최종 출력 전에 재검증한다. 각 코멘트에 대해 스스로 묻는다:
- 인용한 평가 기준이 이 특정 코드 컨텍스트에 실제로 적용되는가?
- 작성자가 이 선택을 의도적으로 했을 수 있는가? (프로젝트 컨벤션, 기술적 제약)
- 제안한 수정이 실제로 올바르고, 사이드 이펙트는 없는가?
- 전체 컨텍스트를 보지 못해 생긴 오탐은 아닌가?
오탐으로 판단되면 해당 코멘트를 최종 출력에서 제거한다. 이 과정은 내부 검증이며 출력에 포함하지 않는다.
### Phase 4: 최종 출력
아래 출력 형식에 따라 최종 리뷰를 작성한다.
## 코멘트 형식
각 코멘트는 다음 구조를 따른다:
```
### [p1] src/components/UserCard.vue:45
- **기준**: VUE-002 (defineProps 제네릭 타입)
- **내용**: Props가 런타임 선언 방식으로 되어 있어 TypeScript 타입 안전성이 보장되지 않습니다.
- **제안**:
```ts
// Before
const props = defineProps({ name: String, age: Number });
// After
const props = defineProps<{ name: string; age: number }>();
```
- **사이드 이펙트**: 없음. 런타임 동작 동일.
- **제안 이유**: 제네릭 형태는 IDE 자동완성과 타입 추론에서 우수하며 팀 표준입니다.
```
## 출력 형식
```
## 코드 리뷰 결과
### 리뷰 범위
- 베이스: `main`
- 브랜치: `feature/user-profile`
- 변경 파일: N개 (파일 목록)
- 변경 의도: (1줄 요약)
---
### [p1] 반드시 수정 (N건)
(코멘트들)
### [p2] 강력 권장 (N건)
(코멘트들)
### [p3] 권장 (N건)
(코멘트들)
### [p4] 사소 (N건)
(코멘트들)
---
### 좋은 점
- (코드에서 잘 된 부분 관찰)
### 추가 리뷰 제안
- `UserCard.vue` — 250줄 초과. `vue-component-review` 스킬로 컴포넌트 상세 리뷰를 권장합니다.
```
## 주의사항
- **코드 수정 금지**: 사용자가 수정을 요청하지 않은 경우 리뷰만 수행한다. 수정이 필요하면 별도로 요청받는다.
- **프로젝트 지침 우선**: 프로젝트 전용 컨벤션과 팀 공통 지침이 충돌하면 프로젝트 지침을 따르되, 차이를 사용자에게 알린다.
- **생성 파일 제외**: `.nuxt/`, `node_modules/`, 자동 생성 파일, 락 파일은 리뷰하지 않는다.
- **단일 파일 심층 리뷰 연계**: 개별 `.vue` 파일의 체크리스트 기반 상세 리뷰가 필요하면 `vue-component-review` 스킬 사용을 제안한다.
- **커밋 컨벤션 확인**: 브랜치 커밋 메시지의 Conventional Commits 준수 여부는 `rules/commit-pr.md` 기준으로 확인하되, 커밋 메시지 생성은 `conventional-commit` 스킬의 역할이다.

View File

@@ -0,0 +1,95 @@
# Nuxt 코드 리뷰 평가 기준
이 문서는 `nuxt-code-review` 스킬에서 코멘트 작성 시 근거로 인용하는 평가 기준 목록입니다.
각 기준은 고유 ID를 가지며, 코멘트에 `기준: VUE-001 (제목)` 형태로 인용합니다.
---
## VUE — Vue 3 컴포넌트 패턴
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| VUE-001 | script setup 사용 | `<script setup lang="ts">`를 기본으로 사용한다. Options API는 신규 코드에서 사용하지 않는다. | p1 | rules/framework-rules.md |
| VUE-002 | defineProps 제네릭 타입 | Props는 `defineProps<T>()` 제네릭 형태로 타입을 명시한다. | p1 | rules/framework-rules.md |
| VUE-003 | defineEmits 제네릭 타입 | Emits는 `defineEmits<{ ... }>()` 제네릭 형태로 선언한다. | p1 | rules/framework-rules.md |
| VUE-004 | ref vs reactive 일관성 | 원시값·단일 객체는 `ref`, 복잡한 상태 트리는 `reactive`. 팀 내에서는 가능한 `ref` 우선. | p2 | rules/framework-rules.md |
| VUE-005 | computed 활용 | 계산된 값은 메서드 대신 `computed`로 정의한다. 템플릿에서 반복 호출되는 계산 로직은 반드시 computed로 추출한다. | p2 | rules/framework-rules.md |
| VUE-006 | 컴포넌트 단일 책임 | 컴포넌트는 단일 책임을 가진다. 200줄을 넘으면 분리를 검토한다. | p2 | rules/framework-rules.md |
| VUE-007 | 불리언 prop 네이밍 | 불리언 prop은 `is`, `has`, `can`, `should` 접두사를 사용한다. | p3 | rules/coding-conventions.md |
| VUE-008 | 이벤트 핸들러 네이밍 | 이벤트 핸들러 함수는 `handle` 또는 `on` 접두사를 사용한다. | p3 | rules/coding-conventions.md |
| VUE-009 | 불필요한 reactive 래핑 | 단순 객체에 `reactive`를 사용해 불필요한 프록시 오버헤드가 생기지 않도록 한다. | p3 | rules/framework-rules.md |
| VUE-010 | 복잡한 템플릿 표현식 | 템플릿 내 복잡한 인라인 로직은 computed나 메서드로 추출한다. | p2 | rules/coding-conventions.md |
| VUE-011 | 비즈니스 로직 composable 추출 | 재사용 가능한 비즈니스 로직은 `composables/` 디렉토리의 `useXxx` 함수로 추출한다. | p2 | rules/framework-rules.md |
| VUE-012 | 파일명 PascalCase | Vue 컴포넌트 파일명은 `PascalCase.vue`를 사용한다. | p2 | rules/coding-conventions.md |
---
## NUXT — Nuxt 3 프레임워크 패턴
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| NUXT-001 | 파일 기반 라우팅 | Nuxt의 파일 기반 라우팅을 사용한다. 수동 라우트 정의는 특수한 경우에만 허용한다. | p2 | rules/framework-rules.md |
| NUXT-002 | useFetch / useAsyncData 사용 | 서버 상태는 `useFetch` / `useAsyncData`를 사용한다. 컴포넌트에서 직접 `$fetch``fetch`를 호출하지 않는다. | p1 | rules/framework-rules.md |
| NUXT-003 | 동적 라우트 파라미터 형식 | 동적 라우트 파라미터는 `[param].vue` 형식을 사용한다. | p2 | rules/framework-rules.md |
| NUXT-004 | 미들웨어 위치 | 라우트 미들웨어는 `middleware/` 디렉토리에 배치하고, 필요한 경우에만 인라인 미들웨어를 사용한다. | p2 | rules/framework-rules.md |
| NUXT-005 | Nitro 서버 라우트 분리 | BFF 로직은 `server/api/` 또는 `server/routes/`에 배치하며, 클라이언트 컴포넌트에서 외부 API를 직접 호출하지 않는다. | p1 | rules/framework-rules.md |
| NUXT-006 | createError 사용 | 서버 라우트에서 에러 응답은 `createError`를 사용하여 일관된 에러 형식을 유지한다. | p2 | — |
| NUXT-007 | SEO 메타 관리 | 페이지 컴포넌트에는 `useHead` 또는 `useSeoMeta`로 메타 태그를 관리한다. | p3 | — |
| NUXT-008 | auto-import 활용 | Nuxt의 auto-import 대상(`ref`, `computed`, `useFetch` 등)은 명시적으로 import하지 않는다. | p4 | — |
---
## TS — TypeScript
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| TS-001 | any 사용 금지 | `any` 타입 사용을 금지한다. 불가피하면 `unknown`을 먼저 고려하고, 사용 시 주석으로 이유를 명시한다. | p1 | rules/coding-conventions.md |
| TS-002 | 함수 시그니처 타입 명시 | 함수의 매개변수와 반환 타입을 명시한다. | p1 | rules/coding-conventions.md |
| TS-003 | 공개 API 타입 export | 타 모듈에서 import되는 타입은 반드시 export한다. | p2 | rules/coding-conventions.md |
| TS-004 | 유니온 타입 관리 | 유니온 타입은 `as const` 또는 별도 타입 alias로 관리한다. 인라인 리터럴 유니온 남용을 피한다. | p2 | rules/coding-conventions.md |
| TS-005 | strict 모드 준수 | `tsconfig.json``strict: true`를 유지한다. 타입 오류를 `!` (non-null assertion)으로 억지 해결하지 않는다. | p1 | rules/framework-rules.md |
| TS-006 | 외부 API 응답 타입 정의 | 외부 API 응답은 반드시 타입을 정의하여 사용한다. 응답을 untyped로 사용하지 않는다. | p1 | rules/framework-rules.md |
---
## STYLE — Tailwind CSS / 스타일링
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| STYLE-001 | 유틸리티 클래스 우선 | Tailwind 유틸리티 클래스를 직접 사용한다. 공통 패턴은 컴포넌트로 추출한다. | p2 | rules/framework-rules.md |
| STYLE-002 | 임의값 클래스 제한 | `w-[123px]`과 같은 임의값 클래스는 디자인 시스템에 없는 값에만 제한적으로 사용한다. | p2 | rules/framework-rules.md |
| STYLE-003 | 조건부 클래스 clsx/cn | 조건부 클래스 조합은 `clsx` 또는 `cn` 유틸리티를 사용한다. 삼항 연산자 / 템플릿 리터럴 남용을 피한다. | p2 | rules/framework-rules.md |
| STYLE-004 | 클래스 순서 | 클래스 순서는 `prettier-plugin-tailwindcss` 프리셋을 따른다. | p4 | rules/framework-rules.md |
| STYLE-005 | @apply 제한 | `@apply`는 꼭 필요한 경우에만 사용한다. 가능한 유틸리티를 직접 나열한다. | p3 | rules/framework-rules.md |
---
## STATE — 상태 관리
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| STATE-001 | Pinia 사용 | 여러 컴포넌트가 공유하는 상태는 Pinia를 사용한다. 컴포넌트 간 props drilling이 3단계 이상이면 Pinia 도입을 검토한다. | p2 | rules/framework-rules.md |
| STATE-002 | Setup Store 패턴 | Pinia 스토어는 Composition API 스타일의 Setup Store로 작성한다. | p2 | rules/framework-rules.md |
| STATE-003 | 서버 상태와 클라이언트 상태 분리 | API 데이터(서버 상태)는 Pinia에 저장하지 않고 `useFetch`/`useAsyncData`로 관리한다. | p2 | rules/framework-rules.md |
| STATE-004 | SSR hydration 안전성 | 스토어 초기값이 SSR/CSR 환경에서 다르면 hydration 불일치가 발생할 수 있다. 서버에서만 유효한 값을 클라이언트 초기값으로 사용하지 않는다. | p1 | — |
---
## PERF — 성능
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| PERF-001 | 컴포넌트 lazy 로딩 | 초기 렌더에 필요하지 않은 무거운 컴포넌트는 `defineAsyncComponent` 또는 `<Lazy>` 접두사로 지연 로딩한다. | p3 | — |
| PERF-002 | 불필요한 반응성 | 변경될 필요가 없는 값을 `ref`/`reactive`로 감싸지 않는다. 정적 설정 객체, 상수 등은 반응형 불필요. | p3 | — |
| PERF-003 | computed vs method | 템플릿에서 반복 호출되는 값은 메서드 대신 `computed`를 사용하여 캐싱 이점을 활용한다. | p2 | rules/framework-rules.md |
| PERF-004 | 번들 사이즈 | 새 외부 라이브러리 추가 시 번들 영향과 대안을 PR 설명에 기록한다. 동일 기능 라이브러리 중복 도입을 금지한다. | p2 | rules/framework-rules.md |
---
## SEC — 보안
| ID | 제목 | 규칙 | 기본 심각도 | 참조 |
|----|------|------|------------|------|
| SEC-001 | 하드코딩 비밀정보 금지 | API 키, 토큰, 비밀번호 등을 소스코드에 하드코딩하지 않는다. 환경변수(`runtimeConfig`)를 사용한다. | p1 | rules/claude-workflow.md |
| SEC-002 | v-html XSS 방지 | `v-html`은 신뢰할 수 있는 서버 데이터에만 사용한다. 사용자 입력값을 `v-html`에 바인딩하지 않는다. | p1 | — |
| SEC-003 | 서버 라우트 입력 검증 | Nitro 서버 라우트에서 쿼리 파라미터, 바디 등 외부 입력값은 반드시 검증 후 사용한다. | p1 | — |