Claude Code 5시간 한도: graceful stop을 점검하고 체크포인트로 이어가기

Claude Code의 5시간 한도에 대응하는 실전 절차입니다. 저장소를 확인하고 복구 가능한 Git 상태를 남긴 뒤 대기, usage credits, 독립 API를 판단합니다.

목차
Claude Code 5시간 한도: graceful stop을 점검하고 체크포인트로 이어가기

Claude Code가 코딩 도중 5시간 구독 한도에 도달했을 때 마지막 답변만 보고 작업이 끝났다고 판단하면 안 됩니다. 새 graceful stop은 best-effort 종료 버퍼입니다. Weekly usage에서 작은 고정 할당량을 사용해 더 안전한 지점에서 멈추려 시도할 수 있지만, clean working tree, commit, 테스트 통과, 기능 완성을 보장하지는 않습니다.

다음 순서로 처리하세요.

  1. 표시된 한도 유형과 reset 시각을 읽습니다.
  2. Staged, unstaged, untracked 변경을 확인합니다.
  3. 가장 작지만 의미 있는 검증을 실행합니다.
  4. 복구 가능한 Git 체크포인트를 만듭니다.
  5. 그다음 대기, usage credits, API 중 무엇으로 계속할지 결정합니다.

이 순서를 따르면 불확실한 중단을 관리 가능한 인계로 바꿀 수 있습니다. 저장소 상태를 모르는 채 추가 사용료부터 지불하는 일도 막을 수 있습니다.

Graceful stop은 완성된 작업이 아니라 종료 여유입니다

Claude Developers 공지는 5시간 한도가 작업 중간에 도달하면 Claude Code가 적절한 정지 지점을 찾으려 시도하고, weekly usage에서 작은 고정 할당량을 받아 가능한 범위를 정리한다고 설명합니다. 핵심은 시도한다는 것이지, 완료를 약속한다는 뜻이 아닙니다.

한 사용자 보고는 예전처럼 편집 중간에 끊기는 것보다 정돈된 상태로 멈췄다고 전했습니다. 유용한 사례지만 한 계정의 경험일 뿐, 모든 저장소에서의 신뢰성을 보장하지 않습니다.

세 가지 메커니즘을 구분하세요.

메커니즘보호하는 것보장하지 않는 것
한도 시 graceful stopAgent가 더 일관된 지점에서 멈출 수 있는 짧은 여유완료, commit, clean tree, 테스트 통과
Claude Code checkpoint/rewind로 세션의 여러 직접 파일 편집을 복원Bash 변경, 대부분의 subagent 편집, 외부 변경, 전체 저장소 이력
Git 체크포인트검토·branch·공유·복원이 가능한 상태 기록검토와 테스트 없는 정확성

Graceful stop이 더 나은 요약이나 덜 끊긴 코드를 남길 수는 있습니다. 실제 인수 검사는 여전히 Git에서 시작합니다.

먼저 어떤 한도가 작업을 막았는지 확인하세요

모든 중단이 5시간 뒤 해제되는 것은 아닙니다. Anthropic의 usage limit 문서는 사용 한도와 context length 한도를 구분하며, 같은 계정에 연결된 Claude 화면 사이에서 usage가 공유될 수 있다고 설명합니다.

메시지를 그대로 읽으세요.

  • Approaching 5-hour limit: 새 범위를 늘리지 말고 체크포인트 준비로 전환할 짧은 시간입니다.
  • 5-hour limit reached — resets …: 표시된 reset 시각을 기준으로 합니다.
  • Weekly limit 또는 모델별 한도는 5시간 창이 reset된 뒤에도 남을 수 있습니다.
  • Capacity나 서비스 오류는 usage 소진과 다릅니다. 추가 결제로 장애가 해결되지는 않습니다.

현재 interactive client가 세션을 열어 둔 채 reset을 기다리고 자동으로 계속하는 옵션을 제공한다면 가장 간단한 경로일 수 있습니다. 다만 설치된 버전과 실행 모드의 안내를 확인하세요. 세션을 종료하면 대기가 끝날 수 있고, background, print, API-key 모드는 동작이 다를 수 있습니다.

