codex app-server와 claude CLI를 직접 구동하며, ccusage식 로컬 비용 스캐너까지 갖춘 784개 Swift 파일·약 18.7만 줄의 진짜 제품이다.
(저장소: steipete/CodexBar · Swift 6.2 · MIT · v0.41.0 · macOS 14+ · ⭐ 약 15.7k · TrendShift 오늘 Daily 6위)
이 저장소가 대체 무엇인가.
CodexBar(제작자 Peter Steinberger, PSPDFKit 창업자)는 Codex·Claude Code·Cursor·Copilot 등 AI 코딩 구독 도구의 "남은 한도"를 메뉴바 아이콘 하나로 보여주는 macOS 앱이다. 핵심 원칙은 "로그인 없음(no login)" — 새 계정을 만들거나 비밀번호를 넣게 하지 않고, 이미 내 컴퓨터에 저장돼 있는 인증 정보(OAuth 토큰 파일·브라우저 쿠키·CLI 자격증명)를 그대로 재사용해 각 제공사의 사용량 API를 대신 호출한다.
AI 코딩 도구를 쓰다 보면 한창 코딩 중에 갑자기 "이번 주 한도 초과"로 막힌다. 연료가 언제 바닥날지 모르고 달리는 차와 같다.
CodexBar는 그 계기판을 달아준다. 다만 탱크가 하나가 아니라 Codex·Claude·Cursor·Copilot… 수십 개다. 각 도구에 일일이 로그인해 대시보드를 열어보는 대신, 메뉴바 바늘 하나로 "얼마 남았고 몇 시간 뒤 다시 찬다"를 즉시 확인한다.
코드상 버전은 0.41.0(빌드 100)이며 라이선스는 관대한 MIT(저작권 2026 Peter Steinberger, 웹사이트 codexbar.app). 실행에는 macOS 14(Sonoma) 이상이 필요하고, Dock 아이콘 없이 메뉴바에만 사는 LSUIElement 방식이다. 별점은 약 15.7k로, 개발자용 소도구치고는 대단히 높다. CHANGELOG가 161KB에 달할 만큼 거의 매일 릴리스되는 초활발 프로젝트다.
"한도 보여주기"는 겉모습이다. 이 레포의 진짜 가치는 NSStatusItem 메뉴바 설계 · Swift 6 엄격 동시성(actor·@MainActor) · 외부 CLI를 JSON-RPC/PTY로 구동 · 로컬 JSONL 로그 스트리밍 파싱 · 브라우저 쿠키 복호화 · Sparkle 자동업데이트 · 크로스플랫폼 CLI 배포를 한 코드베이스에서 통째로 볼 수 있다는 점이다. "네이티브 macOS 유틸리티를 제품 수준으로 만드는 법"의 살아있는 표본이다.
트렌딩 이유와, 흔한 "사용량 추적기"와의 결정적 차이.
보통의 사용량 대시보드는 "우리 서비스에 로그인하라"고 요구한다. CodexBar는 정반대다. 이미 내 기기에 있는 자격증명을 그대로 빌려 쓴다 — Codex는 ~/.codex/auth.json의 OAuth 토큰, Claude는 ~/.claude/.credentials.json이나 키체인 항목, 웹 대시보드는 Safari·Chrome·Firefox 쿠키를 읽는다. README는 "비밀번호를 저장하지 않는다"고 못박고, 파일시스템을 뒤지지 않고 알려진 소수의 경로만 해당 기능을 켰을 때에만 읽는다고 강조한다.
일반 사용량 앱 = "우리 앱에 다시 로그인하세요"(계정·비밀번호를 또 맡긴다). CodexBar = "이미 로그인돼 있잖아요, 그 세션 잠깐 빌릴게요"(새 비밀번호 없이, 내 컴퓨터 안 자격증명만 재사용).
Codex·OpenAI·Claude·Cursor·Gemini·Copilot·Grok·GroqCloud·z.ai·MiniMax·Kiro·Zed·Vertex AI·AWS Bedrock… README가 나열하는 제공사는 약 57~60개다. 이렇게 많은 AI 코딩 도구의 한도를 한 앱에서 보는 경쟁 제품은 사실상 없다. CHANGELOG는 Thanks @기여자 크레딧으로 가득해, 커뮤니티가 새 프로바이더를 계속 밀어넣는 구조임을 보여준다.
CodexBar는 라이브 한도(API 조회)와 별개로, 로컬 로그 파일만 읽어 "지금까지 토큰을 얼마나 썼고 달러로 얼마인지"를 계산하는 비용 스캐너를 내장한다. ~/.codex/sessions/**/*.jsonl과 ~/.claude/projects/**/*.jsonl을 스트리밍으로 훑어 토큰 수를 세고, 내장 모델별 단가표로 곱한다. README는 이 아이디어가 유명한 ccusage(MIT)에서 왔다고 밝힌다. 네트워크 없이도 동작하는 셈이다.
메뉴바 앱이라고 얕볼 규모가 아니다. Swift 소스 784개 파일, 약 18.7만 줄이 6개의 SPM 타깃으로 나뉘고, Sparkle 자동업데이트(EdDSA 서명)·WidgetKit 위젯·21개 언어 현지화(RTL 포함)·프로바이더 장애 폴링·macOS Tahoe 메뉴바 아이콘 복구 경로까지 갖췄다. 심지어 Linux까지 빌드되는 크로스플랫폼 CLI(codexbar)와 Homebrew·AUR 패키지도 제공한다. "네이티브 유틸리티를 어디까지 다듬을 수 있나"의 상한선을 보여준다.
사용량 표시라는 용도는 부차적이다. 진짜 가치는 NSStatusItem 메뉴바 · Swift 6 actor 격리 · 서브프로세스(JSON-RPC)·PTY 자동화 · JSONL 증분 파싱 · 쿠키 복호화 · Sparkle/appcast 배포 · SPM으로 .app 패키징을 실전 규모 코드로 통째로 관찰할 수 있다는 것.
무엇으로 만들어졌나 — 계층별로. (Package.swift·Package.resolved에서 실제 확인)
| 항목 | 값 | 메모 |
|---|---|---|
| 언어 | Swift (tools 6.2) | Swift 6 엄격 동시성 활성(모든 타깃에 StrictConcurrency upcoming feature) |
| 최소 OS | macOS 14 (Sonoma) | Info.plist LSMinimumSystemVersion 14.0 |
| 빌드 | Swift Package Manager | 메인 앱에 .xcodeproj 없음 — Package.swift가 진실원. 위젯만 별도 xcodeproj |
| 패키징 | Scripts/package_app.sh | 손으로 쓴 Info.plist로 CodexBar.app을 즉석 서명(ad-hoc)해 생성 |
| UI | AppKit + SwiftUI 하이브리드 | 메뉴바는 순수 AppKit(NSStatusItem), 설정 창은 SwiftUI Settings 씬 |
| 패키지 | 버전 | 역할 |
|---|---|---|
| Sparkle | 2.9.3 | 자동업데이트(SUFeedURL + EdDSA 공개키). macOS 앱 배포 표준 |
| Commander | 0.2.2 | CLI 인자 파싱 (제작자 본인 라이브러리) |
| swift-crypto | 3.15.1 | 쿠키 복호화·JWT 등 암호화 |
| swift-log | 1.13.2 | 구조적 로깅 |
| KeyboardShortcuts | 2.4.0 | 전역 단축키 (sindresorhus) |
| SweetCookieKit | 0.4.1 | 브라우저 쿠키 추출 (제작자 본인 라이브러리) |
| Vortex | (pin된 리비전) | 파티클 효과 — 주간 리셋 축하 컨페티 |
| swift-asn1 | 1.7.1 | swift-crypto 경유 전이 의존성 |
| CSQLite3 | 시스템 라이브러리 | 브라우저·Windsurf·OpenCode의 SQLite 캐시 읽기(주로 Linux 링크용) |
주목할 점: 의존성 목록이 짧고 대부분 애플 공식(swift-crypto·swift-log·swift-asn1)이거나 제작자 본인 라이브러리(Commander·SweetCookieKit)다. 무거운 서드파티 프레임워크에 기대지 않고 플랫폼 API를 직접 쓰는 취향이 드러난다.
| 타깃 | 종류 | 역할 |
|---|---|---|
| CodexBarCore | 라이브러리 | fetch+parse 엔진 — Codex RPC, PTY 러너, Claude 프로브, 웹 스크래핑, 상태 폴링. Linux에서도 컴파일 |
| CodexBar | macOS 앱 | 상태+UI — UsageStore, SettingsStore, StatusItemController, 아이콘 렌더링 |
| CodexBarCLI | 실행파일 | 크로스플랫폼 codexbar CLI |
| CodexBarWidget | 위젯 | WidgetKit 확장 |
| CodexBarClaudeWatchdog | 헬퍼 | 안정적인 Claude CLI PTY 세션용 별도 프로세스 |
| CodexBarClaudeWebProbe | 헬퍼 CLI | Claude 웹 페치 진단용 도구 |
SwiftFormat + SwiftLint로 스타일을 강제하고(make check), CI(.github/workflows/ci.yml)는 lint를 ubuntu-24.04에서, macOS 테스트를 macos-15-intel 러너에서 Xcode 26.3/26.2로 돌린다. 별도 워크플로 release-cli.yml(19KB)은 macOS·Linux × glibc·musl × arm64·x86_64 조합으로 CLI 타르볼을 굽는다. 이 정도 릴리스 매트릭스는 메뉴바 앱에서 보기 드물다.
"메뉴바 숫자"가 어디서 어떻게 오는가.
공식 docs/architecture.md가 그리는 데이터 흐름은 단순명료하다: 백그라운드 새로고침 → 프로바이더 프로브가 사용량을 가져옴 → 중앙 저장소(UsageStore)에 반영 → 메뉴/아이콘/위젯이 그림. 복잡함은 전부 "프로바이더별로 어디서 어떻게 긁어오나"에 숨어 있다.
.auto → .oauth → .cli, Claude는 .auto → .api → .oauth → .web → .cli 순으로 가능한 소스를 차례로 시도한다. OAuth 토큰이 없으면 CLI로, CLI가 막히면 웹 쿠키로 — "어떻게든 숫자를 가져온다"는 견고함이 이 앱의 설계 철학이다.메뉴바 아이콘은 StatusItemController(@MainActor final class … : NSObject, NSMenuDelegate)가 관리한다. 프로바이더가 여러 개면 statusItems: [UsageProvider: NSStatusItem]처럼 아이콘도 여러 개 만든다. 실제 생성 코드는 이렇게 생겼다:
private static func makeStatusItem(
statusBar: NSStatusBar,
identity: StatusItemIdentity,
defaults: UserDefaults,
legacyDefaultItemIndex: Int?)
-> NSStatusItem
{
let item = statusBar.statusItem(withLength: NSStatusItem.variableLength)
item.autosaveName = identity.autosaveName // 위치 기억
if let button = item.button {
button.imageScaling = .scaleNone // 1:1 렌더(선명한 템플릿 아이콘)
button.setAccessibilityIdentifier(identity.accessibilityIdentifier)
button.toolTip = title
}
return item
}
autosaveName으로 사용자가 옮긴 아이콘 위치를 기억하고, imageScaling = .scaleNone으로 리샘플링 없이 선명하게 그린다. StatusItemController는 메뉴·애니메이션·스위처 등으로 나뉘어 약 120개의 StatusItemController+*.swift 확장 파일로 쪼개져 있다(한 파일이 74KB에 이르기도 한다). macOS Tahoe가 NSStatusItem.isVisible 동작을 바꿔도 살아남도록 방어 코드까지 들어 있다.
가장 기본 경로는 ~/.codex/auth.json의 OAuth 토큰을 읽어, ChatGPT 백엔드의 사용량 엔드포인트를 직접 부르는 것이다:
public static func fetchUsage(accessToken: String, accountId: String?, …)
async throws -> CodexUsageResponse
{
var request = URLRequest(url: Self.resolveUsageURL(env: env))
request.httpMethod = "GET"
request.timeoutInterval = 30
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
if let accountId, !accountId.isEmpty {
request.setValue(accountId, forHTTPHeaderField: "ChatGPT-Account-Id")
}
let response = try await ProviderHTTPClient.shared.response(for: request)
switch response.statusCode {
case 200...299: return try JSONDecoder().decode(CodexUsageResponse.self, …)
case 401, 403: throw CodexOAuthFetchError.unauthorized
…
}
}
엔드포인트는 GET https://chatgpt.com/backend-api/wham/usage, 헤더는 Authorization: Bearer <token>. 응답의 primary_window는 세션 레인, secondary_window는 주간 레인으로 매핑된다. OAuth가 막히면 codex -s read-only -a untrusted app-server를 띄워 JSON-RPC(stdin/stdout)로 account/rateLimits/read를 부르는 CLI 경로로 폴백한다.
라이브 한도와 별개로, 비용 스캐너는 ~/.claude/projects/**/*.jsonl 로그를 직접 읽어 토큰을 센다. 핵심 파싱은 이렇게 단순하다:
guard let message = obj["message"] as? [String: Any] else { return }
guard let model = message["model"] as? String else { return }
guard let usage = message["usage"] as? [String: Any] else { return }
let input = max(0, toInt(usage["input_tokens"]))
let cacheCreate = max(0, toInt(usage["cache_creation_input_tokens"]))
let cacheRead = max(0, toInt(usage["cache_read_input_tokens"]))
let output = max(0, toInt(usage["output_tokens"]))
let cost = CostUsagePricing.claudeCostUSD( // 토큰 × 모델별 단가
model: model, inputTokens: input,
cacheReadInputTokens: cacheRead,
cacheCreationInputTokens: cacheCreate,
outputTokens: output, pricingDate: timestamp, …)
영리한 부분은 파일을 통째로 메모리에 올리지 않는다는 것. CostUsageJsonl이 로그를 256KB 청크로 스트리밍하며 개행(0x0A)으로 잘라 한 줄씩 넘기고, 바이트 오프셋을 기억(resume)해 다음번엔 새로 추가된 바이트만 읽는다. 스트리밍 중복 청크는 message.id + requestId로 dedup한다. 거대한 히스토리도 값싸게 훑는 비결이다.
중앙 상태는 @MainActor @Observable final class UsageStore 하나다. UI가 건드리는 상태는 전부 메인 액터에 묶여 데이터 레이스가 원천 차단된다. 반대로 무거운 외부 I/O는 전용 actor로 분리한다 — actor CodexCLISession, actor ClaudeCLISession, actor ClaudeWebBrowserFetchGate 등. 소스 전체에 async가 1,103회, @MainActor가 700회 등장한다.
@MainActor
@Observable
final class UsageStore {
var isRefreshing = false // 단일화(single-flight) 가드
…
func refresh(forceTokenUsage: Bool = false) async { … }
}
AdaptiveRefreshPolicy는 시계나 ProcessInfo를 스스로 읽지 않는 순수 함수다. "메뉴를 최근에 열었나 + 전원/발열 상태"를 인자로 받아 2~30분 사이의 다음 새로고침 간격을 반환만 한다. 부수효과(시계 읽기)를 밖으로 밀어내면 테스트가 쉬워진다 — 클록을 목킹할 필요 없이 입력만 바꿔 검증한다.
784개 파일이 어디에 어떻게 놓였나.
| 파일 | 크기 | 역할 |
|---|---|---|
| CodexbarApp.swift | 23KB | @main SwiftUI 앱 + AppDelegate 배선(상태 컨트롤러·Sparkle·알림) |
| StatusItemController.swift | 41KB | 메뉴바 UI 본체(+ 확장 ~120개). 프로바이더별 아이템·메뉴·애니메이션 |
| UsageStore.swift | 80KB | @MainActor @Observable 중앙 상태 + 새로고침 오케스트레이션(+ 확장 ~35개) |
| IconRenderer.swift | 48KB | 메뉴바의 동적 사용량 게이지 아이콘을 직접 드로잉 |
| Vendored/CostUsage/CostUsageScanner.swift | 115KB | JSONL 로그 스캐너 코어(비용 계산의 심장) |
| CostUsagePricing.swift | 29KB | 모델별 토큰 단가표(Codex + Claude), 티어·캐시 단가 분리 |
| Providers/Claude/ClaudeUsageFetcher.swift | 67KB | Claude 사용량을 모든 소스에서 가져오는 총괄 |
| Providers/Claude/ClaudeStatusProbe.swift | 45KB | claude CLI의 /usage·/status 출력을 ANSI 제거 후 파싱 |
디렉토리만 봐도 설계 의도가 보인다. 한 프로바이더 = 한 폴더(Providers/Codex/, Providers/Claude/…)로 격리돼 있어, 새 도구를 추가할 때 다른 코드를 건드리지 않는다. 문서(docs/)도 프로바이더별로 약 50개가 따로 있다.
이 코드베이스에서 무엇을 훔쳐 배울 수 있나.
NSStatusItem(variableLength·autosaveName·템플릿 아이콘 1:1 렌더), Dock 없는 에이전트 앱(LSUIElement), 그리고 AppKit 메뉴바 + SwiftUI 설정 화면을 한 앱에 섞는 하이브리드 패턴. SwiftUI Settings 씬을 살리려고 숨겨진 1×1 WindowGroup으로 생명주기를 유지하는 트릭까지 배운다.
교과서 예제가 아니라 18만 줄 규모에서 어떻게 액터를 나누는가를 본다. UI 상태는 @MainActor로 모으고, 외부 프로세스/네트워크 I/O는 전용 actor로 격리하며, 새로고침은 단일화 가드로 한 번에 하나만 돈다. "부수효과를 순수 함수 밖으로 밀어내는" 테스트 친화 설계도 실물로 확인한다.
대용량 *.jsonl을 청크 스트리밍으로 읽고, 바이트 오프셋 resume으로 새 데이터만 처리하며, message.id 기반으로 중복을 제거하고, 모델별 단가표로 토큰을 달러로 환산한다. ccusage의 비용 모델을 Swift로 재구현한 깔끔한 표본이다.
두 가지 상반된 방식을 한 레포에서 본다 — (a) codex app-server를 JSON-RPC over stdin/stdout으로 구동(구조적 프로토콜), (b) 대화형 TUI인 claude를 PTY(가상 터미널)로 자동화하고 /usage를 쳐서 나온 화면을 ANSI 코드 제거 후 파싱(비구조적 출력 긁기). 타임아웃·자식 프로세스 강제 종료 같은 안전장치도 포함이다.
OAuth 토큰을 디스크에서 재사용하고, 브라우저 쿠키를 SweetCookieKit + swift-crypto + 키체인으로 복호화하며(Chromium "Safe Storage"), Copilot은 디바이스 플로우를 쓴다. 소스가 막히면 다음 소스로 우아하게 폴백하는 체인 설계가 핵심이다.
SPM을 배포 가능한 .app의 주 빌드 시스템으로 쓰고, Sparkle의 EdDSA 서명 + appcast로 자동업데이트하며, WidgetKit 위젯은 앱 그룹 JSON 스냅샷으로 데이터를 넘긴다. macOS·Linux를 아우르는 CLI 릴리스 매트릭스, SwiftFormat/SwiftLint, 21개 언어 현지화까지 — "1인 개발자가 어디까지 자동화하나"를 본다.
돌리려면 / 빌드하려면 무엇이 필요한가.
| 구분 | 요구사항 | 메모 |
|---|---|---|
| 실행 OS | macOS 14 (Sonoma) 이상 | Info.plist LSMinimumSystemVersion 14.0, LSUIElement true |
| 빌드 도구 | Swift 6.2+ / Xcode 26.x | CI는 macos-15-intel + Xcode 26.3/26.2 |
| 메모리·CPU | 사실상 무관(가벼움) | 메뉴바 앱 — 무거운 모델을 로컬에서 돌리지 않는다. 주기적 HTTP·파일 읽기가 전부 |
| 디스크 권한 | 전체 디스크 접근(선택) | Safari 쿠키(Cookies.binarycookies)를 읽을 때만 필요. 없어도 대부분 동작 |
| 키체인 | 접근 프롬프트 발생 | Chromium 쿠키 복호화·Claude OAuth 부트스트랩 시. "키체인 접근 끄기" 탈출구 제공 |
| 설치(사용자) | Homebrew / Releases | brew install --cask codexbar, Linux CLI는 AUR codexbar-cli |
사용량을 로그인 없이 가져오는 대가로, CodexBar는 OAuth 토큰 파일·브라우저 쿠키·키체인에 접근한다. README는 파일시스템 전체를 뒤지지 않고 "알려진 소수 경로만", 그것도 해당 기능을 켰을 때만 읽는다고 명시한다. 그래도 민감한 접근인 만큼, 권한 프롬프트가 뜨는 이유를 이해하고 허용 여부를 스스로 판단하는 게 좋다. Screen Recording·손쉬운 사용(Accessibility) 권한은 쓰지 않는다.
읽기만 하면 안 남는다 — 난이도별로 손을 움직여 보자.
레포를 클론하고 ./Scripts/package_app.sh를 돌려 CodexBar.app을 즉석 서명으로 만든 뒤 open CodexBar.app으로 띄운다. 개발 루프는 ./Scripts/compile_and_run.sh, 스타일 검사는 make check. 목표: "SPM만으로 배포용 .app이 나온다"는 감각 잡기.
파이썬 20줄로 ~/.codex/sessions/**/*.jsonl(또는 ~/.claude/projects/**/*.jsonl)을 한 줄씩 읽어 usage.input_tokens·output_tokens를 합산해 본다. 목표: CodexBar 비용 스캐너가 "무슨 데이터를 어디서 읽는지"를 몸으로 이해. 스트리밍으로 읽어 큰 파일도 메모리 폭발 없이 처리해 보기.
빈 SwiftUI 앱에 LSUIElement를 켜고, AppDelegate에서 NSStatusBar.system.statusItem(withLength:)로 아이콘 하나를 띄운 뒤 클릭하면 NSMenu가 열리게 한다. 목표: CodexBar가 하는 일의 뼈대(NSStatusItem + 메뉴)를 60줄로 재현.
과제 ②를 발전시켜, 마지막으로 읽은 바이트 위치를 저장해 두고 다음 실행 때 그 지점부터만 읽는 증분 스캐너를 만든다. 로그가 append-only라는 성질을 이용한다. 목표: CostUsageJsonl의 resume 아이디어 재현 — "다시 스캔이 아니라 이어 스캔".
Sources/CodexBarCore/Providers/의 기존 폴더(예: 단순한 API 키 방식 프로바이더) 하나를 골라 구조를 익힌 뒤, 가상의 새 도구용 프로바이더를 스캐폴딩한다. 데이터 소스 열거형·페처·매핑을 채워 본다. 목표: "한 프로바이더 = 한 폴더" 격리 설계가 왜 확장에 강한지 체득.
이 레포를 온전히 읽으려면 — 5주 코스.
| 주차 | 주제 | 무엇을 / 왜 |
|---|---|---|
| 1주차 | Swift + SwiftUI 기본 | 옵셔널·프로토콜·Codable·@main 앱 구조. CodexBar의 UI/설정을 읽는 토대 |
| 2주차 | AppKit 메뉴바 | NSStatusItem·NSMenu·템플릿 이미지·LSUIElement. 이 앱의 표면을 이루는 기술 |
| 3주차 | Swift 동시성 | async/await·Task·actor·@MainActor·데이터 레이스. 이 코드의 뼈대 |
| 4주차 | 프로세스·파싱 | Process/PTY로 CLI 구동, JSON-RPC, JSONL 스트리밍, ANSI 제거. 데이터 수집의 핵심 |
| 5주차 | 배포·서명 | SPM로 .app 패키징, 코드사이닝/노터라이즈, Sparkle appcast(EdDSA). "제품화" 마지막 마일 |
거꾸로 읽지 말자. 2주차(메뉴바)로 "화면에 뭔가 띄우는" 성취감을 먼저 얻고, 3주차(동시성)에서 "왜 @MainActor가 사방에 붙나"를 이해한 다음, 4주차에서 진짜 어려운 "외부 CLI 긁기"로 들어가는 게 좌절이 적다.
문서에 나온 용어를 한 번에.
actor는 내부 상태를 한 번에 하나의 태스크만 만지게 강제하고, @MainActor는 "메인 스레드에서만"을 뜻한다. 데이터 레이스를 컴파일 단계에서 막는다.UsageStore가 바뀌면 UI가 알아서 다시 그려진다..xcodeproj 없이 Package.swift만으로 앱·CLI·위젯을 모두 빌드한다.appcast.xml이 새 버전 목록을, EdDSA 서명이 위변조 방지를 담당한다.claude 같은 대화형 TUI를 프로그램이 "사람처럼" 조종하려면 필요하다.codex app-server와 stdin/stdout으로 이걸 주고받아 한도를 읽는다.isRefreshing 가드로 새로고침이 겹쳐 도는 낭비를 막는다.더 깊이 파고들 곳.