VibeMon Demo

시작은 작은 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가 달린 귀여운 기기.

ESP32와 Desktop App

아두이노는 처음 써봤지만, 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 정보는 알 수 있지만, modelmemory 정보는 직접 제공되지 않는다.

그런데 이 정보들이 화면에 보이면 훨씬 유용하지 않을까? 어떤 모델을 쓰고 있는지, 컨텍스트 윈도우를 얼마나 사용했는지.

해결책은 statusline이었다. Claude Code의 statusline에는 model과 memory 정보가 표시된다. 그래서 statusline.py를 만들어서:

  1. statusline에서 model, memory 정보를 파싱
  2. 프로젝트별로 캐시 파일에 저장 (~/.claude/vibemon/{project}.json)
  3. 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

clawd kiro claw

픽셀 아트 캐릭터는 각 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와 함께 짰다.

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로 구현했다.

배운 것들

  1. Single Table Design이 정말 효율적이다. DynamoDB에서 PK/SK 조합으로 다양한 쿼리를 처리할 수 있었다.

  2. WebSocket + API Gateway의 조합. 서버리스로 실시간 통신을 구현하는 게 생각보다 복잡했다.

  3. 픽셀 아트는 생각보다 어렵다. 작은 캔버스에 감정을 담는 게 쉽지 않았다.

  4. 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: