(Nil) => {To.Dev;}

Apps

  1. May 2026

DotEmber

바로가기 .Ember

차분한 ADHD-first 맥 앱. 저널, 할 일, 캘린더, 타이머, 뉴스, 통계까지 한 화면에.

베타
  • macOS

.Ember는 macOS용 차분한 ADHD-first 생산성 앱입니다 - 저널, 할 일, 캘린더, 타이머, 뉴스, 통계를 한 조용한 화면에 담아, 그저 켜기만 하면 되도록 만들었습니다.

아래 페이지는 디자인 결정과 그 이유에 대한 이야기입니다.

만들게 된 이유

Journal 화면
Journal 화면

다른 많은 분들처럼, 저도 저널, 할 일, 캘린더, 타이머, RSS를 각각 다른 앱으로 쓰고 있었습니다. 그렇게 비용이 조용히 쌓였습니다 - 다섯 개의 다른 환경, 다섯 가지의 다른 방식. 아침에 세 개를 열어 본 뒤 네 번째에 다다를 즈음에는 이미 의욕이 사라져 있었습니다.

더 정교한 단일 앱이 필요한 게 아니었습니다. 제 하루에 맞는 앱이 필요했습니다. 그래서 .Ember를 만들기 시작했습니다.

ADHD-first: 마찰을 최소한으로

Focus Daily Entry
Focus Daily Entry

하나의 히어로 기능에 집중하고, 나머지를 그 주위에 쌓았습니다. 저 자신의 ADHD 성향 때문에 가장 필요했던 건 최소한의 마찰로 시작하는 집중 이었습니다 - 그래서 Focus Daily Entry 모드가 중심축이 되었습니다. 매일 가장 먼저 열리는 화면은 전체 화면 Journal + Todo 작성 마법사입니다. 사이드바도, 탭도, 어느 화면에 있어야 할지 결정할 필요도 없이 - 그저 쓰고, 오늘 할 일을 적으면 됩니다.

Block 화면
Block 화면

나머지 기능은 모두 이 모드를 중심으로 만들어졌습니다. Block 기능으로 할 일을 맥락별로 묶고(Routine, Work, Personal, 본인이 만드는 그 무엇이든), 한 블록을 그대로 타이머로 실행할 수 있습니다. 명령 팔레트(Cmd+K)는 어디서든 단축키 한 번에 열립니다. 반복 규칙으로 생성되는 할 일은 매일 아침 자동으로 만들어지지만, “오늘 아무것도 못했다”는 느낌을 부풀리지 않습니다 - 시스템이 자동 갱신된 습관과 본인이 직접 입력한 일을 구분하기 때문입니다.

디자인: 최대한 미니멀하게

Timer 화면
Timer 화면

시각 언어는 제가 매일 쓰는 앱들에서 빌려왔습니다 - Obsidian의 vault 모델과 네이티브 macOS의 차분함. 좁은 간격, 두 톤 보라 액센트, 장식적인 크롬은 없음. 좁은 창에서는 사이드바가 아이콘만 남도록 접혀, macOS의 트래픽 라이트 버튼 클러스터가 사이드바 안에 깔끔하게 자리잡습니다. 모든 애니메이션은 100-200ms 안에 끝납니다 - 반응성이 느껴질 만큼 빠르고, 의도가 느껴질 만큼 천천히. 언제나 가능한 한 미니멀하게, 시선이 머물러야 할 곳에 머물 수 있도록.

Local-first: 데이터는 언제나 내 것

Sync 설정
Sync 설정

다른 많은 Obsidian 사용자분들처럼, 저도 데이터를 직접 보관하는 게 마음이 편합니다. .Ember는 모든 것을 사용자가 선택한 폴더 안의 plain Markdown으로 저장합니다 - iCloud Drive 폴더, Documents 하위 폴더, 심지어 USB 스틱까지 가능합니다. Markdown이 원본이고, 그 옆에 있는 SQLite 데이터베이스는 언제든 다시 만들 수 있는 검색 인덱스 캐시일 뿐입니다.