Claude에 추가 편집을 맡기기 전에 저장소를 점검하세요

저장소 root에서 파일을 바꾸지 않는 명령을 실행합니다.

git status --short --branch
git diff --stat
git diff
git diff --cached
git ls-files --others --exclude-standard

Claude Code의 /diff도 도움이 되지만 working tree의 사실 확인은 Git을 기준으로 합니다.

계속하기 전에 다섯 가지에 답할 수 있어야 합니다.

  1. 어떤 파일이 바뀌었는가?
  2. 무엇이 staged, unstaged, untracked 상태인가?
  3. 새 호출만 추가되고 type, test, migration이 빠진 것처럼 반쪽짜리 편집이 있는가?
  4. Lockfile, generated file, config, migration이 예상치 않게 바뀌었는가?
  5. 핵심 문제를 드러낼 가장 작은 명령은 무엇인가?

프로젝트에서 실제로 쓰는 검증을 실행합니다.

# 예시입니다. 저장소의 가장 작은 관련 명령으로 바꾸세요.
npm test -- path/to/affected.test.ts
pytest tests/test_affected.py -q
cargo test affected_module
git diff --check

안전한 정지는 “Claude가 요약을 썼다”는 뜻이 아닙니다. 변경 파일, 남은 작업, 검증 결과, 다음 작은 행동을 설명할 수 있어야 합니다.

Diff가 이해되지 않으면 “나머지를 전부 끝내라”고 요청하지 마세요. 먼저 체크포인트를 만드세요.

복구 가능한 체크포인트를 두 방법 중 하나로 만드세요

방법 1: 검토한 local WIP commit

현재 상태가 충분히 일관되고 local branch에서 임시 commit이 허용될 때 사용합니다.

git status --short --branch
git add -p
# 검토한 새 파일은 명시적으로 추가합니다.
git add -- path/to/reviewed-new-file
git diff --cached --check
git diff --cached
# 여기서 가장 작은 관련 test 또는 lint를 실행합니다.
git commit -m "WIP: checkpoint before Claude Code limit reset"
git rev-parse --short HEAD

git add -A를 무작정 실행하지 마세요. .env, 자격 증명, 비공개 fixture, generated artifact, 큰 binary, debug dump를 확인합니다. Local WIP commit은 복구 지점이지 protected branch에 push해도 된다는 승인이 아닙니다.

짧은 인계 메모도 남기세요.

Goal: token expiry validation 완료
Checkpoint: a1b2c3d
Changed: auth_service.py, tests/test_auth.py
Validation: targeted test 실패. expected 401, received 200
Next step: callers 변경 전에 expiry comparison 확인

방법 2: 저장소 밖에 patch와 검토한 untracked 파일 저장

Commit을 만들고 싶지 않을 때 이 외부 checkpoint 방식을 사용합니다. Script는 먼저 CLAUDE_CHECKPOINT_DIR의 실제 경로를 해석하며, symlink를 통해서라도 저장소 내부를 가리키면 checkpoint 파일을 만들기 전에 중단합니다.

과정은 세 단계입니다. 먼저 staged, unstaged, untracked 후보와 diff를 저장하지 않고 확인합니다. 다음으로 각 group에서 저장할 path 번호를 명시적으로 선택하며, 선택하지 않은 항목은 명확히 제외됩니다. 마지막으로 SAVE REVIEWED를 입력해야만 실제 파일을 씁니다. 선택하기 전에 해당 diff 전체 또는 untracked 파일을 검토하고 키, 비공개 자료, 보존하면 안 되는 내용을 제외하세요. 한 파일의 일부 변경만 안전하다면 변경을 분리하거나 위의 WIP commit 방식을 사용하고, 파일 전체를 선택하지 마세요.

