PolyUI 구축하기: Tauri와 Rust를 이용한 로컬 우선 (local-first) AI 채팅 클라이언트
요약
Tauri와 Rust를 사용하여 구축한 로컬 우선(local-first) AI 채팅 클라이언트인 PolyUI의 아키텍처를 소개합니다. 복잡한 환경 설정 없이 단일 설치 프로그램으로 Ollama 및 OpenAI 호환 API를 사용할 수 있는 구조를 다룹니다.
핵심 포인트
- Tauri v2, React, Rust, SQLite를 활용한 데스크톱 앱 스택
- Zustand 스토어와 코디네이터 모듈을 통한 평탄한 의존성 관리
- 트레이트 기반 설계를 통한 스트리밍 및 영속성 로직의 단위 테스트 용이성
- 멀티 모델 병렬 스트리밍 및 로컬 데이터 보존 기능 지원
저는 지난 몇 달 동안 Ollama를 통해 로컬 LLM(Large Language Model)과 채팅할 수 있는 데스크톱 앱인 PolyUI를 구축하는 데 시간을 보냈습니다 (OpenAI 호환 API와도 작동합니다). 이 포스트는 PolyUI가 어떻게 구성되었는지와 제가 왜 그러한 선택을 했는지에 대한 빠른 투어입니다.
왜 또 다른 채팅 UI인가?
대부분의 로컬 LLM 프론트엔드는 Docker, Python 환경, 또는 둘 다에 의존합니다. 이는 많은 개발자에게는 괜찮지만, 단순히 모델을 로컬에서 실행해보고 싶은 기술적 숙련도가 낮은 친구에게 전달하기에는 마찰(friction)이 너무 큽니다. PolyUI는 단일 설치 프로그램입니다. 컨테이너도, venv(가상 환경)도 필요 없는, 그저 하나의 앱일 뿐입니다.
스택 (Stack)
PolyUI는 Tauri v2 앱입니다. React/TypeScript 프론트엔드와 Rust 백엔드가 결합되어 있으며, SQLite(tauri-plugin-sql을 통해)를 영속성 계층(persistence layer)으로 사용합니다.
프론트엔드 구조:
- 도메인당 하나씩 구성된 Zustand 스토어 (인증(auth), 채팅(chat), 모델(models), 설정(settings), 폴더(folders), 테마(theme)). 스토어들은 서로 직접 임포트하지 않습니다. 스토어 간의 효과(cross-store effects)는 코디네이터 모듈을 통해 전달되므로 의존성 그래프가 평탄하게 유지되고 테스트가 가능합니다.
- 독립적인 기능 모듈 (채팅, 인증, 사이드바, 모델, Ollama, 설정, 메모리, 프로바이더, 폴더, 웹 검색 등).
- UI가 없는 lib 계층: Tauri 구현체가 포함된 EventBus 인터페이스, requestAnimationFrame을 통해 스트리밍되는 텍스트를 배치(batch) 처리하는 순수 토큰 누적기(token-accumulator), 그리고 SQLite 및 인메모리(in-memory) 구현체를 모두 갖춘 대화용 레포지토리(repository) 인터페이스.
백엔드 구조:
- 얇은 Tauri 커맨드 어댑터(command adapters)를 사용하며, 실제 로직은 전용 모듈에 존재합니다. 완료될 때까지 스트리밍하는 도구 호출(tool-calling) 루프, 실제 Tauri 구현체와 테스트 스파이(test-spy) 구현체를 가진 StreamEmitter 트레이트(trait), 그리고 인증, 메모리, 프로바이더 및 모델 관리를 위한 별도의 모듈들로 구성됩니다.
데이터 흐름 (Data flow)
테스트 (Testing)
트레이트 기반의 이음매(trait-based seams, StreamEmitter 및 레포지토리 인터페이스) 덕분에 실제 Tauri 창을 띄우지 않고도 스트리밍 및 영속성 로직을 단위 테스트(unit test)할 수 있습니다. 테스트 스파이나 인메모리 레포지토리로 교체하여 순수 로직에 대해 검증(assert)할 수 있습니다.
현재 기능
- 실시간 병렬 스트리밍 (side-by-side streaming)을 지원하는 멀티 모델 (Multi-model) 대화
- 완전한 Markdown + LaTeX (KaTeX) 렌더링
- 앱 내부에서 Ollama 모델 설치
- 디스크에 전혀 저장되지 않는 임시 채팅을 위한 게스트 모드 (Guest mode)
- 몇 가지 내장된 시스템 프롬프트 (system-prompt) 페르소나 또는 사용자 정의 페르소나
- 모든 데이터는 기기 내에 유지되며, 명시적인 동작 없이는 아무것도 기기를 떠나지 않음
이 프로젝트는 MIT 라이선스를 따릅니다. 저장소는 여기 있습니다: https://github.com/monolabsdev/poly-ui — 스타(stars), 이슈(issues), 그리고 풀 리퀘스트(PRs) 모두 환영하며, 만약 다른 분들이 Tauri를 사용하여 유사한 것을 구축하고 있다면 이 아키텍처가 어떻게 버텨내는지 의견을 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기