# Data Model: 스니펫 팔레트 (Phase 1) 003 스키마를 그대로 따른다. 랭킹·검색은 프론트에서 계산하므로, 저장은 원자적 데이터만 담는다. ## 엔티티 ### Snippet 붙여넣을 코드 조각 한 개. | 필드 | 타입 | 제약 | 설명 | |---|---|---|---| | `name` | string | **PK**, 정규화(대문자·공백→`_`), 유일 | 식별 키, 결과 행 제목 | | `desc` | string | 기본 `""` | 짧은 설명, 결과 부제 · 검색 대상 | | `body` | string | 비어있지 않음, 원문 보존 | 실제 붙여넣어지는 내용 | | `category` | string | 기본 `"코드"` | 분류 라벨 | - 003의 `change_word1/2`는 **가져오지 않음**(안 쓰는 유물). - 저장 시 검증: `name` 정규화 후 비어있지 않음 + 유일(중복 거부), `body` 비어있지 않음. (spec FR-016) - `name`은 편집 불가(키). desc/body/category만 수정. (FR-017) ### Usage 스니펫별 사용 통계. 별도 테이블(003과 동일). | 필드 | 타입 | 제약 | 설명 | |---|---|---|---| | `name` | string | **PK**, Snippet.name 참조 | 어떤 스니펫 | | `count` | integer | 기본 0 | 붙여넣기 성공 누적 | | `last_used` | integer | unix 초 | 마지막 붙여넣기 시각 | - 붙여넣기 성공 시 upsert: `count+1`, `last_used=now`. (FR-011) - Snippet에 대응 Usage가 없으면 `(0,0)`으로 취급. ## SQLite 스키마 (C# 호스트 소유) ```sql CREATE TABLE IF NOT EXISTS snippets ( name TEXT PRIMARY KEY, desc TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', category TEXT NOT NULL DEFAULT '코드' ); CREATE TABLE IF NOT EXISTS usage ( name TEXT PRIMARY KEY, count INTEGER NOT NULL DEFAULT 0, last_used INTEGER NOT NULL DEFAULT 0 ); ``` 파일: `%LOCALAPPDATA%\CodeAssist\snippets.db`. 첫 실행 시 003의 `snippets.db`가 있으면 1회 복사(시드). (research R4·R5) ## 프론트 타입 (`features/snippets/types.ts`) 로컬 엔티티라 `types/api.ts`(서버 DTO 전용) 아님. ```ts export interface Snippet { name: string desc: string body: string category: string usageCount: number // usage.count (없으면 0) lastUsed: number // usage.last_used unix초 (없으면 0) } export interface SnippetInput { // 생성/수정 입력 name: string desc: string body: string category: string } ``` - 브릿지 응답에서 snippets ⨝ usage 조인 결과를 `Snippet`(usageCount/lastUsed 병합)로 받는다. C#가 조인해 내려주거나, 두 테이블을 각각 내려 프론트에서 병합 — 구현 시 단순한 쪽(C# 조인 권장). ## 파생 규칙 (프론트 순수함수) ### 검색 (`core/search.ts`) — FR-005/006 - 입력: 스니펫 목록, 쿼리 문자열, (선택)category. - category가 있으면 그 분류로 먼저 제한. `"전체"`면 전체. - 쿼리를 공백으로 split → 키워드들. 각 키워드(대문자화)가 **전부(AND)** `(name + " " + desc)`의 대문자화 문자열에 포함될 때만 통과. `body`는 검색 안 함. 퍼지 없음. - 빈 쿼리 → (category 제한된) 전체. ### 랭킹 (`core/ranking.ts`) — FR-007 - 정렬 키: `count desc, lastUsed desc, name asc`. - 모든 결과 집합(검색·전체 공통)에 적용. 상위 9개에 `1~9` 번호(Alt+숫자 대응). (FR-008) ## 상태 전이 스니펫: (없음) → 생성 → (편집)* → 삭제. Usage: 붙여넣기 성공마다 count 증가 · last_used 갱신 → 랭킹 상단으로 부상.