Files
CODE_ASSISTANT/docs/superpowers/specs/2026-08-04-windows-webview-launcher-design.md
T
2026-09-16 17:22:14 +09:00

6.4 KiB

윈도우 데스크톱 런처 (WebView2 로 2_frontend 감싸기) — 설계

작성일: 2026-08-04 스코프: B (껍데기 + 창 관리) — raycast/wox/alfred 스타일 런처 셸 상태: 설계 승인됨 → 구현 계획(writing-plans) 대기

1. 뭘 만드나 / 왜

지금 2_frontend/ React SPA 를 윈도우 닷넷(WPF + WebView2) 앱 안에 담아서, 전역 단축키로 소환하는 런처처럼 쓴다. 알맹이(챗·기능)는 지금 React 앱 그대로. 이번에 만드는 건 껍데기(셸) — 어떻게 뜨고 사라지고 상주하냐.

2. 핵심 전략 — V1 껍데기 재활용

d:\project\021.code-assistant\3_windowsApp\ 에 V1 이 있음. 런처 껍데기(단축키·트레이·창관리)는 거기 다 만들어져 있어서 그대로 이식하고, "바뀐 로직"(닷넷이 챗·인증을 네이티브로 하던 부분)만 버린다. V2 는 React 가 /api 를 직접 부르니 그 네이티브 로직이 필요 없음.

V1 요소 V2 처리
CodeAssist.Shell 전체 (HotKeyService·SingleInstanceGuard·ForegroundWindow·PasteService·ClipboardBackup·TrayIconHost·FramelessPaletteWindow·JsonWindowPlacementStore·ViteDevServer) 그대로 이식 — 런처 원시기능, 로직 안 바뀜
App.xaml.cs 뼈대 (단일인스턴스→핫키→포그라운드 캡처→팔레트 토글→트레이) 이식 — V1 이 디버그용으로 주석 처리해둔 트레이·blur-to-hide 를 되살림
WebView A/B 로딩 (DEBUG=vite, RELEASE=가상호스트 SetVirtualHostNameToFolderMapping) 이식 — 소스 경로만 V1 의 web/ → V2 의 2_frontend/ 로 교체
Core.Chat·Core.Auth·네이티브 ChatView/ViewModels·브릿지의 chat/auth/session 핸들러·Markdig 버림 (지금은). V2 React 가 /api 직접 호출로 다 함
브릿지 paste.code·window.hide·window.resize v1 제외, 다음 단계 — 런처 감칠맛(직전 창에 붙여넣기·Esc숨김·자동높이). React 쪽 postMessage 한 줄 필요해서 v1 필수 아님

Core 는 나중에 참고할 것. V1 의 CodeAssist.Core(Auth·Chat: AuthClient, DpapiTokenStore, SseChatClient, HttpSessionClient, 토큰 브릿지 등)는 지금은 안 쓰지만 버리는 게 아니라 레퍼런스로 남긴다. 나중에 (1) auth 브릿지를 되살려 C#↔JS 로 refresh 토큰 영속·자동 재인증을 붙이거나, (2) RELEASE /api 프록시를 네이티브로 처리할 때 이 코드가 출발점이 됨. 구현 시 V1 Core 를 열어보고 참고.

3. 프로젝트 구조 (V2)

2_frontend/            (그대로, 손 안 댐)
3_windowsApp/
  CodeAssist.App/      WPF 진입점 + PaletteWindow + WebHostView (챗/인증 로직 뺀 WebChatView)
  CodeAssist.Shell/    V1 그대로 이식 (재활용 핵심)
  CodeAssist.Tests/    Shell 관련 테스트만 이식 (WindowPlacement, SingleInstance 등)
  • Core 프로젝트는 이번에 안 만든다 — 네이티브 챗/인증이 사라져 쓸 데 없음. auth 브릿지 되살릴 때 그때 V1 Core 참고해서 추가.
  • 스택: .NET 8 (net8.0-windows) · WPF · WebView2 · (WPF-UI 는 창 스타일용, 옵션)

