새 창에서 작업할 타이밍을 알고 싶다면? 클로드코드 statusline으로 세션 컨텍스트 정보 알기

미리 3줄로 보면
  • 뭘 해결하나: 지금 대화가 얼마나 찼는지 몰라 한계를 갑자기 만나는 문제. 화면 맨 아래 한 줄로 잔량이 보이면 끊을 타이밍을 미리 압니다
  • 내가 한 일: statusline 달아줘 두 마디. 스크립트를 짜고 설정에 등록한 건 에이전트입니다
  • 배운 것: 그 에이전트가 두 번 틀렸습니다. 한 번은 스스로 잡았고, 한 번은 제가 화면을 보여줘서 잡혔습니다

🎁 이 글 맨 끝에 클로드코드 사용 역량 평가를 위한 「7축 진단 프롬프트」 전문을 통째로 뒀습니다. 지난 편의 6축 채점에 프롬프트 품질 축을 더해 7축으로 확장한 것입니다. 읽는 건 나중에 하시고, 지금 스크롤을 내려 복사해서 여러분 환경부터 돌려 보세요.

안경 쓰고 트위드 조끼를 입은 딱따구리가 따뜻한 조명의 서재 책상에 앉아 정면을 보며 차분하게 미소 짓고 있고, 뒤쪽 벽에 네 개의 화면이 가지런히 정렬돼 있는 모습
안경 쓰고 트위드 조끼를 입은 딱따구리가 따뜻한 조명의 서재 책상에 앉아 정면을 보며 차분하게 미소 짓고 있고, 뒤쪽 벽에 네 개의 화면이 가지런히 정렬돼 있는 모습 (AI 생성 삽화)

이런 문제 겪고 있다면

클로드코드로 한창 작업하다 대화가 다 찼다는 걸 갑자기 알게 된 적 있으신가요. 그게 올 줄은 전혀 모르고 있었다는 게 문제입니다. 미리 알았으면 적당한 데서 끊고 새 창에서 시작했을 텐데요.

창을 여러 개 띄우면 하나 더 붙습니다. 창을 바꿨는데 여기가 어느 프로젝트인지 순간 헷갈리고, 브랜치를 확인하려 git branch를 또 칩니다. 저는 수학 과외에 쓸 도구들을 AI와 붙이고 있는데, 프로젝트가 넷으로 늘면서 이게 매일 겪는 일이 됐습니다.

지난 편에서 AI에게 제 사용 습관을 채점시켰더니 「상태 가시성」이 10점 만점에 2점이었습니다. 이번 편은 그 2점을 실제로 고치는 이야기입니다.

컨텍스트가 뭔가요

AI가 한 대화에서 기억하고 있는 분량입니다. 담을 수 있는 양에 상한이 있어서, 꽉 차면 앞부분을 요약해 압축하거나 새 대화를 시작해야 합니다. 압축이 돌면 “그때 왜 그렇게 결정했는지”가 사라지므로, 그 전에 끊는 게 좋습니다.

시킨 건 두 마디였습니다

제가 입력한 건 이게 전부입니다.

statusline 달아줘

여기서 미리 말씀드릴 게 있습니다. 아래 나오는 스크립트를 제가 짠 게 아닙니다. 저는 정규식도 파일 다루는 코드도 못 씁니다. 제가 한 일은 “이게 필요하다”고 말한 것, 그리고 나온 결과가 맞는지 판단한 것뿐입니다.

에이전트가 시키지도 않았는데 알아서 한 일이 꽤 됩니다.

알아서 한 일왜 그게 좋은 판단이었나
프로젝트별이 아니라 전역 설정에 등록프로젝트마다 다시 달 필요가 없다
손대기 전에 설정 파일 백업되돌릴 자리를 먼저 만들어 뒀다
등록 후 원본과 대조기존 훅 설정이 날아가지 않았음을 확인
“다른 프로젝트에서도 되냐”고 묻자 말로 답하지 않고 다섯 곳에서 실행git 저장소가 아닌 폴더에서도 안 깨지는지 확인

되돌릴 자리를 먼저 만들고 손대는 것. 제가 혼자 했으면 안 했을 순서입니다.

한 줄이 말해주는 다섯 가지

결과가 이렇습니다. 왼쪽부터 프로젝트명 · 세션 구분자 · 브랜치 · 모델 · 컨텍스트 게이지입니다.

log-to-contents·1ae55  ⎇ main  Opus 5 (1M context)  ▓▓▓▓▓▓░░░░ 62%

