왜 데스크톱인가

이미지 편집 도구는 이미 많습니다. Photoshop, GIMP, Figma 등 훌륭한 도구들이 있죠. 하지만 Photoshop은 월 구독료가 부담되고, GIMP는 학습 곡선이 가파르며, 웹 기반 에디터는 파일 크기·오프라인·파일 시스템 접근에서 제약이 큽니다. "구독료 없이, 인터넷 없이도 돌아가는 Photoshop급 에디터"가 Vora의 출발점이었습니다.

특히 AI 기반 인페인팅은 보통 "이미지를 서버에 업로드 → 결과 내려받기" 구조로 구현됩니다. 민감한 작업물을 외부 서버에 올리는 게 찝찝할 때가 있고, 인터넷이 끊기면 아무것도 못 합니다. Big-LaMa를 사용자의 기기에서 로컬로 돌리면서도, 설치 경험은 네이티브 앱처럼 매끄럽게 만드는 것이 가장 큰 기술적 도전이었습니다.

왜 Tauri인가 — Electron이 아니라

웹 기술(React + Canvas)로 쌓아올린 UI를 그대로 데스크톱에 담을 프레임워크가 필요했습니다. 유력 후보는 ElectronTauri였고, 최종적으로 Tauri를 선택했습니다.

이유는 세 가지입니다. 첫째, 번들 크기 — Tauri는 각 OS 네이티브 웹뷰를 사용해 설치 파일이 훨씬 작습니다. Electron이 Chromium 전체를 번들하는 반면, Tauri는 macOS의 WKWebView, Windows의 WebView2, Linux의 WebKitGTK를 그대로 씁니다. 둘째, 네이티브 셸이 Rust라서 파일 시스템·IPC·크로스플랫폼 빌드를 타입 안전하게 처리할 수 있습니다. 셋째, 번들러가 OS별 네이티브 인스톨러를 직접 생성해줍니다(.dmg / .msi / .deb / .rpm). 저는 GitHub Actions 매트릭스만 짜면 끝이었습니다.

로컬에서 Big-LaMa 돌리기

Big-LaMa는 이미지에서 원하지 않는 객체를 자연스럽게 지우는 인페인팅 모델입니다. 원본은 PyTorch 기반으로 GPU 서버를 전제로 설계되어 있습니다. 이걸 사용자의 맥북/노트북에서 돌리기 위해 두 가지 핵심 결정을 내렸습니다.

첫째, 브라우저 안에서 돌리지 않는다. ONNX Runtime Web + WebAssembly도 초반에 시도했지만, 512×512를 넘는 이미지에서 메모리와 지연이 실용 수준을 벗어났습니다. 데스크톱 앱이라는 선택지를 잡는 순간, 앱이 직접 Python 프로세스를 띄우고 PyTorch로 추론하는 쪽이 훨씬 건전해집니다. Tauri 셸이 Python 워커를 자식 프로세스로 spawn하고, 웹뷰 ↔ Rust ↔ Python 사이를 IPC로 묶었습니다.

둘째, 모델과 Python을 인스톨러에 넣지 않는다. Big-LaMa 가중치와 PyTorch를 번들하면 인스톨러가 수 GB로 불어납니다. 대부분의 사용자는 첫 실행 전에는 그 용량이 필요하지도 않습니다. 그래서 지연 설치 방식을 택했습니다 — 인스톨러는 가벼운 Python 런타임만 들고 오고, 사용자가 처음 AI 기능을 쓰는 순간 앱이 자기 디렉토리 안에 필요한 패키지와 모델을 자동 설치합니다. 관리자 권한이 필요 없고, 앱을 삭제하면 그 디렉토리와 함께 깨끗이 지워집니다.

레이어 시스템 설계

Photoshop의 핵심은 레이어입니다. Vora의 레이어 시스템은 React 상태 관리 + HTML5 Canvas로 구현했습니다. 이미지·텍스트·도형·드로잉·조정 레이어·그룹을 하나의 통합 패널에서 다루도록 설계했고, 최종 합성은 오프스크린 Canvas에서 수행합니다.

구현한 레이어 기능들:

가장 어려웠던 부분은 실시간 렌더링 성능입니다. 10개 이상의 레이어가 있을 때 매 프레임마다 전체를 재합성하면 프레임 드롭이 발생합니다. 이를 해결하기 위해 불변 자료구조 + 변경 감지 기반 증분 렌더링을 도입했습니다. 변경된 레이어만 재렌더링하고 나머지는 캐시된 결과를 사용하는 방식으로, 큰 이미지에서도 반응성을 유지할 수 있었습니다.

18개 편집 도구 만들기

