
시작은 작은 LCD 화면에서
2026년 1월, 책상 위에 놓을 작은 LCD 디스플레이가 갖고 싶었다. ESP32와 172×320 해상도의 작은 화면. 여기에 뭘 띄우면 좋을까?
Claude Code로 코딩하다 문득 떠올랐다. “AI가 지금 뭘 하고 있는지 이 화면에 보여주면 어떨까?”
터미널 창을 계속 쳐다보고 있을 수도 없고, 언제 작업이 끝나는지 알 수도 없었다. 그래서 시작했다 — AI 코딩 어시스턴트가 지금 무엇을 하는지 한눈에 보여주는 모니터를 만들기로.
Phase 1: ESP32용 프로토타입 (1월 24일)
ESP32 기기가 배송 오기 전에 개발을 시작했다. 처음엔 “Claude Code Status Display”라는 이름으로 시작했다. 이 코드는 현재 vibemon-app 레포에서 확인할 수 있다. 목표는 단순했다:
Claude가 thinking 상태면 → 화면에 "생각 중..." 표시
Claude가 working 상태면 → 화면에 "작업 중..." 표시
하지만 그냥 텍스트만 보여주면 재미없잖아? 그래서 픽셀 아트 캐릭터를 넣었다.
캐릭터 눈 모양부터 시작해서 상태별 표정을 하나씩 만들어갔다:
- idle 상태: ■ ■ (기본 눈)
- thinking 상태: ▀ ▀ + 💭 (생각 버블)
- working 상태: 🕶️ (선글라스 - 매트릭스 스타일!)
- notification 상태: ● ● + ? (물음표)
- done 상태: > < (뿌듯한 눈)
Phase 2: Simulator와 Desktop App (1월 24일 ~ 2월 1일)
ESP32 기기가 도착하기 전에 테스트할 방법이 필요했다. 그래서 웹 시뮬레이터를 먼저 만들었다. 브라우저에서 ESP32 화면을 미리 볼 수 있게.
그리고 기다리는 동안 Desktop App도 만들었다. Electron으로 만든 이 앱은 나중에 ESP32 없이도 쓸 수 있는 대안이 됐다.
주요 기능들이 빠르게 추가됐다:
| 기능 | 설명 |
|---|---|
| Always on Top | 다른 창 위에 항상 표시 |
| System Tray | 메뉴바에서 빠르게 제어 |
| Frameless Window | 깔끔한 플로팅 디자인 |
| Floating Animation | 둥실둥실 떠다니는 효과 |
그리고 Claude Code hooks와 연동! Claude Code가 상태를 바꿀 때마다 자동으로 앱에 전달되도록 했다.
# Claude Code hooks로 상태 전송
curl -X POST http://127.0.0.1:19280/status \
-H "Content-Type: application/json" \
-d '{"state":"working","tool":"Bash","project":"my-project"}'
연동 방식의 진화
Claude Code와 VibeMon을 연결하는 방식도 계속 발전했다.
1단계: 로컬 HTTP
처음엔 단순했다. Claude Code hooks에서 Desktop App으로 직접 전송.
curl -X POST http://127.0.0.1:19280/status \
-d '{"state":"working"}'
2단계: USB 시리얼
ESP32가 도착했다! ESP32-C6-LCD-1.47 보드 — 172×320 해상도의 작은 LCD가 달린 귀여운 기기.

