# AI 에이전트

Claude Code가 파일을 통째로 다시 쓰면 출력 토큰이 몇 배가 될까?

@Hong2026-10-26

이 글에 담긴 코드와 결과는 직접 실행해 측정한 값을 바탕으로 작성했습니다. 실측은 Hong과 팀원들이 진행했고, 그 결과를 바탕으로 AI가 글을 쓰고 Hong이 검토합니다.

실습 코드 내려받기

질문
Claude Code가 한 줄 수정에 Edit 대신 Write로 파일 전체를 다시 쓰면 출력 토큰이 얼마나 늘까?
답
204줄 파일의 help 문구 한 줄을 고칠 때 출력 토큰 중앙값은 부분 수정 736, 전체 재작성 4,055로 약 5.5배였고, 늘어난 몫은 거의 다 Write로 끝난 응답(3,546~3,549)이었다.
조건
2026-10-26, Claude Code 2.1.309 claude -p --safe-mode, 기본 모델(claude-opus-5), 조건별 5회, 세션 로그를 응답 유형별로 분해

환경

측정일
2026-10-26
Claude Code
2.1.309, claude -p --safe-mode --permission-mode acceptEdits --output-format json
모델
지정하지 않음(결과 JSON modelUsage의 모델 ID는 10회 모두 claude-opus-5, 세션 로그에 기록된 effort는 high)
권한
--permission-mode acceptEdits, 사용자 설정의 Bash 전체 허용은 --safe-mode에서도 적용됨(허용이 없으면 Bash 호출과 숫자가 다를 수 있음)
대상
src/tokenlab/cli.py 204줄(8,003바이트)의 help 문구 한 줄
반복
부분 수정·전체 재작성 각 5회(섞은 순서), 로그인 오류로 끝난 1회는 다시 실행
비용
구독 로그인으로 호출, 금액은 Claude Code가 API 목록 단가로 환산한 추정치(실제 청구액 아님)
목차
  1. 출력은 마지막 답만이 아닙니다
  2. 먼저 확인할 수 있는 범위 정하기
  3. 실습 준비
  4. 실습 1: 필요한 부분만 수정하기
  5. 실습 2: 같은 작업을 Write로 전체 재작성하기
  6. 실습 3: 다섯 번씩 반복해서 비교하기
  7. 부분 수정이 항상 성공할까?
  8. 정리

이번 시간에는 같은 한 줄 수정을 필요한 부분만 고치는 방식과 파일 전체를 다시 쓰는 방식으로 시켜 보고, 그 차이를 세션 로그와 claude -p로 측정하며 배워가는 시간입니다. claude -p는 대화 화면 없이 프롬프트 하나를 보내고 결과를 받는 실행 방식이에요.

다시 돌리면 숫자가 조금씩 달라질 수 있고, 기본 모델도 시간이 지나면 바뀔 수 있어요.

출력은 마지막 답만이 아닙니다

여기서 다루는 출력은 Claude Code가 마지막에 보여 주는 결과나 설명만이 아니에요. 에이전트가 파일을 읽고, 쓰고, 수정하고, 실행하려고 만드는 도구 호출이 모두 출력에 들어갑니다.

그래서 코드 한 줄만 바꿨는데도 작업 방식에 따라 출력이 크게 달라질 수 있어요.

먼저 확인할 수 있는 범위 정하기

Claude Code가 파일을 읽거나 수정하면, 대화 화면 중간에 작업 과정이 표시돼요. 파일을 읽을 때는 Read, 필요한 부분만 고칠 때는 Edit, 파일 전체를 새로 작성할 때는 Write라고 나와서, 에이전트가 무슨 작업을 했는지 알 수 있어요.

토큰 쪽에서 볼 값은 실행 전체의 출력 토큰인 output_tokens예요.

다만 세션 로그로는 도구 호출 하나마다 토큰을 나눠 볼 수 없어요. 왜 그럴까요? 한 assistant 메시지 안에 여러 도구 호출과 텍스트가 같이 들어 있을 수 있기 때문이에요.

