공개: KoTalk 최신 기준선

This commit is contained in:
Ian 2026-04-16 09:24:26 +09:00
commit debf62f76e
572 changed files with 41689 additions and 0 deletions

11
docs/README.md Normal file
View file

@ -0,0 +1,11 @@
# Docs Index
`docs/`는 공개 저장소 표면을 보조하는 문서 루트입니다. 제품 마스터 플랜 자체는 `문서/`를 기준으로 유지하고, `docs/`는 공개 자산, 저장소 운영 규칙, 보관용 초안을 맡습니다.
## Included Areas
- [assets/](assets/): README, Showcase, 최신 스크린샷, 공개 시각 자산
- [archive/](archive/): 초기 기획 초안과 구조 탐색 문서
- [repository-surfaces.md](repository-surfaces.md): 내부 기준선, 공개 큐레이션 브랜치, 비공개 워크스페이스 규칙
현재 기준의 최종 기획 본문과 UX 아틀라스는 [../문서/README.md](../문서/README.md)를 우선합니다.

View file

@ -0,0 +1,217 @@
# 01. 제품 전략 및 정의
## 합의 관점
이 문서는 아래 6개 전문 관점의 공통 합의안으로 정리한다.
- 제품 전략: 출시 가능성과 포지셔닝을 우선한다.
- UX 설계: 익숙함은 유지하되 더 정돈된 정보 밀도와 반응성을 만든다.
- Windows 데스크톱: 키보드 중심 흐름과 멀티패널 효율을 핵심으로 본다.
- 채팅 플랫폼: 실시간성보다 "안정적 전달, 빠른 재진입, 파일 전송 신뢰성"을 우선한다.
- 브랜드/법무: 카카오톡과 혼동될 수 있는 요소는 배제한다.
- 수익/성장: 소규모 개인 프로젝트로도 운영 가능한 구조를 택한다.
## 제품 비전
- 목표는 "Windows에서 가장 빠르고 정돈된 개인용 메신저"를 만드는 것이다.
- 출발점은 카카오톡 PC의 익숙한 사용 패턴이지만, 최종 제품은 더 세련된 데스크톱 경험과 개인 생산성 보조 기능을 갖춘 독립 서비스여야 한다.
- 핵심 가치 제안:
- 빠른 대화 접근: 실행, 로그인, 방 전환, 검색이 가볍고 즉각적이어야 한다.
- 높은 작업 효율: 키보드 단축키, 멀티패널, 파일/링크/이미지 관리가 우수해야 한다.
- 개인 사이드 프로젝트다운 완성도: 과한 범용 플랫폼이 아니라 "한 명이 끝까지 유지 가능한 범위"에서 품질을 높인다.
## 타깃 사용자
- 1차 타깃: Windows PC를 오래 켜두고 일하거나 공부하는 20~40대 개인 사용자
- 1차 타깃 특성:
- 모바일보다 PC에서 대화와 파일 공유를 더 많이 처리한다.
- 오픈채팅/커뮤니티보다 소수 관계, 개인 네트워크, 협업성 대화에 가치를 둔다.
- 메신저 자체보다 "업무/일상 컨텍스트 전환 비용"을 줄이고 싶어 한다.
- 2차 타깃: 개인 프로젝트 팀, 지인 소모임, 스터디 그룹, 소규모 프리랜서 협업 그룹
## 핵심 JTBD
- "PC에서 일하는 동안, 휴대폰을 들지 않고도 대화를 빠르게 처리하고 싶다."
- "자주 대화하는 사람, 중요한 파일, 링크, 공지를 메신저 안에서 쉽게 다시 찾고 싶다."
- "메신저가 가볍고 안정적이어서 계속 켜 두어도 부담이 없었으면 좋겠다."
- "읽음 상태, 미확인 메시지, 첨부파일, 핀 고정 정보를 한눈에 관리하고 싶다."
- "사적인 메신저라도 디자인이 투박하지 않고, 데스크톱 앱다운 완성도가 있었으면 좋겠다."
## MVP 범위
MVP는 "사람들이 실제로 매일 켜 둘 수 있는 1:1 및 소규모 그룹 메신저"에 집중한다.
- 필수 계정 기능:
- 이메일 또는 휴대폰 기반 가입/로그인
- Windows 디바이스 기준 세션 유지
- 프로필 이미지, 상태 메시지, 표시 이름
- 필수 대화 기능:
- 1:1 채팅
- 그룹 채팅
- 텍스트, 이모지, 이미지, 파일 전송
- 읽음 상태, 전송 실패 재시도, 날짜 구분선, 미확인 배지
- 대화방 고정, 알림 음소거, 검색
- 필수 데스크톱 UX:
- 좌측 채팅 리스트 + 중앙 대화 패널 + 선택형 우측 정보 패널
- 글로벌 검색
- 드래그 앤 드롭 파일 전송
- 단축키 중심 이동
- 시스템 트레이 상주, 알림센터 연동
- 필수 운영 기능:
- 신고/차단
- 관리자용 기본 운영 콘솔
- 장애/로그 모니터링
## MVP에서 제외할 것
- 음성/영상 통화
- 공개 커뮤니티, 대형 오픈채팅
- 스토리, 피드, 쇼츠형 콘텐츠
- 결제, 송금, 선물하기
- AI 비서 전면 탑재
- 과한 커스터마이징 테마 마켓
- 멀티플랫폼 동시 최적화
제외 이유:
- 개인 사이드 프로젝트가 첫 출시까지 가는 데 가장 큰 장애물은 범위 과잉이다.
- 통화/결제/콘텐츠 기능은 법률, 인프라, 운영 부담이 급증한다.
## 기능 우선순위
### P0
- 가입/로그인
- 친구 또는 사용자 검색
- 1:1 채팅
- 그룹 채팅
- 메시지 영속 저장
- 이미지/파일 전송
- 읽음 상태
- 푸시/데스크톱 알림
- 채팅 리스트 정렬과 미확인 배지
- 대화 검색
### P1
- 답장, 전달, 메시지 고정
- 링크/파일/미디어 모아보기
- 멀티 디바이스 로그인 관리
- 차단/신고
- 관리 도구와 운영 로그
### P2
- 예약 전송
- 나에게 보내기
- 읽지 않은 대화 일괄 정리
- 생산성형 미니 기능: 체크리스트, 빠른 메모, 링크 컬렉션
## 단계별 출시 플랜
### Phase 0. 정의 및 검증
- 기준 제품 경험을 분해한다.
- "왜 카카오톡 PC가 편한가"를 기능 복제가 아니라 흐름 단위로 정리한다.
- 5~10명의 잠재 사용자 인터뷰로 Windows 메신저 사용 패턴을 검증한다.
- 브랜드 방향, 시각 언어, 법적 가드레일을 먼저 확정한다.
### Phase 1. Private Alpha
- 본인 + 지인 소수 그룹이 실제로 쓰는 수준까지 구현한다.
- 안정성과 기본 채팅 품질에만 집중한다.
- 서버는 본 VPS에 단일 리전으로 구축하되, 백업과 로그는 반드시 분리한다.
- 성공 기준은 "매일 열어두는가"와 "메시지 손실이 없는가"다.
### Phase 2. Closed Beta
- 50~200명 수준으로 확장한다.
- 검색, 파일, 알림, 운영 도구를 강화한다.
- 설치/업데이트 경험과 계정 복구 흐름을 다듬는다.
- 이 시점부터 지표 기반 개선 루프를 시작한다.
### Phase 3. Public Launch
- 브랜딩과 온보딩을 정식화한다.
- 유료 옵션이 있다면 이 단계에서만 소프트 런칭한다.
- Windows 앱 완성도를 우선 홍보 포인트로 삼는다.
## 차별화 방향
- "카카오톡 대체재"보다 "Windows에서 더 잘 맞는 메신저"로 포지셔닝한다.
- 차별화 포인트:
- 정돈된 데스크톱 레이아웃과 높은 정보 밀도
- 키보드 우선 UX와 빠른 탐색
- 링크/파일/이미지 재발견 경험 강화
- 가볍고 신뢰 가능한 앱 성능
- 개인 사용자와 소규모 그룹에 맞는 프라이빗한 톤
- 피해야 할 차별화:
- 기능 수를 늘려 거대 플랫폼처럼 보이게 하는 것
- AI, 커뮤니티, 콘텐츠 허브를 한 번에 붙이는 것
## 수익화 옵션
- 기본 원칙:
- 초기에는 무료가 맞다.
- 수익화는 제품이 일상 사용 습관에 들어온 뒤 붙여야 한다.
- 가능한 옵션:
- Pro 개인 요금제
- 대용량 파일 업로드
- 긴 메시지/미디어 보관 기간
- 향상된 검색과 기록 내보내기
- 테마/레이아웃 커스터마이징
- 소규모 팀 요금제
- 관리자 기능
- 공유 드라이브형 파일 관리
- 간단한 공지/권한 관리
- 추천 방향:
- 첫 유료 기능은 광고가 아니라 "보관/검색/파일 한도" 기반이 가장 현실적이다.
## 브랜드 및 법적 주의사항
- 카카오톡의 이름, 색상 조합, 아이콘 메타포, 말풍선 스타일, 사운드, 카피 문구를 그대로 차용하지 않는다.
- "카카오톡 PC 클론"처럼 보이는 표현은 내부 참고용으로만 쓰고 외부 문서나 마케팅에는 쓰지 않는다.
- 프로토콜 역공학, 기존 네트워크에 붙는 브리지, 상표 혼동 가능성이 있는 UX 복제는 피한다.
- 참고 대상은 "사용 흐름"과 "데스크톱 생산성 원칙"이어야 하며, 결과물은 별도 브랜드 아이덴티티를 가져야 한다.
- 출시 전 최소 점검 항목:
- 서비스명 상표 검색
- 앱 아이콘과 UI 주요 요소의 혼동 가능성 리뷰
- 이용약관/개인정보처리방침 초안
## 성공 지표
### 제품 핵심 지표
- 주간 활성 사용자 비율
- 7일, 30일 리텐션
- DAU/WAU
- 1인당 일평균 세션 수
- 1인당 일평균 메시지 송수신량
### 경험 품질 지표
- 앱 실행 후 첫 화면 표시 시간
- 메시지 전송 성공률
- 이미지/파일 업로드 성공률
- 알림 수신 성공률
- 검색 성공률 또는 검색 후 클릭률
### 사업성 보조 지표
- 초대 전환율
- 그룹 생성 비율
- 1명 이상의 재초대 발생 비율
- 유료 기능 대기자 등록률
## 최종 권고
- 이 프로젝트는 "카카오톡과 비슷한 것"을 목표로 두면 늦어진다.
- 더 좋은 방향은 "Windows에서 쓰기 가장 좋은 개인 메신저"를 만드는 것이다.
- 첫 출시의 기준:
- 매일 켜 두고 쓸 만큼 빠를 것
- 메시지와 파일이 불안하지 않을 것
- 기존 대형 메신저보다 데스크톱 경험이 더 정돈되어 있을 것
이 기준을 지키면 개인 사이드 프로젝트로도 실제 출시 가능한 수준까지 갈 수 있다.

9
docs/archive/README.md Normal file
View file

@ -0,0 +1,9 @@
# Archive Docs
이 디렉터리는 초기에 작성된 기획 초안과 구조 탐색 문서를 보관합니다.
원칙:
- 현재 기준의 최종 기획 본문은 `문서/`를 우선합니다.
- 여기 있는 문서는 참고용 아카이브이며, 현재 제품 기준과 다를 수 있습니다.
- 공개 브랜치에서는 이 경로를 포함하지 않거나 축약할 수 있습니다.

View file

