Claude Code 프롬프트 학습 시스템

프롬프트를 자동 수집하고, 교정 패턴을 분석해서, AI와의 소통을 개선하는 시스템.
Claude Code 훅 2개 + 커맨드 1개로 완성됩니다.

이 시스템이 하는 일
  1. 매 세션 종료 시 프롬프트를 자동 수집 (훅)
  2. 교정 패턴 감지 — "내가 어떤 지시를 할 때 다시 말해야 했나?"
  3. /프롬프트분석 커맨드로 패턴 분석 + 개선 제안
세션 종료 prompt-logger 훅 JSONL 축적 /프롬프트분석 인사이트
목차
1. 프롬프트 로거 훅 만들기 2. settings.json에 훅 등록 3. 분석 커맨드 만들기 4. 사용법 동작 원리: 교정 패턴 감지 데이터 형식 실제 분석 결과 예시 활용 팁

1프롬프트 로거 훅 만들기

세션 종료 시 transcript에서 사용자 프롬프트를 추출해 월별 JSONL 파일에 저장하는 훅입니다.

~/.claude/hooks/prompt-logger.mjs
#!/usr/bin/env node
/**
 * prompt-logger.mjs — Stop 훅
 * 세션 종료 시 transcript에서 사용자 프롬프트를 추출해
 * ~/.claude/prompt-log/{YYYY-MM}.jsonl에 append한다.
 */
import fs from 'node:fs'
import path from 'node:path'
import os from 'node:os'

function readStdin() {
  try { return fs.readFileSync(0, 'utf8') } catch { return '' }
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))

/** content 블록에서 텍스트 추출 */
function textOf(content) {
  if (typeof content === 'string') return content
  if (Array.isArray(content)) {
    return content
      .filter(b => b && b.type === 'text' && b.text)
      .map(b => b.text)
      .join(' ')
  }
  return ''
}

/** tool_result만으로 구성된 메시지인지 */
function isToolResultOnly(content) {
  return Array.isArray(content) && content.length > 0 &&
    content.every(b => b && b.type === 'tool_result')
}

/** 프롬프트 유형 분류 */
function classifyPrompt(text, isCorrection) {
  if (isCorrection) return 'correction'
  const t = text.trim()
  if (t.endsWith('?') || /^(어떻게|왜|뭐|how|why|what)/i.test(t)) return 'question'
  if (/^(응|좋아|그래|진행|ok|yes|lgtm)$/i.test(t)) return 'feedback'
  return 'instruction'
}

/** 첫 N줄 추출 (AI 응답 요약용) */
function firstLines(text, n = 3) {
  return text.split('\n').filter(l => l.trim()).slice(0, n).join(' ').slice(0, 300)
}

async function main() {
  const payload = JSON.parse(readStdin() || '{}')
  const sessionId = payload.session_id || null
  const transcriptPath = payload.transcript_path
  const cwd = payload.cwd || process.cwd()
  if (!sessionId || !transcriptPath) return

  // transcript flush 대기
  for (let i = 0; i < 8; i++) {
    try {
      if (fs.existsSync(transcriptPath) &&
          fs.statSync(transcriptPath).size > 0) break
    } catch {}
    await sleep(250)
  }
  if (!fs.existsSync(transcriptPath)) return

  const project = path.basename(cwd)

  // transcript 스캔
  const entries = []
  const txt = fs.readFileSync(transcriptPath, 'utf8').trim()
  for (const line of txt.split('\n')) {
    if (!line) continue
    let o
    try { o = JSON.parse(line) } catch { continue }
    const msg = o && o.message
    if (!msg) continue

    if (o.type === 'user' && msg.role === 'user'
        && !isToolResultOnly(msg.content)) {
      const t = textOf(msg.content).trim()
      if (t && !t.startsWith('<system')
          && !t.startsWith('<ide_')
          && !t.startsWith('This session is being continued')) {
        const isCommand = t.includes('<command-name>')
        entries.push({ role: 'user', text: t,
                       ts: o.timestamp || null, isCommand })
      }
    }
    if (o.type === 'assistant' && msg.role === 'assistant') {
      const t = textOf(msg.content).trim()
      if (t) entries.push({ role: 'assistant', text: t,
                            ts: o.timestamp || null })
    }
  }

  // 프롬프트-결과 쌍 + 교정 패턴 감지
  const prompts = []
  for (let i = 0; i < entries.length; i++) {
    if (entries[i].role !== 'user') continue
    const userText = entries[i].text

    // 다음 assistant 응답 찾기
    let resultSummary = ''
    for (let j = i + 1; j < entries.length; j++) {
      if (entries[j].role === 'assistant') {
        resultSummary = firstLines(entries[j].text)
        break
      }
    }

    // 교정 감지: 다음 entry도 user이면 현재가 교정을 유발함
    let followedByCorrection = false
    for (let j = i + 1; j < entries.length; j++) {
      if (entries[j].role === 'user') {
        followedByCorrection = true; break
      }
      if (entries[j].role === 'assistant') break
    }

    // 이전 user 직후 → 교정 프롬프트 (슬래시 커맨드 제외)
    let isCorrection = false
    if (i > 0 && !entries[i].isCommand) {
      for (let j = i - 1; j >= 0; j--) {
        if (entries[j].role === 'user' && !entries[j].isCommand) {
          isCorrection = true; break
        }
        if (entries[j].role === 'assistant') break
      }
    }

    prompts.push({
      ts: entries[i].ts || new Date().toISOString(),
      session: sessionId,
      project,
      prompt: userText,
      word_count: userText.split(/\s+/).length,
      type: classifyPrompt(userText, isCorrection),
      followed_by_correction: followedByCorrection,
      result_summary: resultSummary,
    })
  }

  if (prompts.length === 0) return

  // 월별 JSONL에 append (세션별 중복 방지)
  const logDir = path.join(os.homedir(), '.claude', 'prompt-log')
  try { fs.mkdirSync(logDir, { recursive: true }) } catch {}

  const now = new Date()
  const monthFile = path.join(logDir,
    `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}.jsonl`)

  // dedup: 이 세션이 이미 기록되었으면 skip
  let existingSessions = new Set()
  try {
    const existing = fs.readFileSync(monthFile, 'utf8')
    for (const line of existing.split('\n')) {
      if (!line) continue
      try {
        const o = JSON.parse(line)
        if (o.session) existingSessions.add(o.session)
      } catch {}
    }
  } catch {}

  if (existingSessions.has(sessionId)) return

  const lines = prompts.map(p => JSON.stringify(p)).join('\n') + '\n'
  fs.appendFileSync(monthFile, lines, 'utf8')
}

main().catch(() => {}).finally(() => process.exit(0))

2settings.json에 훅 등록

훅 파일을 만들었으면 Claude Code가 실행하도록 settings.json에 등록해야 합니다.

~/.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [{
          "type": "command",
          "command": "node ~/.claude/hooks/prompt-logger.mjs"
        }]
      }
    ]
  }
}
주의: 기존 settings.json이 있으면 "Stop" 배열에 추가하세요. 덮어쓰면 기존 훅이 사라집니다.

3분석 커맨드 만들기

/프롬프트분석 슬래시 커맨드를 만들어 축적된 데이터를 분석합니다.

~/.claude/commands/프롬프트분석.md
---
description: 프롬프트 로그를 분석해 패턴 발견 + 개선 제안
---

`~/.claude/prompt-log/` 디렉토리의 JSONL 파일을 분석한다.

분석 기간: $ARGUMENTS (숫자 = 최근 N개월, 비어있으면 1개월)

## 분석 관점

### 1. 기본 통계
- 총 프롬프트 수, 유형별 분포 (instruction/question/correction/feedback)
- 평균 단어 수, 가장 긴/짧은 프롬프트

### 2. 교정 패턴 분석 (가장 중요)
- `followed_by_correction: true`인 프롬프트를 모아서 공통점 파악
- "어떤 유형의 지시가 교정을 유발하는가?" - 구체적 예시와 함께 분석
- 교정률 추이 (시간 경과에 따른 변화)

### 3. 효과적 프롬프트 패턴
- 1회에 원하는 결과가 나온 프롬프트의 구조적 공통점 분석

### 4. 개선 제안 (3~5개)
- 교정 패턴에서 발견한 약점 기반 구체적 개선 팁

## 출력
결과를 `~/.claude/prompt-log/insights.md`에 저장한다.

4사용법

설치 후 할 일: 없음
평소처럼 Claude Code를 사용하면 됩니다. 세션이 끝날 때마다 프롬프트가 자동 수집됩니다.

분석 실행

# 최근 1개월 분석
/프롬프트분석

# 최근 3개월 분석
/프롬프트분석 3

수집 데이터 확인

# 이번 달 로그 확인
cat ~/.claude/prompt-log/2025-01.jsonl | head -5 | python3 -m json.tool