아래는 다른 프로젝트 창에서 찍은 실물입니다. 프로젝트 이름과 잔량만 다를 뿐 구조는 같습니다.

클로드코드 화면 맨 아래에 프로젝트명과 세션 구분자, main 브랜치, 모델 이름, 그리고 컨텍스트 게이지가 한 줄로 표시된 상태줄 실물 캡처
클로드코드 화면 맨 아래에 프로젝트명과 세션 구분자, main 브랜치, 모델 이름, 그리고 컨텍스트 게이지가 한 줄로 표시된 상태줄 실물 캡처

제 원래 고민이 이 다섯 조각에 하나씩 대응합니다.

알고 싶던 것전후
지금 어느 프로젝트인가창 제목을 보거나 짐작상시 표시
같은 프로젝트 창 두 개 구분불가능세션 구분자 5글자
어느 브랜치인가git branch 를 침상시 표시
어느 모델로 돌고 있나짐작상시 표시
컨텍스트가 얼마나 찼나알 방법이 없었음게이지 + 75%부터 경고

마지막 줄이 이 글의 제목입니다. 컨텍스트는 다 차야 알던 것이었는데, 이제 반쯤 찼을 때 보입니다. 게이지는 50%부터 노랑, 75%부터 빨강으로 바뀌고 빨강에는 /clear 안내가 붙습니다. 그게 「새 창에서 시작할 타이밍」입니다.

상태줄 한 줄이 프로젝트명과 세션 구분자, 브랜치, 모델 이름, 컨텍스트 게이지 다섯 조각으로 나뉘고 각 조각이 어떤 입력값에서 오는지 화살표로 이어진 구조도
상태줄 한 줄이 프로젝트명과 세션 구분자, 브랜치, 모델 이름, 컨텍스트 게이지 다섯 조각으로 나뉘고 각 조각이 어떤 입력값에서 오는지 화살표로 이어진 구조도

에이전트가 두 번 틀렸습니다

여기가 진짜 배운 부분입니다. 시켜서 받은 결과물이 처음부터 맞지는 않았습니다.

첫 번째 — 스스로 잡았습니다

설정에 등록하기 전에 에이전트가 스스로 한 번 돌려 봤는데, 프로젝트명 자리에 경로가 통째로 찍히고 게이지는 아예 안 나왔습니다. 에러는 한 줄도 없었고요.

에이전트가 저장한 파일을 다시 열어 대조하더니 원인을 찾았습니다. 셸로 여러 줄을 한 번에 쏟아붓는 방식(heredoc)을 썼는데, 그 과정에서 백슬래시가 반으로 줄어든 채로 저장됐던 겁니다.

원래 코드저장된 결과증상
/[\\/]//[\/]/윈도우 경로를 못 나눠 경로가 통째로 찍힘
'(\\d+)''(\d+)'숫자 대신 글자 d를 찾게 돼 게이지가 항상 0

고친 방법이 인상적이었습니다. 문법을 더 파고들어 셸을 길들이는 대신, 에이전트는 그 방식을 아예 버리고 파일 쓰기 도구로 다시 썼습니다. 저는 이 대목에서 배운 게 있어요. 도구가 조용히 값을 바꿔놓는 자리를 만나면, 그 도구를 이기려 들기보다 다른 길로 가는 게 빠르다는 겁니다.

셸로 파일을 쓰다 백슬래시가 반으로 줄어 게이지가 죽고, 에이전트가 저장된 파일을 대조해 원인을 찾은 뒤 파일 쓰기 도구로 다시 써서 정상으로 돌아오기까지의 흐름도
셸로 파일을 쓰다 백슬래시가 반으로 줄어 게이지가 죽고, 에이전트가 저장된 파일을 대조해 원인을 찾은 뒤 파일 쓰기 도구로 다시 써서 정상으로 돌아오기까지의 흐름도

두 번째 — 제가 화면을 보여줘서 잡혔습니다

한참 뒤에 다른 프로젝트에서도 잘 나오는지 확인하려고 캡처를 찍어 보냈습니다. 게이지가 34%를 가리키고 있었고, 저는 그럴듯해서 넘어갔죠.

그런데 에이전트가 그 화면을 보더니 자기 결함을 실토했습니다. 헤더에 Opus 5 (1M context)라고 적혀 있는데 스크립트는 분모를 20만으로 고정해 뒀다는 겁니다. 실제 창은 100만인데요.

