
기본 설정 그대로 두기엔 아까운 Claude Code의 상태 표시줄 (Status Line)
요약
Claude Code의 상태 표시줄(Status Line)을 커스터마이징하여 사용자에게 필요한 정보를 효율적으로 시각화하는 방법을 소개합니다. 스크립트 작성, 설정 파일 업데이트, /statusline 명령어를 활용한 자동화 메커니즘을 다룹니다.
핵심 포인트
- 상태 표시줄 커스터마이징을 통해 모델명, 컨텍스트 사용률, Rate Limit 등을 시각화 가능
- 스크립트 실행 권한 부여 및 settings.json 설정이 필수적임
- stdin으로 JSON 데이터를 받아 stdout으로 출력하는 구조로 동작
- '/statusline' 명령어를 통해 자연어로 스크립트 생성 및 설정 자동화 가능
서론
Claude Code의 상태 표시줄 (Status Line)을 기본 설정 그대로 사용하고 계신 분들이 많겠지만, 자신이 보고 싶은 정보를 나열해 두면 상상 이상으로 쾌적해집니다. 저는 다음 3줄을 항상 표시하고 있습니다.

현재 모델과 컨텍스트 (Context) 사용률, 현재 위치한 디렉토리와 브랜치, 그리고 속도 제한 (Rate Limit) 소비 상황입니다.
/statusline
이라는 상태 표시줄을 커스터마이징하기 위한 기능이 내장되어 있어, 원하는 내용을 전달하면 스크립트 생성부터 설정 파일 업데이트까지 완료됩니다.
이 기사에서는 커스터마이징의 메커니즘과 상태 표시줄에 전달되는 데이터의 전체 목록, 그리고 실례로서 저의 설정을 소개합니다.
상태 표시줄의 구성 요소
구현을 AI에게 맡기더라도, 무엇을 어떻게 설정해야 표시가 바뀌는지 파악해 두는 것이 좋습니다.
상태 표시줄 커스터마이징이 활성화된 상태란 다음 3가지가 갖춰진 상태를 의미합니다.
| 구성 요소 | 충족해야 할 조건 |
|---|---|
| 스크립트 | 임의의 경로에 두고 실행 권한을 부여한다 |
settings.json의 statusLine | type에 "command", command에 스크립트 경로를 지정한다 |
| 스크립트 구현 | stdin의 JSON을 읽고, stdout에 작성한다 |
/statusline 명령이 수행하는 것은 이 3가지를 대행하는 것뿐입니다.
스크립트 배치와 실행 권한
스크립트를 두는 위치에 정해진 규칙은 없지만, /statusline으로 생성하면 ~/.claude/ 하위에 만들어집니다.
실행 권한이 필요하다는 점에 주의하십시오.
chmod +x ~/.claude/statusline.sh
설정 파일 등록
settings.json에 statusLine을 추가하고, type에 "command", command에 스크립트 경로를 지정합니다.
{
"statusLine": {
"type": "command",
...
작성 위치는 사용자 설정(~/.claude/settings.json)과 프로젝트 설정(.claude/settings.json) 중 어느 쪽이라도 상관없습니다. 프로젝트 설정은 사용자 설정을 덮어쓰므로, 특정 프로젝트에서만 표시를 바꿀 수 있습니다.
스크립트 내용
stdin으로 JSON을 받아 stdout으로 출력한 것이 그대로 표시됩니다.
#!/bin/bash
input=$(cat) # ① stdin의 JSON을 읽음
MODEL=$(echo "$input" | jq -r '.model.display_name')
...
입력되는 JSON에 어떤 필드가 포함되는지는 후술할 「표시 가능한 데이터」에서 다룹니다.
스크립트를 만들지 않는 최소 구성
command는 셸(Shell)에서 실행되므로, 스크립트 파일을 준비하지 않고 인라인 명령을 직접 작성할 수도 있습니다.
{
"statusLine": {
"type": "command",
...
/statusline 명령
앞 절의 3가지를 자동으로 준비해 주는 것이 /statusline입니다. 자연어로 지시하면 스크립트 생성부터 설정 파일 업데이트까지 수행합니다.
/statusline 모델명과 컨텍스트 사용률을 프로그레스 바(Progress Bar)로 표시해줘
삭제 역시 마찬가지로 자연어로 전달합니다. 전용 서브 명령어가 있는 것은 아닙니다.
/statusline 삭제해줘
물론 수동으로 settings.json에서 statusLine을 업데이트하거나 삭제해도 상관없습니다.
statusLine의 설정 항목
statusLine에 쓸 수 있는 키는 type과 command뿐만이 아닙니다.
| 키 | 내용 |
|---|---|
type | "command"를 지정함 |
command | 스크립트 경로 또는 인라인 쉘 커맨드 (inline shell command) |
padding | 수평 여백 (글자 수). 기본값은 0 |
refreshInterval | N초마다 커맨드를 재실행함. 최솟값은 1 |
hideVimModeIndicator | 내장된 -- INSERT -- 표시를 억제함 |
padding은 인터페이스에 내장된 여백에 가산됩니다. 터미널 끝으로부터의 절대적인 거리가 아니라, 상대적인 인덴트 (indent)를 제어하는 것이라고 생각하면 좋습니다.
hideVimModeIndicator는 스크립트 측에서 Vim 모드를 그릴 때 사용합니다. 내장된 표시와 중복되지 않도록 하기 위한 것입니다.
업데이트 타이밍
상태 표시줄(statusLine)의 업데이트는 기본적으로 이벤트 드리븐 (event-driven) 방식입니다. 실행되는 시점은 세션 시작 시, 어시스턴트의 메시지 도착 시, /compact 완료 시, 퍼미션 모드 (permission mode) 변경 시, Vim 모드 전환 시입니다 (업데이트에는 300ms의 디바운스 (debounce)가 적용됩니다).
즉, 아무런 조작을 하지 않는 동안에는 재실행되지 않습니다. refreshInterval을 설정하면 이러한 이벤트와 더불어 N초마다 실행되도록 할 수 있습니다. 시계나 남은 시간, 스크립트 외부에서 변하는 git 상태와 같이 이벤트와 무관하게 변화하는 값은 이를 설정하지 않으면 오래된 표시 그대로 남게 됩니다.
출력 표현 방법
데이터 목록에 들어가기 전에, 그 보여주는 방식을 먼저 정리합니다. 표준 출력 (standard output)에 쓴 것은 거의 그대로 터미널에 흐르기 때문에, 사용할 수 있는 것이 단순 텍스트(plain text)뿐만은 아닙니다.
참고로, 상태 표시줄 스크립트는 로컬에서 실행될 뿐 API 토큰을 소비하지 않습니다. 표시를 정교하게 만들어도 비용은 늘어나지 않습니다.
여러 줄
echo나 print 한 번이 한 줄에 대응합니다. 개행을 포함하면 그대로 줄이 늘어납니다.
echo "1행目"
echo "2행目"
ANSI 컬러
ANSI 이스케이프 코드 (escape code)를 통한 색상 지정이 가능합니다. 표준 8색과 밝은 8색, 256색 팔레트, 24bit 트루 컬러 (true color)를 모두 사용할 수 있습니다. 굵게(bold), 흐리게(faint), 이탤릭(italic), 밑줄(underline)도 적용됩니다.
터미널 너비
Claude Code는 스크립트의 출력을 캡처하므로, 스크립트의 표준 입출력은 터미널에 연결되어 있지 않습니다. 터미널에 직접 크기를 묻는 탐지 (stty size, Python의 os.get_terminal_size(), Node의 process.stdout.columns)는 여기에서 실패합니다.
대신 터미널의 너비와 높이가 COLUMNS와 LINES 환경 변수로 전달됩니다 (v2.1.153 이후). tput cols나 Python의 shutil.get_terminal_size()는 터미널에 문의하기 전에 COLUMNS를 확인하므로, 이들은 스크립트 안에서도 올바른 너비를 반환합니다.
이를 사용하면 너비가 부족할 때만 정보를 생략하는 등의 분기 처리를 할 수 있습니다.
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
...
너비 120에서는 Opus · /Users/me/project · ctx 23%가 되고, 너비 80에서는 디렉토리를 생략하여 Opus · 23%가 됩니다.
단, 터미널 리사이즈(resize) 자체가 재실행 트리거는 아닙니다. 창을 좁힌 직후에는 이전 출력이 그대로 남아 있다가, 다음에 스크립트가 실행되는 타이밍에 짧은 형식으로 전환됩니다. 리사이즈에 즉각 대응하게 하고 싶다면 refreshInterval을 병용하십시오.
표시할 수 있는 데이터
stdin으로 전달되는 JSON 필드를 용도별로 나열합니다.
여기에 나열된 것들이 항상 전부 전달되는 것은 아닙니다. 계약이나 실행 옵션, 타이밍에 따라 키(key) 자체가 나타나지 않을 수 있으므로, 값이 없을 때를 대비한 폴백 (fallback)을 준비해 두는 것이 안전합니다.
모델과 세션 상태
| 필드 | 내용 | 예시 |
|---|---|---|
model.id | 모델 ID | claude-opus-5 |
model.display_name | 표시 이름 | Opus 5 |
effort.level | 추론의 effort | low / medium / high / xhigh / max |
fast_mode | Fast mode 활성화 여부 | false |
thinking.enabled | extended thinking 활성화 여부 | true |
output_style.name | 출력 스타일 이름 | default |
vim.mode | vim 모드 | NORMAL / INSERT / VISUAL / VISUAL LINE |
agent.name | 에이전트 이름 (--agent 사용 시) | security-reviewer |
version | Claude Code 버전 | 2.1.220 |
effort.level은 세션 중에 /effort로 변경한 경우에도 해당 값이 반영됩니다. 참고로 ultracode는 독립된 레벨이 아니라 xhigh로 보고됩니다. effort 파라미터에 대응하지 않는 모델의 경우, 이 키 자체가 전달되지 않습니다.
vim.mode를 직접 표시하려면, 앞서 언급한 hideVimModeIndicator를 통해 기본 인디케이터를 꺼두지 않으면 중복으로 나타나게 됩니다.
컨텍스트 윈도우 (Context Window)
| 필드 | 내용 | 예시 |
|---|---|---|
context_window.used_percentage | 사용률 (계산됨) | 8 |
context_window.remaining_percentage | 잔여율 (계산됨) | 92 |
context_window.context_window_size | 최대 크기 (토큰) | 200000 / 1000000 |
context_window.total_input_tokens | 현재 입력 토큰 수 | 15500 |
context_window.total_output_tokens | 최근 응답의 출력 토큰 수 | 1200 |
context_window.current_usage.input_tokens | 내역: 신규 입력 | 8500 |
context_window.current_usage.output_tokens | 내역: 출력 | 1200 |
context_window.current_usage.cache_creation_input_tokens | 내역: 캐시 쓰기 | 5000 |
context_window.current_usage.cache_read_input_tokens | 내역: 캐시 읽기 | 2000 |
exceeds_200k_tokens | 합계가 200k를 초과했는지 여부 | false |
total_input_tokens는 v2.1.132에서 의미가 변경되었습니다. 이전에는 세션의 누적값이었으나, 현재는 컨텍스트 윈도우 내의 양을 가리킵니다. 오래된 기사의 스크립트를 재사용할 때는 이러한 전제의 차이에 주의하십시오.
exceeds_200k_tokens는 실제 윈도우 크기와 무관한 고정 임계값입니다. 1M 컨텍스트 모델을 사용하더라도, 200k를 초과하는 시점에 true가 됩니다.
비용 및 작업량
| 필드 | 내용 | 예시 |
|---|---|---|
cost.total_cost_usd | 세션 추정 비용 (USD) | 0.01234 |
cost.total_duration_ms | 세션 시작 후 경과 시간 (ms) | 45000 |
cost.total_api_duration_ms | API 응답 대기 총 시간 (ms) | 2300 |
cost.total_lines_added | 추가된 행 수 | 156 |
cost.total_lines_removed | 삭제된 행 수 | 23 |
total_cost_usd
는 클라이언트 측에서 계산된 추정치이며, 실제 청구 금액과 일치하지 않을 수 있습니다. v2.1.211 이후부터는 /clear를 통해 새로운 세션이 시작되면 $0로 리셋됩니다.
레이트 리미트 (Rate Limit)
| 필드 | 내용 | 예 |
|---|---|---|
rate_limits.five_hour.used_percentage | 5시간 한도 소비율 (0~100) | 23.5 |
rate_limits.five_hour.resets_at | 5시간 한도가 리셋되는 시각 | 1738425600 |
rate_limits.seven_day.used_percentage | 7일 한도 소비율 (0~100) | 41.2 |
rate_limits.seven_day.resets_at | 7일 한도가 리셋되는 시각 | 1738857600 |
used_percentage는 0~100 사이의 수치이며, 반드시 정수는 아닙니다. 정수로 비교하려면 반올림 처리가 필요합니다. resets_at은 Unix 에포크 초(Unix epoch seconds) 단위이므로, 그대로는 읽을 수 없습니다. 시각 형식으로 변환하거나, 현재 시각과의 차이를 구하여 남은 시간으로 표시해야 합니다.
이 객체는 Pro/Max 계약 상태이면서 세션의 첫 API 응답 이후에만 존재합니다. API 종량제(Pay-as-you-go)를 사용하는 경우에는 존재하지 않습니다.
위치 및 리포지토리 (Location and Repository)
| 필드 | 내용 | 예 |
|---|---|---|
workspace.current_dir | 현재 작업 디렉토리 | /Users/me/project |
cwd | 위와 동일 (같은 값) | /Users/me/project |
workspace.project_dir | 실행 시의 디렉토리 | /Users/me/project |
workspace.added_dirs | /add-dir로 추가한 디렉토리 배열 | [] |
workspace.git_worktree | linked worktree 안에 있을 때의 worktree 디렉토리 이름 | feature-xyz |
workspace.repo.host | origin으로부터 분석된 호스트 | github.com |
workspace.repo.owner | 소유자 (Owner) | anthropics |
workspace.repo.name | 리포지토리 이름 | claude-code |
cwd와 workspace.current_dir에는 동일한 값이 들어갑니다. 공식 문서에서는 workspace.project_dir와 일치한다는 이유로 후자를 권장합니다. project_dir는 실행 시의 디렉토리이므로, 세션 중에 작업 디렉토리가 변경되면 current_dir와 차이가 발생합니다.
풀 리퀘스트 (Pull Request)
| 필드 | 내용 | 예 |
|---|---|---|
pr.number | 현재 브랜치의 오픈된 PR 번호 | 1234 |
pr.url | 해당 URL | https://github.com/anthropics/claude-code/pull/1234 |
pr.review_state | 리뷰 상태 | approved / pending / changes_requested / draft |
화면 하단에 표시되는 PR 배지와 동일한 정보원입니다.
worktree
| 필드 | 내용 | 예 |
|---|---|---|
worktree.name | worktree 이름 | my-feature |
worktree.path | worktree의 절대 경로 | /path/to/.claude/worktrees/my-feature |
worktree.branch | worktree의 브랜치 | worktree-my-feature |
worktree.original_cwd | worktree 진입 전의 디렉토리 | /path/to/project |
worktree.original_branch | worktree 진입 전의 브랜치 | main |
혼란스러운 점은, 최상위 레벨의 worktree.*와
앞서 언급한 workspace.git_worktree가
서로 다른 것이라는 점입니다.
| 진입 조건 | |
|---|---|
workspace.git_worktree | linked worktree 안에 있을 때 (--worktree로 생성된 것도 포함) |
worktree.* | Claude Code를 --worktree로 실행한 세션일 때 |
평소에 git worktree를 사용하고 있다면, 필요한 것은 workspace.git_worktree 쪽입니다.
식별자 (Identifier)
| 필드 | 내용 | 예 |
|---|---|---|
session_id | 세션의 고유 ID | UUID |
session_name | 세션 이름 | my-session |
prompt_id | 처리 중인 사용자 프롬프트의 UUID | UUID |
transcript_path | 대화 로그의 파일 경로 | /path/to/transcript.jsonl |
session_name에 들어가는 것은 --name이나 /rename으로 지정한 이름, 또는 AI가 생성한 세션 타이틀입니다. my-app-3f와 같은 기본 표시 이름으로는 채워지지 않습니다.
prompt_id는 v2.1.196 이후부터 전달됩니다. 첫 번째 사용자 입력이 있기 전까지는 키 자체가 존재하지 않습니다.
스테이터스 라인 (Status Line) 소개
지금까지의 목록을 바탕으로 자신이 무엇을 표시하고 있는지 실례로 두겠습니다.
3행 구성으로 되어 있습니다. 색상을 제외한 텍스트만 나열하면 다음과 같습니다 (실제 표시는 서두의 스크린샷을 참조하세요).
🤖 Opus 5 (xhigh)
🧠 ██░░░░░░░░ 23% │ 📂 myapp/src/api │ 🔀 feature/statusline
🕐 ███████░░░ 65% 17:13 (1h59m) │ 🗓 █████████░ 88% 8/1 22:44 (2d7h)
| 행 | 표시 내용 | 사용 중인 필드 |
|---|---|---|
| 1 | 모델과 effort | model.display_name, effort.level |
| 2 | 컨텍스트 사용률, 디렉토리, 브랜치 | context_window.used_percentage, workspace.current_dir (브랜치는 git에서 가져옴) |
| 3 | 레이트 리미트 (Rate Limit)의 5시간 윈도우와 7일 윈도우 | rate_limits.five_hour.*, rate_limits.seven_day.* |
한 줄에 모두 담을 수도 있었지만, 가로로 길어지면 터미널 너비에 따라 줄바꿈이 발생합니다. 줄바꿈이 되면 높이가 변하여 프롬프트의 위치가 움직이기 때문에, 행을 나누어 세로로 쌓았습니다.
사용 중인 필드는 총 8개입니다. 목록에 나열한 것 중 아주 일부만 사용하고 있습니다.
디렉토리와 브랜치 표시
workspace.current_dir를 그대로 출력하면 너무 길고, 대부분 매번 같은 문자열이라 정보량이 없습니다. 반대로 끝의 디렉토리 이름만 표시하면, src나 api처럼 흔한 이름이 되어 어느 프로젝트인지 알 수 없게 됩니다.
그래서 git 관리 하에 있을 때는 git rev-parse --show-toplevel로 가져온 루트 디렉토리 이름을 앞에 두고, 거기서부터의 상대 경로를 연결합니다. /Users/me/dev/myapp/src/api라면 myapp/src/api가 됩니다. 프로젝트와 위치를 동시에 알 수 있습니다.
git 관리 외의 환경에서는 기준이 되는 루트가 없으므로, workspace.current_dir 전체를 출력하되 앞부분의 HOME만 ~로 치환합니다. /Users/me/Documents/zenn이라면 ~/Documents/zenn이 됩니다.
브랜치명은 전달되지 않으므로 직접 git 명령어를 실행합니다.
레이트 리미트 (Rate Limit) 표시
resets_at은 에포크 초 (Epoch second) 단위이므로, 리셋 시각(17:13)과 남은 시간(1h59m
) 양쪽 모두에 정렬하여 나열하고 있습니다. 시각만 표시하면 "앞으로 몇 시간 남았는지"를 매번 머릿속으로 계산해야 하고, 남은 시간만 표시하면 일정과 대조해 볼 수 없습니다. 5시간 단위 프레임은 시각만, 7일 단위 프레임은 날짜도 함께 표시하고 있습니다.
남은 시간을 표시하면 시간의 경과 자체가 표시 내용이 됩니다. 상태 표시줄 (Status Line)의 업데이트는 이벤트 드리븐 (Event-driven) 방식이므로, 그대로 두면 표시가 과거 상태 그대로 멈춰 버립니다. 따라서 refreshInterval을 300 (5분)으로 설정하여, 조작하지 않는 동안에도 5분마다 다시 그리도록 했습니다.
참고로 rate_limits는 Pro/Max 계약에서만 존재하므로, 이 세 번째 줄은 해당 계약을 이용 중인 사용자에게만 표시됩니다.
스크립트 전체 내용
~/.claude/statusline.sh의 전체 내용입니다.
#!/bin/bash
#--- 표시 파라미터 ---------------------------------------------------------
BAR_WIDTH=10
...
settings.json 측은 이것뿐입니다.
{
"statusLine": {
"type": "command",
...
마치며
목록에 나열한 필드는 많지만, 전부를 올릴 필요는 없습니다. 상태 표시줄 (Status Line)은 항상 눈에 띄는 곳이므로, 정말 필요한 것들로만 압축하는 것이 가독성이 좋습니다.
구현은 /statusline에 맡길 수 있으므로, 나머지는 자신이 무엇을 보고 싶은지를 결정하기만 하면 됩니다. 우선은 지금 가장 알고 싶은 값을 하나 추가하는 것부터 시작해 보세요.
참고
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기