# 총 프롬프트 수
wc -l ~/.claude/prompt-log/*.jsonl

동작 원리: 교정 패턴 감지

핵심 아이디어: AI 응답 없이 사용자가 연속으로 메시지를 보내면, 이전 결과가 불만족스러웠다는 신호입니다.

정상 흐름:
  User: "파일 수정해줘" AI: "수정 완료" User: "좋아 다음"

교정 흐름:
  User: "파일 수정해줘" AI: "수정 완료"
  User: "아니 그게 아니라 이렇게 해줘"교정 감지!

연속 교정:
  User: "이미지 보내줄께"
  User: [Image]
  User: [Image] ← 연속 user = 교정으로 감지
  (이런 오분류를 줄이기 위해 필터를 개선합니다)
감지 대상분류의미
followed_by_correction: true 교정 유발 이 프롬프트의 결과가 불만족 → 다음에 교정 발생
type: "correction" 교정 프롬프트 이전 결과를 바로잡기 위해 보낸 메시지
type: "instruction" 지시 일반적인 작업 요청
type: "question" 질문 정보 요청 ("왜?", "어떻게?")
type: "feedback" 피드백 단순 승인 ("좋아", "진행")

데이터 형식

월별 JSONL 파일에 한 줄씩 저장됩니다.

~/.claude/prompt-log/2025-01.jsonl
{
  "ts": "2025-01-15T09:30:00.000Z",
  "session": "abc123-session-id",
  "project": "my-app",
  "prompt": "settings.json에 Stop 훅 추가해줘",
  "word_count": 5,
  "type": "instruction",
  "followed_by_correction": false,
  "result_summary": "Stop 훅을 추가했습니다. 세션 종료 시..."
}
필드설명
ts프롬프트 타임스탬프 (ISO 8601)
session세션 ID (중복 방지 키)
project작업 디렉토리명
prompt사용자 프롬프트 전문
word_count단어 수
typeinstruction / question / correction / feedback
followed_by_correction이 프롬프트 후 교정이 발생했는가
result_summaryAI 응답 요약 (첫 3줄, 300자)

실제 분석 결과 예시

2주간 716개 프롬프트를 수집한 실제 결과입니다.

항목
총 프롬프트716개
instruction492개 (68.7%)
question183개 (25.6%)
실제 교정률1.2% (오분류 제외)

발견한 교정 패턴

#1. 의도 축소 해석
교정 유발 "확인해줘" → AI가 확인만 하고 끝냄
교정: "확인하고 수정까지 해줘"
개선: 기대하는 행동 범위를 명시 — "확인 + 수정해줘"
#2. 맥락 전환 누락
교정 유발 "이건 됐고 다음 작업..." → AI가 이전 맥락 인용
개선: "이전 작업 완료. 새 주제: DEV-1234" 명시적 전환

발견한 효과적 패턴

#1. 경로 + 행동 명시 교정률 최저
"settings.json에 Stop 훅 추가해줘"
→ 파일명 + 구체적 행동 = 1회 성공
#2. 중간 길이 (10~30단어) 교정률 2.2%
너무 짧으면 의도 불명확, 너무 길면 포인트 분산
→ 핵심만 간결하게

활용 팁

1. 주기적 분석

1~2주마다 /프롬프트분석 실행. 교정률 추이를 보면 AI와의 소통이 개선되는지 확인할 수 있습니다.

2. 교정 패턴 → 습관 교정

같은 교정이 반복되면 프롬프트 습관을 바꿔야 한다는 신호입니다. 예: "확인해줘"를 항상 교정한다면 → "확인하고 수정해줘"로 습관 변경.

3. 프로젝트별 비교

데이터에 project 필드가 있으므로 프로젝트별 교정률을 비교할 수 있습니다. 특정 프로젝트에서 교정률이 높다면 CLAUDE.md에 맥락을 보강하세요.

4. 팀 공유

insights.md를 팀과 공유하면 "효과적인 프롬프트 가이드"가 됩니다. 교정 패턴 TOP 3 + 효과적 패턴 TOP 3만 공유해도 충분합니다.

5. 디렉토리 구조

~/.claude/
  hooks/
    prompt-logger.mjs      # 수집 훅
  commands/
    프롬프트분석.md          # 분석 커맨드
  prompt-log/
    2025-01.jsonl          # 월별 수집 데이터
    2025-02.jsonl
    insights.md            # 분석 결과
    .last-analysis         # 마지막 분석 시점