18 KiB
Windows 데스크톱 클라이언트 기술/아키텍처 결정서
문서 목적
이 문서는 Windows PC 기준으로 먼저 개발하는 메신저 데스크톱 앱의 기술 스택과 애플리케이션 구조를 결정하기 위한 기준 문서다. 목표는 "카카오톡 PC에서 기대하는 속도, 상시 실행성, 익숙한 생산성"을 유지하면서도 더 세련된 UI와 안정적인 확장 구조를 확보하는 것이다.
범위는 데스크톱 클라이언트에 한정한다. 서버 프로토콜 상세, 운영 인프라, 모바일 클라이언트는 별도 문서에서 다룬다.
최종 권고안
Windows 1차 출시 기준 권고 조합은 아래와 같다.
- UI 프레임워크:
WinUI 3 + Windows App SDK - 언어/런타임:
C# + .NET 8 - 아키텍처 패턴:
MVVM + 기능별 모듈 구조(feature-first) - 상태 관리:
CommunityToolkit.Mvvm기반의ViewModel + Domain Store + Repository구조 - 로컬 저장소:
SQLite중심의 오프라인 우선 캐시 - 실시간 통신 추상화:
WebSocket기반 이벤트 스트림 +HTTPS기반 명령/업로드 API - 패키징:
최종 배포는 MSIX 패키지드 앱을 기본으로 하고, 내부 개발 루프에서는 필요 시 unpackaged 디버그 프로필 병행 - 알림:
Windows toast notification + tray resident mode - 자동 업데이트:
MSIX App Installer 업데이트 피드 - 비밀정보 저장:
Windows DPAPI/PasswordVault 계열 저장소
핵심 판단은 단순하다. 이 프로젝트는 처음부터 Windows에서 가장 자연스럽게 느껴져야 하고, 메신저 특유의 "항상 켜져 있는 앱" 경험이 중요하다. 그 기준에서는 Electron보다 WinUI 3가 유리하고, WPF보다 미래 지향적이며, MAUI/Avalonia보다 Windows 통합 품질이 높다.
WinUI 3 vs 대안 비교
1. WinUI 3
가장 균형이 좋다.
-
장점
-
Windows 11 감성에 가장 잘 맞는 기본 컨트롤, 타이포, 재질, 입력 체계를 바로 활용할 수 있다.
-
알림, 앱 아이덴티티, 윈도우 관리, 테마 대응 같은 Windows 통합이 자연스럽다.
-
C#/.NET 생태계를 그대로 쓰면서 성능, 메모리, 유지보수 측면에서 Electron보다 유리하다.
-
향후 Windows 전용 고급 기능을 붙일 때 우회가 적다.
-
단점
-
WPF보다 생태계와 레퍼런스가 덜 성숙했다.
-
일부 고급 데스크톱 패턴, 특히 tray, title bar 세부 제어, 복잡한 virtualization 튜닝은 Win32 interop 이해가 필요하다.
2. WPF
실용성은 높지만 최종 권고안은 아니다.
-
장점
-
데스크톱 안정성과 서드파티 생태계가 매우 강하다.
-
tray, 윈도우 제어, 업데이트, 커스텀 chrome 같은 데스크톱 전통 과제는 가장 익숙하게 풀 수 있다.
-
단점
-
기본 인상이 오래되었고, "트렌디한 Windows 앱" 감성을 얻으려면 커스텀 비용이 커진다.
-
지금 새로 시작하는 Windows 전용 메신저라면 장기 방향성에서 WinUI 3보다 매력이 약하다.
3. Electron
초기 생산성은 좋지만 이 프로젝트의 최적해는 아니다.
-
장점
-
웹 인력 전환이 쉽고, 풍부한 UI 라이브러리를 바로 쓸 수 있다.
-
크로스플랫폼 확장성이 좋다.
-
단점
-
메신저처럼 항상 켜 두는 앱에서 메모리/배터리/네이티브 감성 측면 손해가 크다.
-
Windows 통합이 "가능"한 수준이지 "자연스러운 기본값"은 아니다.
-
보안 경계 관리가 더 까다롭다.
4. Avalonia
기술적으로 괜찮지만 이번 목표와는 다르다.
-
장점
-
크로스플랫폼을 염두에 둔 C# 선택지 중 가장 실전적이다.
-
단점
-
Windows 퍼스트 제품에서 필요한 OS 통합, 알림, 설치체험, 앱 정체성 측면은 WinUI 3보다 약하다.
5. .NET MAUI
권장하지 않는다.
-
장점
-
모바일과 공유할 수 있는 발판은 있다.
-
단점
-
Windows 데스크톱 완성도가 핵심인 메신저에는 맞지 않는다.
-
데스크톱 UX, 창 관리, 리스트 성능, 윈도우 느낌 모두 전용 데스크톱 프레임워크보다 불리하다.
패키징 선택
최종 결론은 MSIX 패키지드 앱이다.
이유는 아래와 같다.
- Windows 알림과 앱 아이덴티티가 안정적이다.
- 설치/제거가 깔끔하고 잔여 파일 문제가 적다.
- 시작 메뉴, 프로토콜 활성화, 알림 활성화, 권한 모델을 제품답게 다루기 좋다.
- 장기적으로 베타/정식 배포 체계를 운영하기 쉽다.
다만 개발 단계에서는 두 가지 현실을 인정해야 한다.
- 내부 개발 속도만 보면 unpackaged 디버그가 더 편할 수 있다.
- 비공개 테스트 배포에서 인증서/설치 체인이 번거로울 수 있다.
실행 전략은 이렇게 잡는다.
- 개발 초기: 개발기 로컬에서는 빠른 디버그 루프를 우선한다.
- 비공개 알파 이후: 배포 검증은 반드시 MSIX 기준으로 한다.
- 외부 배포 시점: MSIX + App Installer를 공식 릴리스 경로로 고정한다.
별도 exe 인스톨러 + 자체 업데이터 조합은 지금 단계에서는 피한다. 메신저 제품은 설치 체인보다 상시 안정성과 Windows 통합이 더 중요하다.
앱 셸 아키텍처
메신저는 일반 CRUD 앱이 아니다. 창이 곧 제품이다. 셸 설계가 곧 사용자 경험의 절반이다.
권장 구조는 단일 메인 셸 + 필요 시 보조 창 구조다.
-
메인 창
-
좌측 고정 내비게이션
-
채팅 목록
-
선택된 대화 뷰
-
우측 상세 패널은 필요 시 확장
-
보조 창
-
이미지 뷰어
-
파일 전송 상세
-
설정
-
로그인/계정 전환
-
향후 통화 팝업
셸 원칙은 다음과 같다.
- 기본은 단일 창 경험으로 시작한다.
- 대화창을 무제한 분리하는 기능은 초기 버전에 넣지 않는다.
- 창을 여러 개 띄우는 순간 동기화, 포커스, 알림 중복, 메모리 문제가 같이 커진다.
- 대신 "이미지 뷰어", "환경설정", "별도 팝업 업무창" 정도만 보조 창으로 분리한다.
탐색 방식은 페이지 네비게이션보다 상태 전환형 셸이 더 적합하다.
- 채팅 목록과 대화 뷰는 페이지 이동보다 같은 셸 내에서 컨텍스트만 바뀌는 구조가 낫다.
- 좌측 메뉴는
채팅,친구,오픈채널/서버형 공간(향후),설정정도로 제한한다. - 메신저에서 과도한 딥 네비게이션은 UX를 해친다.
상태 관리 전략
권장 모델은 가벼운 MVVM + 도메인 스토어다.
전역 단일 상태 저장소 하나로 모든 것을 넣는 구조는 피한다. 메신저는 실시간 이벤트, 임시 입력 상태, 오프라인 큐, 동기화 상태가 얽혀서 거대 스토어가 금방 망가진다.
추천 계층은 아래와 같다.
- View
- ViewModel
- Feature Store
- Repository
- Transport/API Client
- Local DB/Cache
각 계층의 책임은 다음과 같다.
-
ViewModel
-
화면 단위 상태만 가진다.
-
선택된 채팅, 입력창 포커스, 필터 텍스트, 패널 열림 여부 같은 UI 상태를 담당한다.
-
Feature Store
-
기능 단위의 세션 상태를 가진다.
-
예:
ChatListStore,ConversationStore,PresenceStore,NotificationStore -
서버 이벤트와 로컬 변경을 합쳐 화면에 안정적으로 제공한다.
-
Repository
-
로컬 DB와 네트워크를 조합한다.
-
예: "메시지 전송", "대화방 페이지 로드", "읽음 상태 반영", "첨부 업로드"를 트랜잭션처럼 묶는다.
전역 공유가 필요한 상태는 아래 정도로 제한한다.
- 현재 로그인 세션
- 연결 상태
- unread 총합
- 활성 워크스페이스/계정
- 공통 설정
로컬 캐시와 오프라인 동기화
메신저는 네트워크가 흔들려도 "쓸 수 있어 보이는 느낌"이 중요하다. 그래서 SQLite를 단순 캐시가 아니라 로컬 소스 오브 트루스에 가깝게 써야 한다.
권장 원칙은 아래와 같다.
- 앱 진입 시 채팅 목록은 로컬 DB에서 먼저 그린다.
- 선택한 대화의 최근 메시지도 먼저 로컬에서 즉시 보여 준다.
- 서버 응답은 그 뒤에 덮어쓰거나 이어 붙인다.
- 전송 버튼을 누르면 서버 성공 전에도 로컬에 임시 메시지를 만들어 즉시 표시한다.
- 서버 ack를 받으면 임시 ID를 서버 ID로 치환하고 상태를 정정한다.
로컬 DB에 최소한 있어야 하는 도메인은 아래와 같다.
- 계정/세션 메타데이터
- 친구/프로필
- 채팅방 목록
- 채팅방 멤버십
- 메시지 본문
- 메시지 전송 상태
- 읽음 상태
- 첨부파일 메타데이터
- 업로드/다운로드 작업 큐
- 임시 저장 중인 draft
- 사용자 설정
동기화는 cursor 기반 점진 동기화를 전제로 설계한다.
- 채팅 목록용 cursor
- 대화방별 메시지 cursor
- 읽음/반응/멤버 변경용 증분 이벤트
오프라인 큐에 들어갈 항목은 제한한다.
- 텍스트 전송
- 읽음 상태 보고
- 반응 추가/취소
- 첨부 업로드 예약
계정 설정 변경이나 대규모 프로필 변경은 온라인 상태에서만 처리하는 편이 안전하다.
미디어 처리 전략
초기부터 "무거운 클라이언트"가 되지 않도록 범위를 통제해야 한다.
원칙은 서버가 할 수 있는 무거운 일은 서버에서 처리하고, 클라이언트는 표시와 경량 캐시에 집중하는 것이다.
권장 구성은 아래와 같다.
-
이미지
-
원본과 썸네일을 분리 관리한다.
-
리스트에서는 항상 썸네일/축소본만 사용한다.
-
전체 보기 시에만 고해상도 리소스를 읽는다.
-
동영상
-
채팅 리스트/대화창에는 포스터 프레임 중심으로 표시한다.
-
초기 버전에서는 자동 재생을 금지한다.
-
인라인 편집/트랜스코딩은 넣지 않는다.
-
파일
-
다운로드 전에는 메타데이터만 유지한다.
-
파일 열기는 OS 기본 연결 프로그램에 위임한다.
-
악성 확장자/이중 확장자 표시는 UI에서 명확히 한다.
-
음성/녹음
-
초기 MVP에는 제외하는 편이 낫다.
-
넣더라도 녹음, 재생, 업로드를 별도 기능군으로 격리한다.
미디어 캐시는 계층화한다.
- 메모리 캐시: 현재 화면에 보이는 이미지
- 디스크 캐시: 최근 본 이미지/썸네일
- 영구 보관: 사용자가 저장한 다운로드 파일만
클라이언트에서 FFmpeg 같은 대형 의존성을 너무 일찍 넣지 않는다. 초기에는 서버 썸네일 생성 + 클라이언트 표시만으로 충분하다.
알림 전략
권장 조합은 toast notification + 앱 내부 배너 + tray unread 표시다.
필수 시나리오는 아래와 같다.
- 앱이 최소화되어 있거나 뒤에 있을 때 새 메시지 toast
- 앱이 포그라운드일 때는 toast 대신 인앱 배너 또는 해당 대화 강조
- 알림 클릭 시 해당 대화로 정확히 이동
- 중복 toast 방지
- mute된 방, 현재 보고 있는 방, 조용한 시간대 예외 처리
메신저는 "창 닫기 = 종료"보다 "창 닫기 = tray 상주"가 더 자연스럽다.
그래서 초기 설계부터 아래를 반영한다.
- 우상단 X 클릭 시 기본 동작을
tray 최소화로 둘지 정책 결정 - 완전 종료는 tray 메뉴 또는 설정에서 명시적으로 실행
- 1회성 안내를 통해 사용자가 동작을 오해하지 않게 한다
tray 기능은 보조 요소가 아니라 핵심 상시성 UX다.
자동 업데이트
정식 경로는 MSIX App Installer 업데이트 피드가 가장 낫다.
추천 이유는 아래와 같다.
- Windows 친화적인 업데이트 흐름을 유지할 수 있다.
- 설치 파일과 업데이트 정책이 한 체계 안에 있다.
- 알파, 베타, 스테이블 채널을 분리하기 좋다.
운영 원칙은 아래처럼 잡는다.
dev,beta,stable세 채널 분리- 앱 시작 시 무조건 업데이트하지 않고, 유휴 상태나 재시작 시점 반영
- 강제 업데이트는 인증/프로토콜 호환성 깨질 때만 사용
업데이터를 별도 커스텀 프로세스로 처음부터 만들지 않는다. 메신저 본체가 안정화되기 전까지는 관리 포인트만 늘어난다.
보안 경계
메신저 데스크톱 앱은 생각보다 공격면이 넓다. 첨부파일, 링크 프리뷰, 알림 활성화 인자, 로컬 캐시, 토큰 저장이 다 경계다.
반드시 지킬 기준은 아래와 같다.
- 인증 토큰은 평문 파일로 저장하지 않는다.
- refresh token이나 세션 비밀값은 OS 보호 저장소에 넣는다.
- 로컬 SQLite에는 필요한 최소 데이터만 저장한다.
- 서버에서 온 HTML/리치 콘텐츠를 앱 내부에서 임의 렌더링하지 않는다.
- 첨부파일은 "열기"와 "미리보기"의 경계를 분리한다.
- 로그에는 메시지 본문, 토큰, 첨부 URL 원문을 남기지 않는다.
- 크래시 리포트에도 PII 최소화 규칙을 둔다.
프로세스 경계는 초기에 단순하게 가져간다.
- 기본은 단일 앱 프로세스
- 정말 필요할 때만 보조 프로세스 분리
보조 프로세스를 고려할 만한 경우는 아래다.
- 대용량 미디어 전처리
- 별도 업데이트/복구 헬퍼
- 향후 화면 공유/통화 같은 고권한 기능
초기 MVP에서 브라우저 엔진 기반 렌더링, 스크립트 실행형 플러그인, 임의 확장 기능은 넣지 않는다.
IPC와 백그라운드 작업
초기 구조에서 필요한 IPC는 제한적이다.
- 단일 인스턴스 유지
- 두 번째 실행 요청이 오면 기존 인스턴스로 포커스 이동
- 프로토콜 링크 또는 알림 활성화 인자를 기존 인스턴스로 전달
이 용도에는 로컬 named pipe 수준이면 충분하다.
백그라운드 작업은 "모바일 같은 진짜 background task"보다 tray 상주 앱 개념으로 접근하는 것이 맞다.
즉, 메신저 수신과 동기화는 아래를 기본 전제로 삼는다.
- 사용자가 로그아웃하지 않은 이상 앱은 백그라운드에서 살아 있다
- 창만 닫혀도 프로세스는 유지된다
- 연결이 끊기면 지수 백오프로 재연결한다
Windows의 백그라운드 태스크 모델을 과용하지 않는다. 데스크톱 메신저에서는 주 실행 프로세스 상주가 더 단순하고 신뢰성이 높다.
권장 폴더 구조
프로젝트는 레이어만 예쁘게 나눈 구조보다 기능 단위로 이해되는 구조가 유지보수에 낫다.
권장 예시는 아래와 같다.
-
src/App -
앱 진입점, 부트스트랩, 셸, 테마, 리소스, 공통 네비게이션
-
src/Features/Auth -
로그인, 세션 복구, 계정 전환
-
src/Features/ChatList -
채팅 목록, unread, 고정, 검색 진입
-
src/Features/Conversation -
메시지 목록, 입력창, 전송, 읽음 상태, 반응
-
src/Features/Contacts -
친구 목록, 프로필, 차단/숨김
-
src/Features/Notifications -
toast, 배너, 알림 정책
-
src/Features/Settings -
일반, 알림, 파일, 계정, 실험 기능
-
src/Core -
공용 도메인 모델, 인터페이스, 결과 타입, 에러 규약
-
src/Infrastructure/Api -
HTTP/WebSocket 클라이언트, DTO, 직렬화
-
src/Infrastructure/Persistence -
SQLite, 마이그레이션, 리포지토리 구현
-
src/Infrastructure/Media -
썸네일, 다운로드, 임시 파일, 프리뷰
-
src/Infrastructure/Security -
토큰 저장, 암호화, 민감정보 정책
-
src/Infrastructure/Platform -
tray, 프로토콜 활성화, 파일 연결, OS 통합
-
tests/Unit -
ViewModel, Store, 도메인 규칙
-
tests/Integration -
로컬 DB, 동기화, 업로드/다운로드, reconnect 시나리오
핵심은 Features와 Infrastructure를 명확히 분리하는 것이다. 채팅 기능 코드가 SQLite 세부 구현이나 WebSocket payload 형태를 직접 알게 만들면 나중에 반드시 꼬인다.
단계별 구현 전략
Phase 0. 제품 골격 검증
목표는 "이 구조로 메신저 느낌이 나는가"만 확인하는 것이다.
- 로그인 화면
- 메인 셸
- 채팅 목록 mock
- 대화창 mock
- 다크/라이트 테마
- 창 최소화, 리사이즈, 기본 반응형
이 단계에서는 서버 연결보다 셸 품질을 먼저 본다.
Phase 1. 핵심 메시징 MVP
실사용 가능한 최소 메신저를 만든다.
- 계정 로그인/세션 복구
- 채팅 목록 실데이터
- 1:1 채팅
- 텍스트 송수신
- 읽음 상태
- reconnect
- 로컬 캐시 부팅
여기서 가장 중요한 성공 조건은 "앱 재실행 후 바로 이전 대화가 보여야 한다"다.
Phase 2. 데스크톱다운 완성도 확보
카카오톡 PC 대체감을 만드는 구간이다.
- tray 상주
- toast 알림
- 파일 첨부/다운로드
- 전송 실패 재시도
- draft 저장
- 검색 진입
- 채팅방 정렬/고정
이 단계에서 제품 체감 품질이 크게 올라간다.
Phase 3. 성능과 운영성 강화
- 대화방 수천 개, 메시지 수만 건에서도 부드러운 스크롤
- 이미지 캐시 최적화
- 크래시 리포트
- 진단 로그
- 업데이트 채널 운영
- 설정 백업/복원 범위 정의
이 단계 없이 사용자 수를 늘리면 운영이 무너질 가능성이 높다.
Phase 4. 고급 기능 확장
이 단계는 코어가 안정화된 뒤에만 들어간다.
- 멀티 계정
- 그룹 채팅 고급 기능
- 메시지 반응
- 파일 히스토리
- 통화/화면공유
- 관리자/조직 기능
초기부터 이 기능을 욕심내면 아키텍처는 커 보이는데 제품은 느리고 불안정해진다.
반드시 미루는 항목
초기 버전에서 제외하는 편이 좋은 항목들이다.
- 플러그인 시스템
- 내장 브라우저 기반 리치 콘텐츠 렌더링
- 과한 애니메이션
- 멀티 윈도우 대화창 자유 분리
- 클라이언트 측 대형 미디어 트랜스코딩
- 암호화 메신저 수준의 종단간암호화 복잡도
이런 요소는 제품 핵심이 아니라 복잡도 폭탄이 되기 쉽다.
최종 판단
이 프로젝트의 최적 출발점은 WinUI 3 + .NET 8 + MVVM + SQLite 기반 오프라인 우선 구조 + MSIX 배포다.
이 조합은 세련된 Windows UX, 메신저에 필요한 상시성, 실시간성, 설치 품질, 장기 유지보수의 균형이 가장 좋다.
정리하면 아래 네 가지를 흔들지 않는 것이 중요하다.
- Windows 전용 1차 제품답게 네이티브 경험을 우선한다.
- 로컬 캐시는 옵션이 아니라 제품 핵심으로 본다.
- tray, 알림, 재연결은 부가 기능이 아니라 메신저의 본체로 취급한다.
- 초기에 단일 창과 단순 프로세스 구조를 유지해 제품을 먼저 안정화한다.