#클로드코드#작업이력#자동화#생산성도구

클로드 코드 작업 이력 시각화 — 3주 동안 뭘 만들었는지 기억나지 않을 때

클로드 코드 작업 이력 시각화 — 3주 동안 뭘 만들었는지 기억나지 않을 때 핵심 개념을 담은 커버 이미지
클로드 코드 작업 이력 시각화 — 3주 동안 뭘 만들었는지 기억나지 않을 때 핵심 개념을 담은 커버 이미지

지난주에 만든 것도 가물가물할 때가 있어요. 터미널 창을 닫으면 작업 기록이 머릿속에서도 사라지죠. 클로드 코드는 모든 대화를 ~/.claude/projects/ 아래 jsonl 파일로 조용히 기록합니다.

이 글을 읽고 나면 20줄짜리 스크립트로 작업 이력을 날짜별 타임라인으로 복원할 수 있어요.

준비물

이 실습에는 다음이 필요합니다.

  • Node.js 18 이상 (터미널에서 node --version으로 확인)
  • Claude Code 데스크톱 앱 사용 경험 (최소 3~5일 이상 작업한 프로젝트)
  • 텍스트 에디터 (VSCode나 vim 등)

로컬 파일만 읽으므로 API 키는 필요 없습니다.

클로드 코드는 어디에 기록을 남기나요?

macOS나 리눅스라면 ~/.claude/projects/ 경로를 열어보세요. 윈도우는 %USERPROFILE%\.claude\projects\에 있습니다. 프로젝트별로 폴더가 나뉘어 있고, 각 폴더 안에 세션별 jsonl 파일이 담깁니다.

제 경우 blog-publisher 프로젝트 폴더에 42개 jsonl 파일이 있었어요. 파일 이름은 세션 아이디입니다. 2a78d93d-0d8c-4bec-bff1-5e2e9964e392.jsonl 같은 식이죠.

파일을 하나 열어보면 이런 구조예요.

{"type":"user","timestamp":"2026-08-25T14:31:05.456Z","cwd":"/home/user/th-team/blog-publisher","gitBranch":"main","message":{"role":"user","content":"게이트 통과율을 높이려면 어떻게 해야 할까?"}}
{"type":"assistant","timestamp":"2026-08-25T14:31:15.789Z","message":{"role":"assistant","content":"게이트 통과율을 높이려면..."}}
{"type":"attachment","timestamp":"2026-08-25T14:31:20.001Z","cwd":"/home/user/th-team/blog-publisher"}

한 줄이 JSON 객체 하나입니다. assistant, attachment 처럼 다른 종류도 같은 파일에 섞여 쌓이니, 내가 타이핑한 것만 뽑으려면 type === 'user' 로 걸러야 해요.

타임라인 추출 스크립트를 짜봅시다

타임라인 추출 스크립트를 짜봅시다
타임라인 추출 스크립트를 짜봅시다

이제 jsonl 파일들을 읽어서 "언제 무슨 작업을 했는지" 타임라인으로 만들어볼게요.

1단계: 파일 목록 읽기

프로젝트 폴더 안의 jsonl 파일을 전부 찾습니다.

const fs = require('fs');
const path = require('path');

const projectDir = path.join(process.env.HOME, '.claude/projects/-home-user-projects-blog');
const files = fs.readdirSync(projectDir).filter(f => f.endsWith('.jsonl'));

console.log(`찾은 세션 파일: ${files.length}개`);

이렇게 실행하면 → 찾은 세션 파일: 42개 같은 출력이 나옵니다. 프로젝트 경로는 여러분 환경에 맞게 바꾸세요. .claude/projects/ 아래 폴더 이름은 실제 프로젝트 절대경로의 슬래시를 하이픈으로 바꾼 형태예요.

2단계: 각 파일 파싱해서 사용자 메시지 추출

파일을 하나씩 열어서 사용자 메시지만 뽑아냅니다.

const events = [];

files.forEach(filename => {
  const filepath = path.join(projectDir, filename);
  const lines = fs.readFileSync(filepath, 'utf-8').split('\n').filter(Boolean);
  
  lines.forEach(line => {
    try {
      const event = JSON.parse(line);
      if (event.type === 'user' && event.message) {
        const content = typeof event.message.content === 'string' 
          ? event.message.content 
          : '';
        if (!content) return;
        
        const dt = new Date(event.timestamp);
        const kst = new Date(dt.getTime() + 9 * 60 * 60 * 1000);
        
        events.push({
          date: kst.toISOString().split('T')[0],
          time: kst.toISOString().split('T')[1].substring(0, 5),
          message: content.split('\n')[0]
        });
      }
    } catch (e) {
      // 파싱 실패한 줄은 건너뜀
    }
  });
});

console.log(`추출한 메시지: ${events.length}건`);

입력: jsonl 파일 42개 → 출력: 추출한 메시지: 347건

제 경우 3주 동안 347번 질문했더라고요. 타임스탬프는 UTC로 저장돼 있어서 KST로 변환하는 코드를 넣었습니다.

3단계: 날짜별로 그룹화해서 타임라인 출력

날짜별로 묶어서 출력합니다.

const byDate = {};
events.forEach(e => {
  if (!byDate[e.date]) byDate[e.date] = [];
  byDate[e.date].push(e);
});

const dates = Object.keys(byDate).sort();

dates.forEach(date => {
  console.log(`\n[${date}] ────────`);
  byDate[date].forEach(e => {
    console.log(`- ${e.time} ${e.message}`);
  });
});

이렇게 실행하면 이런 식으로 나와요.

[2026-08-25] ────────
- 14:31 게이트 통과율을 높이려면 어떻게 해야 할까?
- 15:42 이 오류 메시지가 뭘 의미하는지 알려줘
- 18:03 테스트 커버리지를 어떻게 올리지?

