베어: 타이포그래피 우선 글쓰기
Bear의 타이포그래피 우선 디자인이 Apple Design Award를 수상한 방법: 중첩 태그, 테마 시스템, 집중 모드, 인라인 Markdown. Swift 구현 패턴 포함.
Bear: 타이포그래피 중심의 글쓰기
"Bear를 사용하면 진정한 Apple 컴퓨터를 다시 사용하는 느낌을 받게 됩니다. 로딩 스피너도, 스켈레톤 로딩 화면도, 토스트 메시지도 없습니다. 오직 부드러운 애니메이션과 언제나 즉시 사용 가능한 콘텐츠만 있을 뿐입니다."
Bear는 집중을 방해하지 않는 디자인의 교과서입니다. 타이포그래피부터 태그 시스템까지 모든 결정이 관리가 아닌 사고에 집중하고 싶은 작가를 위해 설계되었습니다.
핵심 요약
- 로딩 상태 제로 - 콘텐츠는 항상 준비되어 있으며, 동기화는 백그라운드에서 보이지 않게 처리됩니다
- 태그가 폴더를 대체 - 글을 쓰면서 인라인으로
#tags를 입력하는 것이 나중에 폴더를 관리하는 것보다 효율적이며, 노트가 여러 곳에 동시에 존재할 수 있습니다 - 독자를 배려하는 타이포그래피 컨트롤 - 폰트, 크기, 줄 높이, 너비 조절로 사용자가 자신의 눈에 맞게 최적화할 수 있습니다
- 테마로 모든 것을 한 번에 변경 - 28개 이상의 큐레이션된 테마가 개별 색상 선택기보다 낫습니다
- 탈출구로서의 집중 모드 - 방해 요소 제거가 더 필요할 때, 한 번의 제스처로 모든 것을 숨깁니다
Bear가 중요한 이유
Bear는 2017년 Apple Design Award와 여러 Editor's Choice 수상을 통해 노트 앱이 강력하면서도 아름다울 수 있음을 증명했습니다.
주요 성과: - 비개발자도 Markdown을 쉽게 사용할 수 있게 함 - 폴더의 유연한 대안으로 중첩 태그를 고안 - 다양한 글쓰기 상황을 위한 28개 이상의 테마 제작 - 진정한 블랙을 위한 OLED 전용 테마(Dieci) 설계 - 로딩 상태 제로: 콘텐츠는 항상 준비 완료
핵심 디자인 철학
마찰 제거 원칙
Bear는 생각과 텍스트 사이의 모든 장애물을 제거합니다:
마찰 패턴 (다른 앱들) BEAR의 접근 방식
───────────────────────────────────────────────────────────────────
생성 전 폴더 대화상자 그냥 바로 타이핑 시작
화면을 가리는 서식 툴바 인라인 Markdown, 보이지 않음
흐름을 끊는 동기화 스피너 백그라운드 동기화, 표시 없음
메뉴에 흩어진 설정 타이포그래피 항상 손 닿는 곳에
색상 선택기로 하이라이팅 테마로 모든 것을 한 번에 변경
핵심 인사이트: 모든 UI 요소는 잠재적 방해 요소입니다. 가능한 한 많이 제거하세요.
패턴 라이브러리
1. 무한 중첩 태그
Bear의 태그 시스템은 경직된 폴더 계층 구조를 유연한 인라인 정리 방식으로 대체합니다.
기존 폴더 vs Bear 태그:
폴더 방식 BEAR의 태그 방식
───────────────────────────────────────────────────────────────────
📁 Work 노트에 포함: #work/meetings
├── 📁 Meetings 노트에 포함: #work/meetings/q1
│ ├── 📁 Q1
│ │ └── standup-2025-01.md 하나의 노트에 여러 태그 가능:
│ └── 📁 Q2 #work/meetings #action-items #q1
└── 📁 Projects
단일 위치 다중 위치 (가상)
이동 = 파일 작업 태그 = 그냥 입력
파일 브라우저에서 보임 사이드바 + 노트 본문에서 보임
태그 문법:
단일 태그: #ideas
중첩 태그: #work/meetings/2025
깊은 중첩: #journal/2025/01/17
사이드바 결과:
├─ 📁 work
│ └─ 📁 meetings
│ └─ 📄 2025
├─ 📁 journal
│ └─ 📁 2025
│ └─ 📁 01
│ └─ 📄 17
핵심 인사이트: 태그는 메뉴에서 선택하는 것이 아니라 인라인으로 입력합니다. 글을 쓰는 행위 자체가 정리를 만들어냅니다.
2. 타이포그래피 컨트롤 시스템
Bear는 다른 노트 앱들이 숨기는 세밀한 타이포그래피 컨트롤을 제공합니다:
┌─ Typography Settings ──────────────────────────────────────────────┐
│ │
│ Font │
│ [Avenir Next ▼] ← 시스템 폰트 + 커스텀 폰트 │
│ │
│ Size │
│ [─────●────────] 16pt │
│ │
│ Line Height │
│ [────────●─────] 1.6 │
│ │
│ Line Width │
│ [──●───────────] Narrow ← 최적의 읽기 너비 │
│ │
│ Paragraph Spacing │
│ [─────●────────] Medium │
│ │
└────────────────────────────────────────────────────────────────────┘
Swift 구현 접근 방식:
struct TypographySettings: Codable {
var fontName: String = "Avenir Next"
var fontSize: CGFloat = 16
var lineHeightMultiple: CGFloat = 1.6
var lineWidth: LineWidth = .comfortable
var paragraphSpacing: CGFloat = 12
enum LineWidth: String, Codable {
case narrow = "narrow" // ~60 characters
case comfortable = "medium" // ~75 characters
case wide = "wide" // Full width
}
}
// Applied to editor
func applyTypography(_ settings: TypographySettings, to textView: UITextView) {
let style = NSMutableParagraphStyle()
style.lineHeightMultiple = settings.lineHeightMultiple
style.paragraphSpacing = settings.paragraphSpacing
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont(name: settings.fontName, size: settings.fontSize)!,
.paragraphStyle: style
]
textView.typingAttributes = attributes
}
3. 테마 시스템
Bear의 테마는 모든 것을 한 번에 변경합니다—개별 색상 선택이 필요 없습니다.
테마 구조:
struct BearTheme {
// 배경 레이어
let sidebarBackground: Color
let noteListBackground: Color
let editorBackground: Color
// 텍스트 계층
let textPrimary: Color
let textSecondary: Color
let textMuted: Color
// 시맨틱 하이라이트
let tagColor: Color
let linkColor: Color
let codeBackground: Color
let headingColor: Color
// 선택 및 포커스
let selectionColor: Color
let cursorColor: Color
}
// Example: Red Graphite (default light theme)
let redGraphite = BearTheme(
sidebarBackground: Color(hex: "#F7F7F7"),
noteListBackground: Color(hex: "#FFFFFF"),
editorBackground: Color(hex: "#FFFFFF"),
textPrimary: Color(hex: "#333333"),
textSecondary: Color(hex: "#888888"),
textMuted: Color(hex: "#BBBBBB"),
tagColor: Color(hex: "#D14C3E"), // The signature red
linkColor: Color(hex: "#B44B41"),
codeBackground: Color(hex: "#F5F5F5"),
headingColor: Color(hex: "#333333"),
selectionColor: Color(hex: "#FFE4E1"),
cursorColor: Color(hex: "#D14C3E")
)
// Example: Dieci (OLED-optimized)
let dieci = BearTheme(
sidebarBackground: Color(hex: "#000000"), // True black
noteListBackground: Color(hex: "#000000"), // True black
editorBackground: Color(hex: "#000000"), // True black
textPrimary: Color(hex: "#FFFFFF"),
textSecondary: Color(hex: "#888888"),
textMuted: Color(hex: "#555555"),
tagColor: Color(hex: "#FF9500"),
linkColor: Color(hex: "#FF9500"),
codeBackground: Color(hex: "#1C1C1C"),
headingColor: Color(hex: "#FFFFFF"),
selectionColor: Color(hex: "#3A3A3C"),
cursorColor: Color(hex: "#FF9500")
)
테마 카테고리: - 라이트 테마: Red Graphite, High Contrast, Solarized Light - 다크 테마: Dark Graphite, Dracula, Nord - OLED 테마: Dieci, Charcoal (배터리 절약을 위한 완전한 블랙) - 특수 테마: Shibuya Jazz, Everforest (분위기별 테마)
4. 집중 모드
Bear의 집중 모드는 글자 외의 모든 것을 제거합니다—커서조차 은은하게 처리됩니다.
일반 모드
┌────────┬────────────┬───────────────────────────────────────────┐
│ │ │ │
│사이드바│ 노트 목록 │ 에디터 │
│ │ │ │
│ #work │ Meeting │ # Meeting Notes │
│ #ideas │ Ideas │ │
│ #books │ Draft │ Today we discussed... │
│ │ │ │
└────────┴────────────┴───────────────────────────────────────────┘
집중 모드 (키보드 단축키 또는 스와이프)
┌─────────────────────────────────────────────────────────────────┐
│ │
│ │
│ # Meeting Notes │
│ │
│ Today we discussed... │
│ │
│ │
│ │
└─────────────────────────────────────────────────────────────────┘
모든 것이 사라집니다. 오직 글자만 남습니다.
구현 원칙: - 단일 제스처 또는 단축키로 전환 - 애니메이션은 빠르게 (지연되는 전환 없음) - 커서는 은은하게 깜빡이며, 주의를 끌지 않음 - 여백이 텍스트 주변에 숨 쉴 공간을 제공
5. TagCons (시각적 태그 아이콘)
Bear는 일반적인 태그에 자동으로 아이콘을 할당하여 사이드바를 한눈에 파악할 수 있게 합니다.
TagCons가 적용된 사이드바:
├─ 💡 ideas
├─ 📚 books
├─ ✏️ writing
├─ 📝 journal
├─ 🏃 fitness
├─ 💼 work
│ ├─ 📅 meetings
│ └─ 📋 projects
└─ 🎯 goals
아이콘 할당 로직:
enum TagConCategory {
static let mappings: [String: String] = [
"ideas": "💡",
"books": "📚",
"reading": "📖",
"writing": "✏️",
"journal": "📝",
"diary": "📓",
"work": "💼",
"meetings": "📅",
"projects": "📋",
"goals": "🎯",
"fitness": "🏃",
"health": "❤️",
"recipes": "🍳",
"travel": "✈️",
"music": "🎵",
"code": "💻",
]
static func icon(for tag: String) -> String? {
let normalized = tag.lowercased()
return mappings[normalized]
}
}
핵심 인사이트: 아이콘은 자동 할당되지만 사용자가 변경할 수 있습니다. 스마트한 기본값이 설정의 번거로움을 줄여줍니다.
비주얼 디자인 시스템
컬러 팔레트 (Red Graphite 테마)
extension Color {
// Signature accent
static let bearRed = Color(hex: "#D14C3E")
// Backgrounds
static let sidebarBg = Color(hex: "#F7F7F7")
static let editorBg = Color(hex: "#FFFFFF")
// Text
static let textPrimary = Color(hex: "#333333")
static let textSecondary = Color(hex: "#888888")
// Code blocks
static let codeBg = Color(hex: "#F5F5F5")
static let codeText = Color(hex: "#333333")
}
타이포그래피
struct BearTypography {
// Editor fonts
static let bodyFont = Font.custom("Avenir Next", size: 16)
static let headingFont = Font.custom("Avenir Next", size: 24).weight(.semibold)
static let monoFont = Font.custom("SF Mono", size: 14)
// Line heights
static let bodyLineHeight: CGFloat = 1.6
static let headingLineHeight: CGFloat = 1.3
// Optimal reading width
static let maxLineWidth: CGFloat = 680 // ~75 characters
}
애니메이션 철학
로딩 상태 없음
Bear의 핵심 애니메이션 원칙: 콘텐츠는 항상 준비되어 있습니다.
// Anti-pattern: Loading spinner
struct LoadingNote: View {
var body: some View {
ProgressView() // Bear는 절대 이렇게 하지 않습니다
}
}
// Bear의 접근 방식: 낙관적, 즉각적
struct NoteEditor: View {
@State private var note: Note
var body: some View {
TextEditor(text: $note.content)
.onAppear {
// 콘텐츠는 로컬 캐시에서 이미 사용 가능
// 동기화는 백그라운드에서 보이지 않게 진행
}
}
}
부드러운 패널 전환
// 사이드바 접기/펼치기
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
sidebarVisible.toggle()
}
// 집중 모드 전환
withAnimation(.easeInOut(duration: 0.2)) {
focusMode = true
}
Markdown 경험
실시간 미리보기 (인라인 스타일링)
Bear는 입력하는 동시에 Markdown을 렌더링합니다. 분할 화면이 필요 없습니다.
입력 내용: 화면 표시:
───────────────────────────────────────────────────────
# Heading Heading (크고 굵게)
**bold text** bold text (스타일 적용, ** 숨김)
- list item • list item (불릿 렌더링)
`code` code (고정폭, 강조)
[link](url) link (스타일 적용, URL 숨김)
구현 개념:
class MarkdownTextStorage: NSTextStorage {
private var backingStore = NSMutableAttributedString()
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
backingStore.replaceCharacters(in: range, with: str)
edited(.editedCharacters, range: range, changeInLength: str.count - range.length)
endEditing()
}
override func processEditing() {
super.processEditing()
applyMarkdownStyling()
}
private func applyMarkdownStyling() {
// Markdown 패턴에 따라 스타일 적용
// 구문 문자 숨김 (**, `, # 등)
// 일반 텍스트 소스를 유지하면서 인라인 렌더링
}
}
우리 작업에 적용할 교훈
1. 로딩 상태 제로
콘텐츠가 로컬에 존재하면 즉시 보여주세요. 동기화는 백그라운드에서 처리합니다.
2. 태그 > 폴더
글을 쓰면서 인라인으로 태그를 다는 것이 나중에 폴더를 관리하는 것보다 빠릅니다.
3. 타이포그래피가 곧 UX
사용자에게 폰트, 크기, 줄 높이, 너비를 조절할 수 있게 하는 것은 읽기 경험에 대한 존중을 보여줍니다.
4. 테마로 한 번에 모두 변경
사용자에게 12가지 색상을 각각 선택하게 하지 마세요. 완성된 테마를 큐레이션하세요.
5. 집중 모드는 탈출구
방해 요소 제거가 충분하지 않을 때, 한 번의 제스처로 모든 것을 사라지게 합니다.
자주 묻는 질문
Bear의 중첩 태그는 어떻게 작동하나요?
노트 어디에서나 #parent/child/grandchild를 입력하면 Bear가 자동으로 사이드바에 계층 구조를 생성합니다. 폴더와 달리 노트에 여러 태그를 붙일 수 있어 동시에 여러 “위치”에 배치할 수 있습니다. 태그는 메뉴를 탐색하지 않고 입력하는 것만으로 생성됩니다.
Bear는 왜 개별 색상 설정 대신 테마를 사용하나요?
테마는 시각적 일관성을 보장합니다. 사용자가 개별적으로 색상을 선택하면 대비가 낮거나 조화롭지 않은 톤의 조합을 만들기 쉽습니다. Bear의 28개 이상의 큐레이션된 테마는 모든 UI 요소에서 읽기 쉽고 미적으로 일관된 색상 체계를 보장합니다.
Bear의 Markdown은 다른 에디터와 무엇이 다른가요?
Bear는 입력하는 동시에 Markdown을 인라인으로 렌더링합니다. 구문 문자(**, #, 백틱)는 입력 후 숨겨지고 스타일이 적용된 결과만 표시됩니다. 별도의 미리보기 창 없이 편집하면서 최종 모습을 볼 수 있습니다.
Bear는 어떻게 로딩 상태 제로를 달성하나요?
Bear는 모든 콘텐츠를 로컬에 저장하고 캐시에서 즉시 로드합니다. iCloud 동기화는 스피너나 진행률 표시 없이 백그라운드에서 이루어집니다. Bear를 오프라인에서 열어도 모든 것이 작동합니다. 연결이 복구되면 동기화가 조용히 완료됩니다.
Bear 노트를 다른 형식으로 내보낼 수 있나요?
Bear는 Markdown, PDF, HTML, DOCX, 일반 텍스트로 내보내기를 지원합니다. 노트는 Markdown 소스를 유지하므로 데이터는 사용자의 것입니다. 태그 시스템은 형식에 따라 frontmatter 또는 파일 구조로 내보내집니다.