그래서 "Write가 정확하게 몇 토큰을 썼다"라고 말하지 않고, 실제로 어떤 도구를 썼는지와 그 실행 전체의 output_tokens를 함께 확인하는 방향으로 가볼게요.

실습 준비

이 글의 명령은 모두 실습 코드의 claude-edit-vs-write-output/ 디렉터리에서 실행합니다.

  • src/tokenlab/: 수정 대상 cli.py와 세션 로그를 읽는 session_jsonl.py가 든 작은 측정 도구 패키지
  • pyproject.toml: 패키지 설정(Python 3.11 이상, PyYAML)
  • breakdown.py: 세션의 출력 토큰을 내용 유형별로 나누는 스크립트
  • prompt-part.txt, prompt-full.txt: 두 방식의 프롬프트 원문
  • schedule.txt, run.sh: 실행 순서와, 그 순서대로 claude -p를 부르는 스크립트
  • summarize.py: 회차마다 판정하고 방식별 중앙값과 범위를 내는 스크립트
  • .gitignore: git에 올리지 않을 파일 목록

따라 하시려면 Claude Code와 rg, jq가 필요해요. 뒤에서 쓰는 git status와 git restore를 위해 첫 커밋을 해 두고, Python 3.11 이상으로 가상 환경을 만들어 패키지를 설치합니다.

git init -q
git add .
git commit -q -m "start"
python3 -m venv .venv
.venv/bin/python -m pip install -e .

설치가 끝나면 이런 줄이 나와요(발췌).

Successfully installed PyYAML-6.0.2 tokenlab-0.1.0

자 그럼 실습에 앞서 환경부터 정리해 볼게요. 수정할 파일이 깨끗한지, 바꿀 문구가 어디 있는지 봅니다.

git status --short -- src/tokenlab/cli.py
rg -n 'env.add_parser\("capture"|실행 환경 스냅샷' src/tokenlab/cli.py
149:    c = env.add_parser("capture", help="실행 환경 스냅샷")

git status는 아무것도 나오지 않아야 합니다. 뭔가 나왔다면 삭제하거나 되돌리지 말고, 실습 코드를 새로 풀어서 이어 가시면 됩니다.

두 번째 명령에 보이는 capture 서브커맨드의 help 문구만 바꿀 거예요. 이 파일은 wc -l로 세면 204줄입니다.

출력 토큰은 세션 로그에서 한 덩어리 숫자로만 보여서, 그 안에 어떤 내용이 들어 있었는지는 알 수 없어요. 그래서 tokenlab의 세션 파서를 그대로 써서 출력을 내용 유형별로 나눠 주는 breakdown.py를 넣어 뒀어요.

"""최근 세션의 출력 토큰을 내용 유형별로 분해한다. 사용법: python breakdown.py [세션파일]"""
import collections, json, sys
from pathlib import Path
from tokenlab.session_jsonl import latest_session, iter_events

if len(sys.argv) > 1:
    path = Path(sys.argv[1])
else:
    root = Path.home() / ".claude" / "projects"
    newest_dir = max(root.glob("*/"), key=lambda p: p.stat().st_mtime)
    path = latest_session(newest_dir)

per = {}
for ev in iter_events(path):
    if ev.get("type") != "assistant":
        continue
    m = ev["message"]
    usage = m.get("usage") or {}
    kinds = "+".join(sorted({c["type"] if c["type"] != "tool_use" else c["name"]
                             for c in m.get("content", [])}))
    per[m.get("id")] = (usage.get("output_tokens", 0), kinds)

total = sum(o for o, _ in per.values()) or 1
agg = collections.Counter()
for o, k in per.values():
    agg[k] += o
print(path.name)
print(f'{"출력":>7}  {"비중":>5}  내용')
for k, o in agg.most_common():
    print(f'{o:>7}  {o * 100 // total:>4}%  {k}')
print(f'{total:>7}  100%  합계')

그대로 쓰셔도 되지만 세 덩어리로 나눠서 무슨 코드인지는 알고 가볼게요.

첫 덩어리는 어느 세션을 볼지 정합니다. 인자로 세션 파일 경로를 주면 그 세션을 보고, 안 주면 .claude/projects 아래에서 가장 최근 세션을 골라 줘요. 이 글에서는 경로를 직접 넘겨요.

여기서 session_jsonl.py의 latest_session은 그냥 제일 최근 파일을 집지 않고, 사용량이 들어 있는 세션을 만날 때까지 최신 순으로 내려가요. 슬래시 명령만 치고 끝낸 세션도 파일은 생기는데, assistant 응답이 없어 사용량이 비어 있거든요.

그걸 집으면 오류도 없이 0만 찍힙니다. 직접 최신 파일을 집지 않고 이 함수를 그대로 쓰는 이유가 이겁니다.

둘째 덩어리가 이번 글에서 중요합니다. 세션 로그를 한 줄씩 읽어서 assistant 이벤트만 남기고, 그 안에 들어 있던 도구 이름이나 text 같은 유형을 라벨로 붙여요.

그런데 Claude Code는 응답 하나를 한 줄로 쓰지 않고, 응답 안의 내용 조각마다 한 줄씩 씁니다. 생각을 좀 하고 나서 파일을 읽었다면, 그 응답 하나가 thinking 한 줄, Read 한 줄로 남아요.

문제는 그 두 줄에 응답 전체의 output_tokens가 똑같이 적혀 있다는 거예요. 줄을 그냥 다 더하면 같은 토큰을 두 번, 세 번 세게 됩니다.

그래서 메시지 id를 키로 써서 덮어씁니다. 같은 응답이 몇 줄로 쪼개져 있든 딕셔너리에는 한 칸만 남으니까, 출력이 한 번만 세어져요.

덮어쓰기라서 라벨은 마지막 줄 것이 남습니다. 생각이 먼저 나오고 도구 호출이 뒤에 나오니까, 결과적으로 그 응답이 무슨 도구로 끝났는지가 라벨이 되는 거예요.

마지막 덩어리는 출력입니다. 라벨별로 출력 토큰을 합치고 전체 대비 비중까지 붙여서, 큰 순서대로 찍어 줘요. 비중은 소수점을 버리고 계산하니까 전부 더하면 100퍼센트보다 조금 모자랄 수 있어요.

그래서 이 표를 읽을 때 한 가지는 꼭 구분하셔야 합니다. Edit 줄에 찍힌 숫자는 Edit이라는 도구가 쓴 토큰이 아니에요.

Edit으로 끝난 응답들이 통째로 쓴 토큰이라, 그 앞에서 한 생각도 같이 적은 설명도 전부 들어 있어요. 앞에서 어느 도구가 몇 토큰이라고는 말하지 않겠다고 기준을 잡았던 게 이 이유입니다.

이 글의 숫자는 아래 대화형 세션과 같은 프롬프트를 claude -p로 방식마다 다섯 번씩 돌려서 얻었어요. 반복에 쓴 스크립트는 실습 3에서 보여 드릴게요.

실습 1: 필요한 부분만 수정하기

자 먼저 Claude Code가 기본적으로 동작하는 방식으로, 정의를 읽고 해당 줄만 바꾸게 해볼게요. 세션 로그를 나중에 정확히 찾으려고 UUID 하나를 만들어 세션 ID로 넘깁니다. 이 값이 세션 로그 파일 이름이 돼요.

PARTIAL_SESSION_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
claude --safe-mode --session-id "$PARTIAL_SESSION_ID"

--safe-mode는 CLAUDE.md, 스킬, 플러그인, 훅, MCP 서버 같은 개인 설정을 모두 끄고 시작하는 옵션이에요. PARTIAL_SESSION_ID는 뒤에서 세션 로그를 찾을 때 다시 쓰니, Claude Code를 종료한 뒤에도 같은 터미널에서 이어 가 주세요.

세션이 열리면 아래 프롬프트를 그대로 입력합니다.

