Files
networkSetup/.claude/rules/frontend.md
T
jychoi4andClaude Opus 5.5 905e6d4eaa chore: add Claude Code guidelines, rules and skills
- CLAUDE.md: product principles, crate layering, common commands, commits.
- .claude/rules: common, Rust, frontend, Tauri IPC, docs and security rules.
- .claude/skills: check, add-tauri-command, project-model-change,
  change-record, msix-integration-test, backlog-status.

Refs #16

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 11:09:05 +09:00

49 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
paths:
- "src/apps/desktop/src/**"
- "src/apps/desktop/tests/**"
- "src/apps/desktop/index.html"
- "src/apps/desktop/package.json"
- "src/apps/desktop/tsconfig.json"
---
# 프런트엔드(TypeScript · CSS) 규칙
프레임워크 없는 TypeScript. 화면은 문자열 템플릿으로 만들고 `root.innerHTML`에 넣은 뒤 `data-action`/`data-field` 속성으로 이벤트를 연결한다. `tsc --noEmit` strict와 `node --test`를 통과해야 한다.
## 모듈 구조
- `main.ts`는 상태와 조정(invoke 호출, render, action 분기)만 둔다. 화면 조각과 순수 로직은 별도 모듈로 뺀다: `start-screen.ts`, `registry-editor.ts`, `signing-editor.ts`, `project-transition.ts`, `window-chrome.ts`.
- 새 탭·편집기는 `render<Name>(data, disabled): string`을 export하는 모듈로 만들고, `main.ts`에서는 import와 action 분기만 추가한다.
- 테스트는 `tests/<module>.test.mjs`에서 `.ts`를 직접 import한다. Node의 `--experimental-strip-types`로 실행되므로 **`enum`, `namespace`, 매개변수 프로퍼티, 데코레이터를 쓰지 않는다**. 타입은 `type` 별칭과 `import type`으로.
- 순수 함수(그룹화, 경로 표시, 전환 판단)는 DOM 없이 호출 가능해야 테스트할 수 있다. `document`를 쓰는 코드와 분리한다.
## 보안 · 렌더링
- 템플릿에 들어가는 모든 동적 값은 `esc()`/`escape()`를 거친다. 프로젝트 이름, 경로, 오류 메시지, ID도 예외 없다. 테스트는 `<img src=x onerror=...>` 같은 값이 이스케이프되는지 확인한다.
- `tauri.conf.json`의 CSP는 `'self'`만 허용한다. 인라인 `<script>`, 인라인 `style=`, 외부 폰트·CDN을 추가하지 않는다.
- `innerHTML`을 쓰는 곳은 render 함수 안에서만. 이벤트 핸들러는 `addEventListener`로 붙이고 `onclick=` 속성을 쓰지 않는다.
- `any`를 새로 쓰지 않는다. 경로 기반 필드 갱신이 필요하면 `Record<string, unknown>`과 단계별 검사로.
## 상태와 IPC
- `invoke<T>('command', args)`의 `T`는 Rust 반환 DTO와 필드 단위로 일치하는 `type`이어야 한다. Rust 쪽 `#[serde(default)] Option<T>`는 TS에서 `field?: T | null`.
- `isTauri()`가 false인 미리보기 모드에서는 파일 작업을 시도하지 않고 안내 문구를 보여준다.
- 모든 action은 `busy` 플래그로 중복 실행을 막고, `cancel`만 `busy` 중에 허용한다.
- 저장 안 된 변경(`dirty`)이 있을 때 프로젝트를 바꾸는 모든 경로는 `allowProjectTransition`을 거친다. 저장 실패 시 전환하지 않는다.
- 변경 후에는 `invalidate()`로 `report`/`plan`을 무효화한다. 오래된 검증 결과를 화면에 남기지 않는다.
## 접근성 · 문구
- 버튼은 텍스트 또는 `aria-label`을 가진다. 아이콘 전용 버튼(`×`)에는 `aria-label`과 `title`.
- 상태 메시지 영역은 `role="status"` 또는 `aria-live="polite"`.
- 사용자 문구는 한국어, 조치 중심: "변경 사항을 저장한 뒤 다시 검사하세요." 기술 코드는 문장 뒤에 덧붙인다.
- "배포 가능", "설치 완료", "검증됨" 같은 단어는 실제 상태 필드(`deploymentReady`, `installationTest`)가 그렇게 말할 때만 쓴다.
## CSS
- 색상은 `theme.css`의 `var(--color-*)`만 사용한다. 새 색이 필요하면 토큰을 먼저 추가한다. hex 하드코딩 금지(`.primary`의 `#fff`는 기존 예외).
- 선택자는 클래스 기반. ID 선택자와 `!important` 금지.
- 반응형 분기는 기존 `1200px`, `1050px` 중단점을 재사용한다.
- `style.css`는 한 줄 압축 형식이 섞여 있다. 새 규칙은 파일 끝에 사람이 읽을 수 있는 형식으로 추가하고, 기존 압축 블록을 재포맷하지 않는다.