4. 동작 흐름

  1. 부팅 → 트레이 상주 (단일 인스턴스 — 두 번째 실행은 기존 창 소환 후 종료)
  2. Ctrl+Alt+Space → 직전 창 HWND 캡처 → 팔레트 Show (마지막 위치/크기 복원)
  3. 창 안엔 2_frontend React 앱 (DEBUG=vite 핫리로드 / RELEASE=dist 가상호스트)
  4. Esc 또는 포커스 잃음(Deactivated) → Hide (위치·크기 저장)
  5. 트레이 우클릭 → "종료" (평소 X 는 종료 대신 숨김)

5. 창·단축키 기본값 (V1 준용)

  • 전역 단축키: Ctrl+Alt+Space (Alt+Space 시스템 메뉴 충돌 회피)
  • 창: 테두리 있는 크기조절 가능한 일반 창, CenterScreen 최초 위치, 위치·크기 기억(JsonWindowPlacementStore)
  • (다음 단계) web 이 알려준 컨텐츠 높이로 스르륵 리사이즈 — spotlight 느낌. 브릿지 window.resize 되살릴 때.

6. WebView 로딩 (A/B 둘 다 지원, 소스 주소만 분기)

V1 WebChatView.OnLoaded 패턴 그대로:

  • DEBUG: ViteDevServer2_frontendnpm run dev 를 띄우고 포트 대기 → http://localhost:<port> navigate. 핫리로드. /api 는 vite 가 백엔드로 프록시(지금 그대로).
  • RELEASE: 2_frontend/dist 를 출력 폴더로 복사 → SetVirtualHostNameToFolderMapping("appassets.example", wwwroot, ...)https://appassets.example/index.html navigate.
  • UserDataFolder 는 temp 로 명시(초기화 실패 방지), 초기화/네비 로그를 temp 파일에 남김 — V1 패턴 유지.

주의: 2_frontend 는 BrowserRouter 라 RELEASE 가상호스트에서 딥링크 새로고침 시 404 가능 → 필요하면 그때 HashRouter 전환이나 fallback 처리. v1(DEBUG 중심)에선 문제 없음.

7. 알려진 미결(다음 단계) — RELEASE /api 프록시

  • DEBUG(vite): vite 가 /api 프록시 → 지금 인증/쿠키 그대로 동작. v1 은 여기까지 완전히 굴러감.
  • RELEASE(가상호스트): React 가 /api 부르면 가상호스트 폴더에서 찾다 404. 닷넷이 /api 를 백엔드로 프록시해줘야 함(WebResourceRequested 가로채기 등). 근데 이게 쿠키 origin·Entra 로그인과 엮여서 이번에 제외한 auth 스코프와 겹침.
  • 결정: v1 = DEBUG 모드로 런처 껍데기 완성·검증(단축키·트레이·창관리·React 로딩 확인). RELEASE 패키징의 /api 프록시 + 쿠키 origin + Entra 로그인은 auth 스코프 되살릴 때 함께 푸는 다음 단계. (V1 Core 참고)

8. 이번 스코프에서 명시적으로 제외

  • Entra/MSAL 로그인 데스크톱 처리 (Q3 제외됨)
  • 런처 전용 신규 UI (C안 — 나중에)
  • 브릿지 paste/hide/resize (다음 단계)
  • RELEASE 패키징의 /api 프록시 (7번, auth 와 함께)

9. 성공 기준 (v1)

  • Ctrl+Alt+Space 로 창이 뜨고/사라진다
  • 트레이 상주 + 우클릭 종료, X 는 숨김
  • 단일 인스턴스 (두 번째 실행이 기존 창 소환)
  • 창 위치·크기가 기억된다
  • 창 안에 2_frontend React 앱이 뜬다 (DEBUG=vite 핫리로드, /api 정상)
  • Shell 이식분 테스트 통과 (WindowPlacement, SingleInstance 등)