ClOr

ClOr

백엔드 실무 트러블슈팅과 AI 에이전트 구조 분석을 기록합니다.

Claude Code 해부학 (완결)

51만 줄 소스코드를 19편에 걸쳐 분석한 완결 시리즈

전체 시리즈 보기 →

백엔드 트러블슈팅

실무에서 겪은 장애와 해결 과정 기록

전체 시리즈 보기 →

최신 글

article thumbnail

클로드 코드(Claude Code) 소스코드를 분석하다 보면 공식 문서 어디에도 없는 코드를 만나게 됩니다. 터미널에 살아 숨쉬는 다마고치 펫, 빌드 파이프라인이 차단하는 미공개 모델 코드네임, Anthropic 직원의 오픈소스 기여를 위장하는 언더커버 모드까지. 이 세 가지가 전부 소스코드에 들어있습니다.

시리즈: Part 15 — 빌드 시스템 분석 | 전체 목록


목차

  • /buddy를 치면 뭐가 나오나요?
  • 희귀도는 어떻게 정해지나요?
  • 조작할 수 있는 건 아닌가요?
  • "카피바라"가 금지어라고요?
  • 언더커버 모드는 누구를 위한 건가요?
  • 이게 왜 중요한가요?

/buddy를 치면 뭐가 나오나요?

터미널에 ASCII 아트 펫이 나타납니다. src/buddy/ 디렉토리에 구현된 완전한 다마고치 시스템입니다. 종족 18종, 희귀도 5등급, 눈 모양 6종, 모자 8종이 조합되고, 1% 확률의 빛나는(shiny) 변종까지 존재합니다.

핵심은 "결정론적 가챠"입니다. 사용자 ID를 해시해서 PRNG 시드로 쓰기 때문에, 같은 계정이면 항상 같은 펫이 나옵니다. 리롤은 불가능합니다.

// src/buddy/companion.ts
const SALT = 'friend-2026-401'

export function roll(userId: string): Roll {
  const key = userId + SALT
  if (rollCache?.key === key) return rollCache.value
  const value = rollFrom(mulberry32(hashString(key)))
  rollCache = { key, value }
  return value
}

hashString(userId + SALT) → Mulberry32 PRNG 시드 → 종족, 눈, 모자, 스탯 전부 결정. SALT가 friend-2026-401인 건 만우절(4/1) 출시를 노린 겁니다. 실제로 티저 윈도우가 2026년 4월 1~7일로 설정되어 있습니다.

희귀도는 어떻게 정해지나요?

가중치 뽑기입니다. legendary가 나올 확률은 1%, 거기에 shiny 확률 1%가 곱해지면 빛나는 레전더리는 0.01%입니다.

// src/buddy/types.ts
export const RARITY_WEIGHTS = {
  common: 60,    // ★
  uncommon: 25,  // ★★
  rare: 10,      // ★★★
  epic: 4,       // ★★★★
  legendary: 1,  // ★★★★★
} as const

각 펫은 DEBUGGING, PATIENCE, CHAOS, WISDOM, SNARK 다섯 스탯을 가집니다. 희귀도가 높을수록 스탯 하한선이 올라가고, 하나의 주력 스탯과 하나의 약점 스탯이 랜덤으로 정해집니다.

조작할 수 있는 건 아닌가요?

못 합니다. 여기서 Bones/Soul 분리 설계가 빛납니다. 종족·희귀도·스탯 같은 "뼈대(Bones)"는 매번 userId 해시에서 재계산됩니다. config에 저장되는 건 이름과 성격뿐인 "영혼(Soul)"입니다.

// src/buddy/types.ts
export type StoredCompanion = CompanionSoul & { hatchedAt: number }
// Bones are regenerated from hash(userId) on every read
// so users can't edit their way to a legendary.

config를 열어서 legendary로 바꿔봤자, 다음 로드 때 userId 해시로 덮어씌워집니다.

 


"카피바라"가 금지어라고요?

종족 목록을 보면 모든 이름이 String.fromCharCode로 인코딩되어 있습니다. duck도, cat도, capybara도 전부요.

// src/buddy/types.ts
export const capybara = c(
  0x63, 0x61, 0x70, 0x79,
  0x62, 0x61, 0x72, 0x61,
) as 'capybara'

주석이 이유를 알려줍니다. "One species name collides with a model-codename canary in excluded-strings.txt." 빌드 결과물에 "capybara" 문자열이 포함되면 빌드가 실패하는 보안 장치가 있습니다. Capybara는 아직 미공개된 Anthropic 모델의 코드네임입니다.

// src/utils/model/model.ts
function maskModelCodename(baseName: string): string {
  // e.g. capybara-v2-fast → cap*****-v2-fast
  const [codename = '', ...rest] = baseName.split('-')
  const masked =
    codename.slice(0, 3) + '*'.repeat(Math.max(0, codename.length - 3))
  return [masked, ...rest].join('-')
}

maskModelCodename은 내부 코드네임의 첫 세 글자만 남기고 마스킹합니다. 예시가 capybara-v2-fast입니다.


언더커버 모드는 누구를 위한 건가요?

Anthropic 직원이 공개 저장소에 기여할 때 자동 활성화됩니다. USER_TYPE === 'ant'(내부 빌드)에서만 동작하고, 외부 빌드에서는 번들러가 이 코드를 통째로 제거합니다.

// src/utils/undercover.ts
export function isUndercover(): boolean {
  if (process.env.USER_TYPE === 'ant') {
    if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER))
      return true
    return getRepoClassCached() !== 'internal'
  }
  return false
}

활성화되면 시스템 프롬프트에 이런 지시가 주입됩니다: "Do not blow your cover." 커밋 메시지에 "Claude Code", 내부 모델 코드네임(Capybara, Tengu 등), Co-Authored-By 라인이 들어가는 것을 원천 차단합니다. 강제로 끄는 옵션은 없습니다.


이게 왜 중요한가요?

세 기능이 보여주는 건 하나입니다. 코드에는 공식 문서보다 많은 정보가 있다는 것.

excluded-strings.txt로 빌드를 차단하고, maskModelCodename으로 마스킹하고, 언더커버 모드로 커밋 메시지까지 검열하는 삼중 방어선은 "곧 나올 모델이 있다"는 사실을 방증합니다. 다마고치의 SALT friend-2026-401은 출시 일정표고, 피처 플래그 BUDDY는 배포 스위치입니다.


관련 글: Claude Code 프로젝트 구조 분석 · Claude Code 터미널 UI 분석

profile

ClOr

@ClOr

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

ClOr · 백엔드 트러블슈팅과 AI 에이전트 구조 분석을 기록합니다.