iCloud 동기화는 선택사항입니다. Apple의 CloudKit private DB를 통해 데이터는 사용자 본인의 iCloud 계정 안에만 머무릅니다. NilToDev 서버는 없습니다. RSS 피드는 게시자에게서 직접 가져옵니다. AI 기능은 사용자 본인의 API 키를 사용하며 기본값은 OFF 입니다.

Obsidian 사용자라면. 선택 사항인 Obsidian Sync 기능이 있지만, 두 vault는 반드시 서로 다른 폴더여야 합니다 - 하나는 .Ember의 vault, 하나는 Obsidian의 vault. 같은 경로를 가리키시면 안 됩니다. Sync 엔진이 설정하신 주기로 두 vault 사이의 Markdown을 미러링하며, 첫 실행 시 dry-run 미리보기를 띄우고, 충돌이 발생한 파일은 자동으로 보존됩니다. 이미 .obsidian/ 작업공간이 있는 폴더를 .Ember의 vault로 지정하는 사용 방식은 지원하지 않습니다. Q&A의 “Obsidian과 함께 사용할 수 있나요?” 항목을 참고해 주십시오.

Stack: 첫 시도

이번이 제 첫 맥 앱이라, 처음에는 가장 빠르게 움직일 수 있는 스택을 골랐습니다.

  • Electron + electron-vite - 별다른 설정 없이도 크로스 플랫폼 준비 완료.
  • React 19 + TypeScript - 솔직히, 문서와 커뮤니티가 가장 풍부한 생태계.
  • Zustand - 기능별 단일 store, 보일러플레이트도 provider 트리도 없음.
  • better-sqlite3 - 동기, 네이티브, FTS5 검색이 즉시 반환될 만큼 빠름.
  • Apple CloudKit JS - 백엔드를 직접 만들지 않고도 동기화.

이 스택으로 MVP까지는 빠르게 도달했습니다. 하지만 Mac App Store에 패키징할 시점이 되자 허점이 분명해졌습니다. 샌드박스를 고려한 코드 사이닝이 며칠씩 이어지는 패키징 버그의 연쇄가 되었습니다. CloudKit JS는 fetchRecordChanges를 노출하지 않아서, 동기화마다 풀 쿼리를 던져야 했습니다. macOS 26의 Liquid Glass는 Chromium renderer에서 닿을 수 없었습니다. 그리고 이미 Swift로 짜둔 Calendar + StoreKit 코드는 renderer에서 호출하기 위해 Node native module로 다시 감싸야 했습니다.

하지만 진짜 이유는 따로 있었습니다 - .Ember를 iOS에서도 쓰고 싶었기 때문입니다. Electron으로 가면 iOS는 공유할 게 거의 없는 별도의 React Native 코드베이스가 됩니다. Swift라면 iOS는 같은 코어 위의 형제 타깃일 뿐입니다. 패키징의 고생과 Liquid Glass의 격차는 그 결정을 더 쉽게 만들어 주었을 뿐입니다.

그래서 다시 짰습니다.

Stack: 다시 짠 버전

  • SwiftUI + AppKit - SwiftUI는 chrome, AppKit은 hot path(캘린더 그리드, 사이드바, 드래그앤드롭, 메뉴바 트레이). Markdown 편집은 CodeMirror 6.
  • SwiftData - 보조 캐시. vault 안의 Markdown 파일이 여전히 원본입니다.
  • SQLite + FTS5 - 저널 항목과 할 일 전반에 걸친 풀텍스트 검색.
  • CloudKit (private DB) + iCloud Drive - 구조화된 레코드는 CloudKit으로, vault 파일은 iCloud Drive로 동기화. 선택 사항인 Obsidian-mirror로 임의의 폴더와 양방향 동기화도 가능합니다.
  • StoreKit 2 - 14일 체험과 일회성 IAP, 중간에 JS 다리 없음.
  • EventKit - Apple 계정용 네이티브 macOS Calendar 연동.
  • Google Calendar 어댑터 - 멀티 계정 지원, Google Tasks(읽기 전용) 포함.

Mac App Store 전용, 샌드박스, 전용 iCloud 컨테이너. 더 가벼운 바이너리, 더 적은 구성 요소, 그리고 iOS 앱이 두 번째 코드베이스 없이 그대로 올라탈 수 있는 깨끗한 토대로 만들었습니다.