추가 전용 JSONL 트랜스크립트, parentUuid 연결 리스트, 세션 상태 머신, 대화 복구.
Claude Code의 세션 관리 시스템은 대화 히스토리를 추가 전용(append-only) JSONL 파일로 지속하고, parentUuid 기반 연결 리스트로 메시지 체인을 관리하며, 상태 머신으로 세션 수명 주기를 추적합니다. 이 시스템은 중단된 세션의 복구, 효율적인 세션 목록 표시, 클라우드 동기화까지 지원합니다.
session/ 디렉토리 — sessionManager.ts, sessionStore.ts, sessionState.ts, sessionResume.ts, projectSingleton.ts
세션 파일은 ~/.claude/projects/ 디렉토리 아래에 프로젝트 경로를 반영한 구조로 저장됩니다:
# 디스크 레이아웃 예시
~/.claude/projects/
└── home-user-myproject/ # 프로젝트 경로 인코딩
├── sessions/
│ ├── abc123.jsonl # 세션 트랜스크립트 (추가 전용)
│ └── def456.jsonl
├── memory.md # 세션 메모리
└── config.json # 프로젝트 설정
각 프로젝트는 싱글톤 인스턴스로 관리됩니다. 세션 파일에 대한 쓰기는 100ms 배치 큐를 통해 수행되어 디스크 I/O를 최소화합니다. 짧은 시간 내에 발생하는 여러 메시지 추가가 단일 쓰기 작업으로 통합됩니다.
// projectSingleton.ts — 100ms 배치 쓰기
class WriteQueue {
private pending: Entry[] = []
private timer: NodeJS.Timeout | null = null
enqueue(entry: Entry): void {
this.pending.push(entry)
if (!this.timer) {
this.timer = setTimeout(() => this.flush(), 100)
}
}
}
세션 JSONL 파일에는 16종의 엔트리 타입이 기록됩니다. 각 엔트리는 type, uuid, parentUuid, timestamp 필드를 공통으로 가집니다:
user, assistant, system — 대화 메시지tool_use, tool_result — 도구 호출 및 결과session_start, session_end, compaction — 세션 수명 주기interrupted_turn, resumed — 중단 및 복구cost_update, model_change, memory_update, context_window, isMeta — 부가 정보isMeta 메시지isMeta 플래그가 설정된 메시지는 UI에 표시되지 않는 내부 제어 메시지입니다. 예를 들어 인터럽트 후 복구 시 추가되는 "Continue from where you left off." 메시지가 isMeta: true로 표시됩니다. 이 메시지는 모델에게 지시를 전달하지만 사용자에게는 보이지 않습니다.
JSONL 파일의 엔트리는 parentUuid 필드를 통해 연결 리스트를 형성합니다. 이 구조는 대화 브랜칭, 압축 후 재구성, 부분 로드를 가능하게 합니다.
체인 구축은 리프(LEAF) 메시지에서 시작하여 parentUuid를 따라 역방향으로 워킹한 후, 최종적으로 reverse()를 호출하여 시간순으로 정렬합니다:
// sessionStore.ts — 역방향 체인 워킹
function buildChain(leafUuid: string, entries: Map): Entry[] {
const chain: Entry[] = []
let current = entries.get(leafUuid)
while (current) {
chain.push(current)
current = entries.get(current.parentUuid)
}
return chain.reverse() // 시간순 정렬
}
체인 워킹 중 방문한 UUID를 Set으로 추적하여 순환 참조를 감지합니다. 사이클이 감지되면 체인 워킹을 즉시 중단하고 현재까지 수집된 체인을 반환합니다.
세션은 세 가지 상태를 가지는 상태 머신으로 관리됩니다:
idle: 사용자 입력 대기 상태. 새 프롬프트를 받을 준비가 됨running: 모델이 응답을 생성 중이거나 도구를 실행 중requires_action: 권한 프롬프트 등 사용자 액션이 필요한 상태// sessionState.ts — 상태 전이
idle → running // 사용자 메시지 전송
running → idle // 모델 응답 완료
running → requires_action // 권한 프롬프트 필요
requires_action → running // 사용자가 허용/거부
requires_action → idle // 사용자가 중단
세션 재개(resume) 시 마지막 메시지가 assistant 타입이고 stop_reason이 없으면, 이전 세션이 비정상 종료(Ctrl+C, 크래시 등)된 것으로 판단합니다. 이 경우 "Continue from where you left off." isMeta 메시지가 자동으로 추가됩니다.
loadConversationForResume — 4개 소스세션 복구 시 대화 내용을 4개 소스에서 로드합니다:
세션 데이터는 두 개의 원격 경로로 동기화됩니다. 이중 경로는 마이그레이션 기간 동안 이전 경로와 새 경로 모두에서 읽기를 보장합니다.
세션 목록을 표시할 때 전체 JSONL 파일을 읽지 않고, EOF에서 64KB 꼬리 윈도우만 읽습니다. 메타데이터(session_start, 마지막 메시지 요약 등)는 압축이나 업데이트 시 EOF에 재추가되어 항상 꼬리 윈도우 내에 존재하도록 보장합니다.
// sessionStore.ts — 64KB 꼬리 읽기
function readSessionMetadata(path: string): Metadata {
const stat = fs.statSync(path)
const tailOffset = Math.max(0, stat.size - 65536) // 64KB
const tail = fs.readSync(fd, buffer, tailOffset, 65536)
return parseMetadataFromTail(tail)
}
세션 파일은 claude 시작 시가 아니라 첫 실제 user 또는 assistant 메시지가 발생할 때 생성됩니다. 세션 ID는 부트 시 할당되지만, 사용자가 프롬프트를 입력하지 않고 종료하면 빈 세션 파일이 남지 않습니다. 이는 세션 목록의 노이즈를 줄이고 디스크 공간을 절약합니다.
~/.claude/projects/ 아래 추가 전용 JSONL 파일로 지속됩니다reverse()로 시간순 정렬합니다idle, running, requires_action 세 가지 상태를 가집니다isMeta 메시지가 자동 추가됩니다Q1. 세션 파일이 실제 생성되는 시점은?
Q2. parentUuid 연결 리스트의 체인 구축 방법은?
parentUuid 링크를 따라 루트까지 역방향으로 워킹합니다. 수집된 엔트리를 reverse()하여 시간순으로 정렬합니다. 이 방식은 JSONL의 추가 전용 특성과 자연스럽게 맞습니다.Q3. interrupted_turn 감지 시 수행하는 동작은?
isMeta: true 플래그가 설정된 "Continue from where you left off." 메시지가 추가됩니다. 이 메시지는 모델에게 이전 작업을 계속하라는 지시를 전달하지만, 사용자 UI에는 표시되지 않습니다.Q4. 꼬리 윈도우 메타데이터가 EOF에 재추가되는 이유는?
session_start 등의 메타데이터가 파일 끝에 재추가되어, 항상 꼬리 윈도우 내에서 접근 가능하도록 보장합니다.Q5. 세션 상태 머신의 3가지 상태는?
idle(입력 대기), running(응답 생성/도구 실행 중), requires_action(사용자 액션 필요) 세 가지 상태를 가집니다. 이 세 상태 사이의 전이가 세션의 전체 수명 주기를 관리합니다.