Files
HMDesign/docs/guidelines/ai-screen-contract.md

57 lines
8.7 KiB
Markdown

# AI 화면 선택·완료 계약
> Typography: HMDesign의 Font Weight는 400(Regular), 500(Medium), 700(Bold)의 3단계만 사용한다. Font Weight 600은 사용하지 않으며, 카드 제목은 700을 기본으로 한다. 신규 화면과 컴포넌트 생성에도 적용한다.
읽는 순서는 `AGENTS.md` → 아래 해당 기능 행 → [공개 인덱스](component-pattern-reference.md)의 선택 문서와 대표 All Story다. 과거 보고서·verification Story의 기능을 새 요구사항으로 복사하지 않는다.
## 구현 전 기록
요구사항마다 필수 액션·상태·완료 조건을 기록한다. 요청된 메뉴 이름만 Navigation에 넣는다. 미지정이면 [선택 근거](navigation-single-mode.md#선택-근거)를 기록한다. 범위는 `STORYBOOK_ONLY | PRODUCTION_UI | BOTH`, 신규 화면 Runtime은 React다.
| 요구 기능 | 먼저 선택할 Pattern | Component·소비 책임 |
|---|---|---|
| 목록 검색·필터 | FilterBar, 표가 필요하면 DataTable | 조건 → 검색 → 조회 → 초기화. draft/applied 분리. PageHeader 밖 Card 하나. `layout="list-basic"` 가운데, 추가 조건은 `list-extended` 왼쪽. |
| 업무 Dashboard | DashboardLayout·Header·Main·Content | Header 48px, 최소 960px 내부 스크롤. Sidebar 없음. 상태는 Dashboard Status 계약. |
| Form | 별도 공개 Form Pattern 없음 | Label + Input/Textarea/Select/Checkbox를 Card 안에 조합. useId, htmlFor, 오류 메시지, 첫 오류 focus. 중복 Form Component 불필요. |
| 서비스 상단 메뉴 | GNB + GNBMobileMenu | 요청된 경로만 Desktop/Mobile에 동일하게 구성. Sidebar 없음. |
| 계층별 좌측 메뉴 | LNB + LNBMobileHeader/Menu | 공식 독립 LNB 허용. 모바일에도 LNB 모드 유지. |
| 작업 확인 | Dialog 또는 AlertDialog | Title/Description, 취소/진행, Escape와 trigger focus 복귀. 취소·삭제는 Solid. |
| 측면 상세 | Sheet | 방향·안쪽 모서리·키보드 focus는 기존 API. 메뉴 대체로 추가하지 않음. |
| 하단 작업·드래그 | Drawer | desktop 취소→실행, mobile 실행 위·취소 아래. 실기기 drag 별도 검증. |
| 초기/부분 로딩 | LoadingScreen 또는 Skeleton | 기존 콘텐츠 유지 여부를 요구사항으로 결정. |
| 빈 목록·오류 | DataTable emptyMessage / 콘텐츠 HTML / Alert | 임의 Empty Component 불필요. 재시도는 요청된 경우만. |
Pattern → Component → Variant/Prop → Composition 순으로 선택한다. 부적합한 Pattern은 구체적인 API·구조 제약을 기록한다. React composition용 로컬 함수는 허용하지만 HM 위젯과 동일 기능의 중복 구현은 만들지 않는다.
### 의존성 탐색과 명령 결과
패키지의 공개 API와 설치된 `package.json`의 `exports`/`types`를 먼저 확인한다. 내부 선언이 필요한 경우 `rg --files <실제 패키지> -g '*.d.ts'`나 `Test-Path -LiteralPath`로 존재를 확인한 파일만 읽는다. 배포 패키지에 `src/features`가 있다고 추측하거나 없는 경로를 만들어 복구하지 않는다. CWD와 실제 node_modules 연결 위치를 기록한다.
독립 실험 사본에는 `.storybook` 설정이나 브라우저 도구가 제공되지 않을 수 있다. 실제 폴더 목록을 먼저 확인하고 검색 root에는 존재하는 경로만 넣는다. 대표 Story 소스는 Component/Pattern 참조에 사용하지만, Storybook 실행을 요청하지 않은 독립 React 생성에서는 `.storybook` 설정은 선택 참조다. 필수 문서·공개 패키지·bootstrap의 누락은 별도 실패이며, 선택 참조의 부재와 혼용하지 않는다. 도구 목록 검색 무결과는 도구가 다른 환경에도 없다는 증거가 아니다.
`rg`의 exit 1은 오류 진단이 없는 검색 무결과일 때만 정상 검색 결과다. exit 2, 읽기/접근 오류와 Typecheck/Build의 nonzero exit는 별도 실패다. 복합 명령의 마지막 exit 0이 앞선 읽기·컴파일 실패를 가릴 수 있으므로 검증 명령별 종료 코드와 진단을 보존한다. `vite.config.ts`를 읽는 명령은 Build 실행이 아니다. 중단 실험의 CLI 완료를 화면 생성 PASS로 기록하지 않는다.
실행기가 개별 명령 결과를 제공하지 않으면 파일 읽기와 각 검색을 별도 호출한다. 복합 명령의 마지막 exit 1만으로 전체 명령을 정상 무결과로 확정하지 않는다. 명령별 결과가 있으면 모든 단계의 원문·exit·stdout/stderr를 확인하고, 앞선 실제 실패를 보존한다.
## Theme와 접근성
- bootstrap은 `index.css`에서 Tailwind와 공개 HM Tailwind 설정, `hm-theme.css`에서 공개 Theme를 연결한다. 상태 사용 시 공개 Status CSS를 추가한다. [Runtime 계약](runtime-portability.md)을 적용한다.
- 자주 쓰는 변수: `--hm-sem-bg-content`, `--hm-sem-bg-surface`, `--hm-sem-fg-primary`, `--hm-sem-fg-secondary`, `--hm-sem-stroke-default`, `--hm-sem-stroke-input-default`, `--hm-sem-action-primary`. 이름을 유추하지 말고 연결된 CSS의 선언을 확인한다. 공개 `text-primary`, `text-muted-foreground`, `bg-card` utility도 사용 가능하다.
- `--hm-sem-text-*`, `--hm-sem-border-*`, `--hm-sem-stroke-divider`는 선언 확인 없이 쓰지 않는다. 없는 HM token을 임의 fallback 색으로 감추지 않는다. [Token 검사](ai-generation-guard.md#theme-token-검사)를 참고한다.
- Component style/className은 기존 Prop·Token 계약을 먼저 사용한다. 동적 Token 후보와 fallback의 선언 여부를 확인하고, 외부 값·Props·동적 문자열은 실제 DOM·상태·브라우저·폭을 연결한 근거를 남긴다. HM Component의 임의 HEX/RGB/HSL·Spacing/Radius override는 Guard STYLE-001 대상이다. 일반 콘텐츠 레이아웃과 승인 여부가 불명확한 override는 [CSS Intent 검사](ai-generation-guard.md#css-intent와-동적-token-검사-2026-10-10)에서 구분하며, 미실행 Theme·상태와 사용자 디자인 의도는 NOT VERIFIED다.
- 항목명은 공개 Label과 control ID를 연결한다. SelectTrigger/Combobox도 id 또는 aria-labelledby를 연결한다. 반복 조합은 useId를 사용한다. placeholder는 항목명 대체가 아니다.
- Popup의 `aria-haspopup`은 `aria-controls`가 가리키는 실제 role과 일치해야 한다. HM Combobox는 dialog를 열고 내부 검색 입력이 listbox를 제어한다. 기존 Component의 연결 속성을 임의로 덮어쓰지 않는다. GNBUser/LNBProfile의 명시적 접근성 이름도 화면에 보이는 이름·설명을 포함한다.
- 레이아웃·콘텐츠 HTML 허용. 미지원 native 위젯은 구체적인 이유와 이름 연결을 기록한다. 숨겨진 focus 대상, 존재하지 않는 active panel, disabled/readOnly 값 변경을 확인한다.
- 내부 스크롤 화면의 숨김 absolute live region도 위치 기준이 필요하다. [LNB 콘텐츠 연결](../patterns/lnb.md#6-lnb-mode-콘텐츠-연결)의 positioned ancestor와 높이 분배를 따르고, 초기·조회·초기화·완료 상태에서 문서와 main의 scrollHeight/clientHeight를 따로 측정한다. 숨김 1px/clip 스타일만으로 외부 스크롤 방지를 보장하지 않는다. 전역 overflow:hidden으로 가리지 않는다.
- 표의 ID처럼 분절되면 의미가 흐려지는 값은 [DataTable 식별자 소비 예제](../patterns/data-table.md#111-식별자-줄바꿈과-열-폭)를 따른다. 표 내부 가로 스크롤과 실제 전체 ID 식별을 함께 확인하며 Typography/Color를 축소해 해결하지 않는다.
## 완료와 보고
1. 화면 typecheck/build를 수행한다.
2. 실제 entry에 Guard를 한 번 실행한다. 외부 앱도 동일 CLI로 검사한다. Production graph에 Story나 전체 저장소를 넣지 않는다. PARTIAL은 변경 파일을 명시한다.
3. 실제 빌드 결과를 390/768/1280/1440px에서 확인한다. Dashboard는 내부 스크롤, 나머지는 페이지 가로 넘침 없이 구성한다. utility, Label, 키보드·focus 복귀, 보호 상태, 필수 액션을 확인한다.
4. `staticStatus`, 원본 `status`, 브라우저 결과와 결합 결과를 따로 기록한다. source/contract 해시가 일치하는 Runtime 증거만 연결한다. 승인 CSS/native 예외의 미검증은 Runtime PASS로 지우지 않는다.
5. 실제 렌더된 Component·Pattern, 선택/미사용 이유, Navigation, 액션 범위, 실행한 검사와 `NOT VERIFIED`를 보고한다. 미요청 CSV·다운로드·API·추가 메뉴는 구현하지 않는다.
신규 화면은 Story로 등록하지 않는다. Storybook Reference 개선은 사용자 요청 범위에서 별도로 수행한다. Guard PASS는 디자인 점수·스크린리더 인증·backend 성공을 의미하지 않는다.