분모같은 토큰 수(138,313개)의 표시
200,000 (스크립트 고정값)69%
1,000,000 (실제 창 크기)14%

계산은 정확했고 분자도 진짜였습니다. 분모만 다섯 배 작았습니다. 저는 34%면 아직 여유라고 보고 넘겼는데, 실제로는 7% 언저리였던 거죠. 지금은 모델 이름에 적힌 (1M context)를 읽어 분모로 쓰도록 에이전트가 고쳐 놨습니다.

같은 토큰 수를 분모 20만으로 나누면 69퍼센트, 100만으로 나누면 14퍼센트가 되어 같은 대화가 정반대로 보이는 비교도
같은 토큰 수를 분모 20만으로 나누면 69퍼센트, 100만으로 나누면 14퍼센트가 되어 같은 대화가 정반대로 보이는 비교도

뼈아픈 건 이겁니다. 에이전트는 설치를 끝내고 「알아둘 한계」로 분모가 20만 고정이라고 이미 적어 줬습니다. 제가 안 읽었을 뿐입니다.

남이 만들어준 걸 검수하는 세 가지

코드를 못 읽어도 할 수 있는 것들입니다. 저는 세 번째를 안 해서, 다섯 배 부풀려진 숫자를 그대로 믿고 있었습니다.

  1. 「알아둘 한계」를 끝까지 읽습니다. 잘 만드는 에이전트는 자기 결함을 먼저 신고합니다. 설치가 끝났다는 말 다음이 제일 안 읽히는 자리인데, 거기 답이 있었습니다.
  2. 되돌릴 자리가 있는지 묻습니다. “설정 파일 백업했어?” 한마디면 됩니다. 이번엔 알아서 했지만 매번 그러진 않습니다.
  3. 첫날은 숫자를 의심합니다. 모델 이름 옆 괄호를 보세요. (1M context)라고 적혀 있는데 게이지가 유독 높으면, 분모가 20만으로 박혀 있는 겁니다. 저는 그 둘을 나란히 놓지 않아서 못 잡았습니다.

그래서 뭘 하면 되나

  1. 클로드코드에 statusline 달아줘 라고 합니다. 어떤 항목을 보고 싶은지 덧붙이면 더 좋습니다(“프로젝트·브랜치·컨텍스트 잔량”).
  2. 되돌릴 자리를 확인합니다. 설정 파일을 백업했는지, 기존 설정이 보존됐는지 물어봅니다.
  3. 첫날 게이지를 한 번 검산합니다. 쓰는 모델의 창 크기와 표시된 비율이 말이 되는지 봅니다.
  4. 직접 파일을 두고 싶다면 부록 A의 전문을 ~/.claude/statusline.js로 저장하면 됩니다.

FAQ

Q. 코딩을 못해도 되나요?
네. 저도 못 씁니다. 필요한 건 “뭐가 안 보여서 불편하다”를 말하는 것과, 나온 결과를 의심해 보는 것뿐입니다.

Q. 맥이나 리눅스에서도 되나요?
아마 될 겁니다. 다만 제가 확인한 건 윈도우뿐입니다. 경로만 본인 것으로 바꾸면 되는 구조입니다.

Q. 게이지 숫자가 실제와 다르면요?
분모부터 의심하세요. 모델 표시명에 (1M context) 같은 표기가 없으면 20만으로 떨어집니다. 그 기본값을 본인 창 크기로 바꿔 달라고 하면 됩니다.

Q. 상태줄 때문에 느려지지 않나요?
체감은 없습니다. 30MB짜리 기록에서도 파일 끝 300KB만 읽도록 만들어져 있어서, 기록이 더 커져도 읽는 양은 그대로입니다.


부록 A — 에이전트가 내놓은 스크립트 전문

에이전트가 만들어 준 결과물이고, 두 번의 수정이 반영된 최종본입니다. ~/.claude/statusline.js로 저장한 뒤 ~/.claude/settings.json에 아래 설정을 더하면 됩니다. 그대로 가져다 쓰셔도 됩니다.

{
  "statusLine": {
    "type": "command",
    "command": "node \"C:/Users/<이름>/.claude/statusline.js\"",
    "padding": 0
  }
}
// Claude Code statusline: 어느 세션 · 어느 브랜치 · 컨텍스트 얼마나 찼나.
// 상태줄이 작업을 막으면 안 되므로, 무엇이 잘못돼도 조용히 한 줄만 찍고 끝낸다.
const fs = require('fs');
const { execSync } = require('child_process');

