Files
2026-09-16 17:22:14 +09:00

5.3 KiB

완료 보고: 스니펫 팔레트 (001-snippet-palette)

브랜치: feat/snippet-palette · 작성: 2026-08-05 · 상태: 최종 승인 대기

003(파이썬 winkit-snippet)의 스니펫 검색·붙여넣기 기능을 이 앱(React SPA + .NET WebView2)에 재개발 이식. 전역 핫키 Ctrl+1 → Raycast 결 팔레트 → 검색 → 앞 창에 붙여넣기. 저장은 C# 호스트 소유 로컬 SQLite.

계획 vs 결과

Phase / Story 계획 결과
Foundational (T001-T011) 멀티핫키·Ctrl+1·SendToWeb/navigate·SnippetRepository+스키마·시드·브릿지 왕복·라우트 전부. C# 10→17 테스트, 프론트 브릿지 왕복 테스트
US1 / MVP (T012-T024) 검색(키워드 AND, name+desc)·usage 랭킹·Enter/Alt 붙여넣기·프리뷰·Raycast 팔레트 전부. 검색/랭킹 순수함수 + api 테스트
US2 / CRUD (T025-T031) 생성/편집/삭제·EditDialog(클립보드 프리필)·검증(중복·빈값 거부) 전부. repo CRUD xUnit + api 테스트
US3 / category (T032-T034) category 칩 필터 + Ctrl+←/→ 전부. (검색 category 필터는 US1때 이미 구현돼 있어 칩 UI만 추가)
Polish (T035-T037) 최종 검증·quickstart·report T035 / T036 데스크톱 수동확인(사용자) / T037 이 문서

신규 vs 재사용 (재사용 우선 원칙)

신규(핵심 3곳만):

  • C# 2번째 전역 핫키(멀티핫키로 HotKeyService 확장) + Ctrl+1
  • C#→JS navigate 푸시(SendToWeb/PostWebMessageAsJson)
  • React↔SQLite reqId 요청-응답 브릿지 + C# SnippetRepository/SnippetBridge/SnippetSeed

재사용:

  • 팔레트 UI: snap SessionListPage(키보드 nav·IME 안전·Esc 단계) 복제 후 Raycast 리스킨
  • 붙여넣기: 기존 pasteToAppPasteService(앞 창 Ctrl+V) 그대로
  • 핫키/트레이/단일실행/앞창 캡처, memos feature/react-query 패턴

문서화한 정당 편차

  1. 데이터가 @/lib/api/client(HTTP) 미경유 — 로컬 SQLite라 브릿지 백엔드. react-query는 유지.
  2. Snippet 타입이 types/api.ts(서버 DTO) 아닌 feature-local — 로컬 엔티티(snap 방식과 동일).

계획과 달라진 점 / 겪은 문제

  • 시드 소스 부재: 003의 snippets.db가 이 머신에 실제로 없음(포터블/미실행) → 첫 실행 시 빈 DB로 시작(설계상 fallback). 003 데이터 이어받기는 그 파일이 생기면 자동 동작.
  • git index 레이스(Foundational): 프론트·C# implementer를 병렬로 돌렸다가 같은 git index 충돌로 커밋이 섞이고 일부 미커밋됨. working tree(진실원천)로 검증·복구(30159d2). 이후 implementer 순차 원칙 확립.
  • 클릭 모델 정리(US2 리뷰): US1이 행 단일클릭=붙여넣기로 만들어 더블클릭 편집이 죽어있었음 → 단일클릭=선택, Enter/Alt=붙여넣기, 더블클릭/F2=편집(003·spec 정합).
  • 콜드스타트 navigate 드롭(Foundational 리뷰): 앱 시작 직후 첫 Ctrl+1이 유실될 수 있어 pending 버퍼+NavigationCompleted flush로 방어.

검증

  • 프론트: 233/233 통과, build OK. 스니펫 feature lint 0문제. (repo 전체 20 lint 문제는 무관 feature의 pre-existing 빚.)
  • C#: 17/17 통과, build 0 경고/에러.
  • 런타임 E2E(Ctrl+1→검색→붙여넣기, 시드, 붙여넣기 대상 정확성): 데스크톱 앱 실행 필요 — 헤드리스 불가. 사용자 수동 확인 권장(quickstart.md 시나리오 1~11).

남은 것 / 이월(deferred)

  • [수동확인] quickstart 시나리오 — 실제 데스크톱에서.
  • [deferred] 붙여넣기 실패해도 recordUse 됨(기존 paste.code fire-and-forget 프로토콜 한계, ack 추가는 별개 작업).
  • [deferred] list-fetch 에러가 빈상태와 동일 표시(에러 UI 없음).
  • [deferred] HotKeyService.Dispose 단일 _hwnd 가정(현재 두 핫키 같은 hwnd라 무해).
  • [deferred] SnippetBridge reqId 빈값 시 promise 영구 대기(정상 caller는 항상 reqId 세팅).

최종 리뷰 (whole-branch, opus)

APPROVE — 보안(전 쿼리 파라미터화, body는 <pre> escape)·정합성(delete가 usage까지·name 정규화 일관)·브릿지 상관(reqId 유일·리스너 멱등)·동시성(WebView2 UI 스레드 단일) 다 클린. Critical/Important 신규 없음.

리뷰서 나온 신규 Minor 4개는 1회 fix wave로 처리 완료(재리뷰 ALL ADDRESSED):

  • M1/M2: /snippetSnapLayoutProtectedRoute 직속 h-screen 풀스크린으로 이동 → snap 헤더 없이 Raycast 풀윈도 룩 복구 + Ctrl+N 이탈 차단. auth 유지(routes.test.tsx 회귀가드).
  • M3: navigate 버퍼는 pre-ready일 때만 → reload 시 stale 재전송 방지.
  • M4: 비도메인 예외는 raw 메시지 대신 generic 한글 토스트(SQLite 영어 내부 노출 차단).

런타임 미검(헤드리스 한계 — 사용자 확인 필요): 실제 데스크톱에서 Ctrl+1 → 팔레트 풀윈도 중앙 표시(snap 헤더 없음)·검색·앞 창 붙여넣기·Ctrl+N 비이탈. quickstart.md 시나리오로 눈확인 권장.

최종 검증: 프론트 234/234, C# 17/17, 빌드 클린. 스니펫 코드 lint 0문제.

결론: 문서화된 deferral(위 "남은 것") 제외하고 merge 준비 완료.