아두이노는 처음 써봤지만, AI 덕분에 쉽게 설정했다. Claude Code에게 물어보니 순식간에 가이드를 만들어줬다.
아두이노 IDE 설정:
1. Arduino IDE 설치
2. ESP32 보드 매니저 추가 (Preferences → Additional Board URLs)
https://espressif.github.io/arduino-esp32/package_esp32_index.json
3. 보드 선택: ESP32C6 Dev Module
4. 파티션 스킴: Huge APP (3MB No OTA/1MB SPIFFS) ← SSL 지원 필수!
필요한 라이브러리:
| 라이브러리 | 용도 |
|---|---|
| LovyanGFX | TFT 디스플레이 제어 (ESP32-C6 호환) |
| ArduinoJson | JSON 파싱 |
| WebSockets | WebSocket 클라이언트 |
빌드하고 업로드! USB 케이블로 연결하면 시리얼 통신으로 상태가 전송된다.
3단계: 내부 네트워크 HTTP
어차피 ESP32도 WiFi 접속하고 웹서버가 돌아가잖아? USB 케이블 없이 같은 네트워크에서 HTTP로 전송하면 된다.
curl -X POST http://192.168.1.xxx/status \
-d '{"state":"working"}'
4단계: 클라우드 WebSocket
OpenClaw를 지원하면서 욕심이 생겼다. 집에서도, 회사에서도, 모든 AI 에이전트의 상태를 한 화면에서 보고 싶었다.
그래서 vibemon.io 도메인을 등록하고, 웹 대시보드와 WebSocket 서버를 구축했다. 이제 어디서든 실시간으로 모니터링할 수 있다.
OpenClaw는 플러그인 방식으로 VibeMon을 설치할 수 있게 했다. Claude Code의 hooks와 달리, OpenClaw는 플러그인 시스템을 통해 확장 기능을 추가할 수 있었다. 결국 OpenClaw에게도 스스로 설치하게 만들었다 — AI가 플러그인을 설치하고 설정하는 메타한 상황.
숨겨진 난관: model과 memory
Claude Code hooks에서는 state, tool, project 정보는 알 수 있지만, model과 memory 정보는 직접 제공되지 않는다.
그런데 이 정보들이 화면에 보이면 훨씬 유용하지 않을까? 어떤 모델을 쓰고 있는지, 컨텍스트 윈도우를 얼마나 사용했는지.
해결책은 statusline이었다. Claude Code의 statusline에는 model과 memory 정보가 표시된다. 그래서 statusline.py를 만들어서:
- statusline에서 model, memory 정보를 파싱
- 프로젝트별로 캐시 파일에 저장 (
~/.claude/vibemon/{project}.json) - hook이 실행될 때 캐시에서 데이터를 읽어서 함께 전송
# statusline.py - model/memory 정보를 캐시에 저장
cache = {
"model": "Opus 4.5",
"memory": 45
}
# hook - 캐시에서 읽어서 전송
status = {
"state": "working",
"tool": "Bash",
"project": "vibemon",
"model": cache["model"], # statusline에서 가져온 값
"memory": cache["memory"] # statusline에서 가져온 값
}
이렇게 두 개의 스크립트가 협력해서 완전한 상태 정보를 전송할 수 있게 됐다.
반면 Kiro는 model과 memory 정보를 알아낼 방법이 전혀 없었다. statusline 같은 기능도 없고, hook에서 제공하는 정보도 제한적이다. 그래서 Kiro 캐릭터는 state, tool, project만 표시된다. 아쉽지만 어쩔 수 없다.
Phase 3: 멀티 프로젝트 지원 (1월 말)
여러 프로젝트를 동시에 작업하면 어떡하지? → 멀티 윈도우 모드 탄생!

