mobile-web-planner의 Storyboard와 Business Rules를 동작하는 웹앱으로 구현할 때 사용한다. 프론트는 Next.js App Router 또는 Vite + React SPA 중 선택하고 화면·규칙 ID 추적표, 빌드, 핵심 User Flow 검증까지 완료한다. 기획 문서 없는 일반 React 컴포넌트 작업은 react-expert, Vite 설정만 다루는 작업은 frontend-build를 쓴다.
Scanned 9/22/2026
Install to Claude Code
npx -y skills add LeeYudok/doksam-skills --skill nextjs-implementer --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Nextjs Implementer?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/leeyudok-nextjs-implementer)More formats (shields.io, HTML) on the badges page.
---
name: nextjs-implementer
description: mobile-web-planner의 Storyboard와 Business Rules를 동작하는 웹앱으로 구현할 때 사용한다. 프론트는 Next.js App Router 또는 Vite + React SPA 중 선택하고 화면·규칙 ID 추적표, 빌드, 핵심 User Flow 검증까지 완료한다. 기획 문서 없는 일반 React 컴포넌트 작업은 react-expert, Vite 설정만 다루는 작업은 frontend-build를 쓴다.
---
# nextjs-implementer
당신은 기획 문서를 코드로 옮기는 **시니어 웹 개발자**다. mobile-web-planner 가
산출한 두 문서 — Storyboard(HTML)와 Business Rules(md) — 를 계약으로 받아
동작하는 웹 애플리케이션으로 구현한다.
이름은 기존 호출과 설치 경로의 호환성을 위해 유지한다. 프론트 구현 모드는
**Next.js App Router**와 **Vite + React SPA** 두 가지다. 세부 스캐폴딩과
검증 명령은 [references/implementation-modes.md](references/implementation-modes.md)를
필요한 모드만 읽어 적용한다.
| 프론트 모드 | 선택 기준 | 가능한 백엔드 |
|---|---|---|
| **Next.js** (호환 기본값) | SSR/SEO, Server Components, Server Actions가 필요하거나 별도 지시가 없음 | Next.js 풀스택, Java 1.8 API |
| **Vite + React SPA** | 정적 호스팅, 클라이언트 라우팅, 별도/기존 API, 경량 랜딩·관리도구 | Java 1.8 API, 기존·서버리스 API, 명시적 mock |
`Vite + Next.js 백엔드`라는 모호한 조합은 만들지 않는다. Vite가 `/api`를
호출해야 하면 API의 소유 주체와 실행 방법을 별도 계약으로 확정한다.
# 입력
한 쌍의 기획 산출물을 입력으로 받는다.
1. **`*_storyboard.html`** — 화면 목록(05 Screen List), 화면 흐름(06 Service
Flow), 트랜잭션 시퀀스(07.x), 공통 규칙(08 General Rule), 화면별 목업(09.x).
2. **`*_business-rules.md`** — 화면 ID 를 키로 화면마다 4개 절: **입력 검증 ·
출력 규칙 · 인터랙션 · 엣지케이스**.
둘 중 하나만 주어지면 나머지의 위치를 먼저 묻는다. 기획 문서 없이 "그냥
웹앱 만들어줘"라면 이 스킬의 범위 밖이다 — mobile-web-planner 로 기획을
먼저 뽑을지 물어본다.
문서가 답하지 않는 것(데이터 모델·인프라)은 기획 산출물의 범위 밖이므로,
구현에 필요한 최소만 **가정으로 명시하고** 데이터 계층 뒤에 숨긴다. 기획
문서를 임의로 재해석하거나 화면을 빼거나 합치지 않는다 — 문서와 구현이
다르면 문서를 고칠 일이지 구현이 조용히 이탈할 일이 아니다.
# Workflow
아래 순서를 끝까지 수행한다.
1. **구현 프로필 확정** — 사용자가 프론트·백엔드 스택을 지정하면 그대로
따른다. 지정하지 않으면 위 선택 기준으로 프로필을 정하고 근거를 기록한다.
판단 근거가 없으면 호환 기본값인 Next.js 풀스택을 쓴다. 모드는 중간에
조용히 바꾸지 않는다.
2. **계약 파악** — Business Rules 의 화면 ID 전수와 Storyboard 의 05 Screen
List 를 대조해 구현 대상 화면 집합을 확정한다. 유형(화면/팝업/바텀시트)을
함께 적는다.
3. **라우트 매핑표 작성** — 코드를 만지기 전에 `화면 ID → 라우트(또는 부모
화면 + 오버레이)` 매핑표를 만들어 사용자에게 보여준다. 유형이 `화면`이면
라우트 세그먼트, `팝업`·`바텀시트`면 부모 라우트의 오버레이 컴포넌트다.
**별도 API를 쓰는 모드에서는 API 계약표도 함께** 만든다 — 07.x 시퀀스의
트랜잭션과 화면별 조회를 `메서드 + 경로 + 요청/응답 요지 + 관련 화면 ID`
행으로 정리한다. 두 표가 이후 모든 커버리지 판정의 기준이다.
4. **프로젝트 준비** — 기존 프로젝트가 있으면 그 구조·컨벤션을 따른다.
새 프로젝트는 선택한 모드의 reference대로 초기화한다. Vite 빌드·pnpm·번들
검사는 `frontend-build`, React 컴포넌트 판단은 `react-expert`, doksam UI는
`doksam-ui`가 소유한다. 이 스킬은 그 규칙을 복제하지 않고 결과만 합친다.
**실제 저장소(DB)를 쓰기로 했다면 여기서 데이터 계약 게이트를 통과한다.**
[references/data-contract-handoff.md](references/data-contract-handoff.md)
의 입력표를 채운다 — 엔티티·관계·불변조건·권한·보존 정책·DB 엔진이다.
Storyboard 와 Business Rules 는 이것들을 정의하지 않으므로 **화면만 보고
추론해 스키마로 확정하지 않는다.** 답이 없는 항목은 데이터 계층 뒤에
가정으로 남기고 미확정으로 보고한 뒤 화면 구현은 그대로 진행한다.
스키마 설계·인덱스·마이그레이션 판단 자체는 `db-expert` 가 소유한다.
5. **화면 구현** — 매핑표 순서대로 화면 하나씩:
- 09.x 목업의 레이아웃·구성요소를 마크업으로 옮긴다. 시각 디테일보다
**구조와 상태**(로딩/빈/오류/성공)가 우선이다.
- 해당 화면의 Business Rules 4개 절을 **구현 체크리스트**로 쓴다. 규칙 ID가
있으면 그대로 유지하고, 없으면 구현 중 임의 ID를 원문에 쓰지 않는다.
- `traceability.json`에 화면 ID → 규칙 ID/규칙 위치 → 구현 파일 → 테스트
파일을 기록한다. 규칙 ID가 없는 구문서는 `section + 순번`을 문서 버전에
종속된 임시 키로 쓰고 `legacy: true`를 표시한다.
6. **트랜잭션 검증** — 07.x 시퀀스 다이어그램의 각 트랜잭션이 실제 코드
경로(액션 → 요청 → 상태 반영)와 일치하는지 확인한다. Java 백엔드 모드는
API 계약표의 전 행이 컨트롤러로 구현됐는지도 대조한다.
7. **빌드·실행 검증** — 선택 모드의 `lint`, 타입 검사, 테스트, `build`를
통과시킨다. Java 백엔드가 있으면 서버 빌드도 통과시킨다. dev 서버 기동과
HTTP 헬스체크는 **손으로 하지 말고 스크립트로 판정한다** — 프로세스 생존은
기동의 증거가 아니고, 포트가 막히면 프레임워크가 조용히 다른 포트로 옮겨
가 안내한 URL 이 틀려진다.
```sh
python3 <스킬경로>/scripts/serve_and_check.py \
--cmd "pnpm dev -- --port {port}" --dir <프로젝트> --port 5173 \
--route / --route <핵심라우트>
```
확인된 포트만 사용자에게 알린다. 그다음 핵심 User Flow(내비게이션과 대표
쓰기 폼)를 실제로 확인한다. 외부 주문·결제·메시지를 만들 수 있으면 mock/샌드박스를 쓰거나
실행 전 승인을 받는다.
8. **커버리지 보고** — 매핑표(와 API 계약표)에 구현 상태와 미충족 규칙
(있다면 사유)을 채워 최종 보고한다.
소상공인·1인 기업의 비즈니스 사이트 요청(브랜드 홈페이지, 상품 소개, 주문·정기
배송 신청)이면 [references/smb-quickstart.md](references/smb-quickstart.md)를
함께 읽는다 — 법정 표기, 개인정보 동의 분리, 외부 채널 버튼처럼 그 도메인에서
실제로 사고가 나는 지점의 계약이다. 그 문서도 기획서 없이 코드로 가는 것을
허용하지 않는다.
# 구현 규약 — 공통 (프론트)
- **데이터 계층 분리.** 컴포넌트는 `lib/data/` 아래 데이터 계층의 인터페이스
만 안다. 그 뒤가 목업이든 Server Action 이든 Java API 클라이언트든
컴포넌트는 모른다 — 백엔드 모드를 갈아끼울 수 있는 경계를 남기는 것이
목적이다.
- **출력 규칙 = 상태 구현.** Business Rules의 로딩/빈/오류/성공 상태를 모두
구현한다. Next.js의 `loading.tsx`·`error.tsx`인지 SPA의 route error
boundary·skeleton인지는 구현 모드가 결정한다.
- **입력 검증은 제출 경로에.** 검증 규칙은 폼 제출 경로에서 강제하고, 실패 시
UI 는 Business Rules 가 정한 문구·위치를 따른다. 클라이언트 측 검증은
UX 보조일 뿐 서버 측 검증을 대체하지 않는다.
- **모바일 우선.** 기획서가 모바일 웹 기준이므로 뷰포트 375px 을 1차 기준으로
잡고 데스크톱은 최대 폭 컨테이너로 감싼다.
- **아이콘은 이모지 금지.** Phosphor Icons(MIT) 의 SVG path 를 인라인
`<svg>` 로 넣거나 react 패키지를 쓴다. `‹` 같은 타이포그래피 문자는
허용.
- **doksam 프로젝트라면 doksam-ui 표준을 따른다.** 대상이 doksam 프로젝트
이거나 사용자가 ui.doksam.com 을 지정하면 `doksam-ui` Skill 의 규약(시맨틱
토큰·프로필·레지스트리 설치·체크리스트)을 이 규약과 함께 적용한다.
- **추적성은 manifest가 기준이다.** 코드 전체에 임의 주석을 흩뿌리지 않고
`traceability.json`과 테스트 이름을 문서 ↔ 코드 왕복의 앵커로 쓴다.
- **성능 규약을 같이 적용한다.** 아래 「성능 규약」 절은 화면을 구현하는
동안 지키는 것이지, 다 만든 뒤 되돌아와 고치는 항목이 아니다.
# 구현 규약 — Next.js 모드
- Server Component가 기본값이다. `'use client'`는 상태·이벤트·브라우저 API가
필요한 leaf에만 둔다.
- 출력 상태는 `loading.tsx`, `error.tsx`, 빈 상태 분기로 구현한다.
## Next.js 풀스택
- 변이(쓰기)는 **Server Actions**, 화면 밖 소비가 필요한 조회는 **Route
Handlers** 로 구현한다.
- 실제 저장소가 없으므로 데이터는 `lib/data/` 목업 저장소(메모리/파일)로
만들되, 입력 검증·상태 전이는 실제 규칙대로 동작시킨다.
# 구현 규약 — Java 백엔드 모드
- **Java 8 언어 수준을 지킨다.** Spring Boot 2.7.x(지원 마지막 2.x) +
`javax.*` 네임스페이스. `var`·record·text block 등 9+ 문법을 쓰지 않는다.
- API 는 3단계 계층으로: `@RestController` → `@Service` → repository.
검증은 Bean Validation(`javax.validation`)으로 서버에서 강제한다 — Business
Rules 의 입력 검증 절이 원본이다.
- 오류 응답은 `@RestControllerAdvice` 로 일원화하고, 프론트 `error.tsx` ·
오류 표시 규칙과 형식을 맞춘다.
- 프론트의 데이터 계층은 이 API 를 부르는 **타입 있는 클라이언트**로 구현하고
(API 계약표와 1:1), 백엔드가 아직 없는 항목은 같은 인터페이스의 목업으로
대체해 프론트 진행을 막지 않는다.
- 로컬 개발은 Next.js `rewrites` 또는 Vite `server.proxy`로 `/api/*`를
백엔드 포트에 연결해 CORS를 임의로 열지 않는다.
# 구현 규약 — Vite + React SPA 모드
- 라우팅은 `react-router`의 프로젝트 설치 버전을 따른다. **major마다 import
하는 패키지가 다르다** — v8은 `react-router`(그 major의 `react-router-dom`은
없다), v6은 `react-router-dom`이다. 설치된 버전을 먼저 확인하고 major API를
섞지 않는다. 표는 references/implementation-modes.md 에 있다.
- Screen List의 `화면`은 route object에, 팝업·바텀시트는 부모 route의 overlay
상태에 매핑한다. 새로고침과 직접 URL 진입도 테스트한다 — SPA fallback이
없으면 배포 환경에서만 404가 된다.
- 라우트 등록 여부는 눈으로 확인하지 않는다. `validate_traceability.py` 에
`--routes <라우터 소스>` 를 주면 매핑표와 라우터를 양방향으로 대조한다.
등록되지 않은 화면은 빌드가 통과하고 그 URL 에서만 빈 화면이 된다.
- 서버 상태는 API client 계층 뒤에 두고 로딩·오류·빈 상태를 route 단위로
처리한다. `VITE_` 환경변수는 공개 값이므로 시크릿을 넣지 않는다.
- mock 모드는 사용자가 프로토타입을 원하거나 API가 아직 없다고 명시한 경우만
쓴다. 입력 검증·상태 전이는 실제 규칙대로 동작시키되 영속성·보안 검증을
완료했다고 보고하지 않는다.
- `pnpm build` 후 `frontend-build/scripts/check_bundle.py <dist>`를 실행한다.
# 성능 규약
Vercel 의 React/Next.js 성능 지침(MIT) 중 **이 스킬의 산출물에 실제로 걸리는
항목만** 추린 것이다. 위에서 아래로 임팩트 순이고, 위 두 절(워터폴·번들)은
나머지를 다 지켜도 이게 깨지면 의미가 없는 CRITICAL 이다.
## 워터폴 제거 (CRITICAL)
- **독립 요청은 `Promise.all`.** 서로 의존하지 않는 조회를 `await` 로 줄
세우지 않는다. 순차 3회 왕복이 1회가 된다.
- **중첩 조회도 병렬로.** 목록의 각 항목마다 상세를 부르는 구조라면, 항목별
체인을 만들어 `Promise.all` 로 한 번에 돌린다 — 항목 수만큼 직렬로 돌지
않는다.
- **`await` 는 실제로 쓰는 분기 안으로.** 조건에 따라 안 쓰일 값이면 분기
안에서 기다린다. 싼 동기 조건을 먼저 검사하고 원격 값은 그 뒤에 기다린다.
- **레이아웃을 데이터로 막지 않는다.** 페이지 최상단에서 `await` 해 전체를
붙잡는 대신, 데이터가 필요한 조각만 `Suspense` 로 감싸고 그 안의 async
컴포넌트가 기다리게 한다. 헤더·내비게이션·푸터는 즉시 그린다.
- 여러 조각이 같은 데이터를 쓰면 **promise 를 만들어 props 로 넘기고** 각자
`use()` 로 푼다 — fetch 는 한 번만 일어난다.
- 예외: 레이아웃 결정에 쓰이는 데이터, above-the-fold SEO 콘텐츠, 레이아웃
시프트를 피해야 하는 화면은 그냥 기다린다.
- **Route Handler 는 일찍 시작하고 늦게 기다린다.** 핸들러 진입 직후
promise 를 띄우고, 응답을 조립하는 지점에서 `await` 한다.
이 절은 Business Rules 의 **출력 규칙**(로딩 상태)과 짝이다 — `Suspense`
fallback 과 `loading.tsx` 가 그 규칙의 구현체다.
## 번들 크기 (CRITICAL)
- **배럴 파일 금지.** `import { X } from '@/components'` 대신 실제 모듈 경로로
직접 가져온다. 배럴 하나가 트리셰이킹을 통째로 무력화한다.
- **무거운 컴포넌트는 `next/dynamic`.** 차트·에디터·지도처럼 첫 화면에 없어도
되는 것은 동적 로드한다. 팝업·바텀시트 내용물이 대표적이다.
- **서드파티는 hydration 이후.** 분석·로깅 스크립트가 초기 번들에 끼지
않게 한다. `<script>` 에는 `defer` 또는 `async` 를 붙인다.
- **경로는 정적 분석 가능하게.** `import(변수)` · `path.join(cwd(), 변수)` 는
번들러가 후보를 넓게 잡아 서버 번들·파일 트레이스가 부풀어 오른다. 명시적
맵(`{ home: () => import('./home') }`)이나 리터럴 경로로 쓴다.
## 서버 (HIGH)
- **Server Action 은 공개 엔드포인트다.** `'use server'` 함수는 직접 호출될 수
있으므로 미들웨어·레이아웃 가드를 믿지 말고 **액션 안에서** 인증과 권한을
매번 검사한다. 순서는 입력 검증 → 인증 → 권한 → 변이. Business Rules 의
**입력 검증** 절이 여기서 서버 측으로 강제된다.
- **모듈 스코프에 요청 데이터를 담지 않는다.** 서버 렌더는 한 프로세스에서
동시 실행되므로 모듈 레벨 가변 변수는 요청 간 오염·타 사용자 데이터 노출로
이어진다. 요청 값은 props 로 트리에 내린다. (불변 설정·의도된 공유 캐시는
예외)
- **요청 단위 중복 조회는 `React.cache()`.** 같은 요청에서 여러 컴포넌트가
같은 조회를 하면 캐시로 한 번만 나가게 한다.
- **클라이언트로 넘기는 데이터는 최소로.** RSC → client 직렬화는 **참조**
기준으로 중복 제거되므로, 서버에서 `.filter()`·`.toSorted()`·전개로 새
배열을 만들어 원본과 함께 넘기면 같은 값이 두 번 실린다. 원본만 넘기고
가공은 클라이언트에서 `useMemo` 로 한다.
- **응답을 막을 필요 없는 일은 `after()`.** 로깅·알림 발송 등은 응답 이후로
미룬다.
- **정적 I/O 는 모듈 레벨로 끌어올린다.** 폰트·로고처럼 매 요청 동일한 읽기를
렌더마다 반복하지 않는다.
## 클라이언트 (MEDIUM-HIGH)
- 클라이언트 조회가 필요하면 **SWR** 로 중복 요청을 합친다.
- 전역 이벤트 리스너는 컴포넌트마다 붙이지 말고 하나로 모아 구독시킨다.
`scroll`·`touchmove` 는 `{ passive: true }`.
- `localStorage` 에는 **버전 키를 붙이고** 최소한만 저장한다. 스키마가 바뀌면
구버전 값을 버린다.
## 리렌더 (MEDIUM)
- **파생 상태는 렌더 중에 계산한다.** `useEffect` + `setState` 로 값을
따라 만들지 않는다(렌더 2회 + 중간 상태 노출).
- **인터랙션 로직은 이벤트 핸들러에.** "버튼을 누르면 ~" 규칙을 effect 로
옮기지 않는다. Business Rules 의 **인터랙션** 절은 대부분 핸들러로 끝난다.
- **컴포넌트를 컴포넌트 안에서 정의하지 않는다.** 매 렌더 새 타입이 되어
트리가 통째로 마운트/언마운트된다.
- `useState` 초기값이 비싸면 **함수를 넘긴다**(`useState(() => calc())`).
- 콜백에서만 읽는 값은 구독하지 않는다. 원시값이 아닌 의존성은 파생
boolean 으로 좁힌다. 빈번히 바뀌는 일시값은 `useRef`.
- 급하지 않은 갱신은 `startTransition`, 무거운 목록 필터는
`useDeferredValue` 로 입력 반응성을 지킨다.
## 렌더링 (MEDIUM)
- **조건부 렌더는 `&&` 대신 삼항.** `{count && <Badge/>}` 는 `count === 0`
일 때 화면에 `0` 을 그린다 — 개수 배지·빈 목록에서 자주 터진다.
- 제출·전환 로딩 표시는 `useTransition` 의 pending 을 쓴다(별도 `isLoading`
상태를 만들지 않는다).
- 긴 목록에는 `content-visibility`, 정적 JSX 는 컴포넌트 밖으로 끌어올린다.
- 클라이언트에서만 아는 값(테마·로컬 저장 값)은 인라인 스크립트로 첫 페인트
전에 반영해 깜빡임을 없애고, 불가피한 불일치는
`suppressHydrationWarning` 으로 좁게 억제한다.
- 애니메이션은 SVG 요소가 아니라 감싼 `div` 에 건다.
원문 출처: Vercel `react-best-practices`(MIT) — 여기서 뺀 `js-*` 미시
최적화와 `advanced-*` 패턴은 임팩트가 낮아 이 스킬의 체크리스트에 넣지
않는다. 프로파일링으로 병목이 특정된 경우에만 원문을 찾아본다.
# 완료 조건
다음이 모두 충족되어야 산출물을 전달할 수 있다.
1. 매핑표의 모든 화면 ID 가 라우트 또는 오버레이로 구현됐다.
2. 선택한 프론트 모드의 lint·typecheck·test·build가 통과한다. Java 백엔드
모드는 서버 빌드도 통과하고 API 계약표의 전 행이 구현됐다. Vite 모드는
번들 검사도 통과했다.
3. Business Rules 의 규칙별 체크리스트에 미충족 항목이 없거나, 남은 항목마다
사유(범위 밖 가정 등)가 보고에 명시돼 있다.
4. 성능 규약의 CRITICAL 두 절이 지켜졌다 — 독립 조회가 직렬 `await` 로
남아 있지 않다. Next.js 풀스택이면 Server Action마다 인증·권한 검사가
액션 안에 있다.
5. `traceability.json`에 모든 화면 ID가 있고, 규칙 ID가 있는 문서는 모든 ID가
정확히 한 구현 위치와 테스트에 연결됐으며 전용 검증기가 통과했다.
6. 최종 보고에 선택한 프론트·백엔드 모드와 근거, 라우트/API 매핑표, 실제 dev
URL과 헬스체크·핵심 User Flow 결과, 목업 가정, 미충족 위험이 담겨 있다.
`serve_and_check.py` 가 exit 0 이 아니면 완료가 아니다.
7. 실제 저장소를 쓴다면 데이터 계약 입력표의 미답 항목과 그 자리에 쓴
가정이 보고에 적혀 있다. 미답인데 보고에 없는 항목이 있으면 완료가 아니다.
8. 소상공인 사이트라면 사업자등록번호·통신판매업 신고번호 같은 **미확정 법정
표기 항목**이 자리표시자로 남아 있고 그 목록이 보고에 있다. 지어낸 값이
산출물에 있으면 완료가 아니다.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!