개요
Claude Code를 사용해 자기 자신의 사용량을 모니터링하는 macOS 메뉴바 앱을 만든 경험을 공유한다.
배경
Claude Code Max 플랜을 사용하면서 현재 세션에서 토큰을 얼마나 썼는지 메시지를 몇 개 보냈는지 궁금할 때가 많았다. 웹에서 사용량 페이지를 열어볼 수는 있지만 매번 브라우저를 전환하는 것이 번거로웠다.
Claude Code는 ~/.claude/stats-cache.json에 일별 사용량 데이터를 로컬에 저장하고 있다. 이 파일을 읽어서 메뉴바에 항상 표시해두면 편하겠다는 생각에 프로젝트를 시작했다.
그리고 이 앱 자체를 Claude Code로 만들었다. Claude Code가 자기 자신의 사용량을 모니터링하는 앱을 만드는 셈이다.
데이터 소스
Claude Code가 로컬에 저장하는 두 개의 JSON 파일을 사용한다.
| 파일 | 용도 |
|---|---|
~/.claude/stats-cache.json |
일별 메시지/토큰/세션/도구호출 수, 모델별 누적 토큰 |
~/.claude.json |
계정명, 플랜 종류(Pro/Max) |
stats-cache.json의 구조는 다음과 같다.
{
"dailyActivity": [
{
"date": "2026-02-14",
"messageCount": 2511,
"sessionCount": 9,
"toolCallCount": 537
}
],
"dailyModelTokens": [
{
"date": "2026-02-14",
"tokensByModel": {
"claude-opus-4-6": 65342,
"claude-sonnet-4-5-20250929": 11726
}
}
],
"modelUsage": {
"claude-opus-4-6": {
"inputTokens": 15389,
"outputTokens": 282180,
"cacheReadInputTokens": 251600374,
"cacheCreationInputTokens": 5266604
}
},
"totalSessions": 28,
"totalMessages": 8789,
"longestSession": { "duration": 27382247, "messageCount": 85 },
"firstSessionDate": "2026-02-08T14:38:23.545Z"
}
기술 스택
Xcode 프로젝트 없이 Swift Package Manager만으로 구성했다.
- SwiftUI + MenuBarExtra (macOS 14+,
.window스타일) - @Observable 매크로 + @MainActor
- DispatchSource 파일 감시
- 외부 의존성 없음
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "ClaudeUsageMeter",
platforms: [.macOS(.v14)],
targets: [
.executableTarget(
name: "ClaudeUsageMeter",
path: "Sources"
)
]
)
주요 구현
1. MenuBarExtra
macOS 14부터 SwiftUI에서 MenuBarExtra를 사용하면 네이티브 메뉴바 앱을 만들 수 있다. .window 스타일을 적용하면 클릭 시 팝오버 윈도우가 표시된다.
@main
struct ClaudeUsageMeterApp: App {
@State private var viewModel = UsageViewModel()
var body: some Scene {
MenuBarExtra {
PopoverContentView(viewModel: viewModel)
} label: {
MenuBarLabel()
}
.menuBarExtraStyle(.window)
}
}
2. DispatchSource 파일 감시
stats-cache.json 파일이 변경되면 즉시 UI를 갱신하기 위해 DispatchSource로 파일 시스템 이벤트를 감시한다. 폴백으로 60초 주기 타이머도 함께 사용한다.
final class FileWatcher {
private var source: DispatchSourceFileSystemObject?
func start() {
let fd = open(path, O_EVTONLY)
guard fd >= 0 else { return }
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fd,
eventMask: [.write, .rename, .delete],
queue: .main
)
source.setEventHandler { [weak self] in
self?.onChange()
}
source.setCancelHandler { close(fd) }
self.source = source
source.resume()
}
}
3. .app 번들 생성
SPM으로 빌드한 바이너리를 macOS 앱 번들로 패키징하려면 Info.plist를 포함한 디렉토리 구조를 직접 만들어야 한다. LSUIElement를 true로 설정하면 Dock에 나타나지 않는 메뉴바 전용 앱이 된다.
ClaudeUsageMeter.app/
├── Contents/
│ ├── Info.plist
│ ├── MacOS/
│ │ └── ClaudeUsageMeter
│ └── Resources/
│ └── AppIcon.icns
<key>LSUIElement</key>
<true/>
시행착오
1. 서버 사용량을 가져올 수 없다
처음에는 프로그레스 바로 일일 사용량 대비 한도를 시각화하려고 했다. 하지만 웹에서 보이는 플랜 사용률(현재 세션 5%, 주간 10% 등)은 서버 측에서 계산하는 값이고 이 데이터를 가져올 수 있는 공개 API가 없었다.
Anthropic의 Usage & Cost API가 있지만 조직용 Admin API 키(sk-ant-admin...)가 필요하고 개인 플랜의 세션별/주간 사용률과는 다른 데이터였다.
결국 프로그레스 바를 제거하고 stats-cache.json에서 확실하게 보여줄 수 있는 데이터(메시지 수, 토큰 수, 세션 수, 모델별 사용량)에 집중하는 방향으로 전환했다.
2. macOS 아이콘 캐시
Swift 스크립트로 앱 아이콘을 프로그래밍 방식으로 생성했는데 아이콘을 변경해도 macOS의 아이콘 캐시 때문에 반영되지 않는 문제가 있었다. lsregister로 앱을 재등록하고 Finder와 Dock을 재시작해야 갱신되었다.
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f ClaudeUsageMeter.app
killall Finder Dock
구현 결과
한 세션에서 플랜 수립부터 구현, 아이콘 생성, GitHub 푸시까지 완료했다. 최종 프로젝트 구조는 다음과 같다.
Sources/
├── App/
│ └── ClaudeUsageMeterApp.swift
├── Models/
│ ├── StatsCache.swift
│ ├── ClaudeConfig.swift
│ └── UsageSnapshot.swift
├── Services/
│ ├── StatsLoader.swift
│ └── FileWatcher.swift
├── ViewModels/
│ └── UsageViewModel.swift
└── Views/
├── MenuBarLabel.swift
├── PopoverContentView.swift
├── HeaderView.swift
├── TodayStatsSection.swift
├── DailyTrendSection.swift
├── ModelBreakdownSection.swift
└── CumulativeStatsSection.swift
메뉴바 아이콘을 클릭하면 계정 정보, 오늘의 활동, 7일 추이 차트, 모델별 토큰 사용량, 전체 통계를 한눈에 확인할 수 있다.

참고로 이 프로젝트에서 사람이 한 일은 “이거 만들어줘”라고 말한 것뿐이다. 앱 개발, GitHub 저장소 생성, 앱 아이콘 생성까지 전부 Claude Code가 알아서 했다. 본인은 옆에서 구경하다가 가끔 “그거 아닌데”라고 태클을 걸었을 뿐이다.
댓글남기기