프로젝트마다 별도의 창이 뜨고, 활성 상태인 프로젝트는 오른쪽에, 비활성은 왼쪽에 자동 정렬된다. 최대 5개 창까지 지원.
Phase 4: 웹 대시보드 구축 (2월 1일)
로컬에서만 쓰기엔 아쉬웠다. 집, 회사 등 여러 곳의 AI 에이전트 상태를 한 곳에서 보고 싶었다. 그래서 vibemon 웹 대시보드 프로젝트 시작!
기술 스택 선택
- Frontend: Next.js 16 + React 19 + TypeScript
- Database: AWS DynamoDB (Single Table Design)
- Real-time: WebSocket
- Infrastructure: Terraform (API Gateway, Lambda)
- Hosting: AWS Amplify
아키텍처
┌─────────────────────────────────────────────────────────────────────────────┐
│ VibeMon Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Claude Code │ │ Kiro │ │ OpenClaw │ │
│ │ (clawd) │ │ (kiro) │ │ (claw) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ │ Hooks │ Hooks │ │
│ └──────────────────┼──────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ AWS Amplify (Hosting) │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ Next.js 16 + React 19 + TypeScript │ │ │
│ │ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │ │ │
│ │ │ │ POST /api/status │ │ GET /api/statuses,metrics │ │ │ │
│ │ │ │ (Bearer Token Auth)│ │ │ │ │ │
│ │ │ └──────────┬──────────┘ └──────────────┬──────────────┘ │ │ │
│ │ └─────────────┼──────────────────────────────┼──────────────────┘ │ │
│ └────────────────┼──────────────────────────────┼─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ DynamoDB (Single Table Design) │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ Token Profile │ │ Agent Status │ │ Metrics │ │ │
│ │ │ (TTL: 24h) │ │ (TTL: 30m) │ │ (TTL: 24h~90d) │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ └────────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────┼─────────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ EventBridge │ │ API Gateway │ │ CloudWatch │ │
│ │ ┌───────────┐ │ │ WebSocket │ │ Logs │ │
│ │ │ 1min rule │ │ │ ┌─────────────┐ │ │ │ │
│ │ │ 15min rule│ │ │ │ $connect │ │ │ STATUS_UPDATE │ │
│ │ └─────┬─────┘ │ │ │ $disconnect │ │ │ events │ │
│ └────────┼────────┘ │ └──────┬──────┘ │ └────────┬────────┘ │
│ │ └─────────┼───────────┘ │ │
│ ▼ ▼ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Lambda Functions │ │
│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │ Connect │ │ Disconnect │ │ State │ │ Metrics │ │ │
│ │ │ Handler │ │ Handler │ │ Transition │ │ Aggregator │ │ │
│ │ └────────────┘ └────────────┘ └──────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ WebSocket Broadcast │
│ (API Gateway Management API) │
│ │ │
│ ┌─────────────────────────┼─────────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Desktop │ │ Web │ │ ESP32 │ │
│ │ App │ │ Dashboard │ │ Hardware │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
주요 구성 요소:
- Amplify: Next.js 앱 호스팅, 자동 빌드/배포
- DynamoDB: 단일 테이블 설계로 토큰, 상태, 메트릭 저장
- API Gateway WebSocket: 실시간 양방향 통신
- Lambda: 연결 관리, 상태 자동 전환, 메트릭 수집
- EventBridge: 1분/15분 주기 Lambda 트리거
- CloudWatch Logs: 상태 이벤트 로깅, 메트릭 쿼리 소스
토큰 기반 인증으로 각자의 프로젝트만 볼 수 있게 했다. 토큰은 처음 사용할 때 자동 등록되고, 24시간 후 만료된다.
Phase 5: 캐릭터 확장 (2월 초)
Claude Code만 지원하면 뭐하나. 다른 AI 어시스턴트도 지원하자!
| 캐릭터 | 색상 | 대상 |
|---|---|---|
| clawd | 주황색 | Claude Code |
| kiro | 흰색 | AWS Kiro |
| claw | 빨간색 | OpenClaw |

픽셀 아트 캐릭터는 각 AI 에이전트의 공식 캐릭터 이미지를 기반으로 했다. 거기에 상태별 표정을 추가했다 — 깜빡이는 눈, 선글라스, 잠자는 눈, 생각 버블 등. 128x128 PNG 이미지로 렌더링되고, hook 이벤트에 따라 자동으로 캐릭터가 선택된다.
Phase 6: 프로젝트 통합 (2월 5일)
“vibe-monitor”라는 이름이 마음에 안 들었다. 더 깔끔하게 VibeMon으로 통일!
Phase 7: AI가 직접 설치하게 하기
설치 과정을 자동화하고 싶었다. 그런데 생각해보니, AI한테 직접 시키면 되잖아?
설치 가이드를 markdown 파일로 만들고, AI에게 이렇게 말하면 된다:
Read https://vibemon.io/setup.md and follow the instructions to join VibeMon
Claude Code가 직접 setup.md를 읽고, hooks 설정하고, 토큰 생성하고, 테스트까지 해준다. 메타하다.
숫자로 보는 VibeMon
| 항목 | vibemon-app | vibemon |
|---|---|---|
| 시작일 | 2026-01-24 | 2026-02-01 |
| 총 커밋 | 518 | 101 |
| 개발 기간 | 12일 | 4일 |
619개의 커밋, 16일간의 개발. 대부분 AI와 함께 짰다.