외부 방식은 추적 중인 파일의 이름 변경을 감지하면 중단하므로 검토한 WIP commit을 사용하세요. 선택한 unstaged 경로에 staged 변경도 있으면 두 버전을 함께 검토하고 저장해야 합니다. 복원 전에 checkpoint를 저장 단계에서 출력한 절대 경로로 설정하세요. 기준 commit 불일치, 변경이 있는 checkout 또는 어느 단계든 실패하면 복원이 중단됩니다.

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

# Step 1: inspect everything. These commands do not save files.
git status --short --branch
git diff --cached
git diff
git ls-files --others --exclude-standard

# Steps 2-3: choose reviewed paths explicitly, then save only that scope.
python3 /dev/fd/3 3<<'PY'
from datetime import datetime, timezone
from pathlib import Path
import json, os, shlex, subprocess, tarfile


def git(*args):
    return subprocess.run(["git", *args], check=True, stdout=subprocess.PIPE).stdout


def path_list(*args):
    return list(dict.fromkeys(Path(os.fsdecode(x)) for x in git(*args).split(b"\0") if x))


def choose(label, items):
    print(f"\n{label} candidates:")
    if not items:
        print("  (none)")
        return []
    for i, path in enumerate(items, 1):
        print(f"  {i:>3}  {shlex.quote(os.fspath(path))}")
    answer = input(
        "Select reviewed numbers to save "
        "(1,3-5; 'all'; blank saves none): "
    ).strip().lower()
    if not answer:
        return []
    if answer == "all":
        return items[:]
    chosen = set()
    try:
        for token in answer.split(","):
            bounds = [int(x) for x in token.strip().split("-", 1)]
            chosen.update(range(min(bounds), max(bounds) + 1))
    except ValueError as exc:
        raise SystemExit("Invalid selection; nothing was saved.") from exc
    if not chosen or min(chosen) < 1 or max(chosen) > len(items):
        raise SystemExit("Selection is outside the displayed range; nothing was saved.")
    return [path for i, path in enumerate(items, 1) if i in chosen]


def patch(cached, items):
    if not items:
        return b""
    args = ["diff"] + (["--cached"] if cached else [])
    return git(*args, "--binary", "--", *(os.fspath(path) for path in items))


repo = Path(git("rev-parse", "--show-toplevel").decode().rstrip("\n")).resolve()
root = Path(os.environ.get(
    "CLAUDE_CHECKPOINT_DIR", os.fspath(Path.home() / "claude-checkpoints")
)).expanduser().resolve(strict=False)
if root == repo or repo in root.parents:
    raise SystemExit("CLAUDE_CHECKPOINT_DIR must resolve outside the repository.")
root.mkdir(parents=True, exist_ok=True)
root = root.resolve()
if root == repo or repo in root.parents:
    raise SystemExit("CLAUDE_CHECKPOINT_DIR must resolve outside the repository.")

# A path-only selection cannot represent both sides of a detected rename.
if (git("diff", "--cached", "--find-renames", "--diff-filter=R", "--name-only", "-z")
        or git("diff", "--find-renames", "--diff-filter=R", "--name-only", "-z")):
    raise SystemExit("Renames require a reviewed WIP commit; nothing was saved.")

candidates = {
    "staged": path_list("diff", "--cached", "--name-only", "-z"),
    "unstaged": path_list("diff", "--name-only", "-z"),
    "untracked": path_list("ls-files", "--others", "--exclude-standard", "-z"),
}
if not any(candidates.values()):
    raise SystemExit("No staged, unstaged, or untracked changes were found.")
selected = {name: choose(name, items) for name, items in candidates.items()}
if not any(selected.values()):
    raise SystemExit("No reviewed changes were selected; nothing was saved.")
missing_baseline = set(selected["unstaged"]) & (
    set(candidates["staged"]) - set(selected["staged"])
)
if missing_baseline:
    raise SystemExit(
        "Selected unstaged paths also have staged changes. Select both reviewed "
        "versions or use a WIP commit; nothing was saved."
    )

not_saved = {
    name: [path for path in candidates[name] if path not in selected[name]]
    for name in candidates
}

print("\nSelected for saving:")
for name, items in selected.items():
    print(f"  {name}: " + (", ".join(shlex.quote(os.fspath(p)) for p in items) or "(none)"))
