TREK이 무엇을 하는 물건인가.
TREK은 셀프호스팅 여행 플래너 올인원 앱이다. 지도 위에 일정을 드래그하고, 팀원들과 실시간으로 동시 편집하며, 예산을 N빵 정산하고, 짐 체크리스트를 관리하는 모든 기능이 자신의 서버 위에서만 돌아간다. 데이터는 구글도 노션도 모르는 내 서버 SQLite 파일 하나에 저장된다.
여기에 AI MCP 서버까지 내장되어, Claude나 다른 AI 어시스턴트가 OAuth 2.1로 인증해 여행 일정을 직접 생성·수정할 수 있다. "앱스토어 없이 PWA로 폰에 설치"가 가능한 것도 특징이다.
트렌딩 이유와 기존 솔루션 대비 차별점.
TREK이 TrendShift 상위권을 차지한 배경에는 세 가지 흐름이 맞물려 있다. 첫째, 데이터 주권 의식 — 구글·노션에 여행 데이터를 맡기기 싫다는 사람이 늘었다. 둘째, AI 여행 플래너 수요 — ChatGPT/Claude로 일정을 짜는 흐름이 커지면서, AI가 직접 앱 데이터를 읽고 쓸 수 있는 MCP 연동이 매력적이다. 셋째, Docker 셀프호스팅 생태계 — Unraid, TrueNAS, Proxmox 사용자들이 찾는 '올인원 여행 앱' 공백이 있었다.
| 솔루션 | 지도 + 일정 | 실시간 협업 | 예산 정산 | 셀프호스팅 | AI 연동 | 오프라인 |
|---|---|---|---|---|---|---|
| TREK | ✅ Leaflet/Mapbox | ✅ WebSocket | ✅ Splitwise식 | ✅ Docker 1줄 | ✅ MCP 150+ | ✅ Workbox |
| 구글 Maps + 여행 | ✅ | 제한적 | ❌ | ❌ | 제한적 | 제한적 |
| 노션 | ❌ 지도 없음 | ✅ | ❌ | ❌ | AI 블록 | ❌ |
| TripIt | ✅ | ✅ | 제한적 | ❌ | ❌ | 제한적 |
| Wanderlog | ✅ | ✅ | ✅ | ❌ 클라우드만 | ❌ | ❌ |
| Splitwise | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
TREK은 AGPL-3.0이다. 소스를 수정해 네트워크로 서비스하면 수정본 소스도 공개해야 한다. 개인 사용·사내 사용은 괜찮지만 상업 서비스로 제공하려면 반드시 법률 검토가 필요하다.
SQLite 단일 파일 DB는 대규모 동시 쓰기에 한계가 있다. 수십 명 이상의 팀이 동시에 편집하는 엔터프라이즈 환경에는 부적합하다.
서버·클라이언트·DB가 하나의 Docker 이미지에 내장되어 있어 외부 서비스(Redis, Postgres 등) 없이 운영된다. 백업은 ./data/ 폴더 하나만 복사하면 끝. 마이그레이션도 자동 처리된다.
프론트엔드 / 백엔드 / DB / 인프라 각각 상세.
| 기술 | 버전 | 역할 |
|---|---|---|
| React | 19.2.6 | UI 컴포넌트 트리. React 19의 새 Concurrent 기능(Suspense, Actions) 활용 |
| Vite | 최신 | 번들러 + 개발 서버. ESM 기반으로 HMR이 빠름 |
| React Router | v6 | SPA 클라이언트 라우팅. 중첩 라우트 패턴 |
| Zustand | v4 | 전역 상태 관리. Redux 없이 경량 스토어로 여행/사용자 상태 유지 |
| Leaflet + react-leaflet | 1.9.4 / 5.0 | OSM 기반 인터랙티브 지도. 클러스터링, 사진 마커, 경로 표시 |
| Mapbox GL | v3 | 3D 빌딩/지형 렌더링. API 키 필요 (선택) |
| Workbox (Service Worker) | 내장 | PWA 오프라인 캐싱 — 지도 타일, API 응답, 업로드 큐 |
| Dexie | v4 | IndexedDB 래퍼. 클라이언트 측 오프라인 DB |
| @react-pdf/renderer | v4 | 여행 일정 PDF 내보내기 |
| @simplewebauthn/browser | v13 | 패스키(WebAuthn) 로그인 클라이언트 측 |
| lucide-react | 최신 | SVG 아이콘 라이브러리 |
| Axios | v1 | REST API 클라이언트 |
| topojson-client | v3 | Atlas 기능 — 세계 지도 국가 방문 시각화 |
| 기술 | 버전 | 역할 |
|---|---|---|
| Node.js | 22 LTS | 런타임. Docker 이미지 기반 |
| NestJS | 11 | Express 위 DI 프레임워크. 모듈/컨트롤러/서비스 패턴 |
| Express | v4 | NestJS 하부 HTTP 어댑터 |
| TypeScript | v6 | 전체 코드베이스 타입 안전성 |
| ws (WebSocket) | v8 | 실시간 협업 채널. 여행 편집 이벤트 브로드캐스트 |
| jsonwebtoken | v9 | JWT 세션 토큰 발급 / 검증 |
| @simplewebauthn/server | v13 | 패스키(WebAuthn) 서버 측 처리 |
| otplib | v12 | TOTP 2단계 인증 코드 생성/검증 |
| nodemailer | v9 | 이메일 알림 발송 (SMTP) |
| node-cron | v4 | 자동 백업, 환율 업데이트 등 스케줄 작업 |
| @modelcontextprotocol/sdk | v1.28 | AI MCP 서버 내장 — 150+ 도구 노출 |
| Zod | v4 | 런타임 스키마 검증 (공유 스키마는 shared/ 패키지) |
| helmet | v8 | HTTP 보안 헤더 자동 설정 |
| better-sqlite3 | v12 | 동기 SQLite 드라이버 (Node.js Native Addon) |
| undici | v7 | 고성능 HTTP 클라이언트 (Fetch API 대체, 외부 API 호출) |
| fast-xml-parser | v5 | KML/GPX 파일 파싱 (지도 파일 가져오기) |
| jimp | v1 | 이미지 리사이징 / 썸네일 생성 |
| 기술 | 역할 |
|---|---|
| SQLite (better-sqlite3) | 메인 DB. WAL 모드로 동시 읽기 성능 향상. 파일 하나(data/trek.db)로 운영 |
| Docker (단일 이미지) | 서버+클라이언트+DB 모두 내장. Alpine 기반 멀티스테이지 빌드 |
| Helm Chart | Kubernetes 배포용 차트 (charts/ 폴더) |
| @trek/shared | 클라이언트-서버 공유 타입·Zod 스키마·유틸리티 (npm workspace) |
| Vitest | 단위/통합/e2e 테스트 (서버+클라이언트 공통) |
| Playwright | E2E 브라우저 테스트 |
| Open-Meteo API | 날씨 예보 (16일). 무료, API 키 불필요 |
| Google Places API | 장소 검색 (선택). 무료 대안: OpenStreetMap/Nominatim |
| KDE Itinerary | 예약 확인 이메일/PDF에서 항공편·숙박 자동 추출 |
시스템 구조도와 핵심 설계 패턴 3가지.
TREK의 실시간 협업은 ws 라이브러리 기반 단순 WebSocket으로 구현된다. OT(Operational Transformation)나 CRDT 같은 복잡한 알고리즘 없이, 이벤트 브로드캐스트 방식을 사용한다.
이 방식은 구글 독스의 CRDT 방식과 다르다. 구글 독스는 두 사람이 동시에 같은 글자를 수정해도 충돌 없이 합쳐진다. TREK은 "마지막으로 저장한 사람이 이긴다(Last Write Wins)" 방식이다 — 두 사람이 동시에 같은 장소를 이동하면 한 쪽이 덮어씌운다. 여행 플래너 정도의 협업에는 충분하다.
TREK은 두 가지 지도 엔진을 병렬로 지원한다. API 키 없이 쓸 수 있는 Leaflet(OSM)과, 3D 빌딩/지형이 필요할 때 쓰는 Mapbox GL이다. 사용자가 관리자 패널에서 선택하며, 지도 타일 소스도 직접 설정 가능하다.
| 엔진 | 비용 | 3D 지원 | API 키 | 사용 시나리오 |
|---|---|---|---|---|
| Leaflet + OSM | 무료 | ❌ | 불필요 | 기본 지도, 루트 표시, 핀 클러스터링 |
| Mapbox GL v3 | 유료(무료 한도) | ✅ 3D 빌딩/지형 | 필요 | 몰입형 여행 계획, 3D 지형 탐색 |
OIDC_ISSUER와 OIDC_CLIENT_ID 환경변수만 설정하면 어떤 OIDC 제공자와도 연동된다. 회사 SSO를 그대로 쓸 수 있다는 의미.npm workspace 모노레포 구조와 각 폴더의 역할.
하나의 git 저장소에 3개의 독립 패키지(shared/server/client)가 같이 산다. npm install을 루트에서 한 번 실행하면 세 패키지의 의존성이 모두 설치된다. @trek/shared를 서버와 클라이언트가 동시에 import하므로 타입 정의를 한 곳에서만 관리한다 — 마치 공용 창고를 두 팀이 함께 쓰는 구조.
TREK 코드베이스에서 배울 수 있는 기술과 실습 아이디어.
NestJS는 Angular에서 영감받은 DI(의존성 주입) 프레임워크다. TREK의 server/src/nest/를 보면 모듈-컨트롤러-서비스 삼층 구조를 실전에서 어떻게 쓰는지 배울 수 있다. 특히 미들웨어 체인(middleware/auth.ts → middleware/tripAccess.ts)이 HTTP 요청을 단계적으로 검증하는 패턴이 교과서적이다.
server/src/websocket.ts와 client/src/sync/가 쌍을 이룬다. "REST로 먼저 저장 → WS로 이벤트 브로드캐스트"라는 낙관적 업데이트 패턴을 배울 수 있다. React Suspense나 SWR 없이 Zustand 스토어를 WebSocket 이벤트로 직접 갱신하는 패턴도 흥미롭다.
Vite에서 Service Worker를 Workbox로 생성하는 설정, Dexie(IndexedDB)로 오프라인 데이터를 캐시하는 패턴, 그리고 오프라인 중 쌓인 쓰기 작업을 온라인 복귀 시 동기화하는 흐름 — 이 세 가지를 TREK 코드에서 통째로 학습할 수 있다.
server/src/db/에는 SQLite을 WAL(Write-Ahead Logging) 모드로 열어 읽기 성능을 높이는 설정과, 마이그레이션 파일들이 있다. ORM 없이 better-sqlite3로 SQL을 직접 쓰면서도 타입 안전성을 유지하는 방법을 볼 수 있다.
server/src/mcp/는 AI MCP 서버를 직접 구현하는 실전 사례다. @modelcontextprotocol/sdk로 150개 이상의 도구를 정의하고, OAuth 2.1로 보호하는 전체 흐름을 배울 수 있다. MCP를 배우고 싶은 사람에게 최고의 실전 레퍼런스 중 하나다.
패스키(WebAuthn), TOTP 2FA, OIDC SSO를 한 앱에서 동시에 지원하는 코드는 드물다. server/src/services/oidcService.ts, passkeyService.ts, ephemeralTokens.ts를 보면 현대 인증 스택의 전체 그림이 나온다.
자신의 서버에 TREK을 올리기 위한 최소 사양과 설치 명령.
| 항목 | 최소 | 권장 | 비고 |
|---|---|---|---|
| CPU | 1 vCPU | 2 vCPU | 지도 이미지 처리 시 CPU 사용량 증가 |
| 메모리 | 512 MB | 1 GB+ | Node.js + SQLite 캐시 |
| 디스크 | 2 GB | 20 GB+ | 사진·첨부파일 업로드 공간 |
| OS | Linux (Docker) | Linux (Docker) | macOS/Windows Docker Desktop도 가능 |
| 포트 | 3000 | 80/443 (리버스 프록시) | Nginx/Caddy/Traefik 권장 |
| 네트워크 | 없음 (로컬망) | 외부 도메인 + SSL | OIDC·이메일 기능 시 도메인 필요 |
ENCRYPTION_KEY=$(openssl rand -hex 32) docker run -d \
-p 3000:3000 \
-e ENCRYPTION_KEY=$ENCRYPTION_KEY \
-v ./data:/app/data \
-v ./uploads:/app/uploads \
--name trek \
mauriceboe/trek
브라우저에서 http://localhost:3000 접속. 첫 부팅 시 관리자 계정이 ADMIN_EMAIL/ADMIN_PASSWORD 환경변수로 생성되며, 미설정 시 컨테이너 로그(docker logs trek)에 출력된다.
version: "3.8"
services:
app:
image: mauriceboe/trek:latest
container_name: trek
read_only: true # 보안: 컨테이너 파일시스템 읽기전용
security_opt:
- no-new-privileges:true # 권한 상승 차단
cap_drop:
- ALL # 모든 Linux 능력 제거
cap_add:
- CHOWN # 파일 소유자 변경만 허용
- SETUID
- SETGID
tmpfs:
- /tmp:noexec,nosuid,size=128m
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
- TZ=Asia/Seoul
- ALLOWED_ORIGINS=https://trek.example.com
# SSO 설정 (선택)
# - OIDC_ISSUER=https://accounts.google.com
# - OIDC_CLIENT_ID=your_client_id
# - OIDC_CLIENT_SECRET=your_secret
volumes:
- ./data:/app/data
- ./uploads:/app/uploads
restart: unless-stopped
TREK은 민감 데이터(API 키, OAuth 토큰 등)를 ENCRYPTION_KEY로 암호화한다. 이 키를 분실하면 암호화된 데이터를 복구할 수 없다. 반드시 별도 안전한 곳에 보관하라. openssl rand -hex 32로 생성한 값을 환경변수 파일(.env)에 저장하고 백업하라.
helm repo add trek https://mauriceboe.github.io/TREK/
helm install trek trek/trek \
--set env.ENCRYPTION_KEY=$(openssl rand -hex 32) \
--set env.TZ=Asia/Seoul
trek.example.com {
reverse_proxy localhost:3000
# Caddy가 자동으로 Let's Encrypt TLS 처리
}
난이도별 5단계 과제. 개발 환경 없어도 LV.1~2는 바로 시작 가능.
Docker Desktop이 설치된 환경에서 TREK을 로컬로 실행하고, 실제 여행 계획을 만들어보자. 지도에 장소를 핀으로 찍고, 일차별로 드래그해서 배치하고, 예산을 입력해본다.
Google Cloud Console에서 OAuth 2.0 클라이언트를 만들고, docker-compose.yml에 OIDC_ISSUER=https://accounts.google.com 등 환경변수를 추가해 "구글로 로그인" 버튼을 활성화한다. OIDC 콜백 URL 설정도 배울 수 있다.
TREK 관리자 패널에서 MCP 애드온을 활성화하고 API 키를 발급한다. Claude Desktop의 MCP 설정에 TREK 서버를 추가해 "다음 달 오사카 2박3일 일정 만들어줘"라고 Claude에게 물으면 실제 TREK에 여행이 생성되는 것을 경험한다.
TREK 소스를 로컬에서 빌드(npm run dev)하고, NestJS에 새 엔드포인트를 추가해본다. 예: GET /api/trips/:id/stats로 여행별 통계(총 장소 수, 총 예산, 이동 거리)를 반환하는 엔드포인트. 미들웨어 → 컨트롤러 → 서비스 → DB 쿼리 전체 흐름을 직접 짜본다.
server/src/mcp/tools/에 새 도구를 추가한다. 예: "여행 내 모든 장소의 영업시간을 Google Places API로 업데이트하는 도구". @modelcontextprotocol/sdk의 도구 정의 패턴을 따르고, OAuth 스코프를 적절히 설정해 Claude가 호출할 수 있도록 한다. 완성 시 GitHub PR로 기여해보자.
TREK을 완전히 이해하고 기여하기까지 4주 계획.
| 주차 | 주제 | 학습 자료 / 실습 |
|---|---|---|
| 1주차 | Node.js + TypeScript 기초, NestJS 튜토리얼 | NestJS 공식 First Steps, TypeScript Handbook — 타입, 인터페이스, 데코레이터 집중 |
| 2주차 | React 19 + Zustand, Vite PWA 설정 | React 공식 튜토리얼, vite-plugin-pwa 문서, Workbox 캐싱 전략 가이드 |
| 3주차 | WebSocket 실시간 통신, SQLite WAL, 인증(JWT/OIDC/WebAuthn) | ws 라이브러리 공식 문서, SQLite WAL mode 설명, SimpleWebAuthn 튜토리얼 |
| 4주차 | MCP 프로토콜, Leaflet/Mapbox, Docker 보안 설정 | MCP 공식 스펙 문서, Leaflet 퀵스타트, Docker 보안 모범 사례(read_only, cap_drop) |
TREK 코드베이스를 읽을 때 반드시 알아야 할 용어 모음.
| 키워드 | 설명 |
|---|---|
| NestJS | Node.js용 DI(의존성 주입) 백엔드 프레임워크. Angular 스타일의 모듈-컨트롤러-서비스 구조. Express 위에 올라간다. |
| SQLite WAL | Write-Ahead Logging. SQLite의 쓰기 방식을 바꿔 읽기와 쓰기를 동시에 처리 가능하게 하는 모드. TREK처럼 단일 파일 DB에서 동시 접근 성능을 높인다. |
| WebSocket (ws) | HTTP와 달리 한번 연결하면 서버-클라이언트가 양방향으로 지속 통신. TREK 실시간 협업의 핵심 채널. |
| Zustand | React용 경량 전역 상태 관리 라이브러리. Redux보다 코드가 훨씬 간결하며 훅 기반. |
| Leaflet | 오픈소스 자바스크립트 지도 라이브러리. OpenStreetMap 타일을 무료로 사용 가능. TREK의 기본 지도 엔진. |
| Mapbox GL | WebGL 기반 3D 지도 렌더링 라이브러리. 3D 빌딩, 지형, 고성능 벡터 타일. API 키와 사용료 필요. |
| Workbox | Google이 만든 Service Worker 라이브러리. PWA 오프라인 캐싱 전략(Cache-First, Network-First 등)을 간편하게 구현. |
| Dexie | IndexedDB(브라우저 내장 DB)를 쉽게 쓰게 해주는 래퍼 라이브러리. TREK 오프라인 로컬 캐시에 사용. |
| OIDC | OpenID Connect. OAuth 2.0 기반 신원 확인 표준. 구글, Keycloak 등 다양한 IdP와 연동 가능. |
| WebAuthn / 패스키 | 비밀번호 없이 생체인증(지문/얼굴) 또는 보안 키로 로그인. FIDO2 표준 구현. |
| TOTP 2FA | Time-based One-Time Password. Google Authenticator 같은 앱으로 생성하는 6자리 시간 기반 OTP. |
| MCP (Model Context Protocol) | AI가 외부 도구/데이터에 표준 방식으로 접근하는 Anthropic 제안 프로토콜. TREK은 150+ 도구를 OAuth 2.1로 보호해 노출. |
| npm workspace | 하나의 저장소에 여러 패키지를 관리하는 Node.js 기능. TREK: shared/server/client 세 패키지가 공존. |
| Zod | TypeScript 우선 런타임 스키마 검증 라이브러리. 타입과 검증이 동시에 정의되어 타입 안전 API 경계를 만든다. |
| bcryptjs | 비밀번호 해시 라이브러리. 솔트 기반 해시로 레인보우 테이블 공격 방어. 순수 JS 구현. |
| KDE Itinerary | 예약 확인 이메일/PDF에서 항공편·숙박 정보를 자동 추출하는 오픈소스 도구. TREK이 선택 의존성으로 지원. |
| AGPL-3.0 | GNU Affero GPL. 소스 수정 후 네트워크로 서비스하면 수정 소스도 공개해야 하는 강한 카피레프트 라이선스. |
공식 문서와 관련 기술 학습 자료.
| 자료 | 링크 | 설명 |
|---|---|---|
| TREK GitHub | github.com/mauriceboe/TREK | 소스코드, 이슈, 릴리스 노트 |
| TREK 라이브 데모 | demo.liketrek.com | 계정 없이 직접 체험 가능 |
| Docker Hub | hub.docker.com/r/mauriceboe/trek | 공식 이미지, 릴리스 태그 |
| TREK Discord | discord.gg/NhZBDSd4qW | 커뮤니티, 개발자 소통 |
| NestJS 공식 문서 | docs.nestjs.com | First Steps, 미들웨어, DI 가이드 |
| MCP 공식 문서 | modelcontextprotocol.io | MCP 프로토콜 스펙, SDK 가이드 |
| Leaflet 공식 | leafletjs.com | 지도 라이브러리 튜토리얼 및 API |
| SimpleWebAuthn | simplewebauthn.dev | 패스키 서버·클라이언트 구현 가이드 |
| vite-plugin-pwa | vite-pwa-org.netlify.app | Vite PWA 설정, Workbox 전략 |
| SQLite WAL 모드 | sqlite.org/wal.html | WAL 동작 원리 공식 문서 |