@ -0,0 +1,515 @@
# 개인 메신저 프로젝트 백엔드/플랫폼 아키텍처 합의안
이 문서는 기존 Rocky Linux VPS 위에 Docker로 채팅 서버를 구축하는 것을 전제로 한 구현 지향형 백엔드/플랫폼 설계안이다. 목표는 카카오톡 PC 버전 수준의 빠른 체감 응답성과 안정적인 실시간 동기화 경험을 내는 것이다.
합의에 참여한 전문 관점:
- 리얼타임 백엔드 아키텍트
- 플랫폼 엔지니어
- PostgreSQL 데이터 엔지니어
- SRE/관측성 엔지니어
- 보안 엔지니어
- 스토리지/백업 엔지니어
## 1. 전제와 설계 원칙
전제:
- 서버는 기존 Rocky Linux VPS에 배포한다.
- Docker 사용 가능하다.
- 초기 개발 대상은 Windows PC 클라이언트다.
- 초기는 개인 사이드 프로젝트지만, 구조는 다중 디바이스와 사용자 증가를 막지 않아야 한다.
설계 원칙:
- 초기에는 단일 VPS에 맞는 단순성을 우선한다.
- 그러나 프로토콜, 데이터 모델, 메시지 ID, 스토리지 인터페이스는 이후 수평 확장을 막지 않게 설계한다.
- 채팅 메시지의 원본 저장소는 처음부터 PostgreSQL로 통일한다.
- 실시간 연결과 영속 저장을 분리해 생각한다.
- MinIO는 첨부파일 저장소로는 괜찮지만 백업 저장소로 간주하지 않는다.
- 운영 편의보다 복구 가능성을 우선한다.
## 2. 최종 권장 아키텍처 한 줄 요약
초기 최적안은 `단일 애플리케이션 계열 + PostgreSQL + Redis + MinIO + Caddy` 조합이다.
- 클라이언트 외부 프로토콜은 `HTTPS REST + WSS(WebSocket over TLS)`를 사용한다.
- 서버 애플리케이션은 논리적으로 `API/Realtime/Worker`로 분리하되, 초기에는 같은 코드베이스와 같은 이미지에서 역할만 나눠 배포한다.
- 영속 데이터는 PostgreSQL 하나를 기준축으로 삼는다.
- Redis는 presence, 세션 인덱스, 단기 캐시, fan-out 보조 용도로만 사용하고, 진실의 원천으로 쓰지 않는다.
- 첨부파일과 프로필 이미지는 MinIO에 저장하되 S3 호환 계층으로 추상화해서 이후 외부 오브젝트 스토리지로 쉽게 옮길 수 있게 한다.
## 3. 왜 이 아키텍처가 현재 VPS에 가장 적합한가
이 VPS는 단일 머신이다. 따라서 초기에 Kafka, 다수의 마이크로서비스, 복잡한 서비스 메시를 올리면 운영 부담이 가치보다 커진다. 반대로 단일 앱 컨테이너 하나에 모든 역할을 몰아넣고 Redis 없이 메모리만 사용하면, 나중에 멀티 인스턴스나 재시작 복구, presence 일관성, fan-out 제어에서 다시 뜯어고쳐야 한다.
따라서 가장 균형이 좋은 선택은 아래와 같다.
- 앱은 단순하게 유지한다.
- 상태 저장은 PostgreSQL로 고정한다.
- 연결 상태와 단기 이벤트 전달은 Redis에 맡긴다.
- 첨부파일은 애플리케이션 로컬 디스크가 아니라 S3 호환 저장소로 보낸다.
- 프록시는 Caddy로 단순화한다.
이렇게 하면 MVP에서는 운영 난이도가 낮고, 이후 앱 복제본을 늘릴 때도 방향이 유지된다.
## 4. 외부 프로토콜 권장안
### 4.1 클라이언트 통신
권장:
- 인증, 초기 동기화, 대화방 목록, 과거 메시지 조회, 첨부 업로드 시작/완료: `HTTPS REST`
- 실시간 메시지 수신, 읽음 상태, 타이핑 상태, presence, 서버 이벤트 푸시: `WSS`
선택 이유:
- Windows 데스크톱에서 구현과 디버깅이 쉽다.
- 프록시/Caddy/TLS 환경에서 안정적이다.
- 모바일/웹 클라이언트로 확장해도 동일 패턴을 유지하기 쉽다.
- gRPC streaming보다 도입 장벽이 낮고, 브라우저/데스크톱 지원이 직관적이다.
권장하지 않는 초기 선택:
- 순수 TCP 커스텀 프로토콜: 성능 이점보다 운영/보안/프록시 복잡성이 커진다.
- MQTT: 메신저 도메인 모델과 인증/권한/히스토리 조회가 결국 별도 API를 필요로 하므로 단순해지지 않는다.
- WebRTC data channel 중심 설계: P2P는 NAT, 오프라인 저장, 멀티 디바이스 동기화에 불리하다.
### 4.2 앱 레벨 메시지 프레임
초기부터 다음 개념은 반드시 포함한다.
- `message_id`: ULID 또는 UUIDv7
- `conversation_id`
- `sender_user_id`
- `client_request_id`: 중복 전송 방지용
- `server_sequence`: 대화방 단위 또는 사용자 inbox 단위 순서값
- `sent_at`, `delivered_at`, `read_at`
- `event_version`
- `device_id`
이 필드가 있으면 이후 재전송, 읽음 동기화, 멀티 디바이스, 중복 ACK 처리, 서버 재시작 복구가 쉬워진다.
## 5. 서비스 구성 권장안
초기 컨테이너 구성:
- `messenger-caddy`
외부 80/443 수신, TLS 종료, WebSocket 업그레이드 처리, 정적 헬스 라우팅
- `messenger-app`
REST API와 WebSocket 게이트웨이 역할 수행
- `messenger-worker`
outbox 처리, 알림 발행, 썸네일/미디어 후처리, 정리 배치 작업
- `messenger-postgres`
핵심 영속 데이터 저장소
- `messenger-redis`
presence, session map, short-lived cache, publish/subscribe 보조
- `messenger-minio`
첨부파일, 프로필 이미지, 미디어 저장
- `messenger-backup`
PostgreSQL 백업과 MinIO 메타/버킷 동기화 작업
- `messenger-prometheus`
메트릭 수집
- `messenger-loki`
로그 저장
- `messenger-grafana`
대시보드와 알림 뷰
- `messenger-node-exporter` 또는 유사 호스트 메트릭 수집기
VPS CPU, 메모리, 디스크, 네트워크 관측
중요한 점:
- `messenger-app``messenger-worker`는 같은 코드베이스를 쓰는 것이 좋다.
- 초기에는 마이크로서비스로 쪼개지 않는다.
- 역할만 분리해 컨테이너를 나누면, 장애면을 분리하고 추후 스케일 아웃하기 쉬워진다.
## 6. 데이터 저장 전략
### 6.1 PostgreSQL을 중심으로 두는 이유
채팅 서비스는 결국 다음을 안정적으로 다뤄야 한다.
- 대화방 생성/멤버십
- 메시지 영속화
- 읽음 상태
- 첨부 메타데이터
- 차단/숨김/핀/알림 설정
- 운영 감사 로그
이 영역은 관계형 정합성이 중요하다. PostgreSQL은 트랜잭션, 인덱스, JSONB, 전문 검색, WAL 기반 백업, 추후 복제까지 한 번에 가져갈 수 있다.
초기에는 NoSQL보다 PostgreSQL이 훨씬 안전하다.
### 6.2 핵심 테이블 범주
초기부터 분리해서 설계할 범주:
- 사용자/프로필
- 디바이스/세션
- 친구 관계 또는 연락처 매핑
- 대화방
- 대화방 멤버
- 메시지
- 메시지 첨부 메타
- 메시지 전달 상태
- 읽음 커서
- 리액션
- 차단/뮤트/보관
- 감사 로그
- 아웃박스 이벤트
핵심 원칙:
- 메시지 본문은 PostgreSQL에 저장한다.
- 첨부파일 바이너리는 MinIO에 저장하고 DB에는 메타와 경로만 둔다.
- 읽음 상태는 사용자별 커서 모델을 우선한다. 메시지별 읽음 row를 무분별하게 만들면 커진다.
### 6.3 Redis 역할 제한
Redis는 아래만 맡기는 것이 맞다.
- 온라인 사용자 presence TTL
- 웹소켓 세션 라우팅 인덱스
- 단기 rate limit 카운터
- 최근 조회 캐시
- 멀티 인스턴스 fan-out 보조 pub/sub
Redis를 진실의 원천으로 두면 복구와 디버깅이 어려워진다. 메시지 원본이나 유일한 unread 상태를 Redis에 넣는 것은 피한다.
## 7. 실시간 메시징 처리 흐름 권장안
메시지 전송 처리 흐름:
1. 클라이언트가 `client_request_id` 포함 메시지 전송
2. 서버가 권한과 대화방 멤버십 확인
3. PostgreSQL 트랜잭션에서 메시지 row 저장
4. 같은 트랜잭션에서 outbox 이벤트 저장
5. 커밋 성공 후 송신자에게 서버 확정 ACK 반환
6. worker가 outbox를 읽고 수신자 online 세션으로 fan-out
7. 수신 클라이언트 ACK 수신 후 전달 상태 갱신
8. 읽음 이벤트는 별도 커서 업데이트로 처리
이 구조의 장점:
- DB 커밋 전송 실패와 푸시 실패를 분리할 수 있다.
- 서버가 내려갔다가 올라와도 outbox 기준으로 재처리 가능하다.
- 이후 Redis pub/sub에서 NATS JetStream 같은 것으로 바꿔도 모델이 유지된다.
## 8. 첨부파일/미디어 저장 전략
초기 권장:
- 원본 파일은 MinIO에 저장
- 업로드는 서버에서 발급한 제한적 업로드 정책 또는 서버 경유 업로드 중 하나를 선택
- MVP에서는 서버 경유 업로드가 구현은 쉽지만, 대용량에서 병목이 된다
- 가능하면 초반부터 S3 호환 업로드 패턴으로 설계하는 것이 낫다
권장 메타데이터:
- object_key
- original_filename
- mime_type
- byte_size
- image_width, image_height
- checksum
- upload_status
- virus_scan_status 또는 reserve 필드
확장 포인트:
- MinIO를 나중에 Cloudflare R2, AWS S3, Backblaze B2로 옮기기 쉽게 추상화
- 썸네일과 프리뷰는 worker에서 비동기 생성
## 9. 인증/세션 전략
Windows PC만 먼저 개발하더라도 처음부터 아래를 고려한다.
- 사용자 계정과 디바이스 세션 분리
- `device_id`를 가진 장치별 refresh session 유지
- access token은 짧게, refresh token은 서버 추적 가능하게
- 강제 로그아웃, 특정 디바이스 로그아웃, 세션 폐기 가능해야 함
권장:
- 앱 로그인 후 디바이스 세션 생성
- WebSocket 연결 시 access token 검증
- 세션 폐기 시 WebSocket 강제 끊기
이렇게 해야 추후 모바일 앱을 붙여도 모델을 바꾸지 않는다.
## 10. VPS 배포 전략
### 10.1 배포 방식
권장:
- Docker Compose 기반 단일 프로젝트 스택
- systemd에서 compose 스택을 부팅 시 자동 기동
- 상태 저장 볼륨은 명확히 분리
권장 볼륨 범주:
- PostgreSQL data
- Redis data
- MinIO data
- Grafana data
- Prometheus data
- Loki data
- 앱 업로드 임시 디렉터리
주의:
- 기존 VPS에 다른 서비스가 있다면 네트워크와 도메인을 분리해야 한다.
- 외부 80/443을 이미 다른 Caddy가 쓰고 있다면, 새 메신저 스택은 기존 프록시에 역방향 프록시로 붙이는 방식이 가장 안전하다.
- 메신저 전용 서브도메인을 분리한다. 예: `msg.example.com`, `api.msg.example.com`, `ws.msg.example.com`, `media.msg.example.com`
### 10.2 네트워크/포트
외부 공개는 최소화한다.
- 외부 공개: `80`, `443`, `22`
- 내부 전용: PostgreSQL, Redis, MinIO 관리 포트, Grafana, Prometheus, Loki
원칙:
- DB/Redis/MinIO는 외부에 직접 열지 않는다.
- 관리자 UI가 필요하면 VPN 또는 SSH 터널 기반 접근만 허용한다.
### 10.3 리소스 운영 방침
초기에는 다음 우선순위로 리소스를 배정한다.
1. PostgreSQL 메모리 보장
2. 앱/WebSocket 연결 처리 여유 확보
3. MinIO 디스크 여유 확보
4. 관측성 스택은 과하지 않게 운영
관측 스택이 너무 무거우면 다음 순서로 경량화한다.
- Grafana 유지
- Prometheus retention 축소
- Loki retention 축소
- 고해상도 scrape interval 완화
## 11. 관측성 설계
최소한 아래는 반드시 수집한다.
애플리케이션 메트릭:
- 활성 WebSocket 연결 수
- 인증 성공/실패 수
- 메시지 송신 TPS
- 메시지 저장 지연
- outbox 적체 수
- fan-out 지연
- 읽음 이벤트 처리량
- 업로드 성공/실패 수
- 대화방별 초당 메시지 폭주 탐지
인프라 메트릭:
- CPU
- 메모리
- 디스크 사용량
- 디스크 IOPS
- 네트워크 in/out
- 컨테이너 재시작 횟수
- PostgreSQL connections, replication lag 향후 대비
- Redis memory eviction 여부
로그:
- JSON 구조화 로그
- request_id, user_id, device_id, conversation_id, message_id 등 상관 키 포함
- 인증 실패, 권한 오류, 메시지 저장 실패는 별도 레벨 관리
대시보드 우선순위:
1. 서비스 헬스
2. 메시지 송수신 지연
3. DB 상태
4. 첨부 업로드 상태
5. 에러 로그 상위 유형
## 12. 백업 및 복구 전략
가장 중요한 원칙:
- 같은 VPS 안의 다른 디렉터리에 저장한 사본은 백업이 아니다.
- MinIO에 저장한 DB 덤프도 MinIO가 같은 머신에 있으면 재해 복구가 아니다.
권장 백업 구조:
- PostgreSQL: 일간 전체 백업 + 지속적 WAL 아카이빙
- 백업 저장 위치: 외부 오브젝트 스토리지
- MinIO 버킷 데이터: 주기적 외부 스토리지 동기화
- 설정 파일과 compose 파일: Git 또는 암호화된 설정 저장소 별도 보관
복구 목표:
- RPO 목표: 5분에서 15분
- RTO 목표: 1시간 이내
권장 운영 습관:
- 월 1회 복구 리허설
- 최소 스테이징 환경으로 복원 테스트
- 백업 성공 여부만 보지 말고 실제 복구 성공 여부 확인
## 13. 보안 권장안
초기 사이드 프로젝트라도 아래는 반드시 한다.
- root 비밀번호 SSH 로그인을 끄고 키 기반 인증으로 전환
- fail2ban 또는 동등한 차단 장치 적용
- firewalld/ufw로 외부 포트 최소화
- Docker secrets 또는 최소한 권한 제한된 env 파일 사용
- DB/Redis/MinIO 비밀번호 분리
- TLS 강제
- 관리자 계정 MFA 고려
- 민감 로그 마스킹
애플리케이션 보안 체크포인트:
- rate limiting
- 대화방 멤버십 권한 검사
- 첨부파일 MIME 및 확장자 검증
- 업로드 크기 제한
- 악성 파일 검사 훅 준비
- 세션 폐기와 토큰 로테이션
## 14. 스케일링 경로
### 단계 1. MVP
구성:
- 단일 VPS
- 단일 `messenger-app`
- 단일 `messenger-worker`
- PostgreSQL 단일 인스턴스
- Redis 단일 인스턴스
- MinIO 단일 인스턴스
적합한 상황:
- 개인 프로젝트 초기
- 소수 사용자
- 운영자가 혼자일 때
### 단계 2. 같은 아키텍처로 성능 확장
구성 변화:
- 더 큰 VPS 또는 별도 앱 서버
- `messenger-app` 복수 인스턴스
- Redis 기반 세션 공유와 fan-out
- PostgreSQL 튜닝 및 읽기 부하 분산 준비
필수 전제:
- WebSocket 세션 상태가 로컬 메모리에만 있지 않아야 함
- outbox 기반 비동기 처리 구조가 이미 있어야 함
### 단계 3. 상태 저장 계층 분리
구성 변화:
- PostgreSQL을 관리형 또는 별도 전용 서버로 이동
- MinIO를 외부 오브젝트 스토리지로 이동
- Redis를 별도 인스턴스로 분리
- 앱은 여러 대로 수평 확장
이 단계에서 바뀌면 좋은 것:
- 로드밸런서 도입
- 무중단 배포
- 관측성 경보 체계 정교화
### 단계 4. 더 큰 사용량
구성 변화:
- 내부 이벤트 버스를 Redis pub/sub에서 `NATS JetStream`으로 교체 또는 병행
- PostgreSQL 읽기 복제본 도입
- 검색을 외부 검색엔진으로 분리
- 대화방 파티셔닝 또는 사용자 샤딩 검토
Kafka는 이 프로젝트 규모에서 너무 이른 선택일 가능성이 높다. NATS JetStream이 운영 난이도와 기능 균형이 더 좋다.
## 15. 마이그레이션 경로를 부드럽게 만드는 설계 포인트
초기부터 반드시 지켜야 하는 항목:
- 메시지 ID는 시간 정렬 가능한 전역 고유값 사용
- DB 마이그레이션 체계 도입
- 아웃박스 패턴 도입
- 첨부 저장소는 S3 호환 인터페이스 뒤에 숨기기
- 세션/프레즌스는 Redis 기반으로 외부화 가능하게 만들기
- API 응답과 WebSocket 이벤트에 버전 필드 두기
- 기능 플래그 도입
이것만 지켜도 단일 VPS에서 시작한 시스템을 크게 깨지 않고 다음 단계로 옮길 수 있다.
## 16. 자가 구현 서버 vs Matrix/기타 프로토콜 비교
### 16.1 이번 프로젝트의 권장 선택
이번 프로젝트는 `자가 구현 서버`가 더 적합하다.
이유:
- 카카오톡 PC 스타일 UX를 맞추려면 제품 흐름과 프로토콜을 세밀하게 제어해야 한다.
- Matrix는 강력하지만 연합, 브리지, 규격 준수, 이벤트 모델 이해 비용이 크다.
- 단일 VPS에서 개인 프로젝트로 운영하기에는 Synapse 계열은 무겁고 운영 난이도가 높다.
- Element류 생태계와 맞물리는 장점보다, 제품 개성과 속도에서 제약이 더 크게 느껴질 가능성이 높다.
### 16.2 Matrix가 적합한 경우
- 오픈 프로토콜과 연합이 중요할 때
- 외부 클라이언트 호환성이 중요할 때
- 자체 프로토콜 설계보다 표준 기반 확장을 원할 때
### 16.3 기타 선택지
- XMPP: 성숙했지만 현대적인 제품 UX에 맞춘 구현에서는 다시 커스텀 계층이 많이 필요하다.
- MQTT: IoT와 경량 pub/sub에는 좋지만 메신저 영속 모델과 권한 모델이 핵심이 아니므로 주축으로는 비권장이다.
결론:
- MVP와 제품 완성도 우선이라면 자가 구현
- 개방형 생태계 우선이라면 Matrix
이번 케이스에서는 자가 구현이 맞다.
## 17. 실제 구축 순서 권장안
1. 도메인과 서브도메인 구조 확정
2. Docker Compose 스택 초안 구성
3. PostgreSQL 스키마와 마이그레이션 체계 확정
4. 인증/세션 모델 확정
5. REST + WSS 이벤트 계약서 작성
6. outbox 기반 메시지 저장/전달 흐름 구현
7. Redis presence 및 세션 인덱스 도입
8. MinIO 첨부 업로드 흐름 도입
9. Prometheus/Loki/Grafana 기본 대시보드 구성
10. 외부 백업 파이프라인 연결
11. 장애 복구 리허설
12. 부하 테스트 후 튜닝
## 18. 최종 합의안
가장 현실적이고 좋은 방향은 아래다.
- 프로토콜은 `REST + WebSocket`
- 서버 구조는 `모놀리식 코드베이스 + app/worker 역할 분리`
- DB는 `PostgreSQL`
- 캐시/프레즌스는 `Redis`
- 첨부 저장소는 `MinIO(S3 호환)`
- 프록시는 `Caddy`
- 관측성은 `Prometheus + Loki + Grafana`
- 백업은 `외부 오브젝트 스토리지로 분리`
- 확장 경로는 `단일 VPS -> 앱 복제본 -> 상태 저장 계층 외부화 -> NATS/복제/샤딩`
이 구조는 사이드 프로젝트의 실행 가능성과, 이후 실제 사용자가 붙었을 때의 확장 가능성 사이에서 가장 균형이 좋다.