const C = { b: '\x1b[1m', dim: '\x1b[2m', cyan: '\x1b[36m', mag: '\x1b[35m',
            grn: '\x1b[32m', yel: '\x1b[33m', red: '\x1b[31m', r: '\x1b[0m' };

const SPLIT = /[\\/]/;

// 창 크기는 모델 표시명이 알려준다 — "Opus 5 (1M context)". 못 읽으면 200k.
function windowSize(name) {
  const m = (name || '').match(/(\d+(?:\.\d+)?)\s*([MK])\s*context/i);
  if (!m) return 200000;
  return Math.round(Number(m[1]) * (m[2].toUpperCase() === 'M' ? 1e6 : 1e3));
}

// 트랜스크립트 꼬리만 읽는다 — 30MB 파일을 매 렌더마다 통째로 읽지 않기 위해.
function contextTokens(p) {
  const fd = fs.openSync(p, 'r');
  try {
    const size = fs.fstatSync(fd).size;
    const len = Math.min(size, 300000);
    const buf = Buffer.alloc(len);
    fs.readSync(fd, buf, 0, len, size - len);
    const found = buf.toString('utf8').match(/"usage":\{[^}]*\}/g);
    if (!found) return 0;
    const last = found[found.length - 1];
    const n = (k) => {
      const m = last.match(new RegExp('"' + k + '":(\\d+)'));
      return m ? Number(m[1]) : 0;
    };
    return n('input_tokens') + n('cache_creation_input_tokens') + n('cache_read_input_tokens');
  } finally {
    fs.closeSync(fd);
  }
}