브러시, 지우개 같은 기본 도구부터 클론 스탬프, 힐링 브러시, 히스토리 브러시, 베지어 패스까지 — 각 도구마다 고유한 기술적 도전이 있었습니다. 단축키는 Photoshop 호환을 목표로 했습니다 — `[` / `]`로 브러시 크기, `D`로 기본 색상, `X`로 전경/배경 색상 스왑, `Ctrl+T`로 자유 변형, `Ctrl+J`로 레이어 복제, `Ctrl+E`로 병합. 기존 Photoshop 사용자가 손 근육을 다시 훈련시키지 않아도 되도록.

클론 스탬프는 원본 영역의 픽셀을 실시간으로 복제해야 합니다. Canvas의 getImageDataputImageData를 사용하되, 대량의 픽셀 연산은 데스크톱 앱의 이점을 활용해 필요 시 Python 워커로 넘깁니다. 메인 스레드 부하를 낮춰 대형 이미지에서도 60fps를 유지합니다.

베지어 패스 도구는 SVG의 Path 데이터를 Canvas로 변환하는 과정이 핵심입니다. 사용자가 앵커 포인트와 컨트롤 포인트를 직접 조작할 수 있도록 인터랙션 레이어를 별도로 구현했고, 패스를 선택 영역이나 마스크로 변환하는 기능까지 지원합니다.

.vora 프로젝트 파일과 멀티 포맷 내보내기

데스크톱 앱이라는 선택의 가장 큰 보상은 파일 시스템 자유도입니다. 모든 페이지·레이어·마스크·조정 레이어·효과를 단일 JSON 프로젝트 파일(`.vora`)에 담고, `Ctrl+S` / `Ctrl+Shift+S`로 일반 앱처럼 저장·다른 이름 저장할 수 있습니다. 파일 하나로 전체 작업물을 백업하거나 다른 기기로 옮길 수 있고, 자동 저장도 앱이 직접 로컬 디스크에 씁니다.

내보내기는 PNG/JPG/WebP 외에도 멀티페이지 PDF편집 가능한 텍스트가 포함된 PPTX를 지원합니다. 특히 PPTX는 단순한 래스터 내보내기가 아니라 텍스트 레이어를 PPT 텍스트 박스로 변환하기 때문에, 프레젠테이션에 그대로 붙여 계속 편집할 수 있습니다. 이 기능은 Python 쪽 python-pptx로 구현했는데, 앱 안에서 Python 워커를 돌리는 구조 덕분에 자연스럽게 끼워 넣을 수 있었습니다.

3개 OS × 2개 아키텍처를 하나의 릴리스로

macOS(Intel + Apple Silicon), Windows x64, Linux(.deb / .rpm)를 하나의 코드베이스로 내보내야 했습니다. 서명·노타라이제이션·인스톨러 포맷이 OS마다 다르고, Python 번들 경로도 플랫폼별로 달라 엣지 케이스가 끝없이 튀어나왔습니다.

해결책은 "책임 분리"였습니다. Tauri 번들러가 OS별 네이티브 인스톨러를 직접 만들도록 맡기고(`Vora_x.x.x_universal.dmg`, `Vora_x.x.x_x64-setup.exe`, `.deb`, `.rpm`), GitHub Actions 매트릭스 빌드로 릴리스 태그가 달릴 때마다 네 가지 인스톨러를 자동 생성합니다. Python 번들 경로는 OS별 헬퍼 모듈로 추상화해 코드 내부에서는 하나의 API만 보이도록 했고, 자동 업데이트는 Tauri updater로 일원화했습니다.

배운 것들

"할 수 있을까?"라는 질문에 답하는 가장 좋은 방법은 직접 만들어보는 것이다.

Vora를 만들면서 가장 크게 느낀 것은 "웹 UI + 네이티브 셸 + 로컬 AI" 스택의 힘입니다. React로 UI를 쌓고, Rust(Tauri)로 OS 경계를 다루고, Python으로 AI를 돌리는 세 레이어가 서로의 약점을 보완합니다. Electron보다 가볍게 배포할 수 있고, 네이티브보다 UI 반복이 빠르며, 순수 웹보다 AI·파일 시스템·오프라인을 제대로 다룰 수 있습니다.

기술적으로는 "완벽하게 만들고 출시"보다 "동작하게 만들고 개선"하는 접근이 더 효과적이었습니다. 처음부터 18개 도구를 계획한 게 아니라, 핵심(브러시 + 레이어 + 저장)을 먼저 만들고 점진적으로 확장했습니다. 각 도구를 추가할 때마다 기존 아키텍처의 한계를 발견하고 리팩토링하는 과정이 반복되었지만, 그 과정 자체가 더 나은 설계를 만드는 원동력이었습니다.

Vora는 현재 GitHub에서 오픈소스로 공개되어 있고, vora.xiu.kr에서 다운로드할 수 있습니다. 설치 후 인터넷 없이도 돌아가는 Photoshop급 에디터를 직접 만들 수 있다는 걸 증명하는 프로젝트로, 앞으로도 계속 발전시켜 나갈 계획입니다.