13 KB 텍스트 파일이 더 똑똑한 모델을 이겼다: 5가지 Angular 상태 관리 라이브러리를 통한 AI 코드 생성 (codegen) 벤치마킹
요약
5가지 Angular 상태 관리 라이브러리를 대상으로 AI 에이전트의 코드 생성 능력을 벤치마킹한 결과입니다. 모델의 성능은 라이브러리의 설계 품질보다 학습 데이터의 양(corpus)에 더 큰 영향을 받으며, 13KB 정도의 API 요약 파일만으로도 성능 격차를 크게 줄일 수 있음을 보여줍니다.
핵심 포인트
- AI의 코드 생성 정확도는 라이브러리의 인지도와 학습 데이터 양에 비례함
- 신생 라이브러리는 컨텍스트 없이 사용 시 정확도가 급격히 떨어짐
- llms.txt와 같은 작은 API 요약 파일이 AI 성능 향상에 결정적 역할 수행
- 원샷 생성(Cold-shot) 성능은 라이브러리 설계가 아닌 코퍼스 신호임
사전 공개: 저는 테스트된 5가지 라이브러리 중 하나인 SignalTree의 유지 관리자이며, 이 라이브러리는 콜드 런(cold run)에서 가장 낮은 점수를 받았습니다. 따라서 이 글은 "내 것이 얼마나 좋은지 보세요"라는 식의 게시물이 아닙니다. 라이브러리 간의 패턴과 해결책이 매우 흥미로웠기에, Copilot/Cursor/Claude Code를 매일 사용하는 분들에게 이 수치들을 보여드리고 싶었습니다. 전체 테스트 프레임워크는 재현 가능합니다 (한 번의 명령어로 실행 가능, 링크는 하단에 첨부). 저는 이 결과가 맹목적으로 받아들여지기보다 비판적으로 검토되기를 바랍니다.
설정 (Setup)
- 라이브러리 (Libraries): NgRx (classic), NgRx SignalStore, Akita, Elf, SignalTree.
- 에이전트 (Agents): Claude Sonnet 4.6, GPT-5.4, Gemini 3.1 Pro, Perplexity Sonar Pro, Claude Haiku 4.5, GPT-5.4-mini.
- 8가지 프롬프트 (8 prompts): 카운터 (counter), 페이지네이션된 사용자 (paginated users), 디바운스 검색 (debounced search), 파생된 합계 (derived totals), 로그인 양식 (login form), 실행 취소/다시 실행 (undo/redo), 깊게 중첩된 상태 (deep nested state), 멀티 마커 에디터 (multi-marker editor).
- 5개 라이브러리 × 6개 에이전트 × 3가지 프라이밍 모드 (priming modes) = 720개 셀 (cells). Temperature 0. 라이브러리당 동일한 프롬프트 텍스트 사용 (라이브러리 이름만 교체).
- 세 가지 직교 검사 (orthogonal checks)를 통해 점수 산정: 관용적 패턴 일치 (idiomatic-pattern match), 임포트 해결 (import resolution, 모든 임포트가 실제 패키지로 해결되는지 여부), 메서드 유효성 (method validity, 호출된 메서드가 API에 실제로 존재하는지 여부).
이 테스트가 측정하는 것: 원샷 생성 (one-shot generation). 에이전트가 프롬프트를 받고 파일을 반환하면, 우리는 그 점수를 매깁니다. 모델이 자신의 오류를 확인하고 다시 시도할 수 있는 Cursor/Copilot의 채팅 기반의 실제 대화형 사용은 다른 설정이며, 그곳에서의 성능 향상은 더 클 수도, 더 작을 수도 있습니다. 이것은 콜드 샷 (cold-shot) 케이스입니다.
결과 1: 콜드 정확도 (cold accuracy)는 기본적으로 해당 라이브러리가 학습 데이터에 얼마나 포함되어 있는지를 추적함
어떠한 컨텍스트도 제공하지 않고, 단지 "라이브러리 X로 이것을 작성하라"고만 명령했을 때:
| 라이브러리 (Library) | Cold 점수 |
|---|---|
| Akita | 94% |
| ... | |
| 수천 개의 블로그 포스트와 Stack Overflow 답변이 쌓여 수년간 존재해 온 라이브러리들은 90점대의 점수를 기록했습니다. 세트 중 가장 신생이거나 규모가 작은 라이브러리는 약 49%를 기록했습니다. 이 격차는 실제 품질 신호가 아니라, 코퍼스 (corpus) 신호입니다. 모델들이 SignalTree보다 Akita를 단순히 수십 배 더 많이 보았을 뿐입니다. AI 어시스턴트가 아무런 정보 없이(cold) 라이브러리를 얼마나 잘 작성하는지로 라이브러리를 판단할 때마다 이 점을 명심할 가치가 있습니다. 당신은 부분적으로 라이브러리의 설계가 아닌 연령을 측정하고 있는 것입니다. |
발견 2: 단 하나의 검색 가능한 컨텍스트 파일이 그 격차의 대부분을 메운다
나는 npm 패키지 내부에 약 13.5 KB의 llms.txt (일반 텍스트 형식의 API 요약)를 포함하여 배포했고, 이를 컨텍스트(context)에 포함하여 다시 실행했습니다:
| 모드 (Mode) | SignalTree 점수 |
|---|---|
| Cold | 49% |
| ... | |
| 작은 파일 하나로 42%포인트가 상승했습니다. 이는 가장 덜 알려진 라이브러리를 이미 잘 자리 잡은 라이브러리들의 범위까지 끌어올리기에 충분한 수치였습니다. 내가 예상하지 못했던 두 가지가 있습니다: |
- 컨텍스트가 더 많아지면 오히려 악화된다. 두 번째 문서를 추가하자 정확도가 퇴보했습니다. 추가된 소스가 신호를 강화하기보다는 희석하는 것으로 보이며, 특히 Gemini의 경우 노이즈에 과도하게 반응(over-indexing)했습니다. 어느 지점을 넘어서면 도움이 되는 것이 아니라 해가 됩니다.
- 라이브러리 간에 정보가 섞였다. _하나_의 라이브러리 컨텍스트를 로드하는 것이 다른 라이브러리들의 성능을 떨어뜨렸습니다. SignalTree의 API를 컨텍스트로 가진 모델들이 이를 Akita나 Elf에 대한 답변에 교차 오염(cross-pollinating)시키기 시작했습니다:
| 라이브러리 (Library) | Cold | SignalTree의 컨텍스트 로드 시 |
|---|---|---|
| SignalTree | 49 | 91 |
| ... | ||
| 실질적인 교훈: 컨텍스트가 많다고 해서 더 좋은 것은 아니다. 약 15 KB를 넘어서자 수치는 올라가는 것이 아니라 내려갔습니다. 만약 당신이 덜 흔한 라이브러리를 유지 관리하거나 사용한다면, |
실패는 무작위가 아니었습니다. 에이전트(Agents)들은 존재하지 않는 메서드를 계속 호출했고, 그 패턴은 바로 저 자신의 일관성 부족을 정면으로 가리키고 있었습니다. 저는 API 전반에 걸쳐 술어 접근자(predicate accessors)를 두 가지 다른 방식으로 명명했습니다:
// 일부 마커는 is- 접두사를 사용했습니다
saveStatus.isLoading()
users.isEmpty()
...
isLoading()을 학습한 에이전트는 존재하지도 않는 isDirty()를 자신 있게 시도하곤 했습니다. 이것은 AI의 실패가 아니라, AI의 가면을 쓴 인간의 실패입니다. 문서를 읽는 어떤 개발자라도 똑같은 벽에 부딪힐 것입니다. 다만 그들은 더 조용히 실패하며 자신을 탓할 뿐입니다. 저는 이를 기본 이름(FormControl.dirty/.valid와 일치하도록)으로 표준화하고, 기존 이름들은 지원 중단 예정(deprecated) 별칭으로 유지한 채 배포했습니다.
일반화할 수 있는 교훈이자, 제가 이 내용을 변경 로그(changelog)에 묻어두지 않고 글로 쓸 가치가 있다고 생각하는 이유는 다음과 같습니다: 모델이 혼동할 수 있는 API 표면(API surface)은 대개 인간도 혼동할 수 있는 표면입니다. 코드 생성(Codegen) 정확도는 놀랍게도 명명 일관성(naming consistency)을 나타내는 매우 좋은 대리 지표(proxy)이며, 측정 비용도 저렴합니다.
제가 이 문제를 공격한다면
결점이 발견되게 두느니 차라리 제가 먼저 나열하는 편이 낫기에, 제가 가장 먼저 꼽을 세 가지는 다음과 같습니다:
- 네 가지 방식의 이해상충 (Conflict of interest). 저는 다섯 가지 라이브러리 중 하나를 직접 만들었고, 해당 라이브러리의 컨텍스트 파일 (context file)을 작성했으며, 8개의 프롬프트 (prompts)를 선정했고, 채점 기준 (scoring rubric)까지 직접 작성했습니다. 이는 제가 제 자신에게 유리하게 조작할 수 있었던 네 가지 레버 (levers)가 존재함을 의미하므로, 절대적인 백분율 수치를 그대로 신뢰하지 마십시오. 제가 제 편향을 위해 조작할 수 없었던 유일한 수치는 제 라이브러리가 콜드 런 (cold run)에서 가장 낮은 점수를 기록했다는 점입니다. 그리고 제가 실제로 옹호하고자 하는 것은 정확한 수치가 아니라, 상대적인 변화량 (cold→primed deltas, 라이브러리 간의 간섭/bleed)입니다.
- 샘플 크기 / "온도(temperature) 0 ≠ 결정론적 (deterministic)." 이 테스트는 셀(cell)당 한 번의 실행으로 이루어지며, 호스팅된 API에서 temperature 0 설정은 진정으로 결정론적이지 않습니다. 즉, 동일한 프롬프트라도 실행할 때마다 결과가 미세하게 달라질 수 있습니다. 따라서 개별 셀의 결과는 약간의 노이즈 (noise)가 포함되어 있다고 간주하십시오. 이는 경향성을 보여주는 지표일 뿐, 학술적 출판 수준의 데이터는 아닙니다. 제가 지적하는 효과들(약 40%p의 상승, 콜드 런에서 기존 라이브러리들이 90점대에 밀집하는 현상)은 그러한 노이즈를 뚫고 나타날 만큼 충분히 크지만, 두 셀 사이의 3점 차이는 큰 의미가 없습니다.
- 채점 기준이 구조적으로 더 작은 API에 유리함. 세 가지 체크 항목 중 하나는 "호출된 메서드 (method)가 존재하는가"입니다. 전체 메서드 수가 적은 라이브러리는 단순히 정답을 맞히기가 더 쉽습니다. 제가 만든 트리 구조 (tree-shaped) 라이브러리는 예를 들어 클래식한 NgRx보다 표면적 (surface)이 더 작습니다. 따라서 프라이밍 (priming)을 통한 성능 향상의 일부는 "설계가 더 잘 되어서"가 아니라, "틀릴 확률이 적어서" 발생했을 가능성이 있습니다. 이는 완전히 통제된 변수가 아니라 실제적인 혼란 변수 (confound)라고 생각합니다.
그리고 결함이라기보다는 "당연한" 것에 가까운 점이 하나 있습니다. 콜드 점수 $\approx$ 학습 데이터 양 (training-data volume)은 거의 발견이라 할 수 없는 결과입니다. 이는 입 밖으로 내뱉는 순간 거의 자명한 사실 (truism)에 가깝습니다. 유일하게 약간은 덜 자명한 부분은, 검색 가능한 파일 (retrievable file) 하나가 수년간의 코퍼스 (corpus) 존재를 얼마나 저렴하게 대체할 수 있는가 하는 점입니다.
직접 재현해보기
OpenRouter 키 하나와 약 15달러, 그리고 30분이면 충분합니다:
git clone https://github.com/JBorgia/signaltree
export OPENROUTER_API_KEY=sk-or-...
node scripts/ai-codegen-benchmark/runner.mjs
프롬프트 (Prompts, YAML), 채점 기준 (scoring rubric), 어댑터 (adapters), 그리고 셀별 결과 (per-cell results)는 모두 scripts/ai-codegen-benchmark/에 위치합니다. 프롬프트와 채점 기준은 가장 의견이 갈릴 수 있는 부분입니다. 만약 특정 라이브러리에 불공정하다고 느껴지는 부분을 발견하신다면, 그것이 제가 받을 수 있는 가장 유용한 피드백이 될 것입니다.
링크 (Links)
- 벤치마크 소스 + 에이전트별 전체 결과: https://github.com/JBorgia/signaltree/tree/main/scripts/ai-codegen-benchmark
- 리포지토리 (Repo): https://github.com/JBorgia/signaltree
- 데모 + 문서 (Demo + docs): https://jborgia.github.io/signaltree/
의견을 듣고 싶습니다
Copilot / Cursor / Claude Code를 매일 사용하시는 분들께 여쭙고 싶습니다. 특정 라이브러리에 대해 생성된 코드가 좋지 않을 때, 실제로 무엇이 문제를 해결해 주었나요? 커스텀 규칙 파일 (custom rules file), 붙여넣은 문서 (pasted docs), MCP 서버 (MCP server), 혹은 다른 무엇인가요? 저는 특히 "작은 컨텍스트 파일 (small context file)을 전달하는 것"이라는 결과가 제 환경 외에서도 유효한지, 아니면 대화형 피드백 (interactive back-and-forth)이 이를 무의미하게 만드는지 궁금합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기