src/tokenlab/cli.py에서 env capture 서브커맨드의 help 문구만
'실행 환경 스냅샷'에서 '실행 환경 스냅샷 저장'으로 바꿔 줘.

먼저 해당 정의를 읽고, 필요한 부분만 수정해 줘.
다른 파일이나 관련 없는 내용은 수정하지 말고, 테스트는 실행하지 마.

끝에 "테스트는 실행하지 마"를 붙인 건 이유가 있어요. 테스트를 돌리면 명령을 만드는 턴과 결과를 설명하는 말이 따라붙고, 그게 전부 출력으로 잡혀서 수정 방식의 차이에 섞입니다. 비교하려는 것 하나만 바꾸고 나머지는 고정하려고, 재작성 쪽 프롬프트에도 같은 문장을 넣었어요.

제가 claude -p로 돌린 다섯 번 모두 Claude Code는 grep으로 문구가 있는 줄을 찾은 뒤, Read로 140번째 줄부터 20줄만 읽고, Edit으로 그 한 줄을 바꿨어요. 이 grep은 사용자 설정의 Bash 전체 허용이 --safe-mode에서도 적용된 상태에서 실제로 실행됐고, 허용이 없는 환경에서는 Bash 줄과 숫자가 다를 수 있어요. Claude Code를 종료하고 세션 로그를 분해해 볼게요.

PARTIAL_LOG="$(find "$HOME/.claude/projects" -type f -name "$PARTIAL_SESSION_ID.jsonl" -print -quit)"
.venv/bin/python breakdown.py "$PARTIAL_LOG"

이 명령으로 방금 설정한 UUID로 실행한 세션 로그를 하나 찾고, 그 경로를 breakdown.py에 넘겨서 얼마나 사용했는지 확인합니다. 아래는 첫 회차의 출력이고, 첫 줄의 세션 파일 이름은 <uuid>.jsonl로 바꿔 적었어요.

<uuid>.jsonl
     출력     비중  내용
    247    33%  Edit
    207    27%  Bash
    189    25%  Read
     98    13%  text
    741  100%  합계

맨 아래 합계 줄이 실행 전체의 출력 토큰이고, 위의 각 줄은 내용 유형별 몫이에요(비중 합은 98%). 정의를 찾고, 읽고, 한 줄을 고친 게 전부라서 Edit, Bash(grep), Read, 그리고 마지막 설명인 text가 표를 채우고 있어요. 다섯 번 모두 Edit 줄(221~247)이 가장 컸습니다.

합계 741을 기억해 두시고, 결과가 잘 반영됐는지 확인해 볼게요.

git diff -- src/tokenlab/cli.py
.venv/bin/python -m tokenlab env --help | rg 'capture.*실행 환경 스냅샷 저장'
diff --git a/src/tokenlab/cli.py b/src/tokenlab/cli.py
index 726f72f..d021fe8 100644
--- a/src/tokenlab/cli.py
+++ b/src/tokenlab/cli.py
@@ -146,7 +146,7 @@ def main(argv: list[str] | None = None) -> int:
     sub = p.add_subparsers(dest="group", required=True)
 
     env = sub.add_parser("env").add_subparsers(dest="cmd", required=True)
-    c = env.add_parser("capture", help="실행 환경 스냅샷")
+    c = env.add_parser("capture", help="실행 환경 스냅샷 저장")
     c.add_argument("--output", default="artifacts/environment.json")
     c.set_defaults(fn=cmd_env_capture)
 
    capture   실행 환경 스냅샷 저장

diff에는 help 문구 한 줄만 바뀌어 있고, 마지막 명령에도 바뀐 문구만 나와요. 시작할 때 파일이 깨끗했으니 방금 만든 변경만 되돌릴게요.

git restore --source=HEAD --worktree -- src/tokenlab/cli.py

실습 2: 같은 작업을 Write로 전체 재작성하기

자 좋아요. 이번에는 결과는 똑같지만 일부러 비효율적인 방식으로 시켜서, Claude Code가 어떤 도구로 파일을 쓰는지 볼게요.

