.claude/skills/verify-nextjs/SKILL.md
Verifies Next.js 16 pattern compliance — await params/searchParams, PageProps typing, useActionState, Server/Client component boundaries, dynamic imports. Run after adding/modifying pages or layouts.
npx skillsauth add junnv93/equipment_management_system verify-nextjsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
프론트엔드 코드가 Next.js 16 App Router 패턴을 올바르게 준수하는지 검증합니다:
params와 searchParams는 Promise이므로 await 필수useFormState (deprecated) 대신 useActionState 사용next/dynamic을 사용한 코드 분할 패턴 (ssr: false, loading component)| File | Purpose |
| ---------------------------------------------------------- | -------------------------------------------------------------- |
| apps/frontend/app/(dashboard)/equipment/page.tsx | 서버 컴포넌트 + 필터 리다이렉트 참조 구현 |
| apps/frontend/app/(dashboard)/equipment/[id]/page.tsx | 동적 라우트 params 참조 구현 |
| apps/frontend/app/(dashboard)/equipment/error.tsx | Error Boundary 참조 구현 |
| apps/frontend/app/(dashboard)/equipment/loading.tsx | Loading UI 참조 구현 |
| apps/frontend/app/(dashboard)/checkouts/[id]/page.tsx | 동적 라우트 참조 구현 |
| apps/frontend/app/(dashboard)/calibration-plans/page.tsx | searchParams 사용 참조 구현 |
| apps/frontend/components/equipment/EquipmentForm.tsx | Dynamic import 코드 분할 참조 구현 (7개 섹션) |
| apps/frontend/proxy.ts | PPR 인증 프록시 (cacheComponents 레이아웃 non-blocking 핵심) — Next.js 16 proxy 컨벤션 |
동적 라우트의 page.tsx에서 params를 올바르게 await하는지 확인합니다.
# 동적 라우트 page.tsx 목록
find apps/frontend/app -path "*/\[*\]/page.tsx" -type f
# params를 await 없이 직접 접근하는 패턴 탐지
grep -rn "params\." apps/frontend/app --include="page.tsx" | grep -v "await\|props\.params\|Promise\|// \|searchParams"
# 올바른 패턴: await props.params 확인
grep -rn "await props\.params\|await.*\.params" apps/frontend/app --include="page.tsx"
PASS 기준: 모든 동적 라우트 page.tsx에서 await props.params 사용.
FAIL 기준: const { id } = params (await 없이 직접 구조 분해) 발견 시 위반.
searchParams를 올바르게 await하는지 확인합니다.
# searchParams 사용 page.tsx에서 await 확인
grep -rn "searchParams" apps/frontend/app --include="page.tsx" | grep -v "await\|Promise\|// \|type\|interface"
PASS 기준: searchParams 사용 시 await props.searchParams 패턴.
FAIL 기준: await 없이 직접 접근 시 위반.
Next.js 16.1.6+는 middleware.ts 컨벤션을 deprecated하고 proxy.ts로 전환했습니다.
# middleware.ts가 존재하면 오류 — proxy.ts를 사용해야 함
ls apps/frontend/middleware.ts 2>/dev/null && echo "FAIL: middleware.ts는 deprecated" || echo "PASS"
# proxy.ts 존재 및 export proxy 함수 확인
grep -n "export.*function proxy\|export.*proxy" apps/frontend/proxy.ts
PASS 기준:
middleware.ts 파일 없음proxy.ts에 export async function proxy() + export const config 직접 정의FAIL 기준:
middleware.ts 파일 존재 → proxy.ts와 충돌하여 서버 시작 불가config를 다른 파일에서 re-export → Next.js가 인식 못함useFormState가 사용되지 않고 useActionState로 대체되었는지 확인합니다.
# useFormState 사용 탐지
grep -rn "useFormState" apps/frontend --include="*.tsx" --include="*.ts" | grep -v "// \|deprecated\|node_modules"
PASS 기준: 0개 결과 (useActionState로 모두 마이그레이션).
FAIL 기준: import { useFormState } 또는 useFormState( 발견 시 useActionState로 변경 필요.
page.tsx가 서버 컴포넌트(데이터 fetching)이고, 클라이언트 컴포넌트(*Client.tsx, *Content.tsx)에 인터랙션을 위임하는지 확인합니다.
# page.tsx에 'use client' 지시어가 있는 경우 (서버 컴포넌트여야 함)
grep -rn "'use client'" apps/frontend/app --include="page.tsx"
PASS 기준: page.tsx에 'use client'가 없어야 함 (서버 컴포넌트).
FAIL 기준: page.tsx가 클라이언트 컴포넌트면 데이터 fetching을 서버로 이동 필요.
예외: 단순 리다이렉트만 하는 page.tsx는 해당 없음.
주요 라우트에 에러 바운더리가 있고, 올바르게 클라이언트 컴포넌트로 정의되었는지 확인합니다.
# error.tsx가 있는 라우트
find apps/frontend/app -name "error.tsx" -type f | sort
# error.tsx에 'use client' 확인 (필수)
for f in $(find apps/frontend/app -name "error.tsx" -type f); do
if ! grep -q "'use client'" "$f"; then
echo "MISSING 'use client': $f"
fi
done
PASS 기준:
'use client' 지시어 존재 (Next.js 규약)FAIL 기준: error.tsx에 'use client' 누락 시 위반.
주요 라우트에 로딩 UI가 있는지 확인합니다.
# loading.tsx가 있는 라우트
find apps/frontend/app -name "loading.tsx" -type f | sort
# loading.tsx 개수 확인
find apps/frontend/app -name "loading.tsx" -type f | wc -l
PASS 기준: 주요 목록 페이지(equipment, calibration, teams, checkouts 등)에 loading.tsx 존재.
참고:
'use client' 불필요코드 분할을 위한 next/dynamic 사용이 올바른 패턴을 따르는지 확인합니다.
# next/dynamic 사용 파일 탐지
grep -rn "import dynamic from 'next/dynamic'" apps/frontend --include="*.tsx" --include="*.ts"
# dynamic() 호출에서 ssr: false 누락 탐지 (클라이언트 전용 컴포넌트)
grep -rn "const .* = dynamic(" apps/frontend --include="*.tsx" --include="*.ts" -A 3 | grep -v "ssr:\|loading:\|// "
# loading component 없는 dynamic import 탐지
grep -rn "const .* = dynamic(" apps/frontend --include="*.tsx" --include="*.ts" -A 2 | grep -B 2 "ssr: false" | grep -v "loading:"
PASS 기준:
ssr: false 명시 (클라이언트 전용 섹션)loading 컴포넌트 제공 (Skeleton 등).then((mod) => mod.ComponentName))FAIL 기준:
ssr: false 없이 클라이언트 전용 컴포넌트를 dynamic import (hydration 오류 가능)loading 누락으로 로딩 중 빈 화면 표시권장 패턴:
const HeavyComponent = dynamic(
() => import('./HeavyComponent').then((mod) => mod.HeavyComponent),
{ loading: () => <Skeleton className="h-40 w-full" />, ssr: false }
);
| # | 검사 | 상태 | 상세 |
| --- | ----------------------- | --------- | ---------------------------------- |
| 1 | await params | PASS/FAIL | 미적용 page.tsx 목록 |
| 2 | await searchParams | PASS/FAIL | 미적용 page.tsx 목록 |
| 3 | proxy.ts 컨벤션 | PASS/FAIL | middleware.ts 존재 시 FAIL |
| 4 | useFormState 미사용 | PASS/FAIL | 사용 위치 목록 |
| 5 | 서버 컴포넌트 page.tsx | PASS/FAIL | 'use client' page.tsx 목록 |
| 6 | error.tsx / loading.tsx | PASS/INFO | 누락 라우트 목록 |
| 7 | Dynamic imports | PASS/INFO | ssr: false 누락, loading 누락 위치 |
다음은 위반이 아닙니다:
testing
Verifies Zod validation pattern compliance — ZodValidationPipe usage (no class-validator), versionedSchema inclusion in state-change DTOs, controller pipe application, query DTO consistency. Run after adding/modifying DTOs or controller endpoints.
testing
Verifies cross-feature workflow E2E test coverage against critical-workflows.md checklist. Checks WF-01~WF-35 coverage, step completeness, role correctness, side-effect verification, and status transition assertions. Run after adding workflow tests or before PR.
testing
SSOT(Single Source of Truth) 임포트 소스를 검증합니다. 타입/enum/상수가 올바른 패키지에서 임포트되는지 확인. 타입/enum 추가/수정 후 사용.
development
Verifies SQL safety — LIKE wildcard escaping, N+1 query pattern detection, COUNT(DISTINCT) for fan-out JOINs, RBAC INNER JOIN enforcement. Run after adding/modifying search or list API endpoints.