View file

@ -0,0 +1,427 @@
# 품질 전략, 테스트 계획, 릴리즈 게이트, 텔레메트리, 베타 운영안
## 문서 목적
이 문서는 Windows PC 우선 메신저 사이드 프로젝트의 품질 확보 체계를 정의한다. 범위는 데스크톱 앱과 VPS 기반 채팅 백엔드를 함께 포함하며, 알파 단계부터 정식 런치 직전까지의 테스트, 관측성, 장애 대응, 승인 기준, 최종 마감 체크리스트를 다룬다.
이 제안은 최소 6개 관점의 합의안으로 본다.
- QA 리드: 회귀 방지와 출시 게이트 설계
- Windows 데스크톱 엔지니어: 설치, 업데이트, 자원 사용량, 크래시 대응
- 백엔드/SRE: VPS 안정성, 장애 복구, 로그와 모니터링
- UX 리서처: 대화 흐름, 인지 부하, 사용성 검증
- 보안/프라이버시 담당: 로그 최소 수집, 민감정보 처리 원칙
- 릴리즈 매니저: 베타 롤아웃, 빌드 승격, 런치 준비
## 1. 품질 전략 원칙
### 1.1 제품 품질의 정의
이 프로젝트의 품질은 단순히 버그 수가 적은 상태가 아니라 아래 조건을 동시에 만족하는 상태로 정의한다.
- 메신저의 핵심 루프가 끊기지 않는다.
- 느린 네트워크와 일시적 장애에서도 대화 맥락이 유지된다.
- Windows PC 환경에서 설치, 실행, 업데이트, 복구가 자연스럽다.
- UI가 카카오톡 PC 사용 경험을 참고하되, 더 세련되고 명확한 피드백을 제공한다.
- 텔레메트리와 크래시 리포트가 문제를 재현 가능한 수준까지 설명한다.
- 정식 출시 직전에는 치명적 버그를 막는 게이트가 자동화와 수동 검수 양쪽에 존재한다.
### 1.2 품질 우선순위
우선순위는 아래 순서로 둔다.
1. 메시지 손실 방지
2. 로그인/세션 안정성
3. 실시간 연결 복구
4. UI 응답성과 스크롤/입력 안정성
5. 설치, 자동 업데이트, 재실행 복구
6. 미세한 시각 완성도와 인터랙션 폴리시
### 1.3 품질 목표
초기 런치 기준 목표치는 아래처럼 잡는다.
- 앱 크래시 없는 세션 비율: 99.5% 이상
- 메시지 전송 성공률: 99.9% 이상
- 네트워크 일시 단절 후 자동 재연결 성공률: 95% 이상
- 앱 콜드 스타트에서 대화 목록 표시까지: 일반 환경 기준 3초 이내
- 기본 채팅 입력 반응 시간: 체감상 지연 없이 100ms 이하 목표
- 중대한 회귀 버그가 있는 빌드는 외부 베타로 승격 금지
## 2. 테스트 전략 전체 구조
테스트는 아래 4층으로 설계한다.
- Unit Test: 메시지 상태 전이, 입력 검증, 포맷터, 상태 관리, 동기화 로직
- Integration Test: 데스크톱 앱 모듈 간 연계, 로컬 저장소, 인증, 소켓/HTTP 결합
- End-to-End Test: 실제 사용자 시나리오를 앱 + VPS 환경에서 검증
- Manual UX Check: 시각 완성도, 감정선, 읽기 흐름, 미세한 피드백 점검
자동화만으로는 메신저 품질이 충분히 보장되지 않으므로, UI와 상호작용의 감성적 완성도는 반드시 수동 검수를 포함한다.
## 3. 단계별 테스트 계획
## 3.1 Unit Test 범위
가장 먼저 안정화해야 할 단위는 아래와 같다.
- 메시지 모델: 생성, 수정, 삭제, 전달 상태, 읽음 상태, 실패 상태 전이
- 대화방 정렬 로직: 최신 메시지 기준 정렬, 고정 대화방 우선 노출
- 검색 로직: 채팅방 이름, 메시지 텍스트, 사용자 이름 검색 결과 일관성
- 입력창 로직: 멀티라인 입력, 엔터 전송/줄바꿈 규칙, IME 조합 중 입력 처리
- 파일 첨부 로직: 허용 포맷, 크기 제한, 업로드 대기/실패 상태
- 세션 상태: 로그인 유지, 토큰 만료, 재인증 트리거
- 재연결 전략: backoff, 중복 연결 방지, 연결 상태 배지 표시 조건
- 알림 로직: 포그라운드/백그라운드 조건별 알림 발송 정책
- 로컬 캐시: 최근 대화, 프로필, 읽음 위치 복원
Unit Test의 기준은 단순 커버리지 수치보다, 상태 전이가 많은 로직을 빠짐없이 명세하는 데 둔다.
## 3.2 Integration Test 범위
데스크톱 앱과 백엔드 경계, 또는 앱 내부 모듈 경계에서 아래를 검증한다.
- 로그인 후 사용자 정보, 대화 목록, 최근 메시지 초기 로드
- WebSocket 연결 수립 전후의 REST fallback 동작
- 네트워크 재연결 이후 누락 메시지 동기화
- 이미지/파일 업로드 후 메시지 항목과 미디어 URL 연결
- 읽음 처리 전송 후 상대/서버 상태 반영
- 알림 클릭 시 정확한 대화방으로 포커스 이동
- 로컬 저장소 손상 또는 오래된 캐시 버전에서 안전한 복구
- 앱 업데이트 후 기존 세션과 캐시 유지 여부
- 서버 응답 지연, 중복 응답, 순서 뒤집힘 상황에서 UI 일관성 유지
## 3.3 End-to-End Test 시나리오
필수 E2E 시나리오는 아래를 포함한다.
- 신규 사용자 로그인 후 첫 채팅 시작
- 기존 사용자 재실행 후 대화 목록 복원
- 1:1 채팅 메시지 송수신
- 다자간 채팅방 입장, 메시지 수신, 읽음 상태 변화
- 이미지 첨부, 업로드 완료, 실패 후 재시도
- 앱 재실행 중 미전송 메시지 복구
- 네트워크 끊김 중 입력/전송 시도 후 복구
- 중복 로그인 또는 세션 만료 상황 처리
- 검색에서 채팅방 진입 후 뒤로 이동
- 알림 클릭으로 특정 메시지 문맥 진입
- 오래된 메시지 스크롤 페이징
- 매우 긴 대화방 이름, 긴 메시지, 이모지, 한글 IME 혼합 입력
- 서버 재시작 중 클라이언트 복구
E2E는 로컬 스텁 환경만으로 끝내지 말고, 실제 VPS 스테이징 환경에서도 주기적으로 돌려야 한다.
## 4. 수동 UX 검수 계획
자동 테스트가 통과해도 아래 항목은 사람이 직접 판단해야 한다.
- 첫 실행 시 정보 구조가 즉시 이해되는가
- 좌측 대화 목록, 중앙 대화 영역, 상단 상태 정보의 시선 흐름이 자연스러운가
- 선택 상태, hover, unread, muted, pinned 상태가 한눈에 구분되는가
- 메시지 입력창의 높이 변화와 스크롤 이동이 거슬리지 않는가
- 새 메시지 도착 시 시각적 피드백이 과하지 않으면서 놓치지 않게 설계됐는가
- 읽음 표시, 전송 중, 실패 상태의 의미가 직관적인가
- 설정 화면이 과도하게 복잡하지 않은가
- 오류 문구가 사용자 책임으로 들리지 않고, 해결 가능성을 제시하는가
- 폰트 렌더링, 한글 자간, line-height, 아이콘 선 굵기가 일관적인가
- 다크/라이트 모드가 있다면 대비와 시선 분리가 안정적인가
수동 UX 검수는 최소 3종 장비 조합으로 수행한다.
- 일반적인 FHD 노트북
- 고배율 디스플레이가 적용된 고해상도 Windows PC
- 저사양 또는 메모리 제약이 있는 테스트 장비
## 5. 네트워크 복원력 시나리오
메신저는 정상 네트워크보다 비정상 네트워크에서 품질 차이가 크게 드러난다. 아래 시나리오는 별도 회복력 테스트 묶음으로 관리한다.
### 5.1 연결 품질 저하
- 고지연 환경
- 패킷 손실 환경
- 업로드만 느린 환경
- 순간적인 DNS 실패
- TLS 핸드셰이크 지연
### 5.2 연결 중단과 복구
- Wi-Fi 꺼짐 후 재연결
- 노트북 절전 진입 후 복귀
- VPN on/off 전환
- 사내망/공용망 전환
- 백엔드 WebSocket 프로세스 재시작
- VPS 재부팅
### 5.3 동기화 이상
- 메시지 ACK 지연
- 서버는 수신했지만 클라이언트가 ACK를 못 받은 상태
- 동일 메시지 중복 수신
- 오래된 이벤트가 늦게 도착
- 읽음 이벤트가 메시지보다 먼저 도착
### 5.4 기대 동작
각 네트워크 이상 시나리오에서 앱은 아래 동작을 만족해야 한다.
- 연결 상태를 숨기지 않고 명확하게 알려준다.
- 전송 실패 메시지는 사라지지 않고 재시도 가능해야 한다.
- 재연결 이후 중복 메시지를 만들지 않아야 한다.
- 사용자 입력은 가능한 한 보존되어야 한다.
- 회복 후에는 최신 상태와의 차이를 자동 동기화해야 한다.
## 6. 텔레메트리 전략
텔레메트리는 문제를 빨리 발견하고 우선순위를 정하기 위한 최소 수집 원칙으로 설계한다. 메시지 본문, 파일 내용, 개인식별 가능 정보는 수집하지 않는다.
### 6.1 핵심 이벤트
- 앱 실행, 종료, 비정상 종료
- 로그인 성공/실패
- 대화 목록 로드 성공/실패/지연
- 채팅방 진입
- 메시지 전송 시도/성공/실패
- 파일 업로드 시도/성공/실패
- WebSocket 연결, 끊김, 재연결
- API 오류 코드 집계
- 업데이트 다운로드/적용 성공 여부
### 6.2 핵심 지표
- DAU/WAU
- 세션 길이
- 대화방 진입 대비 실제 메시지 전송 비율
- 메시지 전송 실패율
- reconnect 횟수와 성공률
- 앱 버전별 크래시율
- API 엔드포인트별 실패율과 p95 응답 시간
- 특정 릴리즈 이후 회귀 지표 변화
### 6.3 대시보드 구성
릴리즈 직후 가장 먼저 보는 대시보드는 아래 4개다.
- 안정성 대시보드: 크래시율, 비정상 종료, 재실행 루프
- 메시징 대시보드: 전송 성공률, ACK 지연, 중복 메시지 감지
- 연결성 대시보드: WebSocket 단절, 재연결 성공률, 서버 에러율
- 릴리즈 대시보드: 버전별 설치 성공률, 업데이트 적용 성공률, 롤백 필요 신호
## 7. 크래시 핸들링 전략
### 7.1 클라이언트
- 비정상 종료 시 다음 실행에서 안전 복구 모드 진입 여부를 판단한다.
- 크래시 리포트에는 앱 버전, OS 버전, 메모리 상태, 직전 화면, 최근 오류 범주를 포함한다.
- 민감정보와 메시지 본문은 제외한다.
- 크래시 직전 사용자가 작성 중이던 미전송 텍스트는 가능한 범위에서 복구한다.
- 반복 크래시가 특정 화면 진입에서 발생하면 해당 기능을 임시 비활성화할 수 있어야 한다.
### 7.2 서버
- 프로세스 재시작 정책을 설정한다.
- 앱 서버, DB, 스토리지, 리버스 프록시 각각의 헬스 체크를 분리한다.
- 장애 시 최근 배포 이력과 리소스 사용량을 즉시 연동 확인 가능해야 한다.
- 치명 장애 발생 시 운영 알림 채널로 즉시 통지한다.
### 7.3 크래시 triage 우선순위
- P0: 앱 실행 불가, 로그인 불가, 메시지 손실 가능성
- P1: 반복 크래시, 재연결 실패, 파일 업로드 핵심 기능 불가
- P2: 특정 화면 진입 시 크래시, 우회 가능하지만 경험 훼손 큼
- P3: 드문 환경에서의 비핵심 기능 크래시
## 8. 릴리즈 단계와 게이트
릴리즈는 `Alpha -> Closed Beta -> Open Beta -> RC -> Launch` 흐름으로 관리한다.
### 8.1 Alpha
목적:
- 핵심 채팅 루프가 성립하는지 확인
- 구조적 결함 조기 발견
대상:
- 개발자 본인과 내부 소수 테스터
필수 기준:
- 로그인, 대화 목록, 메시지 송수신, 기본 재연결이 동작
- 치명 크래시가 재현성 있게 남아있지 않음
- 텔레메트리와 크래시 리포트 수집 가능
차단 조건:
- 메시지 유실 재현
- 세션 꼬임
- 앱 시작 불가
### 8.2 Closed Beta
목적:
- 다양한 Windows 환경에서 회귀와 설치/업데이트 문제 발견
- 사용성 불만과 혼란 포인트 수집
대상:
- 신뢰 가능한 외부 사용자 20명에서 100명 규모
필수 기준:
- 자동 업데이트 또는 업데이트 유도 플로우 안정화
- 주요 E2E와 네트워크 복원력 시나리오 통과
- 고우선순위 이슈 대응 프로세스 마련
차단 조건:
- 버전 업 이후 캐시 손상
- 특정 GPU/해상도 조합에서 UI unusable
- 알림/포커스 이동이 신뢰할 수 없는 수준
### 8.3 Open Beta
목적:
- 실제 사용 패턴과 부하 기반 안정성 검증
- VPS 백엔드의 운영 내구성 확인
대상:
- 초대 또는 신청 기반의 확장된 사용자군
필수 기준:
- 크래시율과 전송 실패율이 목표치 근처에서 안정화
- 서버 모니터링, 알림, 백업, 장애 대응 문서화 완료
- 주요 UX 불만의 상위 항목 정리와 개선 반영
차단 조건:
- 피크 시간대에서 서버 병목
- 업로드/다운로드 기능의 잦은 실패
- 보안/개인정보 위험 신호
### 8.4 RC
목적:
- 정식 출시 후보 빌드 확정
필수 기준:
- P0, P1 이슈 0건
- 승인된 예외 목록만 남아있음
- 회귀 테스트, 수동 UX 체크, 설치/업데이트 검증 완료
- 릴리즈 노트, 알려진 이슈, 지원 대응 문안 준비 완료
### 8.5 Launch
목적:
- 안전한 공개 전환과 초기 운영 안정화
필수 기준:
- 모니터링 대시보드 활성화
- 운영 온콜 또는 대응 시간대 확보
- 롤백 경로와 이전 안정 버전 보관
- 초기 72시간 관찰 계획 확정
## 9. 베타 롤아웃 전략
### 9.1 배포 원칙
- 한 번에 전체 공개하지 않고 점진적으로 확장한다.
- 데스크톱 앱 버전과 서버 릴리즈를 명확히 매핑한다.
- 서버 측 기능 플래그로 위험 기능을 단계적으로 연다.
### 9.2 추천 롤아웃 흐름
1. 내부 알파 100%
2. 클로즈드 베타 10명
3. 클로즈드 베타 30명
4. 클로즈드 베타 100명
5. 오픈 베타 제한 공개
6. 정식 출시 전 RC 고정
7. 런치 후 초기 사용자군 10% 수준 점진 확대
### 9.3 베타 피드백 수집
- 앱 내 피드백 진입점 제공
- 이슈 제보 시 자동 첨부 가능한 진단 요약 제공
- 설문은 짧게 유지하고, 채팅 사용 맥락 기반 질문으로 설계
- 정량 지표와 정성 피드백을 따로 보지 말고 함께 해석
## 10. 승인 기준과 완료 정의
### 10.1 기능 승인 기준
한 기능은 아래를 만족해야 완료로 본다.
- 명세된 핵심 시나리오가 자동화 테스트 또는 재현 가능한 체크리스트로 검증됨
- 실패 상태 UI와 문구가 존재함
- 텔레메트리 이벤트가 연결됨
- 접근성 기본 기준을 위반하지 않음
- Windows 배율, 창 크기 변화, 포커스 이동에서 깨지지 않음
### 10.2 릴리즈 승인 기준
릴리즈는 아래를 모두 충족해야 승격한다.
- P0, P1 미해결 이슈 없음
- 최근 변경 범위에 대한 회귀 테스트 완료
- 설치, 실행, 로그인, 메시지 송수신, 파일 첨부, 재연결 수동 검수 완료
- VPS 서버 상태, 디스크 사용량, DB 백업, 스토리지 상태 확인 완료
- 모니터링과 알림이 실제로 동작함
## 11. 최종 폴리시와 마감 체크리스트
### 11.1 데스크톱 앱 최종 체크
- 설치/삭제가 정상 동작한다.
- 첫 실행 경험이 매끄럽다.
- 업데이트 후 설정, 세션, 캐시가 의도대로 유지된다.
- 창 최소화, 복원, 다중 모니터 이동이 안정적이다.
- 알림 클릭 시 정확한 대화방으로 이동한다.
- 입력창, 스크롤, 붙여넣기, 드래그 앤 드롭이 예상대로 동작한다.
- 고배율 디스플레이에서 흐릿한 요소가 없다.
- 폰트, 아이콘, 간격, hover, selection이 일관적이다.
### 11.2 백엔드/VPS 최종 체크
- 프로세스 자동 시작과 재시작 정책이 확인됐다.
- 리버스 프록시, 앱, DB, 스토리지 헬스 체크가 정상이다.
- 백업과 복원 절차가 실제 검증됐다.
- 로그 보존 정책과 디스크 사용량 경보가 설정됐다.
- TLS, 도메인, 인증서 자동 갱신 상태가 확인됐다.
- 서버 재기동 후 클라이언트 재연결이 정상 동작한다.
### 11.3 런치 72시간 체크
- 크래시율, 전송 실패율, 재연결 실패율을 2시간 단위로 본다.
- 상위 오류 5개를 매일 triage 한다.
- 사용자 피드백을 UX, 성능, 안정성으로 분류한다.
- 심각한 회귀가 있으면 기능 플래그 off 또는 롤백을 즉시 검토한다.
## 12. 권장 운영 리듬
추천 리듬은 아래와 같다.
- 매일: 크래시/오류/전송 실패 지표 확인
- 주 2회: 회귀 테스트와 베타 피드백 정리
- 주 1회: 릴리즈 후보 점검 회의
- 베타 기간 중: 매 릴리즈마다 수동 UX 검수 세션 수행
## 13. 최종 권고
이 프로젝트의 성공 여부는 UI를 얼마나 비슷하게 만들었는지보다, 사용자가 불안 없이 메시지를 보내고 다시 돌아왔을 때 맥락이 보존되는지에 달려 있다. 따라서 출시 게이트는 시각적 유사성보다 메시지 신뢰성, 재연결 복원력, 설치/업데이트 안정성, 관측성 완성도를 우선해야 한다.
정식 출시 직전에는 새로운 기능 추가를 멈추고 아래 4가지만 집중하는 것이 가장 낫다.
- 크래시 제거
- 네트워크 복원력 보강
- 텔레메트리와 운영 대시보드 정제
- UI 폴리시와 마이크로 인터랙션 최종 다듬기

