230 lines
17 KiB
Markdown
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 비교·설치물 비교·판정 기록은 범위에서 뺀다
|
|
- “비교하다 결론이 나면 중간에 멈춤” 규칙을 없앤다
|
|
- 아직 안 만든 기능은 구현 중에만 분명한 오류로 알리고, 완료 시점에는 남아 있으면 안 된다
|