[2026-08-26] ────────
- 09:15 어제 만든 스크립트가 왜 안 도는지 모르겠어
- 10:33 내부 중복 게이트 임계값을 조정해야 할까?

날짜마다 뭘 고민했는지 한눈에 보입니다.

프로젝트와 브랜치를 함께 추적하려면?

하루 동안 여러 작업을 오가면 맥락이 섞여요. user 레코드에는 cwd와 gitBranch 필드가 함께 들어있습니다. 프로젝트 이름만 보면 "blog-publisher에서 뭘 했다" 정도만 알 수 있지만, 브랜치까지 찍으면 기능 단위로 묶입니다.

2단계 코드로 돌아가서, events.push 에 넘기는 객체의 마지막 줄 끝에 쉼표를 붙이고 그 아래에 두 줄을 얹으세요.

  project: path.basename(event.cwd || ''),
  branch: event.gitBranch || 'main'

cwd는 user 레코드에 직접 들어있어서 별도 상태 변수가 필요 없어요. event.cwd를 바로 쓰면 됩니다.

찍는 쪽은 3단계의 안쪽 반복문입니다. 거기 있던 출력 한 줄을 아래 두 줄로 교체하세요. 브랜치가 기본값이 아닐 때만 꼬리표에 붙이면 화면이 덜 지저분해요.

  const tag = e.branch !== 'main' ? `[${e.project}:${e.branch}]` : `[${e.project}]`;
  console.log(`- ${e.time} ${tag} ${e.message}`);

실행하면 이렇게 나옵니다.

[2026-08-29] ────────
- 11:05 [blog-publisher:feat/gate-refactor] 검증자 피드백 반영
- 14:32 [thundorun] 관리자 대시보드 차트 렌더링 개선
- 16:48 [blog-publisher:feat/gate-refactor] 내부 중복 임계값 조정

같은 프로젝트지만 브랜치가 다르면 별개 작업이죠. 저는 이걸로 기능 개발 타임라인을 추적해요.

서브에이전트 기록을 걸러내면 더 정확해집니다

클로드 코드가 자동으로 돌린 서브에이전트 기록도 jsonl에 쌓입니다. isSidechain 필드가 true인 레코드가 그거예요. 제가 직접 물어본 것과 섞이면 타임라인이 부풀어 오릅니다.

2단계 코드의 if (event.type === 'user' && event.message) 블록 첫 줄에 이걸 추가하세요.

if (event.isSidechain) return;

이 한 줄로 347건이 219건으로 줄었어요. 실제로 제가 타이핑한 질문만 남으니 "그날 뭘 고민했나"가 훨씬 명확하게 보입니다.

여러 프로젝트를 한 타임라인으로 합치고 싶다면 ~/.claude/projects/ 아래 폴더를 순회하면 됩니다. fs.readdirSync로 프로젝트 폴더 목록을 먼저 뽑고, 각 폴더의 jsonl을 읽으세요. 저는 3개 프로젝트를 합쳐서 주간 보고를 만들어요.

흔한 실수와 해결법

흔한 실수와 해결법
흔한 실수와 해결법

처음 돌렸을 때 빈 결과가 나왔어요. 프로젝트 폴더 이름을 틀리게 적었더라고요. .claude/projects/ 아래 실제 폴더 이름은 ls ~/.claude/projects/로 먼저 확인하세요.

두 번째는 JSON 파싱 에러예요. 일부 줄이 잘린 파일이 있어서 try-catch로 감쌌습니다. 347건 중 3건 스킵됐는데 타임라인 보는 데는 지장 없었어요.

타임존 문제도 있었습니다. 타임스탬프가 UTC라서 제 시각과 9시간 차이 났죠. 위 2단계 코드는 UTC에 9시간을 더해 KST로 변환합니다.

자주 묻는 질문

Q. 여러 프로젝트를 한 번에 보려면? A. .claude/projects/ 아래 모든 폴더를 순회하면 됩니다. fs.readdirSync로 프로젝트 폴더 목록을 먼저 뽑고, 각 폴더의 jsonl을 읽으세요.

Q. 어시스턴트 응답도 함께 보고 싶어요. A. type === 'assistant' 레코드도 같은 방식으로 추출하면 됩니다. 응답이 길어서 첫 100자만 자르는 게 좋아요.

Q. 특정 키워드가 들어간 대화만 찾을 수 있나요? A. 메시지 추출할 때 if (content.includes('게이트')) 같은 필터를 추가하세요. 저는 "배포" 키워드로 14건을 찾았고, 절반이 금요일 저녁이었어요.

Q. 이 스크립트를 cron으로 돌려서 매주 요약을 받을 수 있나요? A. 가능합니다. 출력을 마크다운 파일로 저장하게 수정하고, 0 9 * * 1 cron 표현식으로 매주 월요일 아침에 돌리면 지난주 작업 요약이 생겨요.

마무리

스크립트 전체는 30줄 정도예요. 한 번 만들어두면 "지난달에 뭐 했더라?" 할 때마다 꺼내 쓸 수 있습니다.

저는 이걸로 주간 회고를 자동화했어요. 매주 월요일 아침 타임라인을 보면서 지난주 시간을 어디에 썼는지 점검합니다. 같은 문제를 반복해서 물어본 게 보이면 문서화하거나 스크립트로 만들죠.

다음 단계로는 작업 시간 통계를 내보는 것도 좋습니다. 첫 메시지와 마지막 메시지 타임스탬프 차이로 세션 길이를 계산하면, 이번 달 순수 코딩 시간을 측정할 수 있어요.

이 글이 도움이 됐다면 공유해 주세요
X 공유

관련 글