카카오톡처럼 편리하지만, 모든 데이터는 내 집 NAS에 안전하게 보관되는 크로스플랫폼 메신저 만들기 프로젝트
모바일(Android, iOS)부터 데스크톱(Windows, macOS)까지 코드 하나로 개발하고, 집에 있는 Ugreen NAS(Docker)를 백엔드 서버로 구축하는 전체적인 아키텍처와 로드맵을 공유합니다.
1. 프로젝트를 시작하게 된 계기
가족들과 친구들이 매일 사용하는 메신저 앱. 카카오톡이나 라인처럼 친숙하고 편리하지만, 아래와 같은 아쉬움이나 고민이 있었습니다:
- 데이터 주권과 프라이버시: 가족들의 사진, 일상 대화, 소중한 파일들을 외부 클라우드가 아닌 우리 집 NAS에 안전하게 보관하고 싶다.
- 크로스플랫폼 지원: 부모님 스마트폰(Android/iOS)은 물론, 일할 때 쓰는 컴퓨터(Windows, Mac)에서도 매끄럽게 돌아가야 한다.
- 가족 친화적인 쉬운 사용성: QR 코드 같은 번거로운 인증 대신, 카톡 링크 전송이나 휴대전화 연락처로 쉽게 초대할 수 있어야 한다.
그래서 집에서 24시간 가동 중인 유그린(Ugreen) NAS를 개인 서버로 활용하고, Flutter로 4개 OS를 모두 지원하는 프라이빗 메신저 ‘딩동(DingDong)’ 프로젝트를 시작하게 되었습니다.
2. 우리가 만들 핵심 기능 (Feature Scope)
- 💬 실시간 1:1 및 가족 그룹 채팅: 카카오톡처럼 안 읽은 인원 수(노란 숫자 1) 표시 및 실시간 읽음 처리
- 📞 1:1 보이스톡(음성 통화): WebRTC 기반 고음질 음성 통화 (잠금화면에서도 일반 전화처럼 수신)
- 📷 사진 및 대용량 파일 전송: 네트워크가 끊겨도 이어서 올릴 수 있는 안정적인 업로드, 내 NAS 하드디스크에 원본 영구 저장
- 🔗 초간단 친구/가족 초대: 카카오톡이나 문자로 원클릭 링크 공유, 내 폰 주소록(연락처) 기반 친구 매칭
- 🖥️ PC 멀티 디바이스 로그인: 모바일 앱에서 한 번의 승인으로 데스크톱 앱 간편 로그인
3. 전체 시스템 아키텍처
클라우드 비용을 매달 내지 않고, 안정적이면서도 유지보수가 쉬운 구성을 목표로 설계했습니다. 백엔드는 Go 단일 바이너리로 만들어, NAS 위에서 메모리를 적게 쓰고 관리할 컨테이너 수도 최소로 줄였습니다.
flowchart TB
subgraph Clients["클라이언트 (Flutter 단일 코드베이스)"]
A[Android 스마트폰]
I[iPhone / iPad]
M[Mac 데스크톱]
W[Windows PC]
end
subgraph Internet["외부 서비스 (푸시 전용)"]
FCM["Google FCM (안드로이드 푸시)"]
APNS["Apple APNs (아이폰 VoIP/푸시)"]
end
subgraph HomeNAS["우리 집 유그린 NAS (Docker Compose)"]
CADDY["Caddy 웹서버 (HTTPS / WSS 자동 SSL)"]
API["Go API 서버 (REST + WebSocket, 단일 바이너리)"]
PG[(PostgreSQL 16 - 대화/회원 정보, sqlc로 접근)]
FILES[("NAS HDD 볼륨 - 사진/파일 원본")]
LK["LiveKit 서버 (WebRTC 음성 통화 중계)"]
end
Clients -->|HTTPS / WSS 실시간 접속| CADDY
CADDY --> API
API --> PG & FILES
API -.->|통화 토큰 발급| LK
Clients <-->|WebRTC 음성 스트림| LK
API -.->|백그라운드 알림 전송| FCM & APNS
FCM & APNS -.->|앱 깨우기| Clients
linkStyle default stroke-width:3px,stroke:#333333왜 이 기술들을 선택했을까요?
| 계층 | 선택한 기술 | 선택 이유 (초보 개발자 팁) |
|---|---|---|
| 앱 클라이언트 | Flutter (Dart) | 코드 1개로 Android, iOS, Windows, macOS 4개 플랫폼 앱이 모두 나옵니다. UI 컴포넌트가 OS에 구애받지 않고 카톡과 똑같이 그려집니다. |
| 로컬 캐시 DB | Drift (SQLite) | “오프라인 우선” 구조를 위해 필수입니다. 집 인터넷이나 NAS가 잠시 끊겨도 앱에서 이전 대화를 즉시 볼 수 있고, 오프라인 작성 후 재연결 시 자동 전송됩니다. |
| 백엔드 서버 | Go (Golang) | 컴파일하면 파일 하나로 끝나는 초경량 서버입니다. 고루틴 덕분에 WebSocket 연결을 수백 개 유지해도 NAS 메모리를 거의 쓰지 않고, Docker 이미지도 아주 작게 만들 수 있습니다. |
| DB 접근 계층 | sqlc | SQL을 직접 쓰면 타입이 있는 Go 코드를 자동으로 만들어 줍니다. ORM의 숨은 쿼리 없이 메시지 조회 쿼리를 내 손으로 통제할 수 있고, 오타는 컴파일 단계에서 잡힙니다. |
| 데이터베이스 | PostgreSQL 16 | 안정성과 트랜잭션 보장이 뛰어납니다. 대화 순번(seq)을 매겨 메시지 유실 없는 동기화를 구현하기에 최적입니다. |
| 접속 상태 / 알림 큐 | Go 내장 기능 (메모리 + 채널/워커) | 가족 규모에서는 별도 Redis 없이 서버 메모리에 접속 상태를 두고, 푸시 알림은 고루틴 워커가 보내는 것으로 충분합니다. 컨테이너가 하나 줄어 관리도 쉬워집니다. |
| 음성 통화 | LiveKit (Self-hosted) | WebRTC 기반 오픈소스 통화 서버입니다. 복잡한 P2P 통신이나 TURN 서버 설정 없이, Docker 컨테이너 하나로 깔끔하게 음성 통화 인프라가 해결됩니다. |
| 리버스 프록시 | Caddy | Nginx보다 설정이 10배 간단하며, 도메인만 적어두면 무료 Let’s Encrypt SSL 인증서(HTTPS)를 알아서 발급/갱신해 줍니다. |
4. 핵심 기술 포인트 & 구현 꿀팁
① 실시간 채팅과 “안 읽은 숫자 1” 처리
- 방별 순번(Seq) 방식: 기기 시계 차이로 인한 메시지 순서 꼬임을 방지하기 위해, 서버에서 방마다 1, 2, 3… 형태로
seq번호를 부여합니다. - 가벼운 읽음 계산: 메시지마다 “누가 읽었는지” 리스트를 저장하면 서버에 부하가 큽니다. 대신 **”각 멤버가 마지막으로 읽은 순번(Last Read Seq)”**만 기록하여,
현재 순번 - 내가 읽은 순번으로 안 읽은 개수를 가볍게 계산합니다.
② 가족과 친구를 편하게 초대하는 방법
- 카톡/문자 원클릭 링크: 앱 내 [초대하기]를 누르면 카톡 공유창이 떠서 바로 초대 링크가 전송됩니다. 링크 클릭 시 설치 안내 및 딥링크를 통해 가입 즉시 친구로 연결됩니다.
- 연락처(주소록) 매칭: 내 폰 주소록의 전화번호를 안전하게 단방향 암호화(SHA-256 해시)하여 서버와 대조합니다. 이미 가입된 가족은 바로 추가하고, 미가입된 가족은 문자 초대장을 보낼 수 있습니다.
③ 유그린 NAS(집 서버) 환경에 맞춘 최적화
- HDD 수면 모드 방지: NAS의 하드디스크가 절전 모드에 들어가면 첫 메시지 반응이 몇 초 느려질 수 있습니다. 따라서 DB와 Docker 컨테이너는 NVMe SSD 볼륨에 두고, 용량이 큰 사진/파일 원본만 대용량 HDD 볼륨에 저장하도록 분리했습니다.
- Docker Compose 배포: 모든 서버 환경이
docker-compose.yml파일 하나로 관리되므로, NAS를 교체하거나 다른 PC로 이전할 때도 명령어 한 줄로 복구할 수 있습니다.
5. 앞으로의 개발 로드맵
앞으로 실제 코드를 작성하고 기능을 완성해 나갈 단계별 계획입니다:
- [1단계] NAS 환경 셋업 & 기술 검증: Caddy + Go API + PostgreSQL + LiveKit을 NAS Docker에 올리고, 외부(LTE) 접속 및 통화 테스트
- [2단계] 기본 로그인 & 친구 추가: 초대 링크 공유 및 주소록 매칭 기능 구현
- [3단계] 실시간 문자 채팅 완성: 오프라인 로컬 저장, WebSocket 실시간 전송, 안 읽은 숫자 카운팅
- [4단계] 사진/대용량 파일 전송 & 푸시 알림: 이어올리기(Tus) 프로토콜 및 모바일 푸시 알림 연동
- [5단계] 보이스톡(음성 통화): LiveKit 연동 및 스마트폰 잠금화면 수신 UI(CallKit) 연동
- [6단계] 데스크톱(Windows/Mac) 최적화 & 가족 실사용 테스트
6. 다음 편 예고
다음 글에서는 Mac에서 Flutter & VS Code 개발 환경 구축하기를 다루겠습니다.
메신저 개발이나 개인 NAS를 활용한 셀프 호스팅에 관심 있으신 분들은 다음 편도 기대해 주세요! 궁금한 점이 있으시다면 언제든 댓글로 남겨주세요.
📂 Part 1. 기획 & 아키텍처 설계
- ✅ [개발일지 #01 | 기획] 유그린 NAS와 Flutter로 만드는 나만의 프라이빗 메신저 ‘딩동’ (기획 & 아키텍처 편)
📂 Part 2. 홈서버 & 인프라 구축
- ✅ [개발일지 #02 | 인프라] Mac에서 Flutter & VS Code 개발 환경 구축하기 (초보자 완벽 가이드)
- ✅ [개발일지 #03 | 인프라] 유그린 NAS에 Caddy & PostgreSQL 구축하기
📂 Part 3. 백엔드(API) & 실시간 통화 서버
- ✅ [개발일지 #04 | 백엔드] 유그린 NAS에 Go(Golang) 초경량 메신저 백엔드 서버 구축하기
- ✅ [개발일지 #05 | 백엔드] 메신저 DB 스키마 설계 & Go sqlc 연동하기
- ✅ [개발일지 #06 | 백엔드] 초대코드 회원가입, Argon2id 암호화 & 다중 기기 JWT 인증 구현하기
- ⏳ [#08 | 백엔드] 연락처 HMAC 해시 친구 매칭 & 1:1 / 그룹 채팅방 생성 API (예정)
- ⏳ [#10 | 백엔드] Gorilla WebSocket 실시간 채팅 허브 & 단조 증가 순번(
seq) 동기화 (예정)
📂 Part 4. 모바일 앱 개발 (iOS / Android)
- ✅ [개발일지 #07 | 클라이언트] Flutter 앱 첫 연동: Secure Storage 토큰 관리, Dio 자동 갱신 & 로그인 화면 구현
- ⏳ [#09 | 클라이언트] 친구 목록 동기화 & 채팅방 목록 UI 연동하기 (예정)
- ⏳ [#11 | 클라이언트] 실시간 WebSocket 채팅방 화면 & 끊김 없는 대화 동기화 구현 (예정)
- ⏳ [#12 | 앱-공통] 300MB 대용량 원본 사진/영상 전송 및 미디어 처리 (예정)
- ⏳ [#13 | 앱-푸시] 앱이 꺼져 있어도 울리는 푸시 알림(FCM/APNs) 및 보이스톡 연동 (예정)
📂 Part 5. 정식 배포 & 운영
- ⏳ [#14 | 배포] 백엔드 서버 도커 이미지 빌드 및 유그린 NAS 정식 배포 (예정)
- ⏳ [#15 | 운영] DB 자동 백업 스크립트 구성 및 가족 실사용 후기 (예정)