Files
CODE_ASSISTANT/specs/004-tauri-shell/spec.md
T
2026-09-16 17:22:14 +09:00

230 lines
17 KiB
Markdown

# Feature Specification: Tauri 데스크톱 앱 전환
**Feature Branch**: `004-tauri-shell`
**Created**: 2026-08-11
**Status**: Draft
**Input**: User description: "이 프로젝트를 Rust + Tauri 버전으로 정의하고 수정을 시작한다. Tauri를 기본 데스크톱 앱으로 삼고, 기존 3_windowsApp(.NET WPF + WebView2)은 참고용 legacy로 남긴다."
## 이 기능이 뭔지 한 줄
기존 React 화면과 사용자 데이터를 그대로 살리면서, **Rust + Tauri를 CodeAssist의 기준 Windows 데스크톱 앱으로 완성한다.**
---
## User Scenarios & Testing *(mandatory)*
### User Story 1 - 프론트 한 벌로 Tauri와 브라우저에서 뜬다 (Priority: P1)
지금 화면(챗봇·스니펫 팔레트)은 데스크톱 껍데기가 넣어준 통로로 바깥과 얘기한다. 이 통로를 실행 환경에 맞게 고르면 화면 코드를 갈라 만들지 않고도 Tauri 앱과 브라우저 개발 화면을 함께 유지할 수 있다.
기존 WebView2 통로도 참고용 legacy 앱이 실행될 수 있을 만큼 남기되, 앞으로의 기본 실행·배포 기준은 Tauri다.
**Why this priority**: 화면과 Tauri 사이 통로가 먼저 열려야 창 제어·붙여넣기·캡쳐·스니펫을 차례로 연결할 수 있다. 화면을 한 벌로 유지하면 이후 기능도 같은 UI와 계약을 그대로 쓴다.
**Independent Test**: Tauri에서 현재 화면이 뜨고 데스크톱 동작이 Tauri 통로로 전달되는지 확인한다. 같은 화면을 브라우저로 열었을 때 데스크톱 기능만 빠지고 화면은 깨지지 않으면 통과다.
**Acceptance Scenarios**:
1. **Given** Tauri 앱을 실행했고, **When** 챗봇이나 스니펫 화면을 열면, **Then** 기존 React 화면이 그대로 뜨고 데스크톱 동작은 Tauri로 전달된다
2. **Given** 데스크톱 껍데기 없이 브라우저에서 화면만 열었고, **When** 데스크톱 전용 버튼이 있는 곳에 가면, **Then** 그 버튼은 안 보이거나 눌러도 아무 일 없고 화면은 안 깨진다
3. **Given** 참고용 legacy 앱을 실행했고, **When** 기존 기능을 사용하면, **Then** 별도 화면 코드 없이 남겨둔 WebView2 통로를 사용한다
4. **Given** 화면 코드를 쓰는 쪽(버튼·페이지 등), **When** 이번 변경 내역을 보면, **Then** 통로를 다루는 파일 바깥은 손댄 데가 없다
---
### User Story 2 - Tauri 앱을 단축키와 트레이로 쓴다 (Priority: P2)
CodeAssist를 켜두면 트레이에 상주한다. 사용자는 기존에 익숙한 전역 단축키로 챗봇·스니펫·캡쳐를 바로 부르고, 챗봇 단축키를 다시 누르면 창을 숨긴다.
창을 옮기고 크기를 바꾸거나 항상 위에 고정한 상태는 앱을 다시 켜도 복원된다. 앱을 두 번 실행해도 창은 하나만 유지된다.
**Why this priority**: CodeAssist의 기본 사용 흐름은 다른 프로그램에서 일하다 단축키로 필요한 도구를 잠깐 부르는 것이다. 이 흐름이 먼저 안정돼야 붙여넣기와 캡쳐를 실제 업무처럼 검증할 수 있다.
**Independent Test**: Tauri 앱 하나를 켜고 `Ctrl+Shift+7/8/9`를 차례로 눌러 각 기능이 열리는지, 챗봇 토글·트레이·창 상태 복원·단일 실행이 동작하는지 확인한다.
**Acceptance Scenarios**:
1. **Given** 앱이 실행 중이고, **When** `Ctrl+Shift+7`을 누르면, **Then** 스니펫 팔레트가 열린다
2. **Given** 앱이 실행 중이고, **When** `Ctrl+Shift+8`을 누르면, **Then** 챗봇 창이 열리고 다시 누르면 숨는다
3. **Given** 앱이 실행 중이고, **When** `Ctrl+Shift+9`를 누르면, **Then** 화면 캡쳐가 시작된다
4. **Given** 창 위치·크기·항상 위 상태를 바꾸고 앱을 다시 켜면, **Then** 마지막 상태가 복원된다
5. **Given** 앱이 이미 실행 중이고, **When** 앱을 한 번 더 실행하면, **Then** 새 창을 만들지 않고 기존 창을 불러낸다
6. **Given** 단축키가 다른 프로그램에 이미 먹혀서 등록에 실패했을 때, **When** 앱을 켜면, **Then** 앱이 죽지 않고 트레이로 창을 부를 수 있으며 무엇이 실패했는지 알 수 있다
---
### User Story 3 - 코드가 직전 앱에 붙는다 (Priority: P3)
작업하다 단축키로 CodeAssist를 불러 코드를 고르면, 창이 닫히면서 **아까 쓰던 그 앱**에 코드가 그대로 들어간다.
붙이기가 실패해도 코드는 클립보드에 남아서 사용자가 직접 붙여넣을 수 있다.
**Why this priority**: 스니펫과 챗봇의 코드 결과를 다시 복사하러 다니지 않는 것이 데스크톱 앱의 핵심 가치다. 포커스 전환이 느린 프로그램에서도 틀린 창에 붙지 않아야 한다.
**Independent Test**: 메모장·코드 에디터·느리게 뜨는 업무용 프로그램을 대상으로 각각 여러 번 붙여넣어 보고 결과와 대상을 확인한다.
**Acceptance Scenarios**:
1. **Given** 메모장에 커서를 두고, **When** 단축키로 창을 불러 코드를 고르면, **Then** 창이 닫히고 메모장에 그 코드가 들어간다
2. **Given** 창 전환이 느린 프로그램이 대상일 때, **When** 같은 동작을 하면, **Then** 코드가 엉뚱한 곳에 들어가거나 절반만 들어가는 일이 없다
3. **Given** 대상 앱이 붙여넣기를 거부하는 상황일 때, **Then** 코드는 클립보드에 남아 있어 사용자가 직접 붙일 수 있다
4. **Given** 창을 불러낸 상태에서, **Then** 어느 앱에 붙게 되는지 화면에 표시된다
---
### User Story 4 - 화면 일부를 잘라 챗에 붙인다 (Priority: P4)
단축키를 누르면 화면이 어두워지고, 마우스로 원하는 영역을 끌어서 고르면, 챗 입력창에 그 그림이 첨부된 채로 창이 뜬다. Esc로 취소하면 아무 일도 없다.
모니터가 여러 대이고 각 모니터 배율이 서로 달라도, **고른 영역과 잘린 그림이 일치**한다.
**Why this priority**: 화면에 보이는 오류·코드·업무 화면을 바로 대화에 붙이면 설명을 다시 타이핑할 필요가 없다. 좌표가 어긋나면 다른 내용을 보내게 되므로 정확도가 우선이다.
**Independent Test**: 배율이 서로 다른 모니터 두 대를 놓고, 각 모니터와 두 모니터에 걸친 영역을 여러 번 캡쳐해 고른 영역과 결과가 맞는지 눈으로 대조한다.
**Acceptance Scenarios**:
1. **Given** 배율 100% 모니터에서, **When** 영역을 끌어 고르면, **Then** 고른 영역과 똑같은 그림이 챗 입력창에 첨부된다
2. **Given** 배율이 다른 두 번째 모니터에서 같은 동작을 하면, **Then** 결과가 어긋나지 않는다
3. **Given** 영역 고르는 중에 Esc를 누르면, **Then** 아무것도 첨부되지 않고 화면이 원래대로 돌아간다
4. **Given** 영역을 거의 안 끌고 클릭만 하면, **Then** 캡쳐가 안 되고 조용히 취소된다
---
### User Story 5 - 기존 스니펫을 그대로 쓴다 (Priority: P5)
Tauri 앱으로 스니펫 팔레트를 처음 열어도 **기존 CodeAssist에서 쓰던 스니펫 목록이 그대로** 나온다. 새로 만들거나 고친 내용과 자주 쓰는 순서는 앱을 껐다 켜도 유지된다.
**Why this priority**: 이미 쌓아둔 코드 조각을 잃지 않아야 Tauri 앱을 바로 실제 업무에 쓸 수 있다. 데이터 이전을 따로 요구하지 않는 것이 전환의 핵심 조건이다.
**Independent Test**: 기존 스니펫 데이터가 있는 상태에서 Tauri 팔레트를 열어 목록을 확인하고, 하나를 수정·사용한 뒤 앱을 다시 켜서 변경과 사용 순서가 유지되는지 확인한다.
**Acceptance Scenarios**:
1. **Given** 기존 CodeAssist 스니펫이 저장돼 있고, **When** Tauri 앱에서 팔레트를 열면, **Then** 기존 목록이 그대로 보인다
2. **Given** Tauri 앱에서 스니펫을 만들거나 고쳤고, **When** 앱을 다시 켜면, **Then** 변경 내용이 유지된다
3. **Given** Tauri 앱에서 스니펫을 사용했고, **When** 목록을 다시 열면, **Then** 사용 횟수와 최근 사용 시각이 반영된 순서로 보인다
---
### Edge Cases
- 전역 단축키를 다른 프로그램이 이미 쓰고 있으면? → 앱은 계속 실행하고, 등록하지 못한 조합을 사용자에게 알린다
- 앱을 두 번 실행하면? → 두 번째 창을 새로 띄우지 않고 이미 떠 있는 창을 불러낸다
- 창이 화면 밖에 저장돼 있다가 모니터 구성이 바뀌면? → 화면 안 보이는 자리로 복원하지 않는다
- 아직 안 만든 기능을 화면이 부르면? → 조용히 성공한 척하지 않고 아직 안 됐다는 걸 사용자가 알 수 있게 한다
- 창이 준비되기 전에 껍데기가 화면에 알림을 보내려 하면? → 앱이 죽지 않고, 화면이 준비된 뒤 필요한 현재 상태를 읽을 수 있다
- 기존 스니펫 파일이 없으면? → 빈 목록으로 시작하고 새 스니펫을 정상 저장할 수 있다
- 참고용 legacy 앱을 Tauri와 동시에 켜면? → 같은 전역 단축키가 충돌할 수 있으며 동시 사용은 지원 범위가 아니다
---
## Requirements *(mandatory)*
### Functional Requirements
**통로 자동 감지 (US1)**
- **FR-001**: 화면은 실행 시점에 Tauri / 참고용 legacy / 껍데기 없음(브라우저) 중 어느 환경인지 스스로 판별해야 한다
- **FR-002**: 판별 결과에 따라 바깥과 얘기하는 방식을 자동으로 고르되, 화면을 쓰는 쪽 코드는 실행 환경을 몰라도 되어야 한다
- **FR-003**: 껍데기가 없으면 데스크톱 전용 동작은 조용히 넘어가고 화면은 정상 동작해야 한다
- **FR-004**: Tauri 앱과 브라우저 개발 화면은 같은 화면 결과물 하나를 써야 한다
- **FR-005**: Tauri가 화면에 보내는 알림(이동·붙여넣기 대상·캡쳐 결과)은 기존 계약 문서의 모양을 유지해야 한다
**데스크톱 앱 동작 (US2)**
- **FR-006**: Tauri 앱은 `Ctrl+Shift+7`=스니펫, `Ctrl+Shift+8`=챗봇, `Ctrl+Shift+9`=캡쳐 전역 단축키를 제공해야 한다
- **FR-007**: 창 위치·크기·항상 위 고정 상태를 저장하고 다음 실행에 복원해야 한다
- **FR-008**: 앱은 트레이에 상주하고, 열기 / 항상 위에 고정 / 종료를 제공해야 한다
- **FR-009**: 앱 인스턴스가 하나만 뜨도록 막아야 한다
- **FR-010**: 단축키 등록에 실패해도 앱은 계속 동작해야 하며, 무엇이 실패했는지 사용자가 알 수 있어야 한다
- **FR-011**: 챗봇 단축키는 토글로 동작해야 한다 — 보고 있으면 숨기고, 아니면 마지막 챗봇 자리로 데려온다
- **FR-012**: 창은 제목표시줄 없이 뜨고, 화면 안의 지정된 자리를 끌어서 창을 옮길 수 있어야 한다
**붙여넣기 (US3)**
- **FR-013**: 창을 부르기 직전에 쓰던 앱을 기억하고, 코드 선택 시 그 앱에 넣어야 한다
- **FR-014**: 붙여넣은 내용은 클립보드에 남겨 실패 시 사용자가 직접 붙일 수 있어야 한다
- **FR-015**: 어느 앱에 붙게 되는지 화면에 표시해야 한다
**화면 캡쳐 (US4)**
- **FR-016**: 단축키로 영역 선택 화면을 띄우고, 끌어서 고른 영역을 그림으로 만들어야 한다
- **FR-017**: 모니터가 여러 대이고 배율이 서로 달라도 고른 영역과 결과가 일치해야 한다
- **FR-018**: 취소(Esc·너무 작은 영역)는 아무 일도 일어나지 않아야 한다
- **FR-019**: 캡쳐한 그림은 새 대화 입력창에 첨부로 들어가야 한다
**스니펫 (US5)**
- **FR-020**: 기존 CodeAssist 스니펫 데이터를 별도 이전 작업 없이 그대로 읽고 써야 한다
- **FR-021**: 목록·생성·수정·삭제·사용기록이 기존 사용자 흐름과 동일하게 동작해야 한다
- **FR-022**: 저장 실패·중복 등의 오류는 사용자가 읽을 수 있는 한글 메시지로 화면에 떠야 한다
**완성 기준 (전 구간 공통)**
- **FR-023**: 아직 안 만든 기능은 **조용히 성공한 척하지 않고**, 아직 안 됐다는 걸 사용자가 알 수 있게 해야 한다
- **FR-024**: US1~US5를 모두 끝내 평소 업무 흐름 중간에 미구현으로 막히는 곳이 없어야 한다
- **FR-025**: 기본 실행·배포·사용 문서는 Tauri 앱을 기준으로 해야 한다
- **FR-026**: 기존 .NET 앱은 참고용 legacy로 남기되 새 기능·기본 배포·완료 검증 대상에서 제외해야 한다
- **FR-027**: 설치물과 실행 창에는 실험판 표기 없이 제품명 `CodeAssist`를 보여줘야 한다
### Key Entities
- **브릿지 메시지**: 화면과 Tauri가 주고받는 약속. 방향(화면→껍데기 / 껍데기→화면), 종류, 딸린 값. 기존 계약 문서가 원본
- **창 상태**: 창의 위치·크기·항상 위 고정 여부. 다음 실행에 복원할 값
- **스니펫**: 이름(고유)·설명·본문·분류 + 사용 횟수·마지막 사용 시각. 기존 CodeAssist 데이터를 이어서 사용
- **붙여넣기 대상**: 창을 부르기 직전 쓰던 앱의 제목과 프로그램 이름. 소환 시점에 한 번 찍어 보관
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: 화면 결과물 한 벌이 Tauri 앱과 브라우저에서 모두 뜨며, 통로를 다루는 파일 바깥의 화면 코드 변경은 0줄이다
- **SC-002**: 프론트 자동 검사와 데스크톱 순수 로직 검사가 전부 통과한다
- **SC-003**: `Ctrl+Shift+7/8/9` 세 단축키가 각각 스니펫·챗봇·캡쳐만 실행하고, 반복 사용 중 무반응이나 오작동이 없다
- **SC-004**: 사용자가 Tauri 앱으로 단축키 소환 → 코드 선택 → 직전 앱 붙여넣기 흐름을 처음부터 끝까지 막힘없이 마친다
- **SC-005**: 사용자가 캡쳐 흐름을 끝까지 마치고, 배율이 다른 실제 모니터 각각과 두 모니터에 걸친 영역에서 고른 영역과 결과가 일치한다
- **SC-006**: 기존 스니펫의 목록·수정·사용기록이 Tauri 앱 재실행 뒤에도 유지된다
- **SC-007**: Tauri release 설치물이 만들어지고, 설치물과 실행 창에 제품명 `CodeAssist`가 표시된다
- **SC-008**: 기본 실행·배포·검증 명령에서 기존 .NET 앱을 요구하지 않는다
## Assumptions
- **Tauri가 기준 앱이다** — 앞으로 기본 실행·배포·새 데스크톱 기능은 Tauri를 대상으로 한다
- **기존 .NET 앱은 참고용 legacy다** — 코드는 남기지만 새 기능·배포·회귀 보장 대상은 아니다
- **기존 계약 문서가 원본이다** — 브릿지 메시지 약속은 `specs/001-snippet-palette/contracts/bridge-messages.md``specs/002-capture-to-chat/contracts/bridge-capture.md`를 이어서 쓴다
- **표준 단축키는 Tauri가 쓴다** — `Ctrl+Shift+7/8/9`를 Tauri에 배정하며 legacy 앱과 동시 실행은 지원하지 않는다
- **기존 스니펫 데이터는 보존한다** — `%LocalAppData%\CodeAssist\snippets.db`를 그대로 이어서 쓴다
- **Rust 개발 환경은 준비됐다** — rustc/cargo와 Windows 빌드 도구 설치·링크 확인이 끝난 상태다
- **미완성 스캐폴드는 이어서 쓴다** — 생성기 산출물은 유지하고, 손으로 쓴 Rust 소스는 컴파일하며 다시 확인한다
- **이 PC 한 대, 사용자 한 명이 우선 대상이다** — 여러 기기·여러 사용자 대상 검증은 범위 밖이다
- **자동 계측 장치는 만들지 않는다** — 실제 동작 검증에 필요한 최소 코드만 만든다
- **US1~US5는 모두 필수다** — 비교 결과에 따라 중간에 멈추는 경로는 없다
## Dependencies
- 기존 브릿지 계약 문서 2개
- 화면(`2_frontend`)의 통로 파일들
- 기존 CodeAssist 스니펫 데이터 파일
- 설치가 끝난 Rust·Windows 빌드 환경
## 정해진 것
### D1: Tauri를 기준 데스크톱 앱으로 삼는다 (2026-09-09)
- 기본 실행·배포·새 기능 대상은 `4_rust_tauri`
- 제품명에서 실험판 표기를 뺀다
- Tauri가 기존 표준 단축키 `Ctrl+Shift+7/8/9`를 사용한다
- `3_windowsApp`은 삭제하지 않고 참고용 legacy로 남긴다
- legacy 앱과 Tauri 앱의 동시 실행은 지원하지 않는다
### D2: 기능을 끝까지 옮긴다 (2026-09-09)
- US1~US5를 모두 완료한다
- A/B 비교·설치물 비교·판정 기록은 범위에서 뺀다
- “비교하다 결론이 나면 중간에 멈춤” 규칙을 없앤다
- 아직 안 만든 기능은 구현 중에만 분명한 오류로 알리고, 완료 시점에는 남아 있으면 안 된다