// -b 로 브랜치, -uno 로 untracked 무시(대형 레포에서 빠르게).
function git(cwd) {
  const out = execSync('git status --porcelain=v1 -b -uno', {
    cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 1500,
  });
  const lines = out.split('\n');
  const br = (lines[0].match(/^## ([^.\s]+)/) || [, '?'])[1];
  return br + (lines.slice(1).some(Boolean) ? '*' : '');
}

let d = {};
try { d = JSON.parse(fs.readFileSync(0, 'utf8')); } catch {}

const cwd = (d.workspace && d.workspace.current_dir) || d.cwd || process.cwd();
const proj = cwd.replace(/[\\/]+$/, '').split(SPLIT).pop() || '?';
const tag = (d.session_id || '').slice(0, 5);
const model = (d.model && d.model.display_name) || '';

let branch = '';
try { branch = git(cwd); } catch {}

let pct = null;
try {
  if (d.transcript_path && fs.existsSync(d.transcript_path)) {
    pct = Math.min(99, Math.round(contextTokens(d.transcript_path) / windowSize(model) * 100));
  }
} catch {}

const parts = [];
parts.push(C.b + C.cyan + proj + C.r + (tag ? C.dim + '·' + tag + C.r : ''));
if (branch) parts.push(C.mag + '⎇ ' + branch + C.r);
if (model) parts.push(C.dim + model + C.r);
if (pct !== null) {
  const col = pct >= 75 ? C.red : pct >= 50 ? C.yel : C.grn;
  const fill = Math.round(pct / 10);
  parts.push(col + '▓'.repeat(fill) + C.dim + '░'.repeat(10 - fill) + C.r +
             col + ' ' + pct + '%' + C.r + (pct >= 75 ? ' ' + C.red + '/clear' + C.r : ''));
}
process.stdout.write(parts.join('  '));

부록 B — 7축 진단 발주서 전문

지난 편의 6축 채점을 프롬프트 품질까지 넣어 7축으로 확장한 발주서입니다. 통째로 복사해서 여러분의 클로드코드에 그대로 붙이면 됩니다.

# 발주: 전 프로젝트 Claude Code 사용 역량 감사 (7축)

## 목적
"클로드코드를 잘 쓰고 있는가"를 프로젝트 한 곳이 아니라 전 프로젝트 실측으로 판정한다.
아부·완곡어법 금지. 근거 없는 칭찬 금지. 각 판정은 파일 경로나 수치를 달고 나온다.

## 측정 대상
~/.claude/projects/ 아래 전 프로젝트 트랜스크립트(.jsonl),
각 프로젝트의 .claude/settings*.json / CLAUDE.md / skills / agents / hooks,
~/.claude/settings.json (전역).

## 7축과 측정 방법 (반드시 수치로)

1. 반복의 자산화 — 스킬·훅·커맨드·에이전트 수, CLAUDE.md 존재/길이.
   같은 절차를 자연어로 재설명한 흔적이 몇 번인지 세라.
2. 검증 루프 — 완료 판정이 테스트/명령 종료코드인가, 사람 눈인가.
   "확인해봐/맞아?/잘 됐나" 류 확인 요청 프롬프트 비율.
3. 위임의 폭 — subagent·background task·worktree 실사용 횟수 vs
   사람이 여러 세션을 손으로 병렬 운용한 정황(동시간대 세션 겹침).
4. 컨텍스트 위생 — 프로젝트별 세션 수 / 세션당 평균 MB / 컴팩션 횟수.
   🔴 컴팩션은 반드시 온전한 패턴으로 셀 것:
      grep -c '"subtype":"compact_boundary"' <transcript.jsonl>
      짧은 형태(compact_boundary)는 그 단어를 언급만 해도 세어져 값이 부풀려진다.
   기준: 컴팩션 1회 이상이면 그 지점이 원래 /clear 했어야 할 자리다.
5. 마찰 제거 — allowlist 규칙 중 (a)전역 allow가 비어 있어 프로젝트마다 되풀이 승인하는 항목
   (`Bash(git add:*)`와 `Bash(git add *)`는 공식 문서상 둘 다 유효하다 — 문법을 원인으로 삼지 말 것)
   (b)일회성 명령 전문이 박힌 쓰레기 항목. 각각 파일:항목으로 열거.
6. 상태 가시성 — statusline·output style 설정 여부, HANDOVER/WORKLOG 최신성,
   "지금 어디까지 했지" 류 프롬프트 빈도.

7. 프롬프트 퀄리티 (가장 비중 크게)
   전 프로젝트 user 메시지를 뽑아 아래를 실측하라.
   추출 예: jq -r 'select(.type=="user") | .message.content' *.jsonl
   ⚠️ user 타입에는 도구 실행 결과도 섞인다. tool_result 를 걸러야 사람 프롬프트만 남는다.

   7-1 정정률 — 직전 응답을 되돌리는 프롬프트 비율
       ("아니 그게 아니라", "다시", "취소", "원래대로", "왜 그걸 했어")
   7-2 성공기준 동봉률 — 검증 가능한 완료조건이 있는 비율
   7-3 증거 동봉률 — 파일경로·에러 원문·재현 절차를 준 비율 vs 무증거 신고 비율
   7-4 배치성 — 독립 요청을 한 메시지에 묶은 비율 vs 한 줄씩 쪼갠 비율
   7-5 되묻기 유발률 — Claude가 질문으로 되받은 비율
   7-6 길이 분포 — 20자 미만 단문 비율, 그 단문 뒤 정정률
   7-7 스킬 활용 — /스킬 호출 vs 같은 일을 자연어로 다시 설명한 비율

   각 지표에 대해 실제 프롬프트 3개를 원문 인용하고,
   같은 의도의 개선판을 옆에 나란히 써라(before/after).
   추상적 조언 금지 — 그대로 복붙할 수 있는 문장이어야 한다.

## 출력 형식
1. 7축 점수표(10점 만점) + 프로젝트별 편차 — 어느 프로젝트가 최악인지 명시
2. 전 프로젝트 공통 문제 / 특정 프로젝트 국한 문제를 분리
3. 프롬프트 before/after 최소 10쌍
4. 처방을 효과 순으로 5개, 각각 소요시간과 실행 명령까지
5. 마지막에 아부 없는 한 문단 총평

## 금지
- 측정 안 한 축을 추정으로 채우지 말 것. 못 쟀으면 "미측정"이라 쓸 것.
- 점수는 주관 채점이다. 실측치와 섞어 제시하지 말 것.
- 프롬프트 원문 인용 시 비밀·개인정보는 마스킹.
- 트랜스크립트는 크다. 전문 로딩 말고 jq/grep 집계로 처리할 것.

돌려 보시면 아마 저처럼 낮은 점수가 나올 겁니다. 저는 지난 편에서 6축으로 채점받아 60점 만점에 29점이었습니다. 결과가 나오면 한 번은 되물어 보세요 — 「이 판정의 근거가 뭐냐」고요. 이 글의 상태줄도 한 번은 에이전트가 스스로, 한 번은 제가 화면을 내밀어서 제자리를 찾았습니다.

댓글 남기기