View file

@ -0,0 +1,14 @@
# Messenger Planning Set
이 폴더는 Windows PC 기준 개인 메신저 사이드 프로젝트의 기획 문서를 모아두는 planning set이다.
문서 목록:
1. [01-backend-platform-architecture.md](01-backend-platform-architecture.md)
채팅 서버 아키텍처, VPS 배포 전략, 데이터/스토리지, 관측성, 백업, 확장 및 마이그레이션 전략
2. [windows-desktop-client-architecture.md](windows-desktop-client-architecture.md)
Windows-first 데스크톱 기술 스택 선정, WinUI 3 vs 대안 비교, 패키징, 오프라인 캐시, 알림, 보안 경계, 단계별 구현 전략
3. [06-quality-release-and-launch.md](06-quality-release-and-launch.md)
품질 전략, 테스트 계획, 릴리즈 게이트, 텔레메트리, 베타 운영안
이 폴더는 제품 기획 세트 중 실행/운영 관점 문서를 모아둔다. 백엔드, 플랫폼, 품질, 릴리즈, 운영 문서를 계속 추가한다.

View file

@ -0,0 +1,489 @@
# 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, 알림, 재연결은 부가 기능이 아니라 메신저의 본체로 취급한다.
- 초기에 단일 창과 단순 프로세스 구조를 유지해 제품을 먼저 안정화한다.