특별한 기능들
1. 상태별 애니메이션
| 상태 | 배경색 | 눈 모양 | 텍스트 | 트리거 |
|---|---|---|---|---|
| start | Cyan | ■ ■ + ✦ | Hello! | 세션 시작 |
| idle | Green | ■ ■ (깜빡임) | Ready | 입력 대기 |
| thinking | Purple | ▀ ▀ + 💭 | Thinking | 프롬프트 제출 |
| planning | Teal | ▀ ▀ + 💭 | Planning | Plan 모드 활성화 |
| working | Blue | 🕶️ (선글라스) | (도구별) | 도구 실행 중 |
| packing | Gray | ▀ ▀ + 💭 | Packing | 컨텍스트 압축 |
| notification | Yellow | ● ● + ? | Input? | 사용자 입력 필요 |
| done | Green | > < | Done! | 도구 완료 |
| sleep | Navy | ─ ─ + Z | Zzz… | 5분 비활성 |
2. Working 상태 텍스트
도구에 따라 다른 텍스트가 표시된다:
| Tool | 표시 텍스트 |
|---|---|
| Bash | Running, Executing, Processing |
| Read | Reading, Scanning, Checking |
| Edit | Editing, Modifying, Fixing |
| Grep | Searching, Finding, Looking |
3. 메모리 바
컨텍스트 윈도우 사용률을 그라데이션으로 표시:
- 0-74%: 💚 초록
- 75-89%: 💛 노랑 (경고)
- 90-100%: ❤️ 빨강 (위험!)
4. 클릭해서 터미널 포커스
macOS에서 VibeMon 창을 클릭하면 해당 프로젝트의 iTerm2/Ghostty 탭으로 자동 전환! AppleScript로 구현했다.
배운 것들
-
Single Table Design이 정말 효율적이다. DynamoDB에서 PK/SK 조합으로 다양한 쿼리를 처리할 수 있었다.
-
WebSocket + API Gateway의 조합. 서버리스로 실시간 통신을 구현하는 게 생각보다 복잡했다.
-
픽셀 아트는 생각보다 어렵다. 작은 캔버스에 감정을 담는 게 쉽지 않았다.
-
AI와 페어 프로그래밍. Claude Code와 함께 개발하면서 VibeMon을 만들고, 그 VibeMon으로 Claude Code를 모니터링했다. 메타하다.
마무리
VibeMon은 “이 작은 LCD 화면에 뭘 띄울까?”라는 단순한 질문에서 시작했다. 지금은 ESP32 하드웨어, Desktop 앱, 웹 대시보드까지 지원하는 프로젝트로 성장했다.
다음 목표는:
- 더 많은 AI 도구 지원 (Cursor, Windsurf 등)
- 팀 기능 (여러 사용자의 상태를 한 화면에)
- ESP32 전용 케이스 디자인 (3D 프린팅)
귀여운 픽셀 캐릭터가 코딩하는 동안 함께해주니, 혼자 코딩하는 것 같지 않다.
감사합니다
이 프로젝트는 AI와 함께 만들었습니다. Claude Code와 페어 프로그래밍을 하면서, VibeMon으로 Claude Code를 모니터링하고, 그 과정을 다시 VibeMon에 반영하는 순환 구조가 재미있었습니다.
긴 글 읽어주셔서 감사합니다. VibeMon을 사용해보시고, 피드백이나 기여는 언제든 환영합니다!
Links:
- Dashboard: vibemon.io
- Desktop App: npm - vibemon
- GitHub: nalbam/vibemon, nalbam/vibemon-app