7.5k 스타의 데스크탑 노트 앱이 어떻게 Notion·Obsidian 사이에서 자리잡았는지, Electron + TypeScript + Go 미들웨어 + P2P 동기화 스택을 한 번에 들여다본다.
기본 개념부터.
Anytype(애니타입)은 개인용 데스크탑 지식 관리 앱이다. macOS·Windows·Linux에서 돌고, 노트·할 일·위키·일기·간단한 데이터베이스를 한 곳에 모은다. Notion(노션)이나 Obsidian(옵시디언)을 써본 사람이라면 "그 부류"라고 보면 된다.
다른 점은 세 가지다. 첫째, 데이터가 내 컴퓨터 안에만 산다. 클라우드에 자동으로 올라가지 않는다. 둘째, 여러 기기끼리 동기화할 때는 P2P(피어 투 피어) 방식으로 직접 주고받고 그 트래픽은 종단간 암호화돼서 회사 서버조차 못 읽는다. 셋째, 페이지의 "타입(Type)"을 사용자가 직접 정의한다 — 책·영화·프로젝트·사람 같은 카테고리를 본인 데이터 모델로 만들 수 있다.
이 글의 핵심 메시지.
Anytype은 Notion의 블록 편집 경험 + Obsidian의 로컬 저장 철학 + Signal 수준의 종단간 암호화를 한 데스크탑 앱에 합친 시도다. Electron 셸 안에 React 화면을 띄우되, 데이터 처리는 Go로 짠 자체 미들웨어 anytype-heart가 맡는다.
핵심 베팅: "클라우드 SaaS가 사라져도 데이터를 인질로 잡히지 않는다"는 가치가 이제는 충분히 매력적이라는 것. 그래서 2026년 5월 기준 7.5k 스타, 32,225 커밋, 285번의 릴리스를 찍었다.
트렌딩 이유 — 경쟁 제품과 다른 점.
2026년 5월 25일 TrendShift 1위에 올랐다. 그냥 "노트 앱이 떴다"가 아니다. 같은 시기 트렌딩 상위권에 PDF 자동화, 윈도우 유틸, 지식 큐레이션 같은 "클라우드 의존을 줄이는 도구"들이 동시에 올라왔는데, Anytype은 그 흐름의 헤드라인이었다.
2024년 에버노트(Evernote) 가격 인상 사건 — 무료 사용자에게 노트 50개·노트북 1개 제한을 걸자 수십만 명이 "내 10년치 노트가 인질이 됐다"고 반발했다. Anytype 사용자 폭증의 한 축이 이 시점이다.
Anytype의 답은 명확하다: "우리 서버에 의존하지 마라. 우리가 망해도 데이터는 네 컴퓨터에 그대로 있다." 가입조차 이메일 대신 12단어 복구 문구(seed phrase)로 만든다 — 암호화폐 지갑처럼.
경쟁 제품 비교로 정리하면:
Notion은 클라우드-퍼스트라서 인터넷이 없으면 못 쓰고, 회사 정책 한 번 바뀌면 가격·기능이 흔들린다. Anytype은 오프라인에서도 그대로 돌고, P2P 동기화는 옵션이다. 대신 Notion의 풍부한 협업·통합·임베드 기능은 아직 없다.
Obsidian은 마크다운 파일을 직접 다루는 방식이라 텍스트 노트엔 강하지만 데이터베이스·관계형 뷰는 플러그인 의존이 크다. Anytype은 처음부터 Type(타입)과 Relation(관계) 개념을 내장해서 노션 식 데이터베이스를 기본 제공한다.
Logseq도 로컬-퍼스트지만 동기화는 Logseq Sync(유료) 또는 git 같은 외부 도구에 맡긴다. Anytype은 동기화 자체가 P2P 종단간 암호화로 내장돼 있다. any-sync라는 별도 라이브러리로 분리해서 만들었다.
프론트엔드·백엔드·인프라 한 번에.
레포 언어 비율부터 보면 흥미롭다. TypeScript 88.8%, SCSS 9.9%, JavaScript 0.9%, Go는 0.2%뿐이다. 그런데 앱의 진짜 두뇌는 Go로 쓴 별도 레포 anytype-heart에 있다 — 99.7% Go, 19,746 커밋. 이 레포 anytype-ts는 그 두뇌 위에 씌운 데스크탑 셸이다.
화면은 React로 그리고, 상태 관리는 MobX로 한다. MobX는 Redux보다 "마술적"이지만 코드가 짧다 — makeAutoObservable 한 줄로 클래스 안의 속성·메서드가 전부 반응형으로 바뀐다. 컴포넌트는 observer로 감싸면 끝.
블록 단위 텍스트 편집은 페이스북이 만든 Lexical로. @lexical/link, @lexical/list, @lexical/markdown, @lexical/rich-text, @lexical/react 등 7개 서브 패키지를 직접 조합해서 자체 에디터를 구축한다. Notion 식 슬래시(/) 명령, 인용, 코드 블록, 수식 같은 걸 다 여기서 처리.
그래프 뷰는 D3의 force-directed layout으로 노드를 떠다니게 만들고, 무거운 렌더링은 Pixi.js(WebGL 2D)로 GPU 가속. 다이어그램은 Mermaid, 손그림 도형은 Excalidraw, 그래프 자동 배치는 ELK(Eclipse Layout Kernel) — 이 정도면 "노트 앱"이라기보다 작은 IDE 수준이다.
PDF는 pdfjs-dist 5 + react-pdf 10, 수식은 KaTeX, 드래그앤드롭은 @dnd-kit(core/modifiers/sortable/utilities 4종), 가상 리스트는 react-virtualized, 모션은 motion 12, 코드 하이라이트는 prismjs. 한 마디로 "최신 React 생태계 풀세트".
Electron 41(거의 최신)을 쓰고, 배포는 electron-builder로 macOS .dmg(arm64+x64), Windows NSIS 인스톨러, Linux AppImage/.deb/.rpm/.tar.gz까지 한 명령으로 만든다. 자동 업데이트는 electron-updater가 DigitalOcean Spaces(anytype-release.fra1.digitaloceanspaces.com)에서 받아온다.
UI 옆에서 anytypeHelper라는 Go 바이너리가 자식 프로세스(child process)로 실행된다. 이게 별도 레포 anyproto/anytype-heart로 관리되고, 99.7% Go, 19,746 커밋, 485 릴리스 규모다. 데이터 저장·검색·동기화·암호화 모든 무거운 일을 여기서 처리한다.
UI와 미들웨어는 gRPC-Web으로 통신한다. .proto 파일에서 TypeScript 바인딩을 ts-proto로 자동 생성. 즉 UI는 미들웨어가 노출한 API를 "원격 함수처럼" 호출할 뿐이다.
Anytype의 P2P + 종단간 암호화 핵심 로직은 any-sync라는 또 다른 레포로 분리돼 있다. CRDT(Conflict-free Replicated Data Type, 충돌 없는 복제 데이터 타입) 기반으로 여러 기기에서 동시에 편집해도 자동으로 합쳐진다. 회사가 백업 노드를 제공하지만, 사용자가 직접 자체 노드를 띄울 수도 있다.
패키지 매니저는 Bun(npm·yarn 대체, Zig로 작성), 번들러는 Vite 6 + @vitejs/plugin-react-swc(SWC 컴파일러로 Rust 기반 초고속 빌드), 린트·포맷은 Biome 2.4(Rust 기반 ESLint+Prettier 대체) + ESLint 9 병행. 테스트는 Vitest 4, 컴포넌트 카탈로그는 Storybook 8.
개발 환경 설치는 Nix flake(flake.nix) 한 줄로 끝낸다. nix develop --command $SHELL로 들어가면 Go·Node·Python 의존성이 한 번에 셋팅된다.
시스템 구조도 + 핵심 설계 패턴.
그림 한 장으로 전체 흐름을 그리면 이렇다.
┌──────────────────────────────────────────────────────────┐
│ 사용자 기기 A (예: 맥북) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Electron Renderer (Chromium 안의 React 화면) │ │
│ │ React 18 + MobX + Lexical 에디터 │ │
│ │ ↑↓ gRPC-Web (HTTP/1.1 위의 protobuf) │ │
│ └────────────────────────────────────────────────────┘ │
│ ↕ IPC (preload.cjs) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Electron Main (electron.js, Node.js) │ │
│ │ 윈도우 관리·트레이·OS 통합·OS 키체인(keytar) │ │
│ └────────────────────────────────────────────────────┘ │
│ ↕ child_process.spawn │
│ ┌────────────────────────────────────────────────────┐ │
│ │ anytypeHelper (Go 바이너리, anytype-heart) │ │
│ │ ─ 로컬 객체 저장소 (암호화된 BoltDB류) │ │
│ │ ─ Tantivy 풀텍스트 검색 │ │
│ │ ─ any-sync 라이브러리 │ │
│ └────────────────────────────────────────────────────┘ │
└──────────┬───────────────────────────────────────────────┘
│
│ any-sync 프로토콜 (libp2p 추정, 종단간 암호화)
▼
┌──────────────────────────────────────────────────────────┐
│ 다른 피어들 — 같은 사용자의 다른 기기 / 백업 노드 │
│ (모두 암호화된 덩어리만 보고 평문은 절대 보이지 않음) │
└──────────────────────────────────────────────────────────┘
UI(TypeScript)와 데이터 처리(Go)를 같은 프로세스에 넣지 않고 완전히 다른 바이너리로 분리했다. 둘은 gRPC로만 대화한다. 덕분에 iOS·안드로이드용 클라이언트도 같은 anytype-heart를 gomobile로 묶어서 그대로 재활용한다.
P2P + 종단간 암호화 로직을 any-sync라는 별도 레포로 빼냈다. CRDT(충돌 없는 복제 데이터 타입)로 여러 기기 동시 편집을 자동 병합한다. "라이브러리화"의 의미는 명확하다 — 다른 앱도 같은 동기화 엔진을 가져다 쓸 수 있게.
모든 객체는 Type(책·영화·프로젝트…)을 가지고, Type끼리는 Relation(저자·태그·연관 페이지…)으로 연결된다. SQL의 테이블·외래키 같은 개념을 사용자가 직접 정의할 수 있게 한 것 — Notion 데이터베이스를 한 단계 더 일반화한 모델.
가입 시 이메일·비밀번호를 받지 않는다. 대신 12단어 복구 문구(seed phrase)를 만들어서 사용자가 직접 보관한다. 암호화폐 지갑과 같은 원리. 잃어버리면 회사조차 복구 못 한다 — 그게 "진짜 zero-knowledge"의 대가다.
회사가 "비밀번호 잊으셨나요?"를 못 해준다. 종단간 암호화의 진짜 의미는 회사도 사용자의 데이터를 볼 수 없다는 것이고, 그 대가는 회사가 사용자 계정도 복구해 줄 수 없다는 것. Anytype 쓰는 사람은 시드를 비밀번호 매니저나 종이에 따로 백업해야 한다.
앱이 처음 시드를 보여줄 때 화면 캡처가 아니라 직접 손으로 메모하고, 1Password/Bitwarden 같은 비밀번호 매니저의 "Secure Note"에 넣어두자. 두 곳에 보관하면 좋다 — 하나는 디지털, 하나는 종이.
레포 폴더가 어떤 의미인지 한 번에.
anytype-ts/ ├── src/ ← 메인 TypeScript 소스 (88.8%) │ React 컴포넌트·MobX 스토어·gRPC 클라이언트 ├── electron/ ← Electron 메인 프로세스 │ ├── js/ │ │ └── preload.cjs ← 렌더러 ↔ 메인 IPC 다리 │ └── hook/ ← 빌드 훅 (beforebuild·aftersign·sign.js) ├── extension/ ← 크롬·파이어폭스 Web Clipper 확장 │ 웹페이지 → Anytype 노트로 저장 ├── go/ ← Native Messaging Host (Go 작은 바이너리) │ 브라우저 확장 ↔ 데스크탑 앱 통신 ├── dist/ ← 빌드 산출물 │ ├── anytypeHelper ← Go 미들웨어 바이너리가 들어옴 │ ├── anytypeHelper.exe ← 윈도우용 │ └── lib/ ← protobuf 정의 + 생성된 JSON ├── nightly/ ← 야간 빌드 설정 ├── patches/ ← patch-package로 노드 모듈 패치 ├── scripts/ ← 빌드·릴리스 노트 자동화 스크립트 ├── docs/ ← 개발자 문서 ├── .storybook/ ← Storybook 컴포넌트 카탈로그 설정 ├── .claude/ ← Claude Code 설정 (AI 코딩 친화) │ ├── package.json ← 의존성 + 70개+ npm scripts ├── flake.nix ← Nix 재현 가능 빌드 환경 ├── bun.lock ← Bun 의존성 잠금 ├── biome.json ← Biome 린트 설정 ├── vite.config.ts ← 메인 Vite 빌드 설정 ├── vite.extension.config.ts ← 확장 빌드용 ├── vite.web.config.ts ← 브라우저 직접 실행용 ├── vite.worker.config.ts ← Pixi.js Web Worker용 ├── vite.auto-observer.ts ← auto-observer 빌드용 ├── middleware.version ← anytype-heart 미들웨어 버전 핀 ├── update.sh ← 미들웨어 다운로드 스크립트 └── AGENTS.md / CLAUDE.md ← AI 코딩 에이전트용 컨텍스트
주목할 점 1. Vite 설정 파일이 5개나 된다 — 데스크탑·확장·웹·워커 빌드를 각각 다르게 한다. 모노레포가 아니라 단일 레포지만 빌드 타깃은 사실상 4개다.
주목할 점 2. extension/과 go/ 폴더의 조합. 웹 클리퍼 확장(JS)이 직접 데스크탑 앱과 IPC를 못 하니까, 작은 Go 바이너리(Native Messaging Host)를 다리로 끼워 넣었다. 브라우저 표준 메시징 프로토콜이다.
주목할 점 3. .claude/와 AGENTS.md가 루트에 있다. Claude Code·Cursor 같은 AI 코딩 에이전트가 이 레포를 읽을 때 "프로젝트 구조와 아키텍처를 먼저 이해하라"는 안내문 역할이다. AGENTS.md를 보면 MobX 패턴·IPC 패턴·라우팅까지 자세히 설명해 둔다.
이 레포에서 무엇을 배울 수 있는가.
"왜 Electron인데 Go 바이너리가 같이 도는가"를 패키지부터 IPC까지 추적해 보면, Electron 앱이 단순 "웹 페이지 + Node"가 아니라 여러 런타임을 묶은 마이크로커널처럼 설계될 수 있다는 걸 배운다. electron.js → preload → renderer → gRPC → child_process Go까지 한 흐름을 따라가 보자.
scripts/generate-protos.sh 한 줄로 ts-proto가 ../anytype-heart/*.proto를 읽어 TypeScript 인터페이스 + gRPC 클라이언트 클래스를 자동 생성한다. "타입 정의를 한 곳에 두고 양쪽 언어가 공유"하는 패턴은 Go↔Python·Rust↔TS 모든 다언어 프로젝트에서 그대로 쓰인다.
Redux 보일러플레이트(action·reducer·dispatcher)가 무겁다고 느꼈다면 MobX가 답이 될 수 있다. makeAutoObservable 한 줄 + observer 래퍼로 끝. Anytype은 도메인별 스토어 분리(authStore·blockStore·detailStore…)로 거대한 앱을 단순한 클래스 모음으로 정리했다.
"Notion 같은 블록 에디터를 직접 만들어 보고 싶다"면 Lexical(페이스북 차세대 에디터 프레임워크)이 정답이다. Anytype의 소스는 Lexical 위에 슬래시 명령·블록 변환·드래그 정렬 같은 고급 기능을 실제 프로덕션 코드로 어떻게 짜는지 보여주는 가장 좋은 사례다.
package.json의 "build" 블록 하나에 macOS notarize·Windows signtool·Linux AppImage/.deb/.rpm/.tar.gz 설정이 모두 들어 있다. "한 명령으로 5개 운영체제 배포"가 어떻게 가능한지 그대로 베끼면 자기 일렉트론 앱에 바로 적용할 수 있다.
"내 컴퓨터에선 되는데" 문제를 끝내는 가장 강력한 도구가 Nix flake다. flake.nix 한 파일에 Go·Node·Python·protoc 버전을 못박아 두면, 어떤 운영체제든 nix develop 한 줄로 같은 환경이 만들어진다. Anytype의 flake.nix는 실제 프로덕션 프로젝트가 Nix를 어떻게 쓰는지 보여주는 살아있는 예시다.
돌리려면 뭐가 필요한가.
최종 사용자(end user)가 데스크탑 앱을 그냥 쓰기만 한다면 요구사항은 가볍다.
개발자(developer)가 직접 빌드하려면 의존성이 좀 까다롭다.
anytype-heart 빌드용setuptools — ARM에서 keytar 리빌드용protoc(protobuf 컴파일러) — TS 바인딩 재생성 시nix develop 한 줄 — 위 전부 자동 셋팅macOS 빌드를 직접 서명·공증하려면 Apple Developer ID(연회비 $99)가 추가로 필요하지만, 환경 변수 ELECTRON_SKIP_NOTARIZE=1로 건너뛸 수 있다 — 개인 학습용 빌드는 이 옵션을 쓰자.
난이도별 5단계.
download.anytype.io에서 OS에 맞는 인스톨러 받고, 시드 문구를 비밀번호 매니저에 백업한다. "독서 노트" Type을 하나 만들고 책 제목·저자·평점 Relation 3개를 추가. "내 데이터를 내가 정의한다"는 감각을 먼저 체험.
데스크탑 + 휴대폰(또는 두 데스크탑) 같은 와이파이에서 같은 시드로 로그인. 비행기 모드로 인터넷 끊고도 LAN 안에서 동기화가 도는지 확인. 네트워크 캡처 도구로 트래픽이 암호화돼 있는지 직접 보면 어디서 어디로 데이터가 흐르는지 감이 잡힌다.
레포 클론 → bun install → ./update.sh로 미들웨어 다운로드 → bun run start:dev. Electron 창이 뜨면 --user-data-dir=./my_dir로 사용자 데이터 분리. 핫 리로드 확인: src/ 안 React 컴포넌트를 수정하면 즉시 반영되는지.
bun run build:ext → bun run ext:manifest:chromium으로 크롬 매니페스트 전환. Chrome 확장 페이지에서 "압축 해제된 확장 프로그램 로드"로 dist/extension/ 폴더 선택. Native Messaging Host(go/nativeMessagingHost.go)가 데스크탑과 어떻게 연결되는지 추적해 보자. 여기서 IPC 기초가 단단해진다.
anyproto/anytype-heart 레포를 클론 → make install-dev-js로 빌드 → 자체 작성한 Node.js 스크립트에서 grpc-web으로 미들웨어 호출. ObjectCreate·ObjectSearch 같은 API를 직접 호출해 보면 "Anytype은 UI 빼고도 헤드리스 백엔드로 쓸 수 있다"는 점이 보인다. 자기만의 클라이언트(터미널? 텔레그램 봇?)를 만들 발판.
주차별 6주 계획.
공식 튜토리얼(electronjs.org/docs)로 Hello World → Main/Renderer/Preload 3가지 프로세스가 왜 분리됐는지 이해. contextBridge·ipcRenderer 패턴 직접 작성. Anytype의 electron/js/preload.cjs를 읽으면 실전 패턴이 머리에 들어온다.
Redux 안 써본 사람도 좋고, 써본 사람은 비교 차원에서. makeAutoObservable + observer + Context 패턴 작은 Todo 앱으로 직접. Anytype src/store/를 둘러보고 어떻게 도메인별로 스토어를 쪼갰는지 모방.
.proto 파일로 메시지 정의 → ts-proto로 TypeScript 자동 생성 → grpc-web으로 브라우저에서 호출. 작은 Go 서버 + React 클라이언트 토이 프로젝트. JSON REST보다 더 짧은 코드로 타입 안전한 API를 만들 수 있다는 걸 체감.
"local-first software" 매니페스토(Ink & Switch, 2019) 읽기 → Yjs 또는 Automerge 같은 CRDT 라이브러리로 작은 협업 에디터 만들기. any-sync를 직접 쓰긴 어렵지만 같은 원리의 라이브러리로 충분히 이해 가능.
Lexical 공식 플레이그라운드 → 자기만의 노드 타입(Custom Node) 등록 → 슬래시 명령 메뉴 추가. Anytype의 블록 편집기가 어떻게 Lexical 위에서 작동하는지 src/component/block/ 디렉토리 따라 읽기.
자기 Electron 앱을 electron-builder로 macOS .dmg + Windows .exe + Linux AppImage 동시 빌드. 코드 사이닝·notarize·자동 업데이트(Generic provider)까지 한 사이클 돌려 보면 "개인 개발자도 진짜 데스크탑 앱을 배포할 수 있다"는 자신감이 생긴다.
이 글에 나온 용어 한눈에 정리.
.proto 파일로 메시지 정의..proto 파일 하나 만들어서 양쪽 언어가 공유하게 해 보면, JSON으로 손짠 인터페이스가 얼마나 깨지기 쉬웠는지 새삼 느낀다.makeAutoObservable 한 줄의 충격을 경험해 보자.flake.nix로 못박아 두면 새 컴퓨터·동료 합류·CI 셋팅이 한 줄로 끝난다. Anytype의 flake.nix를 복사해서 시작.