소스맵 파일 하나가 51만 줄을 공개했다 — npm 배포 파이프라인 보안 설계
Claude Code npm 패키지의 소스맵 유출 사고를 계기로 files 허용 목록·소스맵 분리·번들링과 난독화·사고 대응 체크리스트 등 클로즈드소스 배포 파이프라인의 보안 설계를 정리한다.
2026-08-14 · 최초 발행 2026-05-05
Anthropic의 AI 코딩 도구 Claude Code의 npm 패키지(@anthropic-ai/claude-code) v2.1.88 버전에 59.8MB에 달하는 소스맵 파일이 실수로 포함되어 배포됐다. 이 소스맵을 통해 1,906개 파일, 약 51만 2천 줄의 미난독화(unobfuscated) TypeScript 소스코드가 사실상 공개 상태에 놓였다. 클로즈드소스 상용 AI 도구의 내부 구현이 단 하나의 배포 파이프라인 실수로 전 세계에 노출될 수 있음을 보여준 사례다. npm 레지스트리의 특성상 일단 배포된 패키지는 즉시 전 세계에 전파되고 미러링되므로, 사후 삭제만으로는 완전한 봉쇄가 불가능하다는 점에서 이런 유형의 유출은 일반적인 보안 사고와 다른 무게를 가진다.
무엇이, 왜 새어나갔는가
@anthropic-ai/claude-code는 터미널에서 동작하는 Anthropic의 공식 AI 코딩 CLI 도구다. 상용 소프트웨어로서 소스코드를 공개하지 않는 클로즈드소스 제품이며, npm에는 트랜스파일·번들링·난독화된 JavaScript 파일만 배포되는 것이 원래 설계였다. 그러나 v2.1.88 빌드·배포 과정에서 .js.map 확장자의 소스맵 파일들이 패키지에 포함된 채 퍼블리시됐다.
소스맵 파일의 내용은 단순한 디버깅 힌트가 아니었다. 번들된 JavaScript 코드를 원본 TypeScript 소스 1,906개 파일로 완전히 역매핑할 수 있는 정보가 담겨 있었고, 이를 통해 Claude Code의 핵심 에이전트 루프 구현, 도구 호출(tool use) 파싱·실행 로직, 프롬프트 구성과 컨텍스트 관리 메커니즘, 내부 API 클라이언트와 인증 흐름, 에러 처리·재시도 전략이 노출됐다. 59.8MB라는 소스맵 파일 크기는 이 패키지가 얼마나 대규모 TypeScript 코드베이스 위에서 구현됐는지를 단적으로 보여준다. 일반적인 npm 패키지가 수십~수백 KB 수준인 것을 감안하면 패키지 크기 이상(anomaly) 탐지만으로도 조기에 발견할 수 있었던 사고다.
소스맵(source map)은 번들링·트랜스파일 과정에서 변환된 코드(보통 난독화·압축된 JavaScript)를 원본 소스(TypeScript, ES6+ 등)와 연결하는 JSON 포맷의 메타데이터 파일로, 브라우저 개발자 도구나 Node.js 디버거가 런타임 오류를 원본 소스 라인에 매핑하도록 설계됐다. 문제는 소스맵이 원본 소스의 sourcesContent 필드에 실제 소스코드 전체를 문자열로 내장하는 경우가 많다는 점이다. .js.map 파일 하나를 열면 대응하는 TypeScript 파일의 전체 내용이 그대로 읽힌다 — 배포 파이프라인에서 소스맵 파일을 반드시 제외해야 하는 근본적인 이유다.
배포 파일 목록은 허용 목록으로 관리한다
npm 패키지 배포에서 포함·제외 파일을 제어하는 방법은 두 가지다. package.json의 files 필드로 허용 목록(allowlist)을 명시하거나 .npmignore 파일로 제외 목록(denylist)을 관리한다. 보안 관점에서는 허용 목록 방식이 훨씬 안전하다. 무엇을 포함할지 명시하면 나머지는 자동 제외되므로, 소스맵처럼 새로 생성된 파일이 실수로 끼어들 여지가 없다.
// package.json — 허용 목록 방식 (권장)
{
"name": "@example/closed-source-tool",
"files": [
"dist/index.js",
"dist/cli.js",
"dist/types/**/*.d.ts",
"README.md",
"LICENSE"
]
}
.npmignore를 쓰는 경우에는 소스맵 패턴을 반드시 포함해야 한다.
# .npmignore — 소스맵 제외 필수 항목
**/*.js.map
**/*.ts.map
src/
tsconfig*.json
*.tsbuildinfo
.env*
test/
__tests__/
coverage/
.npmignore가 없으면 npm은 .gitignore를 대신 쓴다. 그런데 .gitignore는 소스코드 관리 목적으로 작성되므로 빌드 아티팩트(소스맵 포함)를 npm 배포에서 제외하기 위한 패턴이 빠져 있는 경우가 많다. 이 차이를 인지하지 못한 채 빌드 파이프라인을 구성하는 것이 소스맵 유출의 가장 흔한 원인 중 하나다.
빌드 도구 수준에서도 소스맵 생성 자체를 배포 빌드에서 분리하는 설계가 필요하다.
esbuild를 쓰는 경우 배포 빌드에서 --sourcemap=false를 명시하거나 --sourcemap=external로 외부 파일로 분리한 뒤 files 필드에서 제외하는 방식을 쓴다. webpack에서는 devtool: false(production 기본값)를 확인한다. TypeScript 컴파일러(tsc)는 tsconfig.prod.json에서 "sourceMap": false, "inlineSources": false를 명시하는 것이 권장된다. 소스맵이 운영 디버깅에 반드시 필요하다면 비공개 클라우드 스토리지(S3, GCS 등)에 별도 보관하고 내부 오류 추적 시스템(Sentry의 url_prefix 기능 등)과 연결하는 방식을 쓴다. 이렇게 하면 소스맵의 디버깅 가치를 보존하면서 외부 노출은 차단할 수 있다.
내부 도구나 부분 공개 상용 패키지는 공개 npm 레지스트리 대신 프라이빗 레지스트리(Verdaccio, GitHub Packages, AWS CodeArtifact, JFrog Artifactory 등)를 쓰면 추가 보호 계층을 확보할 수 있다. 접근 제어, 패키지 스캔, 업로드 전 검증 훅으로 소스맵 파일이 포함된 패키지가 배포되기 전에 자동 탐지할 수 있다. 또한 배포 파이프라인에 npm pack --dry-run 또는 npx publint 같은 검증 단계를 CI에 필수 게이트로 넣으면, 실제 퍼블리시 전에 패키지에 포함될 파일 목록을 사전 검토할 수 있다. Claude Code 사건 같은 실수는 이 단계가 CI 파이프라인에 없거나 검토 결과를 확인하지 않을 때 발생한다.
소스맵을 지웠다고 끝이 아니다
소스맵 제거만으로는 충분하지 않다. 배포된 JavaScript 코드 자체도 역공학을 어렵게 만드는 처리가 필요하다. 일반적으로 세 단계 처리를 조합한다. 첫째는 번들링(bundling)으로, esbuild·webpack·rollup 등으로 여러 모듈을 단일 파일로 병합하면 모듈 간 의존성 구조를 파악하기 어려워진다. 특히 --minify 옵션으로 변수명을 단축하면 로직의 의미를 유추하기 훨씬 어렵다. 둘째는 난독화(obfuscation)로, JavaScript Obfuscator나 terser의 --mangle 설정으로 변수명·함수명·클래스명을 무의미한 문자열로 치환하고 제어 흐름을 복잡하게 변환(control flow flattening)한다. 번들링이 구조를 숨긴다면 난독화는 의미를 숨긴다. 셋째는 죽은 코드 제거(tree-shaking)와 상수 인라이닝으로, 빌드 시점에 알 수 있는 환경 변수·설정값·플래그를 인라인하면 배포 코드에서 설정 구조를 역추적하기 어렵다.
이 흐름이 보여주듯, 아무리 정교한 난독화를 적용해도 소스맵이 함께 배포되면 모든 보호가 한 번에 무력화된다. 소스맵 제어가 코드 보호의 가장 앞선 우선순위여야 하는 이유다.
클로즈드소스 상용 도구는 소스코드를 숨기는 것 외에도 런타임에서 사용 조건을 검증하는 메커니즘이 필요하다. 라이선스 키 검증, 사용량 측정(telemetry), 엔드포인트 인증 등의 런타임 보호는 소스코드를 확보했다 해도 도구를 그대로 재배포하거나 라이선스를 우회하기 어렵게 만든다. 구성 요소로는 핵심 기능이 서버를 경유하도록 설계해 오프라인 무단 사용을 차단하는 서버 사이드 API 키 검증, 라이선스를 특정 기기·환경에 바인딩하는 기기 핑거프린팅, 특정 기능을 클라이언트 코드가 아닌 서버에서 활성화해 코드 복사로는 기능을 재현할 수 없게 하는 기능 플래그 서버, 일정 주기로 라이선스 서버에 연결해 유효성을 확인하는 정기적 온라인 검증이 있다. Claude Code의 경우 핵심 추론이 Anthropic API를 경유하므로 소스코드를 복사해도 API 키 없이는 도구 자체를 운용할 수 없다는 구조적 보호가 이미 존재한다. 그러나 내부 아키텍처·프롬프트 설계·에이전트 로직이 노출되면 경쟁사가 유사한 제품을 설계하는 참조 자료로 활용할 수 있다는 지식재산 위험은 별개로 남는다.
이 유출이 특히 아픈 이유
일반적인 소프트웨어 소스코드 유출과 달리, AI 코딩 도구의 소스코드 유출은 세 가지 특수한 위험을 수반한다. 첫째는 프롬프트 엔지니어링 노출이다. Claude Code 같은 도구의 핵심 경쟁력 중 상당 부분은 모델에 전달하는 시스템 프롬프트, 컨텍스트 구성 방식, 도구 스키마 설계에 있다. 이 내용이 소스코드를 통해 드러나면 경쟁사가 수개월의 연구개발을 단축할 수 있다. 둘째는 보안 취약점 분석이다. 소스코드를 분석하면 인증 흐름, API 키 처리, 입력 검증 로직의 취약점을 훨씬 쉽게 식별할 수 있다 — 소스맵 유출 후 보안 연구자(또는 공격자)들이 가장 먼저 하는 작업이 이 부분이다. 셋째는 신뢰 훼손이다. AI 코딩 도구는 사용자의 코드베이스, 자격증명, 작업 환경에 광범위한 접근 권한을 갖는다. 도구의 내부 구현이 외부에 노출되면 사용자가 이 도구가 수집하거나 전송하는 데이터에 의구심을 가질 수 있고, 이는 브랜드 신뢰도에 직접 영향을 미친다.
소스맵 유출이 발생했을 때의 대응은 정해진 순서로 진행되어야 한다.
npm의 unpublish 정책상 배포 후 72시간 이내에는 unpublish가 가능하지만, 이미 npm install로 설치된 패키지나 jsDelivr·Unpkg 같은 다양한 미러에 캐시된 파일은 통제할 수 없다. 사후 삭제보다 사전 예방이 압도적으로 중요한 이유다.
배포 전 자동화 게이트로 걸러낸다
소스맵 유출을 포함한 npm 배포 보안 문제를 사전에 방지하는 체크리스트는 CI/CD 파이프라인의 자동화 게이트로 구현되어야 한다.
추가로 package.json과 실제 패키지 내용의 일관성을 검증하는 npx publint, 의존성 취약점을 스캔하는 npm audit, 배포 전 패키지 보안을 분석하는 Socket Security나 Snyk, 배포 버전 간 크기 이상 증가를 감지하는 패키지 크기 모니터링(Claude Code 사건에서 59.8MB 소스맵은 크기만으로도 명확한 이상 신호였다) 같은 자동화 도구 연동이 권장된다. 배포 파이프라인의 각 단계에서 이 검증을 자동화하면 사람의 실수가 프로덕션에 도달하기 전에 차단하는 방어선을 구축할 수 있다.
@anthropic-ai/claude-code v2.1.88 소스맵 유출 사건은 npm 배포 파이프라인의 작은 설정 실수가 51만 2천 줄의 독점 소스코드를 즉시 공개 상태로 만들 수 있다는 점을 보여준다. 소스맵은 개발자 경험을 향상시키는 중요한 도구이지만, 클로즈드소스 상용 소프트웨어의 배포 패키지에는 절대 포함되어서는 안 된다. package.json의 files 허용 목록 설정, CI 파이프라인에서의 npm pack --dry-run 검증, 소스맵 분리 저장 정책이라는 세 가지 기본 원칙만 지켜도 이 유형의 유출은 충분히 예방할 수 있다. AI 코딩 도구가 사용자 환경에 대한 광범위한 접근 권한을 갖는 시대일수록, 도구 제공자의 배포 보안 수준은 사용자 신뢰의 핵심 기반이 된다.
Sources
- Massive source code leak from Claude Code CLI via accidentally included source maps
- npm package security: Understanding .npmignore and the files field
- Source Maps and Security in Production JavaScript
- JavaScript source map security risks and best practices
- How to protect your npm package from accidental source leaks | Socket Security