print("Excluded and NOT saved:")
for name, items in not_saved.items():
    print(f"  {name}: " + (", ".join(shlex.quote(os.fspath(p)) for p in items) or "(none)"))
if input("Type SAVE REVIEWED to save exactly this scope: ").strip() != "SAVE REVIEWED":
    raise SystemExit("Confirmation did not match; nothing was saved.")

current = {
    "staged": path_list("diff", "--cached", "--name-only", "-z"),
    "unstaged": path_list("diff", "--name-only", "-z"),
    "untracked": path_list("ls-files", "--others", "--exclude-standard", "-z"),
}
if current != candidates:
    raise SystemExit("The candidate list changed during review; rerun from step 1.")

stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
checkpoint = (root / f"{repo.name}-claude-{stamp}").resolve(strict=False)
if checkpoint == repo or repo in checkpoint.parents:
    raise SystemExit("Checkpoint path unexpectedly resolves inside the repository.")
checkpoint.mkdir(exist_ok=False)
(checkpoint / "base-commit.txt").write_bytes(git("rev-parse", "HEAD"))
(checkpoint / "status.txt").write_bytes(git("status", "--short", "--branch"))
(checkpoint / "staged.patch").write_bytes(patch(True, selected["staged"]))
(checkpoint / "unstaged.patch").write_bytes(patch(False, selected["unstaged"]))
(checkpoint / "untracked.list0").write_bytes(b"".join(
    os.fsencode(os.fspath(path)) + b"\0" for path in selected["untracked"]
))

manifest = {
    "selected": {name: [os.fspath(p) for p in items] for name, items in selected.items()},
    "not_saved": {name: [os.fspath(p) for p in items] for name, items in not_saved.items()},
}
(checkpoint / "selection.json").write_text(
    json.dumps(manifest, ensure_ascii=True, indent=2) + "\n", encoding="utf-8"
)
omitted = [
    f"{name}\t{json.dumps(os.fspath(path), ensure_ascii=True)}"
    for name, items in not_saved.items() for path in items
]
(checkpoint / "not-saved.txt").write_text(
    "\n".join(omitted) + ("\n" if omitted else "(none)\n"), encoding="utf-8"
)
if selected["untracked"]:
    with tarfile.open(checkpoint / "untracked.tar.gz", "w:gz", dereference=False) as archive:
        for path in selected["untracked"]:
            archive.add(repo / path, arcname=path.as_posix(), recursive=False)

partial = bool(omitted)
(checkpoint / "scope.txt").write_text(
    "PARTIAL: see not-saved.txt; this is not a complete recovery point.\n"
    if partial else
    "REVIEWED SCOPE: every listed candidate was saved; ignored files remain outside scope.\n",
    encoding="utf-8",
)
print(("PARTIAL checkpoint" if partial else "Reviewed checkpoint") + f" saved to {checkpoint}")
if partial:
    print("Some listed changes were not saved; read not-saved.txt before recovery.")
PY

selection.json은 선택 및 제외한 path를 기록하고, not-saved.txt는 저장하지 않은 모든 항목을 나열하며, scope.txt는 범위를 표시합니다. staged, unstaged, untracked 후보 중 하나라도 제외되면 terminal과 scope.txt가 결과를 PARTIAL로 표시합니다. 이 경우 완전한 복구 지점이 아니며 선택한 범위만 복원할 수 있습니다. Ignored 파일은 계속 범위 밖에 있고, checkpoint는 코드가 정상 동작한다는 증거가 아닙니다.

복원하기 전에 scope.txt와 not-saved.txt를 읽으세요. 기록된 base commit의 clean checkout에서 저장된 범위만 복원합니다:

(
    set -eu
    checkpoint="/absolute/path/printed-by-the-save-step" # Replace with the actual checkpoint directory.
    test -d "$checkpoint"
    repo_root="$(git rev-parse --show-toplevel)"
    cd "$repo_root"
    for required in base-commit.txt scope.txt not-saved.txt staged.patch unstaged.patch untracked.list0; do
        test -f "$checkpoint/$required"
    done
    cat "$checkpoint/scope.txt" "$checkpoint/not-saved.txt"
    test "$(git rev-parse HEAD)" = "$(cat "$checkpoint/base-commit.txt")"
    test -z "$(git status --porcelain --untracked-files=all)"
    if [ -s "$checkpoint/staged.patch" ]; then
        git apply --index "$checkpoint/staged.patch"
    fi
    if [ -s "$checkpoint/unstaged.patch" ]; then
        git apply "$checkpoint/unstaged.patch"
    fi
    if [ -s "$checkpoint/untracked.list0" ]; then
        tar -xzkf "$checkpoint/untracked.tar.gz"
    fi
    git status --short
)

현재 저장소가 그 base commit이 아니라면 patch를 강제로 적용하지 마세요. 저장한 commit에서 clean branch나 worktree를 만든 뒤 그곳에서 복원하고 의도적으로 통합합니다.

Claude Code checkpoint는 유용하지만 Git을 대체하지 않습니다

Claude Code의 공식 checkpointing 문서는 세션의 직접 파일 편집을 추적하고 /rewind로 검토하거나 복원할 수 있다고 설명합니다. 세션의 최근 100 checkpoint를 보관하고 conversation에 연결하므로 resume 이후에도 사용할 수 있다고 안내합니다.

잘못된 agent turn을 되돌리기에는 유용하지만 저장소 backup은 아닙니다. 문서상 제한에는 Bash로 만든 변경, 대부분의 subagent 편집, 외부 동시 변경, 일부 symlink/hardlink 사례가 포함됩니다. Git은 지속적인 commit ID, branch, 검토 가능한 patch, Claude 세션과 독립된 복구를 제공합니다.

두 계층을 함께 사용하세요.

  • /rewind: 세션 수준의 빠른 rollback.
  • Git commit 또는 외부 patch bundle: 지속적인 저장소 체크포인트.

공식 CLI reference는 다음을 문서화합니다.

claude -c
claude -r "<session>"

Resume 후에도 편집을 허용하기 전에 git status를 다시 실행하세요. Conversation이 복구됐다고 filesystem이 이전 요약과 같다는 뜻은 아닙니다.

체크포인트를 만든 뒤 대기, usage credits, API를 선택하세요

상황첫 선택이유
Reset이 가깝고 checkpoint가 안전하며 deadline에 여유가 있음대기추가 종량제 비용과 인증 변경이 없음
같은 UI와 conversation이 필요하고 종량제 비용을 수용함Usage credits대상 계정은 included usage 이후에도 계속 가능
별도 key, provider balance, project budget이 필요함APISubscription과 billing/auth를 분리 가능
여러 정상 근무일에 반복적으로 한도에 걸림Plan과 workflow 검토한 번의 중단은 영구 upgrade 근거가 약함

경로 변경보다 멈추는 비용이 낮으면 기다리세요

Reset이 가깝고 checkpoint가 끝났다면 대기가 보통 더 안전합니다. 정확한 시각과 handoff를 기록하세요. Client가 wait-and-resume을 명시적으로 제공할 때만 세션을 열어 두는 동작에 의존하세요.

돌아온 뒤:

git status --short --branch
git diff --stat

그다음 구체적인 다음 작업을 적으세요. “계속해”만 입력하면 agent가 상태를 추측하게 됩니다.

Context 연속성이 가장 중요하면 usage credits를 사용하세요

Anthropic의 usage credits 공식 문서는 대상 Pro/Max 사용자가 Settings > Usage에서 추가 pay-as-you-go usage를 켜고 funds와 지출 제한을 설정할 수 있다고 설명합니다. 청구는 subscription과 별도이며 일반 5시간 창은 계속 reset됩니다.

켜기 전에:

  • 월간 cap과 alert를 설정합니다.
  • Auto-reload를 허용할지 결정합니다.
  • UI가 usage credits로 계속 중이라고 표시하는지 확인합니다.
  • 긴급 checkpoint 또는 acceptance test가 끝나면 중지합니다.