View file

@ -0,0 +1,359 @@
# Windows-first 메신저 시각/인터랙션 설계 방향
## 1. 디자인 테제
이 제품의 목표는 "카카오톡 PC의 즉시성, 익숙한 효율, 낮은 진입장벽"은 유지하면서, Windows 데스크톱 환경에서 더 차분하고 세련되며 오래 써도 피로하지 않은 메시징 경험을 제공하는 것이다.
핵심 방향은 다음과 같다.
- 한눈에 읽히는 정보 밀도: 화면을 넓게 쓰는 PC 환경에서 정보량은 충분히 담되 답답하거나 복잡해 보이지 않아야 한다.
- 즉시 조작 가능한 구조: 클릭 한두 번 안에 이동, 검색, 파일 전송, 대화 전환, 알림 확인이 끝나야 한다.
- 차분한 고급감: 과한 장식 대신 균형 잡힌 비율, 재질감, 미세한 움직임, 명확한 위계를 통해 프리미엄 감각을 만든다.
- Windows 네이티브 감성: 타이틀 바, 창 상태, 포커스, 컨텍스트 메뉴, 단축키, 다중 창 사용 방식에서 데스크톱 소프트웨어다워야 한다.
- 장시간 사용 최적화: 업무 중 하루 종일 켜두는 앱이라는 전제 하에 대비, 타이포, 상태 표현, 알림 피로도를 설계한다.
한 문장으로 정리하면 다음과 같다.
`친숙한 메신저 구조 위에, 생산성과 심미성이 정교하게 올라간 Windows용 프리미엄 데스크톱 메신저`
## 2. 데스크톱 정보구조
기본 정보구조는 3단 구성을 중심으로 설계한다.
- 1단: 좌측 글로벌 내비게이션
- 2단: 리스트 영역
- 3단: 메인 콘텐츠 영역
필요 시 우측 보조 패널이 열리는 4단 구조까지 확장한다.
상위 메뉴는 다음 정도로 제한한다.
- 채팅
- 친구 또는 연락처
- 알림
- 보관함 또는 자료함
- 설정
기본 진입점은 `채팅`이다. 메신저의 중심 과업이 대화라는 점을 흐리지 않는다. 친구 목록과 기타 기능은 중요하지만 1차 목적을 방해하지 않도록 한 단계 낮은 밀도로 둔다.
권장 구조는 다음과 같다.
- 좌측 아이콘 바: 전역 기능 전환
- 중앙 리스트 패널: 채팅방, 친구, 알림, 검색 결과 등 현재 선택된 전역 기능의 목록
- 우측 메인 패널: 대화 스레드, 친구 상세, 알림 상세, 설정 화면
- 필요 시 우측 인스펙터 패널: 참여자 목록, 첨부 파일, 링크, 검색 내역, 채팅방 정보
이 구조의 장점은 카카오톡 PC 사용자의 정신 모델을 크게 벗어나지 않으면서, Slack이나 Discord류 데스크톱 협업 앱처럼 "맥락 패널"을 유연하게 붙일 수 있다는 점이다.
## 3. 셸과 내비게이션
### 3.1 앱 셸 원칙
- Windows 타이틀 바와 자연스럽게 어울리는 상단 영역을 설계한다.
- 커스텀 크롬을 쓰더라도 창 이동, 스냅, 최대화, 최소화, 시스템 메뉴 등 기본 OS 동작을 해치지 않는다.
- 상단은 과하게 비워두지 말고, 검색, 빠른 실행, 현재 상태 같은 핵심 기능을 밀도 있게 담는다.
권장 셸 구성은 다음과 같다.
- 상단 바: 앱명 또는 현재 컨텍스트, 통합 검색, 빠른 새 채팅, 사용자 상태/프로필, 창 컨트롤
- 좌측 내비게이션 레일: 아이콘 중심, 선택 상태가 강하게 드러나는 구조
- 중앙 작업 영역: 콘텐츠 중심
### 3.2 내비게이션 패턴
- 전역 전환은 좌측 레일에 고정한다.
- 각 전역 섹션 안의 세부 이동은 중앙 리스트에서 해결한다.
- 우측 패널은 탐색이 아니라 "맥락 확장" 용도로만 쓴다.
- 브레드크럼은 사용하지 않는다. 메신저에서는 깊은 계층보다 현재 컨텍스트 표시가 중요하다.
### 3.3 검색 경험
검색은 데스크톱 메신저의 핵심 경쟁력이다. 상단 통합 검색은 다음을 지원해야 한다.
- 채팅방 검색
- 사람 검색
- 메시지 본문 검색
- 파일/링크 검색
- 최근 검색
검색 UI는 단순 입력창이 아니라 커맨드 팔레트와 리스트 검색의 중간 형태가 좋다. 입력 즉시 그룹화된 결과가 드롭다운으로 뜨고, 방향키와 엔터로 이동 가능해야 한다.
## 4. 리스트, 채팅, 디테일 패널 동작
## 4.1 리스트 패널
리스트는 단순 나열이 아니라 생산성 도구여야 한다.
필수 항목 구성
- 아바타
- 이름
- 마지막 메시지 미리보기
- 시간
- 읽지 않음 배지
- 고정 또는 알림 해제 상태
동작 원칙
- 행 높이는 너무 좁지 않게 설정하되, 기본 밀도는 "업무용으로 충분히 빽빽한 수준"을 유지한다.
- 마우스 호버 시 보조 액션이 은근히 드러난다.
- 우클릭 컨텍스트 메뉴가 풍부해야 한다.
- 드래그 앤 드롭으로 고정 순서 변경, 파일 전송, 창 분리 같은 데스크톱 행동을 적극 수용한다.
상태 표현
- 선택 상태는 배경색 + 얇은 강조선 + 타이포 웨이트 조합으로 표현한다.
- 읽지 않음은 색 하나에만 의존하지 말고 배지, 굵기, 프리뷰 톤까지 조합한다.
- 음소거/보관/고정 상태는 아이콘 크기를 작게 유지해 시각적 소음을 줄인다.
### 4.2 채팅 패널
채팅 패널은 이 제품의 중심이다. "읽기 쉬움"과 "입력 흐름의 부드러움"이 최우선이다.
기본 구조
- 상단 헤더: 채팅방 이름, 참여자 수, 상태, 검색, 통화/추가 액션
- 메시지 영역: 타임라인
- 하단 입력 영역: 입력창, 첨부, 이모지, 전송, 보조 액션
권장 동작
- 새 메시지가 들어와도 사용자가 과거 메시지를 읽는 중이면 자동 하단 점프를 강요하지 않는다.
- "새 메시지 n개" 점프 배너를 제공한다.
- 날짜 구분선, 읽음 기준선, 시스템 메시지는 메시지 버블보다 한 단계 낮은 시각 강도로 표현한다.
- 메시지 선택 모드, 멀티 선택, 복사/전달/삭제 등 PC다운 조작을 지원한다.
- 긴 대화에서 스크롤 성능과 가상화 품질을 매우 높게 가져간다.
메시지 레이아웃 원칙
- 내 메시지와 상대 메시지는 명확히 분리하되, 카카오톡처럼 지나치게 장난스럽지 않게 정제한다.
- 버블 최대 폭은 넓은 모니터에서도 읽기 좋은 선에서 제한한다.
- 텍스트, 이미지, 파일, 답장, 링크 프리뷰, 시스템 공지의 규칙이 일관돼야 한다.
- 연속 메시지는 아바타와 여백을 절약해 리듬감을 준다.
입력창 원칙
- 한 줄 입력을 기본으로 하되 여러 줄로 자연스럽게 확장된다.
- 첨부, 캡처, 파일 끌어놓기, 클립보드 이미지 붙여넣기 등 PC 특화 행동을 막지 않는다.
- 입력 보조 버튼이 너무 많아져 웹 툴바처럼 보이지 않게 한다.
### 4.3 디테일 패널
우측 디테일 패널은 선택적으로 열리고 닫혀야 한다.
용도
- 채팅방 정보
- 참여자
- 공유 파일
- 링크
- 미디어
- 대화 내 검색 결과
원칙
- 항상 고정 노출하지 않는다.
- 메인 채팅 공간을 잠식하지 않도록 기본 너비를 절제한다.
- 탭이 많아지면 세그먼트드 컨트롤 또는 상단 탭으로 정리한다.
## 5. 타이포그래피, 컬러, 머티리얼
### 5.1 타이포그래피
Windows 데스크톱에서는 가독성과 밀도가 핵심이다. 기본 방향은 다음과 같다.
- 기본 UI 폰트는 Windows 환경과 잘 맞는 높은 가독성의 산세리프를 사용한다.
- 채팅 본문과 리스트 본문은 숫자와 한글, 영문 혼합 시 안정적인 폰트를 우선한다.
- 제목용 폰트와 본문용 폰트의 역할을 지나치게 벌리지 않는다.
타이포 위계 예시
- 앱/섹션 타이틀: 강한 존재감, 그러나 과장되지 않음
- 리스트 제목/채팅방명: 명확한 식별성
- 본문: 장시간 읽기에 최적화
- 메타 정보: 시간, 상태, 보조 정보는 한 단계 낮은 대비
디자인 느낌은 "날카로운 스타트업 툴"보다 "매끈한 프리미엄 커뮤니케이션 앱" 쪽이 적합하다.
### 5.2 컬러
카카오톡의 노란색 아이덴티티를 직접 복제하는 대신, 다음 방식이 더 좋다.
- 기본 베이스는 뉴트럴 톤 위주
- 액센트 컬러는 한 가지 메인 색으로 통일
- 읽지 않음, 성공, 주의, 오류는 명확히 분리
권장 컬러 전략
- 배경: 완전한 흰색보다 미세하게 따뜻하거나 차가운 오프 화이트
- 패널 분리: 얇은 구분선보다 톤 차와 표면 재질감 중심
- 액센트: 채팅 전송, 선택 상태, 활성 탭, 주요 CTA에 일관되게 사용
- 경고/오류: 과포화 붉은색 남용 금지
트렌디함은 강한 원색이 아니라, 절제된 중립 팔레트 위에 정확한 강조색을 올리는 방식에서 나온다.
### 5.3 머티리얼과 표면
웹 카드 UI처럼 모든 것을 박스로 자르지 않는다.
권장 원칙
- 좌우 패널은 표면 레이어 차이로 구분
- 모달, 팝오버, 컨텍스트 메뉴는 Windows 특유의 얇은 재질감과 깊이감 사용
- 메시지 버블은 과도하게 둥글거나 젤리처럼 보이지 않게 조절
- 반투명, 블러, 그림자는 최소한으로 정교하게 사용
느낌은 "플랫 + 아주 얕은 입체감"이 적합하다. 과한 유리 효과는 피하고, 정보가 또렷하게 보이는 것을 우선한다.
## 6. 모션 원칙
모션은 눈에 띄기보다 사용 맥락을 설명해야 한다.
핵심 원칙
- 짧고 정확한 응답
- 방향성이 있는 전환
- 상태 변화 설명
- 방해하지 않는 부드러움
권장 모션
- 리스트 항목 선택: 짧은 배경 전이
- 패널 열림/닫힘: 수평 슬라이드 + 페이드
- 새 메시지 도착: 미세한 페이드/슬라이드
- 토스트/배너: 아래 또는 우상단에서 짧게 등장
- 검색 결과 표시: 즉시성 중심, 과한 애니메이션 금지
피해야 할 것
- 모든 클릭에 바운스
- 과장된 스프링
- 모바일 앱 같은 통통 튀는 반응
- 긴 페이드로 인한 느린 인상
## 7. 상태 설계
데스크톱 메신저는 상태 수가 많다. 상태 표현은 미세하지만 분명해야 한다.
필수 상태
- 기본
- 호버
- 포커스
- 활성
- 선택
- 비활성
- 로딩
- 동기화 중
- 오프라인
- 오류
- 전송 중
- 업로드 중
- 읽음/안 읽음
- 입력 중
- 방해 금지
상태 표현 원칙
- 색만으로 상태를 구분하지 않는다.
- 리스트와 채팅, 버튼과 입력창 전반에서 상태 언어를 통일한다.
- 로딩은 스켈레톤과 점진 표시를 기본으로 하고, 의미 없는 무한 스피너 남용을 피한다.
## 8. 온보딩
온보딩은 "설명"보다 "즉시 사용"을 목표로 설계한다.
권장 흐름
1. 첫 실행
2. 로그인 또는 계정 생성
3. 프로필 기본 설정
4. 연락처/친구 연결 또는 건너뛰기
5. 첫 채팅 시작 유도
6. 알림, 시작 프로그램, 트레이 최소화 여부 등 PC 친화 옵션 제안
온보딩 원칙
- 초기 단계 수를 최소화한다.
- 권한 요청은 필요한 순간에만 한다.
- 데스크톱 메신저답게 "항상 켜두는 앱" 설정을 자연스럽게 안내한다.
- 빈 상태 화면이 첫 대화를 열도록 적극적으로 돕는다.
빈 상태 화면에서 추천할 행동
- 새 대화 시작
- 연락처 가져오기
- 테스트 채팅방 입장
- 파일을 끌어 대화 시작하기
## 9. 접근성
이 프로젝트는 트렌디함보다 완성도를 우선해야 한다. 접근성은 부가 기능이 아니라 품질 기준이다.
필수 기준
- 키보드만으로 주요 흐름 수행 가능
- 포커스 링 명확
- 충분한 명도 대비
- 축소/확대 환경에서 레이아웃 붕괴 방지
- 스크린 리더용 의미 구조 설계
- 모션 축소 옵션 제공
특히 중요한 항목
- 채팅 리스트와 메시지 타임라인의 키보드 탐색
- 읽지 않음/전송 실패/입력 중 상태의 비색상 표현
- 작은 배지, 시간, 상태 아이콘의 저시력 대응
- 한글/영문/숫자 혼합 가독성
## 10. 카카오톡 PC에서 유지할 것
완전히 새롭게 만들기보다, 사용자가 무의식적으로 기대하는 패턴은 유지하는 편이 좋다.
- 좌측 중심의 익숙한 섹션 전환 구조
- 채팅 리스트 우선 진입
- 읽지 않은 채팅을 빠르게 훑는 흐름
- PC다운 우클릭 중심 조작
- 가벼운 창 크기 조절과 빠른 실행감
- 과도한 학습 없이 바로 채팅 가능한 구조
즉, 사용자는 "낯설지 않다"고 느껴야 하고, 동시에 "왜 이게 더 좋지?"라고 느껴야 한다.
## 11. 현대화할 것
다음 요소는 확실히 현대화하는 것이 좋다.
- 더 정교한 정보 밀도와 위계
- 우측 맥락 패널의 도입
- 검색을 단순 필터가 아닌 핵심 기능으로 승격
- 메시지 타입별 시각 시스템 정교화
- 반응성 높은 다중 상태 설계
- 디스플레이 스케일링과 고해상도 환경 대응
- 다크 모드가 아니라, 먼저 "좋은 라이트 모드" 완성 후 테마 확장
- 알림, 배지, 토스트를 세련되게 통합
추가로 현대화할 가치가 높은 부분
- 멀티 윈도우 또는 팝아웃 채팅
- 드래그 앤 드롭 UX
- 최근 파일/링크/미디어 탐색
- 검색 기반 이동
- 키보드 단축키 체계
## 12. 피해야 할 안티패턴
- 웹 메신저를 데스크톱 창 안에 얹은 듯한 넓은 여백과 느린 반응
- 모바일 UI를 단순 확대해 붙인 큰 버튼과 과도한 둥근 모서리
- 카드 남용으로 인한 조각난 정보 구조
- 색과 그림자 과다 사용
- 기능은 많은데 기본 채팅 흐름이 무거워지는 구조
- 입력창 툴바에 기능을 끝없이 누적하는 방식
- 알림, 배지, 강조색이 동시에 소리치는 시각 언어
- 다크 모드에서만 그럴듯하고 라이트 모드가 약한 설계
- 윈도우 스냅, 다중 모니터, DPI 스케일링을 고려하지 않은 레이아웃
- 상태 피드백이 늦어 사용자가 불안해지는 인터랙션
## 13. 최종 비주얼 톤 제안
가장 적합한 톤은 다음 세 가지 키워드로 정리된다.
- 정제됨
- 민첩함
- 오래 써도 질리지 않음
무드는 "밝은 생산성 툴"과 "프리미엄 개인 메신저"의 중간 지점이 좋다. 지나치게 기업 협업 도구처럼 딱딱하지도 않고, 반대로 캐주얼 메신저처럼 장난스럽지도 않아야 한다.
비주얼 방향을 한 줄로 요약하면 다음과 같다.
`카카오톡 PC의 익숙한 작업 흐름을 유지하되, Windows 11 시대의 고급 생산성 앱 수준으로 정밀하게 다듬은 메신저`
## 14. 디자인 의사결정 체크리스트
새 화면이나 컴포넌트를 만들 때마다 다음 질문으로 검증한다.
- 이 요소는 채팅이라는 핵심 과업을 더 빠르게 만드는가
- Windows 데스크톱에서 자연스러운가
- 장시간 사용 시 피로를 줄이는가
- 색 없이도 상태를 이해할 수 있는가
- 마우스와 키보드 모두에서 효율적인가
- 익숙함을 해치지 않으면서도 더 세련된가
- 웹 앱처럼 보이지 않는가
이 체크리스트를 통과하지 못하면 화려해 보여도 채택하지 않는다.