FULL_SESSION_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
claude --safe-mode --session-id "$FULL_SESSION_ID"
src/tokenlab/cli.py에서 env capture 서브커맨드의 help 문구를
'실행 환경 스냅샷'에서 '실행 환경 스냅샷 저장'으로 바꿔 줘.

이번에는 Edit를 사용하지 말고, Write로 파일 전체 내용을 다시 작성해서 반영해 줘.
다른 내용은 바꾸지 말고, 테스트는 실행하지 마.

다섯 번 모두 Claude Code는 먼저 Read로 파일 전체를 읽고, 그다음 Write로 파일을 썼어요. 다섯 번 중 네 번은 시키지 않았는데도 마지막에 git diff로 바뀐 곳을 스스로 확인했습니다.

만약 지시를 따르지 않고 Edit만 썼다면 파일을 재작성하지 않은 거라, 그 회차는 숫자를 비교하지 말고 빼시면 됩니다. 이번 다섯 번은 모두 Write를 썼고 Edit을 쓴 회차는 0번이었어요. Claude Code를 종료하고 사용량을 볼게요.

FULL_LOG="$(find "$HOME/.claude/projects" -type f -name "$FULL_SESSION_ID.jsonl" -print -quit)"
.venv/bin/python breakdown.py "$FULL_LOG"
<uuid>.jsonl
     출력     비중  내용
   3549    87%  Write
    205     5%  Bash
    154     3%  Read
    147     3%  text
   4055  100%  합계

첫 회차 합계는 4,055로, 같은 결과를 낸 부분 수정 첫 회차 741의 5배가 넘습니다. 윗줄을 보면 부분 수정 표에는 없던 Write가 올라와 있고, 그 줄 하나가 87%예요. 다섯 번 모두 Write 줄이 가장 컸고 85~92%를 차지했습니다.

이유는 단순합니다. Edit은 바뀌는 부분만 출력으로 내보내고, Write는 파일 내용을 처음부터 끝까지 다시 내보내요. 우리가 건드린 cli.py는 204줄이고, 한 글자를 고치든 열 줄을 고치든 Write를 쓰면 204줄이 전부 출력으로 나갑니다.

그래서 이 비용은 고친 크기가 아니라 파일 크기를 따라가요. 실제로 다섯 번 모두 Write에 파일 전체(7,636자)를 넘겼고, Write 줄은 3,546~3,549로 거의 같았어요. 이번에 잰 건 204줄 파일 하나지만, 파일이 클수록 격차가 벌어지니 천 줄짜리 파일에 전체 재작성이 걸리면 그 한 번이 꽤 아픕니다.

확인과 되돌리기는 실습 1과 같아요. help 확인은 같은 한 줄을 보여 주고, 다섯 번의 diff는 실습 1의 diff와 한 글자도 다르지 않았어요.

.venv/bin/python -m tokenlab env --help | rg 'capture.*실행 환경 스냅샷 저장'
git restore --source=HEAD --worktree -- src/tokenlab/cli.py

다만 여기까지는 한 번씩 실행한 숫자라, 이것만 보고 "이 정도 차이구나" 하고 넘어가면 안 됩니다. 토큰 사용량은 실행마다 튈 수 있으니 여러 번 실행해서 중앙값과 흔들림 폭을 같이 보셔야 해요.

실습 3: 다섯 번씩 반복해서 비교하기

run.sh는 schedule.txt에 적힌 순서대로 두 프롬프트를 claude -p로 보내요. 한 회차를 돌리는 핵심 부분은 이렇습니다(발췌).

  git clone -q "$SRC" "$dir"
  sid="$(uuidgen | tr '[:upper:]' '[:lower:]')"
  echo "$sid" > "$OUT/$name.sid"
  prompt="$(cat "$HERE/prompt-$cond.txt")"
  (cd "$dir" && claude -p "$prompt" --safe-mode --session-id "$sid" \
    --permission-mode acceptEdits --output-format json < /dev/null) \
    > "$OUT/$name.json" 2> "$OUT/$name.err"
  git -C "$dir" diff --stat > "$OUT/$name.diffstat"
  git -C "$dir" diff > "$OUT/$name.diff"