같은 UI와 context를 유지하지만 subscription의 무료 초과분은 아닙니다.

별도 인증이나 예산이 필요하면 API를 사용하세요

API는 별도 인증·청구 경로입니다. Subscription allowance를 자동으로 이어받지 않으며, environment variable 하나만 바꿔도 저장된 login이 그대로 남을 수 있습니다. Claude Code에서 /status를 확인하고 active provider 또는 gateway를 확정한 뒤 provider usage log에서 request를 확인하세요.

BetterToken은 Anthropic-compatible pay-as-you-go API이며 Claude subscription의 확장이 아닙니다. 별도 key와 balance가 필요할 때 선택할 수 있지만, 어떤 provider에서도 Git checkpoint와 handoff가 먼저입니다.

같은 모델의 API 표시 가격을 비교하되 작업 총비용으로 부르지 마세요

아래 표준 text-token 가격은 2026-10-01에 확인했습니다. 단위는 모두 100만 token당 USD입니다. Anthropic 값은 공식 API pricing, BetterToken 값은 pricing page을 사용했습니다.

정확한 model IDAnthropic inputAnthropic outputBetterToken inputBetterToken output
claude-opus-5-5$4.00$20.00$2.72$13.60
claude-sonnet-5$2.00$10.00$1.36$6.80
claude-haiku-4-5-20251001$1.00$5.00$0.68$3.40

이 표는 같은 model ID의 표준 input/output 가격만 비교합니다. 다음 세 가지는 직접 같은 것이 아닙니다.

  • Subscription usage: plan에 포함된 allowance이며 plan limit 적용.
  • Usage credits: 대상 Claude UI 안에서의 종량제 연속 사용.
  • API usage: 별도 provider account에서 token과 기능에 따라 청구.

실제 작업 비용은 input/output token, cache read/write, long context 또는 fast mode, retry, tool call, 계정 규칙에 따라 달라집니다. 표시 가격이 낮다는 사실만으로 특정 저장소 작업 전체가 더 싸다고 결론낼 수 없습니다.

재개할 때 “계속해” 대신 검증 가능한 handoff를 제공하세요

다음 세션에 짧은 상태를 전달합니다.

Goal: 완료 시 충족할 조건
Base: branch와 commit/checkpoint ID
Changes: staged, unstaged, untracked
Validation: 실행한 정확한 command와 결과
Known failure: 정확한 error 또는 failing assertion
Do not touch: 민감하거나 무관한 file
Next action: 작고 testable한 한 단계

먼저 Claude Code가 current diff를 확인하고 다음 행동을 다시 말하게 하세요. 오래된 summary, 다른 branch, 불완전한 graceful stop을 새 변경 전에 발견할 수 있습니다.

자주 묻는 질문

Graceful stop이 자동으로 commit하나요?

아니요. 공식 설명은 더 안전한 정지 지점을 찾으려는 시도이지 Git commit 약속이 아닙니다.

작업 완료를 보장하나요?

아니요. 남은 작업을 설명하고 멈추거나 변경 일부만 끝낼 수 있습니다. 완료 여부는 test와 acceptance criteria로 판단합니다.

Wrap-up에 추가 비용이 드나요?

공지된 작은 고정 할당량은 weekly usage에서 가져옵니다. Usage credits와 API는 별도 종량제 경로이므로 계정에 표시된 billing mode를 확인하세요.

/rewind가 Git을 대체할 수 있나요?

아니요. 세션의 직접 편집에는 유용하지만 문서화된 제외 범위 때문에 모든 저장소 변경을 나타내지 못합니다.

Weekly limit 메시지가 나오면 어떻게 하나요?

5시간 reset으로 해제되지 않을 수 있습니다. Checkpoint를 저장하고 client의 limit type과 reset time을 확인한 뒤, 추가 usage를 사기 전에 어떤 counter가 막는지 확정하세요.

LLM 워크플로를 최적화할 준비가 되셨나요?

하나의 API로 모델을 연결하고 키와 AI 비용을 관리하세요.

무료로 시작하기