View file

@ -0,0 +1,11 @@
# Windows Messenger Planning Set
이 폴더는 Windows PC 기준 개인 사이드 프로젝트 메신저의 기획 문서 세트를 모아두는 공간이다.
문서 목록
- `01-visual-interaction-direction.md`: 카카오톡 PC 사용성을 참고하되 더 현대적인 Windows 데스크톱 메신저로 재해석한 시각/인터랙션 설계 방향
문서 작성 원칙
- 웹 앱을 데스크톱 셸에 억지로 넣은 느낌이 아니라, Windows 데스크톱 소프트웨어다운 밀도와 반응성을 우선한다.
- 익숙함과 차별화를 동시에 가져간다. 학습 비용은 낮추고, 완성도는 명확하게 높인다.
- 설계 문서는 구현 이전의 의사결정 기준서 역할을 하며, 추후 제품/기술/브랜드 문서와 연결된다.

View file

@ -0,0 +1,14 @@
# Latest Screenshots
이 디렉터리는 원격 저장소에서도 바로 확인할 수 있는 `최신 기준 제품 스크린샷`을 보관합니다.
규칙:
- README는 이 경로의 이미지를 직접 참조합니다.
- 새 릴리즈나 큰 UI 변경이 있으면 이 폴더의 스크린샷도 함께 갱신합니다.
- 릴리즈 번들용 스크린샷과 별개로, 저장소 안의 최신 기준 화면을 유지합니다.
- 모바일 웹 스크린샷은 `scripts/ci/capture-vstalk-web-screenshots.cjs`로 다시 생성할 수 있습니다.
- 현재 포함:
- Windows 데스크톱 셸
- Windows 온보딩/대화 화면
- `vstalk` 모바일 웹 온보딩/목록/검색/보관/대화 화면

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,19 @@
<svg width="1200" height="320" viewBox="0 0 1200 320" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="320" fill="#F7F8FA"/>
<rect x="40" y="44" width="1120" height="232" rx="28" fill="white" stroke="#E5E7EB" stroke-width="2"/>
<text x="84" y="102" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="32" font-weight="700">Contribution Path</text>
<text x="84" y="134" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="18">처음 방문자도 어디서 시작해야 하는지 바로 읽히도록, 기여 동선을 짧게 유지합니다.</text>
<rect x="84" y="176" width="220" height="64" rx="18" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="114" y="216" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="20" font-weight="700">1. Read Status</text>
<rect x="336" y="176" width="220" height="64" rx="18" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="366" y="216" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="20" font-weight="700">2. Pick A Surface</text>
<rect x="588" y="176" width="220" height="64" rx="18" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="618" y="216" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="20" font-weight="700">3. Open Issue / PR</text>
<rect x="840" y="176" width="236" height="64" rx="18" fill="#111827"/>
<text x="870" y="216" fill="white" font-family="Segoe UI, Arial, sans-serif" font-size="20" font-weight="700">4. Update Docs + Proof</text>
<path d="M304 208H336" stroke="#9CA3AF" stroke-width="2"/>
<path d="M556 208H588" stroke="#9CA3AF" stroke-width="2"/>
<path d="M808 208H840" stroke="#9CA3AF" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,21 @@
<svg width="1200" height="340" viewBox="0 0 1200 340" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="340" rx="28" fill="#F8F8F7"/>
<rect x="32" y="32" width="1136" height="276" rx="24" fill="white" stroke="#E5E7EB"/>
<text x="64" y="88" fill="#111827" font-family="Arial, sans-serif" font-size="32" font-weight="700">Evaluation Paths</text>
<text x="64" y="120" fill="#6B7280" font-family="Arial, sans-serif" font-size="16">방문자, 기여자, 운영자가 각자 가장 빨리 들어오는 길</text>
<rect x="64" y="156" width="328" height="116" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="88" y="192" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Visitor</text>
<text x="88" y="222" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">README → Project Status → Showcase</text>
<text x="88" y="244" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">Live Web / Releases / Screenshots</text>
<rect x="436" y="156" width="328" height="116" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="460" y="192" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Contributor</text>
<text x="460" y="222" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">First Contribution → Community</text>
<text x="460" y="244" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">Contributing / UX Atlas / Roadmap</text>
<rect x="808" y="156" width="320" height="116" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="832" y="192" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Operator</text>
<text x="832" y="222" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">Releasing → Deploy → Release Assets</text>
<text x="832" y="244" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">Download Host / Forge Releases</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -0,0 +1,41 @@
<svg width="1200" height="520" viewBox="0 0 1200 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="520" fill="#F7F8FA"/>
<rect x="48" y="48" width="1104" height="424" rx="28" fill="white" stroke="#E5E7EB" stroke-width="2"/>
<text x="88" y="110" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="36" font-weight="700">Open Source Surface</text>
<text x="88" y="144" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="18">코드만이 아니라 상태, 문서, 릴리즈, 라이브 채널까지 한 화면에서 읽히는 저장소를 목표로 합니다.</text>
<rect x="88" y="188" width="196" height="216" rx="22" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="112" y="232" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="24" font-weight="700">Code</text>
<text x="112" y="268" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Desktop</text>
<text x="112" y="294" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Mobile Web</text>
<text x="112" y="320" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">API</text>
<text x="112" y="368" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="16" font-weight="600">실제 구현의 중심</text>
<rect x="304" y="188" width="196" height="216" rx="22" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="328" y="232" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="24" font-weight="700">Docs</text>
<text x="328" y="268" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">README</text>
<text x="328" y="294" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Status</text>
<text x="328" y="320" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Master Plan</text>
<text x="328" y="368" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="16" font-weight="600">방향과 현실의 정합성</text>
<rect x="520" y="188" width="196" height="216" rx="22" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="544" y="232" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="24" font-weight="700">Releases</text>
<text x="544" y="268" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Forge Releases</text>
<text x="544" y="294" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Download Host</text>
<text x="544" y="320" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Checksums</text>
<text x="544" y="368" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="16" font-weight="600">받아볼 수 있는 결과물</text>
<rect x="736" y="188" width="196" height="216" rx="22" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="760" y="232" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="24" font-weight="700">Live</text>
<text x="760" y="268" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">vstalk.phy.kr</text>
<text x="760" y="294" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Health</text>
<text x="760" y="320" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Screenshots</text>
<text x="760" y="368" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="16" font-weight="600">지금 직접 확인 가능한 표면</text>
<rect x="952" y="188" width="152" height="216" rx="22" fill="#FFFFFF" stroke="#E5E7EB" stroke-width="2"/>
<text x="976" y="232" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="24" font-weight="700">People</text>
<text x="976" y="268" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Issues</text>
<text x="976" y="294" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">PRs</text>
<text x="976" y="320" fill="#6B7280" font-family="Segoe UI, Arial, sans-serif" font-size="16">Maintainers</text>
<text x="976" y="368" fill="#111827" font-family="Segoe UI, Arial, sans-serif" font-size="16" font-weight="600">기여와 운영의 접점</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,49 @@
<svg width="1200" height="620" viewBox="0 0 1200 620" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="40" y1="20" x2="1160" y2="600" gradientUnits="userSpaceOnUse">
<stop stop-color="#0B1220"/>
<stop offset="1" stop-color="#111827"/>
</linearGradient>
<linearGradient id="lineA" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#38BDF8"/>
<stop offset="1" stop-color="#22C55E"/>
</linearGradient>
<linearGradient id="lineB" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#F59E0B"/>
<stop offset="1" stop-color="#FB7185"/>
</linearGradient>
</defs>
<rect x="20" y="20" width="1160" height="580" rx="28" fill="url(#bg)" stroke="#1F2937"/>
<text x="64" y="86" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="34" font-weight="700">Platform Journey</text>
<text x="64" y="122" fill="#94A3B8" font-family="Arial, sans-serif" font-size="18">Three product surfaces, one release story: live entrypoint, desktop focus, Android parallel rollout.</text>
<rect x="64" y="188" width="320" height="156" rx="24" fill="#0E1828" stroke="#233246"/>
<text x="96" y="234" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Mobile Web</text>
<text x="96" y="270" fill="#86EFAC" font-family="Arial, sans-serif" font-size="30" font-weight="700">vstalk.phy.kr</text>
<text x="96" y="310" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Lowest friction entrypoint.</text>
<text x="96" y="336" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Quick signup, short reply, fast re-entry.</text>
<rect x="440" y="188" width="320" height="156" rx="24" fill="#0E1828" stroke="#233246"/>
<text x="472" y="234" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Windows Desktop</text>
<text x="472" y="270" fill="#93C5FD" font-family="Arial, sans-serif" font-size="30" font-weight="700">Primary productivity client</text>
<text x="472" y="310" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Compact shell, list, chat, send, buildable zip.</text>
<text x="472" y="336" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Targeting search, focus, and multiwindow flow.</text>
<rect x="816" y="188" width="320" height="156" rx="24" fill="#0E1828" stroke="#233246"/>
<text x="848" y="234" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Android APK</text>
<text x="848" y="270" fill="#FCD34D" font-family="Arial, sans-serif" font-size="30" font-weight="700">Parallel next channel</text>
<text x="848" y="310" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Push, repeat use, attachment flow,</text>
<text x="848" y="336" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">and release parity with desktop assets.</text>
<path d="M384 266H440" stroke="url(#lineA)" stroke-width="4" stroke-linecap="round"/>
<path d="M760 266H816" stroke="url(#lineB)" stroke-width="4" stroke-linecap="round" stroke-dasharray="10 10"/>
<circle cx="440" cy="266" r="7" fill="#38BDF8"/>
<circle cx="816" cy="266" r="7" fill="#F59E0B"/>
<rect x="64" y="410" width="1072" height="130" rx="24" fill="#0E1828" stroke="#233246"/>
<text x="96" y="454" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="20" font-weight="700">Public release path</text>
<text x="96" y="492" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">Repository docs and screenshots -> Forge Releases -> download-vstalk.phy.kr mirror -> latest links by OS</text>
<text x="96" y="520" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">The project treats release notes, screenshots, and downloadable artifacts as one continuous public surface.</text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,54 @@
<svg width="1200" height="520" viewBox="0 0 1200 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="40" y1="20" x2="1160" y2="500" gradientUnits="userSpaceOnUse">
<stop stop-color="#0B1220"/>
<stop offset="1" stop-color="#111827"/>
</linearGradient>
<linearGradient id="accentA" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#38BDF8"/>
<stop offset="1" stop-color="#22C55E"/>
</linearGradient>
<linearGradient id="accentB" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#F59E0B"/>
<stop offset="1" stop-color="#FB7185"/>
</linearGradient>
<filter id="shadow" x="-40" y="-40" width="1280" height="600" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feDropShadow dx="0" dy="18" stdDeviation="24" flood-color="#020617" flood-opacity="0.28"/>
</filter>
</defs>
<rect x="20" y="20" width="1160" height="480" rx="28" fill="url(#bg)" stroke="#1F2937"/>
<text x="64" y="86" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="34" font-weight="700">Product Pillars</text>
<text x="64" y="122" fill="#94A3B8" font-family="Arial, sans-serif" font-size="18">A Korean-first messenger project tuned for calm, fast, and transparent communication.</text>
<g filter="url(#shadow)">
<rect x="58" y="176" width="330" height="258" rx="24" fill="#0E1828" stroke="#233246"/>
<rect x="86" y="206" width="134" height="42" rx="14" fill="url(#accentA)"/>
<text x="110" y="233" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="18" font-weight="700">Korean-first UI</text>
<text x="86" y="292" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Short copy, low friction,</text>
<text x="86" y="326" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">real Korean usage flow.</text>
<text x="86" y="372" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">Designed around onboarding, read-reply loops,</text>
<text x="86" y="398" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">and recovery language that feel natural.</text>
</g>
<g filter="url(#shadow)">
<rect x="435" y="176" width="330" height="258" rx="24" fill="#0E1828" stroke="#233246"/>
<rect x="463" y="206" width="152" height="42" rx="14" fill="url(#accentA)"/>
<text x="486" y="233" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="18" font-weight="700">Windows priority</text>
<text x="463" y="292" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Compact desktop UX with</text>
<text x="463" y="326" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">multiwindow headroom.</text>
<text x="463" y="372" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">The project bets on search, focus tools,</text>
<text x="463" y="398" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">and task-oriented message recovery.</text>
</g>
<g filter="url(#shadow)">
<rect x="812" y="176" width="330" height="258" rx="24" fill="#0E1828" stroke="#233246"/>
<rect x="840" y="206" width="170" height="42" rx="14" fill="url(#accentB)"/>
<text x="863" y="233" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="18" font-weight="700">Transparent surface</text>
<text x="840" y="292" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Docs, screenshots,</text>
<text x="840" y="326" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">releases, and status aligned.</text>
<text x="840" y="372" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">This repo treats public project surfaces as</text>
<text x="840" y="398" fill="#94A3B8" font-family="Arial, sans-serif" font-size="17">part of the product, not afterthoughts.</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,26 @@
<svg width="1200" height="360" viewBox="0 0 1200 360" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="360" rx="28" fill="#F8F8F7"/>
<rect x="32" y="32" width="1136" height="296" rx="24" fill="white" stroke="#E5E7EB"/>
<text x="64" y="88" fill="#111827" font-family="Arial, sans-serif" font-size="32" font-weight="700">Public Contract</text>
<text x="64" y="120" fill="#6B7280" font-family="Arial, sans-serif" font-size="16">이 저장소가 공개면에서 지키려는 네 가지 약속</text>
<rect x="64" y="152" width="248" height="128" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="88" y="190" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Transparent State</text>
<text x="88" y="220" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">README와 상태표는 실제 구현보다</text>
<text x="88" y="242" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">좋아 보이도록 과장하지 않는다.</text>
<rect x="336" y="152" width="248" height="128" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="360" y="190" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Release Discipline</text>
<text x="360" y="220" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">릴리즈, 스크린샷, 다운로드,</text>
<text x="360" y="242" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">CHANGELOG를 같이 맞춘다.</text>
<rect x="608" y="152" width="248" height="128" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="632" y="190" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Korean-first UX</text>
<text x="632" y="220" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">번역체보다 실제 한국어 흐름과</text>
<text x="632" y="242" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">낮은 피로도의 사용성을 우선한다.</text>
<rect x="880" y="152" width="248" height="128" rx="20" fill="#FAFAFA" stroke="#E5E7EB"/>
<text x="904" y="190" fill="#111827" font-family="Arial, sans-serif" font-size="22" font-weight="700">Open Planning</text>
<text x="904" y="220" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">문서와 비판적 리뷰를 공개해</text>
<text x="904" y="242" fill="#6B7280" font-family="Arial, sans-serif" font-size="15">방향과 한계를 숨기지 않는다.</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,55 @@
<svg width="1200" height="520" viewBox="0 0 1200 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="40" y1="30" x2="1160" y2="490" gradientUnits="userSpaceOnUse">
<stop stop-color="#0B1220"/>
<stop offset="1" stop-color="#111827"/>
</linearGradient>
<linearGradient id="chip" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#38BDF8"/>
<stop offset="1" stop-color="#22C55E"/>
</linearGradient>
</defs>
<rect x="20" y="20" width="1160" height="480" rx="28" fill="url(#bg)" stroke="#1F2937"/>
<text x="64" y="86" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="34" font-weight="700">Release Surface</text>
<text x="64" y="120" fill="#94A3B8" font-family="Arial, sans-serif" font-size="18">The repository keeps code, screenshots, release rules, and download surfaces aligned.</text>
<rect x="60" y="190" width="230" height="190" rx="24" fill="#0D1726" stroke="#233246"/>
<text x="90" y="236" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Build sources</text>
<text x="90" y="274" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Code + Docs + Screenshots</text>
<text x="90" y="318" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">README</text>
<text x="90" y="344" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">문서/ planning set</text>
<text x="90" y="370" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">latest repo screenshots</text>
<rect x="350" y="190" width="230" height="190" rx="24" fill="#0D1726" stroke="#233246"/>
<text x="380" y="236" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Packaging</text>
<text x="380" y="274" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Release assets</text>
<text x="380" y="318" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">portable zip</text>
<text x="380" y="344" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">apk channel next</text>
<text x="380" y="370" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">checksums and metadata</text>
<rect x="640" y="190" width="230" height="190" rx="24" fill="#0D1726" stroke="#233246"/>
<text x="670" y="236" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Public surfaces</text>
<text x="670" y="274" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Forge Releases</text>
<text x="670" y="318" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">versioned assets</text>
<text x="670" y="344" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">release notes</text>
<text x="670" y="370" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">source of record</text>
<rect x="930" y="190" width="210" height="190" rx="24" fill="#0D1726" stroke="#233246"/>
<text x="960" y="236" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Delivery</text>
<text x="960" y="274" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Download host</text>
<text x="960" y="318" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">windows/latest</text>
<text x="960" y="344" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">android/latest</text>
<text x="960" y="370" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">version.json</text>
<path d="M290 286H350" stroke="url(#chip)" stroke-width="4" stroke-linecap="round"/>
<path d="M580 286H640" stroke="url(#chip)" stroke-width="4" stroke-linecap="round"/>
<path d="M870 286H930" stroke="url(#chip)" stroke-width="4" stroke-linecap="round"/>
<circle cx="350" cy="286" r="6" fill="#38BDF8"/>
<circle cx="640" cy="286" r="6" fill="#22C55E"/>
<circle cx="930" cy="286" r="6" fill="#F59E0B"/>
<rect x="786" y="74" width="304" height="50" rx="16" fill="#101B2C" stroke="#223045"/>
<text x="814" y="106" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Principle: what ships must match what the repo says.</text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,84 @@
<svg width="1200" height="760" viewBox="0 0 1200 760" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="80" y1="40" x2="1080" y2="720" gradientUnits="userSpaceOnUse">
<stop stop-color="#0F172A"/>
<stop offset="1" stop-color="#111827"/>
</linearGradient>
<linearGradient id="accentA" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#22C55E"/>
<stop offset="1" stop-color="#0EA5E9"/>
</linearGradient>
<linearGradient id="accentB" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="#F59E0B"/>
<stop offset="1" stop-color="#FB7185"/>
</linearGradient>
<filter id="shadow" x="-40" y="-40" width="1280" height="840" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feDropShadow dx="0" dy="18" stdDeviation="24" flood-color="#020617" flood-opacity="0.32"/>
</filter>
</defs>
<rect x="24" y="24" width="1152" height="712" rx="32" fill="url(#bg)"/>
<rect x="24" y="24" width="1152" height="712" rx="32" stroke="#1F2937"/>
<text x="70" y="92" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="34" font-weight="700">System Overview</text>
<text x="70" y="128" fill="#94A3B8" font-family="Arial, sans-serif" font-size="18">Windows-first messenger with a live mobile web entrypoint and a transparent release surface.</text>
<g filter="url(#shadow)">
<rect x="72" y="184" width="270" height="148" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="102" y="230" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Windows Desktop</text>
<text x="102" y="260" fill="#93C5FD" font-family="Arial, sans-serif" font-size="32" font-weight="700">Avalonia 12</text>
<text x="102" y="296" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Conversation list, chat view, send flow,</text>
<text x="102" y="320" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">portable build channel.</text>
<rect x="72" y="376" width="270" height="148" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="102" y="422" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Mobile Web</text>
<text x="102" y="452" fill="#86EFAC" font-family="Arial, sans-serif" font-size="32" font-weight="700">vstalk.phy.kr</text>
<text x="102" y="488" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">PWA shell, quick signup, list, chat,</text>
<text x="102" y="512" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">same-origin API and WebSocket flow.</text>
<rect x="72" y="568" width="270" height="112" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="102" y="614" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Android</text>
<text x="102" y="644" fill="#FCD34D" font-family="Arial, sans-serif" font-size="28" font-weight="700">Next parallel client</text>
</g>
<g filter="url(#shadow)">
<rect x="462" y="246" width="286" height="224" rx="28" fill="#0B1220" stroke="#334155"/>
<rect x="490" y="278" width="230" height="54" rx="16" fill="url(#accentA)"/>
<text x="521" y="313" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="24" font-weight="700">ASP.NET Core 8 API</text>
<text x="492" y="372" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Protocols</text>
<text x="492" y="402" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">REST bootstrap, conversations, messages</text>
<text x="492" y="428" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">WebSocket realtime events</text>
</g>
<g filter="url(#shadow)">
<rect x="862" y="164" width="270" height="148" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="892" y="210" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Storage now</text>
<text x="892" y="242" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="30" font-weight="700">SQLite</text>
<text x="892" y="278" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">Alpha storage with simple deployment</text>
<text x="892" y="302" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">and low operational overhead.</text>
<rect x="862" y="356" width="270" height="148" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="892" y="402" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Operational surface</text>
<text x="892" y="434" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="28" font-weight="700">Caddy + nginx</text>
<text x="892" y="470" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">TLS, same-origin mobile web,</text>
<text x="892" y="494" fill="#94A3B8" font-family="Arial, sans-serif" font-size="16">shared VPS reverse proxy routing.</text>
<rect x="862" y="548" width="270" height="132" rx="24" fill="#0B1220" stroke="#243244"/>
<text x="892" y="594" fill="#E2E8F0" font-family="Arial, sans-serif" font-size="18" font-weight="700">Target stack later</text>
<text x="892" y="626" fill="#F8FAFC" font-family="Arial, sans-serif" font-size="26" font-weight="700">PostgreSQL / Redis / MinIO</text>
</g>
<path d="M342 258H462" stroke="#38BDF8" stroke-width="4" stroke-linecap="round"/>
<path d="M342 450H462" stroke="#38BDF8" stroke-width="4" stroke-linecap="round"/>
<path d="M342 624H462" stroke="#F59E0B" stroke-width="4" stroke-linecap="round" stroke-dasharray="10 10"/>
<path d="M748 358H862" stroke="#34D399" stroke-width="4" stroke-linecap="round"/>
<path d="M748 430H862" stroke="#34D399" stroke-width="4" stroke-linecap="round"/>
<path d="M748 614H862" stroke="#F59E0B" stroke-width="4" stroke-linecap="round" stroke-dasharray="10 10"/>
<circle cx="462" cy="258" r="7" fill="#38BDF8"/>
<circle cx="462" cy="450" r="7" fill="#38BDF8"/>
<circle cx="462" cy="624" r="7" fill="#F59E0B"/>
<circle cx="862" cy="358" r="7" fill="#34D399"/>
<circle cx="862" cy="430" r="7" fill="#34D399"/>
<circle cx="862" cy="614" r="7" fill="#F59E0B"/>
</svg>

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,29 @@
# Repository Surfaces
이 문서는 공개 저장소에서 무엇을 어떤 이름으로 노출하는지 정리합니다.
## Public Naming
- 제품 노출명: `KoTalk`
- 한글 표기: `코톡`
- 웹 진입 도메인: `vstalk.phy.kr`
- 다운로드 미러: `download-vstalk.phy.kr`
## Repository Structure
- `src/`, `tests/`: 제품 코드와 검증 코드
- `docs/`: 공개 보조 문서와 시각 자산
- `문서/`: 제품 마스터 플랜과 UX 아틀라스
- `deploy/`: 범용 배포 골격
- `release-assets/`: 릴리즈 메타데이터와 배포 자산 스테이징
## Public Writing Rules
- README는 첫 방문자가 30초 안에 판단할 수 있게 유지합니다.
- 공개 문서에는 실제 운영 힌트, 비밀값, 내부 메모를 적지 않습니다.
- 공식 서비스와 오픈소스 저장소는 같은 표면처럼 쓰지 않습니다.
## Current Technical Note
현재 저장소의 코드 네임스페이스와 프로젝트 파일은 아직 `PhysOn.*`를 사용합니다.
이 문서는 공개 브랜드 기준을 먼저 정리한 것이며, 소스 네임스페이스 정렬은 별도 작업입니다.