회차마다 첫 커밋 상태를 runs/<방식>-<회차>/lab으로 새로 복제해서 앞 회차의 수정이 남지 않게 했어요. part는 부분 수정, full은 전체 재작성 프롬프트이고, 결과는 out/에 남깁니다.

복제할 원본은 SRC 환경 변수로 정하고 기본값은 현재 디렉터리인데, 제가 잴 때는 src/tokenlab/이 같고 app/, tests/ 같은 다른 실습 파일이 더 들어 있는 저장소를 SRC로 넘겨 복제했어요. 두 방식이 같은 원본을 썼고, Claude Code는 cli.py만 찾아 읽었습니다.

-p와 결과 형식(--output-format json) 말고 대화형 세션과 다른 옵션은 --permission-mode acceptEdits 하나예요. 수정 허락을 눌러 줄 사람이 없어서, 두 방식에 똑같이 수정 권한만 줬습니다. 순서는 한 방식을 몰아서 돌리지 않도록 회차마다 섞었어요.

cat schedule.txt | tr '\n' ','
./run.sh
full 1,part 1,full 2,part 2,full 3,part 3,part 4,full 4,part 5,full 5,
full-01: 4055 output_tokens
part-01: 741 output_tokens
full-02: 0 output_tokens
part-02: 736 output_tokens
full-03: 4068 output_tokens
part-03: 736 output_tokens
part-04: 710 output_tokens
full-04: 4036 output_tokens
part-05: 739 output_tokens
full-05: 3832 output_tokens

full-02가 0인 건 결과 JSON의 result가 "Not logged in · Please run /login"으로 끝난 로그인 오류였기 때문이에요. run.sh는 실행 디렉터리가 이미 있으면 건너뛰니, 이 회차는 runs/full-02와 out/full-02.*를 다른 곳으로 옮기고 full 2 한 줄만 담은 순서 파일을 SCHEDULE 환경 변수로 넘겨 다시 돌렸어요(full-02: 4128 output_tokens).

summarize.py는 회차마다 breakdown.py를 돌리고, 쓴 도구와 diff가 그 한 줄뿐인지, 복제본의 help에 바뀐 문구가 나오는지를 판정해 한 줄씩 찍어요. 셋이 모두 맞은 회차만 모아 중앙값과 범위(최솟값~최댓값)를 낸 마지막 부분만 볼게요(발췌).

.venv/bin/python summarize.py | tail -10
부분 수정(part): 5회 중 비교 대상 5회
  breakdown 합계: 736 (710~741)
  JSON output_tokens: 736 (710~741)
  비용 추정: $0.1066 ($0.1057~$0.1067)
  num_turns: 4 (4~4)
전체 재작성(full): 5회 중 비교 대상 5회
  breakdown 합계: 4,055 (3,832~4,128)
  JSON output_tokens: 4,055 (3,832~4,128)
  비용 추정: $0.2649 ($0.2407~$0.2842)
  num_turns: 4 (3~4)

회차별 줄을 보면 열 번 모두 지시한 도구를 썼고 diff와 help 확인도 모두 맞아서, 빠진 회차는 없어요. 출력 토큰 중앙값은 부분 수정 736, 전체 재작성 4,055로 약 5.5배입니다.

두 범위는 전혀 겹치지 않아서, 이 차이는 실행마다 튀는 폭으로 설명할 수 없어요. breakdown.py 합계와 결과 JSON의 output_tokens도 열 번 모두 같았습니다.

num_turns는 API 요청 수가 아니라 도구 호출 수에 1을 더한 값에 가까워요. 전체 재작성 쪽 최솟값 3은 마지막 git diff 확인을 하지 않은 회차예요.

부분 수정 첫 회차의 결과 JSON에서 필요한 필드만 골라 볼게요(발췌).

