| name | new-widget |
| description | my-pegboard에 새 위젯 타입을 추가할 때 사용한다. 위젯 하나가 프론트 폴더 하나 + Rust provider 하나에 완결되도록 하고, 등록을 빠뜨려 위젯이 재시작 때 사라지는 사고를 막는다. "위젯 만들어줘", "새 위젯 추가", "GitHub 위젯", "위젯 스캐폴드" 같은 요청에 쓴다. |
새 위젯 추가
my-pegboard의 위젯은 폴더 하나에 완결된다. 새 위젯을 만들면서 다른 위젯의
코드를 열게 된다면 그건 설계가 어긋난 것이다 (CLAUDE.md "철칙").
이 스킬은 두 가지를 한다:
- 등록 7곳을 빠뜨리지 않게 한다 — 하나라도 놓치면 조용히 깨진다
- 위젯의 성격을 먼저 정하게 한다 — 그게 나머지 결정을 전부 좌우한다
0. 먼저 읽을 것
docs/DECISIONS.md 3장(개수 제한) · 4장(등록 구조)
docs/DESIGN.md 2장(WidgetShell) · 3장(위젯 상태 7종)
src/widgets/types.ts — 위젯 계약 전체가 여기 있다
기존 결정을 뒤집어야 한다면 멈추고 사용자에게 확인할 것. 근거를 남기지 않은
번복은 다음 사람이 같은 논의를 처음부터 다시 하게 만든다 (DECISIONS 21장).
1. 성격을 먼저 정한다
나머지 모든 결정이 여기서 갈린다. 세 축을 사용자에게 확인한다.
축 1 — 데이터를 누가 가져오나
| 유형 | 예 | 데이터 경로 | pollable |
|---|
| 외부 API | Jira, GitHub | Rust provider → 캐시 → IPC | true |
| 로컬 파일 | Todo | Rust storage → IPC | false |
| 자체 로드 | Web(iframe) | 위젯이 스스로 | true(재로드 의미) |
외부 API면 providers/<name>/를 만들고 캐시·재시도·rate limit을 Rust가 진다.
프론트에서 fetch()를 부르지 않는다 — 토큰이 WebView로 내려가면 안 된다.
축 2 — 인스턴스가 여러 개일 수 있나
"여러 개 놓을 이유가 실제로 있나"를 묻는다. 개수 제한은 부담이 아니라
의미로 정한다.
- 여러 개 — 위젯마다 다른 쿼리를 본다 (Jira: "내 티켓" / "팀 티켓")
- 하나 — 모든 인스턴스가 같은 것을 본다 (Todo가 그래서 1개로 바뀌었다)
여러 개인데 같은 데이터를 공유하면 동기화 문제가 생긴다. 그때는
store/<name>.ts에 공유 zustand 스토어를 두고 위젯별 사본을 만들지 않는다
(캐시를 두 군데 두지 않는다 — CLAUDE.md).
축 3 — 쓰기가 있나
읽기 전용이면 실패해도 직전 데이터를 계속 보여주면 된다.
쓰기가 있으면 아래를 반드시 정한다:
- 낙관적 업데이트를 할 것인가 — 로컬 파일이면 하지 마라. 숨길 느림이 없고,
실패를 얼버무리면 사용자가 저장됐는지 모른다
- 되돌릴 수 있나 — 없으면 되돌릴 수 없다고 화면에 적는다
- 멱등한가 — 아니면 재시도 금지. 네트워크 실패 시 "만들어졌을 수 있음"을
표시하고 버튼을 잠근다 (Jira 티켓 생성이 그 사례)
2. 등록 7곳 — 하나라도 빠지면 조용히 깨진다
이 목록이 이 스킬의 핵심이다. references/registration.md에 각 위치의
정확한 코드와 빠뜨렸을 때의 증상이 있다.
| # | 파일 | 빠뜨리면 |
|---|
| 1 | src-tauri/src/storage/board.rs WidgetType | board_save가 보드 파일 전체를 거부한다. 위젯이 재시작 때 사라진다 |
| 2 | 같은 파일 instance_limit() | 컴파일 실패 (match 누락) |
| 3 | 같은 파일 as_str() | 컴파일 실패 |
| 4 | src/widgets/types.ts WidgetType | 타입 에러 |
| 5 | src/widgets/<name>/index.ts + registerWidget() | 추가 메뉴에 안 뜬다 |
| 6 | src/main.tsx의 import '#/widgets/<name>' | 레지스트리에 등록되지 않는다. import 자체가 부수효과다 |
| 7 | src/board/WidgetHost.tsx 렌더링 분기 | View가 렌더링되지 않고 미구현 안내만 뜬다 |
WidgetHost 분기는 데이터 훅 유무와 관계없이 필수다. 현재 기본 분기는
definition.View를 렌더링하지 않고 "아직 구현되지 않았습니다"를 보여준다.
데이터를 자체로 가지지 않는 위젯도 ready envelope으로 View를 그리는
분기가 필요하다.
1번이 가장 위험하다. 프론트만 고치면 개발 중에는 잘 도는 것처럼 보이고,
앱을 껐다 켰을 때 위젯이 사라진다. Rust enum에 변형이 없으면 serde가 보드
파일 전체를 거부하기 때문이다.
3. 만들 파일
src/widgets/<name>/
├─ index.ts WidgetDefinition + registerWidget() — 이 파일만 바깥이 본다
├─ View.tsx 본문
├─ ConfigForm.tsx 설정 폼
└─ <name>.test.tsx 테스트
필요하면 순수 로직을 별도 파일로 뺀다(columns.ts, blocked.ts, plainTextToAdf.ts).
순수 함수로 빼는 기준은 "테스트하고 싶은가"다 — 렌더 트리를 거쳐야 검증되는
로직은 이미 잘못 놓인 것이다.
외부 API를 쓰면 src-tauri/src/providers/<name>/도 만든다
(client.rs types.rs error.rs mod.rs + tests/).
Rust 파일을 만든 뒤에는 반드시 부모 모듈인 providers/mod.rs,
commands/mod.rs, storage/mod.rs(해당할 때)에도 노출한다.
4. 지켜야 하는 규칙
조용한 실패 금지 (CLAUDE.md 대전제 2)
- 실패는 화면에 드러난다. 콘솔에만 남기지 않는다
- 미지원·미구현도 드러낸다 (ADF 렌더러가 모르는 노드에 회색 박스를 그리는 이유)
- 감지할 수 없는 실패라면 그 사실을 문서에 적고 대안 경로를 상시 노출한다
(web 위젯의 "브라우저에서 열기"가 그것)
목록을 비우지 않는다
한 번 데이터가 그려진 뒤로는 갱신 중이든 실패든 직전 목록을 유지한다.
스켈레톤으로 갈아끼우지 않는다. 이게 Jira 웹과의 차이다.
컴포넌트는 평범하게
영리한 추상화를 넣지 않는다. 사용자가 화면을 보고 "여기 색 바꿔줘"라고 했을 때
어느 파일인지 바로 짚을 수 있어야 한다.
상태는 7종 중에서 고른다
types.ts의 WidgetStatus를 쓴다. 새 상태를 만들기 전에 기존 7개로 표현되는지
먼저 본다.
5. 검증
cd src-tauri && cargo test
cd .. && bun run typecheck
bun run test
bun run lint
실제 앱 검증 전 데이터 보호
./run.sh --build는 실행 중인 my-pegboard를 종료하고, 배포판과 같은
io.devookim.MyPegboard 데이터 디렉터리를 쓴다. 새 위젯 타입이 든 board.json은
구버전 앱에서 손상 파일로 판정될 수 있다.
- 사용자 확인 없이
./run.sh --build를 실행하지 않는다.
- 실행 전
~/Library/Application Support/io.devookim.MyPegboard/board.json을 작업
디렉터리 밖의 임시 디렉터리에 백업하고 백업 경로를 보고한다.
- 검증 후 앱을 종료하고 원래
board.json을 복원한 뒤에만 구버전 앱을 실행한다.
- 백업할 파일이 없었다면 검증으로 생성된 파일을 지우기 전에
사용자에게 확인한다.
백업이 준비된 뒤에 ./run.sh --build로 실제 앱을 연다.
손으로 반드시 확인할 것:
- 위젯을 추가하고 앱을 껐다 켠다 — 살아 있나? (등록 1번 검증)
- 개수 상한까지 추가해본다 — 막히나?
- 최소 크기(
minLayout)로 줄여본다 — 읽히나?
- 연결/데이터가 없는 상태 — 빈 화면이 아니라 안내가 뜨나?
cargo test가 src/ipc/bindings.ts를 다시 만든다. 생성물을 손으로 고치지 말 것.
6. 문서
새 위젯은 결정을 동반한다. 남기지 않으면 다음 사람이 같은 논의를 반복한다.
docs/DECISIONS.md — 위젯 장 신설 또는 3장 개수표 갱신
docs/DESIGN.md — 배치도 + 상태별 표현
CLAUDE.md — 개수 제한 표
- 뒤집은 결정이 있으면 21장에 근거와 함께
참조
references/registration.md — 등록 7곳의 정확한 코드와 증상
references/patterns.md — 기존 위젯 3종에서 뽑은 반복 패턴
references/checklist.md — 작업 순서 체크리스트