
Claude Code의 컨텍스트(Context) 인계 과정 205 세션 운영 후, 지시 사항이 오류를 증폭시키는 현상
요약
Claude Code를 76일간 장기 사용하며 구축한 세션 간 컨텍스트 인계 메커니즘의 한계와 부작용을 분석합니다. 요약된 정보가 '확정된 전제'로 인식되면서 오차가 증폭되고 지시 사항의 정확도가 떨어지는 현상을 다룹니다.
핵심 포인트
- 대규모 로그 처리를 위해 SessionStart와 요약본 주입 방식의 3단계 인계 구현
- 요약 과정에서 조건부 정보가 단정적 결론으로 변질되어 오류가 증폭됨
- 컨텍스트 용량 제한으로 인해 전체 로그 읽기 대신 전략적 요약이 필수적임
- 특정 키워드(계속해서 등)를 감지하여 요약본을 주입하는 자동화 로직 적용
이 글에 대하여
저는 Claude Code를 하나의 프로젝트에서 76일 동안 계속 구동했습니다. 대화 로그는 205 세션 분량이 남아있고, 원본 트랜스크립트는 총 991MB입니다.
이 과정에서 '세션을 넘나들며 컨텍스트를 인계하는' 메커니즘을 만들었습니다. SessionStart로 지시 사항의 시작 부분을 주입하고, '계속해서'라고 입력하면 이전 로그의 요약본이 자동으로 들어옵니다. 작동합니다.
문제는 작동한 후에 발생했습니다.
인계 파일은 다음 세션에게 '지난 관측'이 아니라 **'확정된 전제(前提)'**로 읽힙니다. 원래 측정치가 n=1, 조건부, 임시적인 것이었더라도, 지시 사항에는 단정형의 결론만 남게 됩니다. 증거는 남아있지 않습니다. 결과적으로, 오차는 사라진 채 자신감만 증폭되어 갑니다.
인계 구현 방법을 작성한 글은 일본어로도 많이 찾을 수 있지만, 실제로 구현한 후에 무엇이 망가지는지는 스스로 발견할 수 없었습니다. 이 글은 그 기록입니다.
숫자는 모두 이 글을 위해 재측정했습니다. 지시 사항에 쓰여 있던 숫자는 이미 오래되었습니다 (후술합니다).
먼저 규모 — '전체 읽기'는 처음부터 불가능
| 대상 | 실측값 |
|---|---|
| Obsidian에 저장한 대화 로그 | 205 건 |
| ... | 4.57MB / 44.0MB (10MB 초과가 32건) |
| 현재의 지시 사항 파일 | 2,249 줄 / 144,987자 |
| 보관된 과거 지시 사항 | 24 파일 / 1,527KB |
프로젝트의 CLAUDE.md | |
| 900 줄 / 130,981자 |
측정일: 2026-08-03 · 대상은 1개 프로젝트 전체입니다. 원본 로그 건수가 저장된 로그보다 적은 이유는 cleanupPeriodDays로 오래된 것이 삭제되기 때문입니다.
이 규모가 인계 설계의 출발점이었습니다. 중앙값 4.5MB의 로그를 읽게 하면 컨텍스트가 즉사합니다. 그래서 요약하는 것 외에는 선택지가 없습니다. 그리고, 요약하기 때문에 망가집니다.
구현한 3단계
1. SessionStart — 지시 사항의 시작 부분만 무조건 주입
SessionStart 후크를 사용하여 프로젝트 직하의 지시 사항 파일의 머리글 40줄만 additionalContext로 주입합니다. 전체(2,249줄)는 넣지 않습니다. 머리글에 '다음으로 할 일'을 반드시 작성한다는 규약과 맞물려 있습니다.
2. UserPromptSubmit — '계속해서'를 감지했을 때만 요약본 주입
UserPromptSubmit의 stdout은 그대로 컨텍스트에 들어갑니다. 여기에 이전 로그의 요약본을 흘려보냅니다.
STRONG_RE = re.compile(
r"(続きから|つづきから|前回|前のセッション|さっきのセッション|再開|申し送り|"
r"引き継|この前|昨日|一昨日|先ほどのセッション|resume|pick up where)")
...
발동 조건을 2단계로 설정했습니다. '계속해서(続きから)', '이전(前回)' 같은 강한 단어는 세션의 어느 시점에서든 발동하며, '계속(続き)', 'continue' 같은 단독으로는 의미가 정해지지 않는 약한 단어는 세션 초반에서만 발동합니다. '계속해서(続けて)'는 재개 신호일 수도 있고 '그대로 진행하라'는 뜻일 수도 있기 때문입니다.
초반인지 아닌지는 트랜스크립트의 assistant 줄 수로 판정하고 있습니다.
def is_young(payload: dict) -> bool:
tp = payload.get(
범위를 넓게 잡으면 툴 로그(tool log)를 대량으로 수집하게 되어 다이제스트(digest)가 지저분해집니다. **의도적으로 좁게 설정해 두었습니다.**
노이즈 제거를 위해 한 종류가 더 필요했습니다. 대화 로그의 끝부분은 저장 명령의 전개로 채워지기 때문에, 이를 제거하지 않으면 **최근의 실제 지시 사항이 밀려나게 됩니다**.
USER_NOISE_RE = re.compile(
r"^(<command-|<local-command-|<system-reminder>|Base directory for this skill:|"
r"Caveat: The messages below)"
)
실제로 주입되는 것은 다음과 같은 골격이 됩니다 (내용은 숨겼습니다).
🔁 세션 재개 자동 주입 (resume-context 훅)
재개를 나타내는 단어를 감지했으므로, 이 cwd의 최신 Obsidian 대화 로그
다이제스트를 주입했다. 작업을 시작하기 전에 이를 읽을 것.
...
서두의 지시문이 중요했습니다. 다이제스트만 전달하면 "아마 이 내용의 다음 단계겠지"라며 시작해 버립니다. **"한 문장으로 말할 수 없다면 거슬러 올라가라", "추측으로 시작하지 마라"를 주입문 자체에 심어 두었습니다.** `--rank 1`, `--rank 2`와 같이 한 단계씩 이전으로 거슬러 올라갈 수 있으며, 상한은 5개로 정해 두었습니다.
### 왜 매 세션마다 전부 넣지 않는가
훅(hook)의 주석에 그대로 적혀 있습니다.
매 세션마다 무조건 다이제스트를 주입하면, 이력과 무관한 단발성 작업에서도 수천 토큰을 상시 과금하게 됩니다. 재개 의사가 확인되었을 때만 비용을 지불하는 것이 옳습니다.
`SessionStart` 측에는 "이력이 N건 있음"이라는 두 줄의 이정표만 남겨 둡니다.
## 망가지는 방식 1 —— 결론은 남지만, 증거는 남지 않는다
여기서부터가 본론입니다.
인계가 작동하기 시작한 후부터, 기록 속에 자기 수정이 쌓여갔습니다. 오늘 측정한 출현 횟수입니다.
| 용어 | 프로젝트 `CLAUDE.md` | 인계 파일 |
|---|---|---|
| 정정 | 32 | 9 |
| 오류 | 28 | 15 |
| 철회 | 13 | 8 |
| 오독 | 4 | — |
| 오판정 | 5 | — |
나아가, **이전 세션의 기술을 직접 지목하며 "오류", "철회"라고 명시한 행이 15행** 있습니다 (`grep "前セッション"` 결과 중 `오류|철회|정정|오독|위음성|사용하지 말 것`으로 필터링한 건수, 2026-08-03).
가장 비용이 많이 발생한 사례를 들어보겠습니다.
**공개한 툴의 이름을 바꿨더니 검색 순위가 사라졌다**는 관측이 있었습니다. 인계 파일에는 다음과 같이 남았습니다 —— "**이름 변경은 검색 인덱스를 파괴한다**". 다음 세션은 이를 전제로 하여, 이름 변경이라는 수단을 선택지에서 제외했습니다.
다음 날 다시 측정해 보니, **파괴된 것은 인덱스가 아니라 측정 코드**였습니다. 순위 취득에 사용하던 행 셀렉터(row selector)가 1페이지 24건 중 **6건밖에 가져오지 못하고 있었던 것**입니다. 스크롤을 해도 늘어나지 않았습니다. "범위 외"는 실재하지 않았고, 단지 이쪽에서 보이지 않았을 뿐이었습니다.
문제는 오측정 그 자체가 아닙니다. **오측정은 반드시 일어납니다.** 문제는 인계 파일에 남은 것이 "이름 변경은 인덱스를 파괴한다"라는 **결론뿐**이었고, "행 셀렉터로 DOM에서 계산함 · n=42 · 1회만 수행"이라는 **측정 방법이 남지 않았다**는 점입니다. 측정 방법이 적혀 있었다면, 다음 세션에서 "n=42는 1페이지의 건수와 맞지 않는 것 아닌가"라고 의심할 수 있었을 것입니다.
동일한 구조의 철회가 다른 곳에도 여러 개 남아 있습니다.
- "A 그룹의 중앙값은 B 그룹의 **36배**" → 모집단을 깊게 다시 추출했더니 **2.5배**로 줄어듦 (앞부분만 읽으면 상층부 데이터만 모집단으로 오인함)
- "기존 코드가 더 위험함" → 300번의 요청을 실행했더니 **발생 0건**으로 확인되어 철회
- "상한은 1시간마다 1개씩 생김" → 에러 본문 내용대로 **롤링 24시간 기준 5개**라는 오독이었음
이 모두 **작성된 시점에는 정직한 보고**였습니다. 거짓말을 한 것이 아닙니다. 단정적인 형태로 작성되었고, 증거가 덧붙여지지 않았을 뿐입니다.
## 망가지는 방식 2 —— 가변적인 상태를 단정적으로 적어버린다
이 글을 쓰기 위해, 인계 파일에 적혀 있던 "정정 23 · 오류 20 · 철회 9"라는 숫자를 다시 측정했습니다. 실제로는 **32 · 28 · 13**이었습니다.
인계 파일이 거짓말을 한 것은 아닙니다. **작성된 8/2 시점에는 정확했습니다.** 다만, 그것이 오늘의 값은 아니었을 뿐입니다.
「현재 N개 공개 중」「순위 3위」「남은 수 M건」——**이러한 종류의 가변적인 상태를 인계 사항에 단정적인 문장으로 쓰면, 다음 세션은 그것을 현재 값으로 읽어들입니다.**
대책은 간단했습니다. **기계가 읽을 수 있는 사실은 단정적으로 쓰지 말고 명령어로 만든다.**
❌ 인계 사항에 작성
현재 20개 공개 중·그중 2개는 공개 대기 중
✅ 인계 사항에 작성
...
실제로 만든 정점 관측 (Fixed-point observation) 스크립트의 서두 주석에는 다음과 같이 적혀 있습니다.
- pic = false … 아이콘 미등록. 인계 사항이 아니라 이 값으로 판단할 것
- (2026-08-01에 인계 사항이 1회 틀렸음)
한 줄의 명령어는 100줄의 정확한 인계 사항보다 열등하지 않습니다.
## 고장 나는 방식 3 —— 망가진 로그가 다음 세션으로 전염된다
이것은 인계(Handover) 특유의 사고입니다.
긴 세션에서는 도구 호출(Tool call)이 `malformed` 상태가 되어 망가지는 경우가 있습니다. 망가진 호출이 컨텍스트(Context)에 남으면, 그것이 **본보기(Example)**가 되어 다음 호출도 망가집니다.
인계를 구현하면, 이 망가진 어구가 **저장 → 다음 세션에서 읽기** 경로를 통해 넘어옵니다. 한 세션의 사고가 프로젝트 전체의 사고가 됩니다.
대처 방법은 두 가지였습니다.
- **저장 시에 결정론적인 (Deterministic) 스크립트로 새니타이즈(Sanitize)한다** (LLM에게 청소를 시키지 않는다. 청소 담당자까지 침식되기 때문)
- **대화 로그 본체를** (컨텍스트를 잡아먹을 뿐만 아니라, 파손된 어구의 전염원이 되므로) 읽을 때는 반드시 다이제스트(Digest)를 경유하며, `Read`로 직접 여는 것을 금지한다.
## 대책으로 정한 3가지 규칙
고장 나는 방식들이 모두 정리되었으므로, 규약을 3가지 추가했습니다.
**① 사실에는 「측정 방법·n·날짜」를 반드시 첨부한다**
❌ sitemap checker는 3위
✅ sitemap checker #3 (2026-08-02 · Store UI를 풀 페이지 전환하여
main 내 링크를 DOM 순으로 추출 · n=24)
3가지 점을 적을 수 없는 것은 사실로서 적지 않고, `(미검증)`, `(추정)`, `(n=1)`을 명시합니다.
**② 출처 없는 단정 위에 중요한 판단을 쌓기 전에 1회 다시 측정한다**
「중요한 판단」은 되돌릴 수 없는 작업(이름 변경 · 삭제 · 공개 · 전송), 여러 시간을 투입하는 작업, 방침 전환으로 한정했습니다. 전부 다시 측정하고 있다면 일을 할 수 없습니다. 재측정 비용이 판단 비용을 상회한다면, **그 단정을 근거로 사용하지 않고 판단을 보류한다**고 정했습니다.
**③ 기계가 읽을 수 있는 사실은 단정이 아니라 스크립트로 만든다**
위에서 언급한 바와 같습니다.
예외도 정했습니다. **사용자의 결정·선호는 재측정할 수 없으므로** 인계 사항이 유일한 기록이 됩니다. 공식 문서의 문구나 API 사양과 같은 불변의 1차 정보도 인계해도 됩니다 (단, URL을 병기할 것). 「했다 / 하지 않았다」의 작업 기록은 git 히스토리로 추적할 수 있습니다.
## Windows에서 빠진 2가지 함정
훅(Hook) 구현 측의 이야기입니다. 일본어 환경이라면 반드시 겪게 될 것이라 생각합니다.
**① sys.stdin이 cp932로 열린다**
Windows의 Python은 표준 입력을 cp932로 엽니다. 훅으로 들어오는 입력은 UTF-8이므로, 텍스트 계층을 경유하면 **일본어 프롬프트가 깨져서 탐지 패턴과 일치하지 않게 됩니다.**
까다로운 점은, **예외가 발생하지 않고 조용히 일치하지 않게 된다**는 것입니다. 「이어서」가 깨지면 훅은 아무 일도 하지 않은 채 침묵합니다. 작동하지 않고 있다는 사실을 알아차릴 수 없습니다.
Windows의 Python은 sys.stdin을 cp932로 연다 (실측 2026-07-30).
훅 입력은 UTF-8이므로, 텍스트 계층을 경유하면 일본어 프롬프트가 깨져
탐지 패턴과 일치하지 않게 된다. 반드시 바이트 열로 받아서 명시적으로 디코딩할 것.
...
**새로운 훅은 반드시 일본어가 포함된 페이로드(Payload)로 테스트하십시오.** 실제로 이 문제로 2건의 실질적인 피해를 입었습니다.
**② 훅은 사용자의 프롬프트를 절대로 망가뜨려서는 안 된다**
`UserPromptSubmit`은 입력 경로에 개입합니다. 여기서 예외를 던지면 사용자의 입력 자체가 통과되지 않게 됩니다. 모든 예외를 무시(Catch-all)하는 방침으로 정했습니다.
```python
if __name__ == "__main__":
try:
sys.exit(main())
...
같은 이유로, 주입(Injection)은 1 세션당 1회만 수행하도록 설정했습니다 (state 파일로 억제). 대화 도중 '인계(引き継ぎ)'라는 단어가 나올 때마다 수천 토큰이 삽입되는 것은 사고입니다.
인계(Handover)에 써야 할 것과 쓰지 말아야 할 것
76일간의 철회(Retraction) 사례를 나열한 결과, 다음과 같이 정리했습니다.
| 종류 | 인계 사항에 작성할까? | 이유 |
|---|---|---|
| 사용자의 결정·선호 | ✅ 작성 | 재측정이 불가능함. 인계 사항이 유일한 기록임 |
| 불변의 1차 정보 (규약·API 사양) | ✅ 작성 (URL 병기) | 변하지 않음. 단, 출처는 필요함 |
| 측정값·순위·건수 | ⚠️ 측정 방법·n·날짜와 함께 작성 | 조건이 사라지면 다음 세션에서 오독함 |
| 가변적인 상태 (공개 본수·잔여 건수) | ❌ 작성하지 않음 → 명령어로 대체 | 작성하는 순간 데이터가 낡아버림 |
| "했다 / 하지 않았다" | ❌ 작성하지 않음 | git 이력으로 추적 가능 |
이 설계의 한계
솔직하게 적어둡니다.
- 세션을 분할하지 않으면 된다는 해답은 아닙니다. 긴 세션은
malformed(형식 오류)와 도구 결과의 작화(Hallucination)라는, 훨씬 탐지하기 어려운 방식으로 망가집니다. 분할은 부작용이 더 가벼운 쪽을 선택한 결과입니다. - 다이제스트(Digest)는 말미 쪽으로 편향됩니다. 세션 중반의 중요한 발견은 놓칠 수 있습니다. 마커(Marker) 단어를 늘리면 이번에는 도구 로그(Tool log)가 지저분해집니다. 이 부분은 트레이드오프(Trade-off)로 남아 있습니다.
- 규칙 ①~③은 산문 형태의 규약이므로, 확률적으로만 준수됩니다. 훅(Hook)과 같은 결정론적인 방식이 아닙니다. 실제로 이 글을 쓰기 시작한 시점에도 인계 사항의 숫자는 낡은 상태였습니다.
- 출현 횟수의 집계는 단어의 출현 횟수이지 '정정 건수'가 아닙니다. 하나의 정정으로 인해 해당 단어가 여러 번 등장할 수 있습니다.
요약
인계 구현 자체는 어렵지 않습니다. 훅(Hook) 2개와 다이제스트 생성 스크립트 1개면 작동합니다.
어려운 것은 그 이후였습니다.
- 인계 파일에 남는 것은 결론이지 증거가 아니다
- 결론만 읽은 다음 세션은 그것을 측정값과 동일한 가중치로 믿는다
- 그래서 오차는 사라지지 않고, 자신감만 늘어난다
- 해결 방법은 "잊지 않도록 하는 것"이 아니라, 측정 방법·n·날짜를 함께 운반하는 것이다
- 그리고, 기계가 읽을 수 있는 사실은 인계 사항에 쓰지 말고 명령어로 만든다
세션이 바뀌어도 기억이 이어지도록 하는 것이 인계의 목적이었습니다. 실제로 해보니, 기억이 이어지는 것과 오류가 이어지는 것은 같은 경로였습니다.
이 구성 세트(CLAUDE.md + 가드 훅 + 세션 복귀)를 환경에 맞춰 제작하여 납품하는 서비스도 운영하고 있습니다 → Claude Code의 안전 운용 세트를 만듭니다 (코코나라)
Discussion

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