jq '{output_tokens: .usage.output_tokens, total_cost_usd, modelUsage: (.modelUsage | map_values({outputTokens, costUSD}))}' out/part-01.json
{
  "output_tokens": 741,
  "total_cost_usd": 0.10671349999999999,
  "modelUsage": {
    "claude-opus-5": {
      "outputTokens": 741,
      "costUSD": 0.10671349999999999
    }
  }
}

모델 ID는 modelUsage의 키(claude-opus-5) 그대로 적어 두세요. 기본 모델은 시간이 지나면 바뀔 수 있어서, 나중에 숫자를 비교할 때 이 기록이 필요해요.

비용 추정 중앙값은 $0.1066 대 $0.2649로 약 2.5배예요. 출력보다 배수가 작은 건 두 방식 모두 시스템 프롬프트 같은 입력을 읽는 비용이 함께 들어 있기 때문이에요.

다만 재작성 쪽은 입력도 늘었어요. 결과 JSON의 cache_creation_input_tokens가 부분 수정 5,419~5,445, 재작성 11,854~14,482였는데, 파일 전체를 읽고 다시 쓴 내용이 대화 기록에 들어가기 때문으로 보여요.

부분 수정이 항상 성공할까?

Claude Code는 보통 필요한 부분만 수정하려고 하지만, 수정할 위치를 정확히 찾지 못하면 다시 읽고, 다시 시도하고, 더 큰 수정 방식으로 돌아갈 수 있어요. 대표적인 경우는 이렇습니다.

  • 같은 코드가 여러 곳에 반복돼 어느 부분을 고쳐야 할지 모호할 때
  • 파일을 읽은 뒤 다른 작업이 같은 파일을 바꿨을 때
  • 이전 변경이 남아 있어 처음에 읽은 상태와 달라졌을 때

같은 조건을 만들어도 매번 같은 방식으로 실패한다는 보장이 없어서, 이런 실패는 일부러 만들지 않을게요. 대신 실제로 출력이 평소보다 크게 늘어난 세션을 만났을 때, 화면에서 Write가 실행됐는지와 세션 전체의 output_tokens를 같이 보시면 됩니다. 이 두 가지가 전체 재작성이나 반복 탐색이 있었는지 확인하는 출발점이 돼요.

정리

범위: Claude Code 2.1.309의 claude -p --safe-mode(수정 권한 acceptEdits), 기본 모델 claude-opus-5, 204줄짜리 src/tokenlab/cli.py의 help 문구 한 줄 수정, 부분 수정과 전체 재작성 각 5회.

  • 같은 한 줄 수정에서 출력 토큰 중앙값은 부분 수정 736(710~741), 전체 재작성 4,055(3,832~4,128)로 약 5.5배였어요. diff는 열 번 모두 같았으니, 전체를 다시 쓰라는 지시는 같은 결과에 출력만 더 씁니다.
  • 전체 재작성의 출력은 85~92%가 Write로 끝난 응답이었고, Write에 파일 전체를 넘긴 그 응답은 다섯 번 내내 3,546~3,549였어요. 잰 건 204줄 파일 하나지만 Write는 파일 전체를 다시 내보내니, 큰 파일일수록 Edit으로 고치게 두세요.
  • 비용 추정치는 $0.1066 대 $0.2649로 약 2.5배였어요. 입력 비용이 함께 들어 있어 출력 배수와 다르니, 비용은 결과 JSON에서 따로 확인하세요.
  • breakdown.py의 Edit·Write 줄은 그 도구로 끝난 응답 전체의 출력이에요. 도구별 토큰으로 읽지 말고, 실제로 쓴 도구와 실행 전체의 output_tokens를 같이 보세요.
  • Write로 다시 쓰라는 지시는 다섯 번 모두 지켜졌어요. 따라 하실 때 Edit을 쓴 회차가 나오면 비교에서 빼고 개수를 적고, 범위가 겹치지 않을 때만 차이가 있다고 말하세요.

이 시리즈의 다음 글에서는 도구 호출이 아니라 사람이 읽는 설명과 보고를 어디까지 줄여도 되는지, 반복되는 작업에는 어떤 형식으로 답을 받으면 좋은지 다룹니다.

내 링크 만들기