텍스트로 그리는 작은 도식들
스크린샷이나 figma 링크를 본문에 거는 대신, 텍스트 안에 그림을 그려넣는 걸 좋아한다. 외부 자산이 필요 없고, 깃 히스토리에 남고, 검색되고, 5년 후에도 깨지지 않는다.
그리는 도구는 대부분 박스 글자 한 묶음과 화살표 몇 개. 본문 안에 자연스럽게 숨어 있다가, 필요한 순간에 한눈에 구조를 보여준다.
아키텍처: 박스와 화살표
시스템 전체 흐름을 설명할 때. 컴포넌트는 박스, 통신은 화살표.
┌─────────────────┐ ┌─────────────────┐
│ Browser (UI) │ ───► │ CDN (cache) │
└─────────────────┘ └────────┬────────┘
│ miss
▼
┌─────────────────┐
│ Workers (SSR) │
└────────┬────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ D1 (SQL) │ │ R2 (img) │ │ KV (sess)│
└──────────┘ └──────────┘ └──────────┘
위에서 아래로 흐르는 단방향이라는 사실, 캐시 미스가 어디서 발생하는지, 어떤 저장소가 뭐를 담는지 — 한 화면이면 충분하다.
트리: 디렉토리 / 계층
파일 구조나 데이터 계층을 보여줄 때. 표준 tree(1) 출력 형태가 가장 익숙하다.
src/
├── lib/
│ ├── db.ts
│ ├── auth.ts
│ └── markdown.ts
├── pages/
│ ├── index.astro
│ ├── admin/
│ │ └── index.astro
│ └── posts/
│ └── [slug].astro
└── styles/
├── tokens.css
└── typography.css
├── │ └── 네 글자 조합. 마지막 자식만 └──, 나머지는 ├──. 들여쓰기 자식은 │ 또는 (마지막 자식의 자손인지에 따라).
파이프라인: 변환 흐름
데이터가 단계별로 변형되는 과정. 화살표 한 줄로 끝나는 경우가 많다.
[markdown] → [parse] → [render] → [cache] → [response]
│ │ │ │
│ │ │ └─► KV 1day TTL
│ │ └─► shiki theme 적용
│ └─► markdown-it 토큰화
└─► PUT /api/posts body_md
메인 라인은 직선, 곁가지는 아래로 빠짐. "이 단계에서 무슨 일이 일어나는가"를 옆에 짧게 적는다.
시퀀스: 시간 순 이벤트
여러 주체가 시간을 따라 메시지를 주고받는 흐름. UML sequence diagram의 ASCII 버전.
Client Worker D1
│ │ │
│ PUT /post ──► │
│ │ UPDATE ────►│
│ │ │
│ │◄──── ok │
│ │ │
│◄─── 200 ───── │
│ │ │
수직선이 각 주체의 lifeline. 가로 화살표가 메시지. 시간은 위에서 아래로. 비동기인 곳엔 점선(╌►)을 쓰기도 한다.
콜스택: 호출 깊이
에러 추적이나 함수 호출 흐름을 보여줄 때. 들여쓰기로 깊이를 표현한다.
handleRequest(req)
└─ updatePost(id, body)
├─ getById(id)
│ └─ db.prepare(...).first()
└─ renderMarkdownAsync(body_md)
├─ getHighlighter() ← cold start ~1.2s
└─ md.render(body_md)
└─ 와 ├─ 만 있으면 깊이가 보인다. 화살표 옆에 시간/메모를 붙이면 디버깅 노트로 유용.
의사결정: 분기 트리
조건에 따라 갈라지는 로직.
저장 요청 들어옴
│
├─ If-Match 헤더 있나?
│ ├─ yes → updated_at 비교
│ │ ├─ match → 저장
│ │ └─ stale → 409 반환
│ └─ no → 비교 없이 저장 (위험)
│
└─ revise reason ?
├─ 'manual' → revisions에 1건 추가
└─ 'publish' → 'publish' reason으로 추가
if/else가 중첩되는 코드보다 빠르게 읽힌다. 나는 새 라우트 설계할 때 종종 먼저 이걸 그리고 코드를 짠다.
상태 기계: 라이프사이클
어떤 객체가 어떤 상태에서 어떤 상태로 갈 수 있는지.
┌─────────────────────────┐
│ │
▼ │
┌───────┐ publish ┌───────────┐ archive ┌──────────┐
│ draft │ ────────► │ published │ ────────► │ archived │
└───────┘ └─────┬─────┘ └──────────┘
▲ │
└────── unpublish ────┘
원/박스가 상태, 화살표가 전이. 라벨에 트리거(이벤트 이름)를 적는다. 이거 한 장이면 "이 객체가 어떻게 살아가나"가 끝.
시간선: 정확한 순서
날짜순/시간순 사건. 마크다운 리스트보다 시각적으로 흐름이 살아난다.
2026-04-20 ─┬─ 프로젝트 시작 (Astro + D1)
│
2026-04-22 ─┼─ TipTap 에디터 붙임
│
2026-04-24 ─┼─ Shiki + ink theme 시도
│
2026-04-25 ─┼─ github-light로 정착
│
2026-04-26 ─┴─ 첫 글 게시
점이 사건, 수직선이 흐름. "몇 일에 무엇이"를 한 화면에서 인식.
표: 비교 / 속성
옵션 비교나 속성 매트릭스. 마크다운 표는 터미널에서 깨지므로, 코드블록 안에 직접 그린다.
옵션 지연 검색 Time Travel
─────────────────────────────────────────────────────
Git-as-DB ~500ms grep commit log
D1 (SQLite) ~10ms FTS5 30일 PITR
File system ~1ms ripgrep 스냅샷
첫 줄은 헤더, 두 번째 줄은 가로선(─), 그 다음은 데이터. 칼럼 폭은 직접 맞춘다 — 시간이 좀 걸리지만 한 번 그리면 영원히 깨끗하다.
끝
도식의 핵심은 한 화면 안에 구조를 다 보이게 만드는 것이다. 너무 크면 의미가 없고, 너무 작으면 정보가 빈다. 보통 8~15줄 사이가 적당하다.
글자만으로 그릴 수 있다는 건 큰 자유다. 외부 도구도, 이미지 호스팅도, 깨질 링크도 없다. 마크다운 파일 한 곳에 영원히 남는다.