대규모 코드베이스로 LLM 확장하기
(blog.kierangill.xyz)- 대규모 코드베이스에서 LLM을 효과적으로 활용하기 위해서는 ‘가이드(guide)’와 ‘감독(oversight)’에 대한 투자가 핵심임
- 가이드는 문맥과 환경을 제공해 LLM이 더 나은 선택을 하도록 돕고, 감독은 결과를 검증하고 방향을 제시하는 역할을 수행함
- 프롬프트 라이브러리를 구축해 코드베이스의 규칙, 문서, 모범 사례를 LLM이 이해할 수 있도록 하는 것이 중요함
- 기술 부채 관리와 코드 구조의 단순성, 모듈화, 일관성이 LLM의 코드 이해력과 생산성 향상에 직접적으로 연결됨
- 자동화된 감독과 검증 체계를 통해 LLM이 안전하고 일관된 코드를 생성하도록 보조하는 것이 장기적 확장성의 핵심임
LLM 확장을 위한 핵심 개념
- LLM을 대규모 코드베이스에 적용하는 방법은 아직 정립되지 않았으나, 가이드와 감독에 대한 투자가 가장 효과적인 접근으로 제시됨
- 가이드(Guidance) 는 LLM이 올바른 선택을 하도록 돕는 문맥과 환경을 의미하며, 감독(Oversight) 은 생성된 결과를 검증하고 방향을 조정하는 역할을 담당함
가이드에 대한 투자
- LLM이 한 번의 시도로 고품질 코드를 생성하는 ‘원샷(one-shotting)’ 을 달성하려면, 명확한 가이드가 필요함
- 반대로, 결과가 부적절해 수동 수정이 필요한 경우는 재작업(rework) 으로 비효율적임
- LLM은 코드 내의 모든 선택(변수명, 함수 구조, 기술 스택 등)을 생성하므로, 프롬프트가 비즈니스 요구만 담고 나머지는 추론 가능하거나 인코딩된 상태가 이상적임
프롬프트 라이브러리 구축
-
프롬프트 라이브러리는 코드베이스의 문서, 모범 사례, 구조적 맵 등을 포함한 LLM용 문맥 집합임
- LLM의 출력이 빗나갈 때마다 “무엇을 명확히 해야 했는가”를 검토하고 이를 라이브러리에 추가
- 포괄성과 간결성의 균형이 중요함
- 예시에서는
@prompts/How_To_Write_Views.md,@prompts/The_API_File.md등 문서를 LLM에 제공해 기능 개발을 안내함 - 프롬프트가 충분히 구체적이어야 하지만, 생성된 코드의 모든 줄을 검토해야 함
환경과 코드 품질
-
기술 부채(technical debt) 가 많은 코드베이스는 LLM 활용 효율을 저하시킴
- Meta의 사례에서 기술 부채로 인해 자동화 목표 달성이 어려웠다고 언급됨
- 깨끗한 코드, 모듈화, 명확한 네이밍, 단순한 구조가 LLM의 이해도와 정확도를 높임
- Django 예시에서는 각 앱의 진입점을
_api.py파일로 두어 LLM이 필요한 기능을 빠르게 찾을 수 있도록 구조화함- 예:
visit_api.handoff_to_doctor(user)형태로 외부 접근을 단일화 -
_api패턴을 프롬프트 라이브러리에 명시해 LLM이 올바른 위치를 참조하도록 유도
- 예:
감독에 대한 투자
- LLM 자동화는 엔지니어를 대체하기보다 팀을 강화하는 방향으로 접근해야 함
- 감독은 팀, 정렬(alignment), 워크플로우에 대한 투자로 이어짐
- 팀 차원에서는 설계 역량 향상이 중요하며, 이는 아키텍처 품질로 연결됨
-
디자인 역량 강화 방법으로는 책·블로그·코드 읽기, 마스터워크 복제, 직접 구현 연습 등이 제시됨
- 예: TLDraw, SerenityOS Jakt 등의 코드 분석을 통해 설계 감각을 확장
자동화된 감독
- 일부 설계 검증은 프로그램적으로 자동화 가능함
- 예: 타입 오류나 규칙 위반을 환경에서 즉시 피드백
-
‘안전성(safety)’ 은 추상화를 보호하는 것임
- Pierce의 정의에 따르면, 안전한 언어는 프로그래머가 의도치 않게 추상화를 깨뜨리지 않도록 보장함
- 예시: Django 앱 간 내부 파일 직접 접근을 금지하는 규칙을 AST 기반 검사 스크립트로 자동화
-
from visit import logic.internal_file형태의 불법 접근을 탐지
-
검증(Verification)
- 설계와 구현 외에도 검증 단계(코드 리뷰, QA) 가 품질 확보에 필수적임
- 작업량이 증가할수록 검토 속도가 병목이 되므로, 다음과 같은 개선 방안이 제시됨
- 개발 환경 없이도 QA 수행 가능하도록 진입 장벽 완화
- 테스트 데이터 생성 등 테스트 작성이 간단한 환경 구축
- 반복되는 PR 피드백을 문서화해 LLM이 일부 리뷰를 자동 수행하도록 지원
- 보안 규칙을 프레임워크 기본값으로 내장
결론 및 추가 관찰
- LLM은 새로운(greenfield) 프로젝트에서 특히 잘 작동함
- 기존 맥락이 없고 일관성 요구가 낮기 때문
- 프로젝트가 커질수록 일관성과 모듈화가 생산성을 좌우함
- 검증된 구성요소를 재사용하는 모듈형 구조가 효율적 개발의 핵심임
Hacker News 의견들
-
모델이 점점 개선되면서 복잡한 코드베이스나 긴 파일도 다룰 수 있게 되었음
그래서 나는 반복적으로 쓰는 간단한 프레임워크 루프를 만들었음- Research: 현재 기능을 설명하게 해서 관련 파일을 컨텍스트에 로드함
- Plan: 새 기능 구현이나 리팩터링의 베스트 프랙티스를 브레인스토밍하게 함. 그 결과를 md 파일로 작성시킴
- Clear: 컨텍스트를 완전히 초기화함. 단순 압축보다 결과가 좋음
- Execute plan: 계획만 다시 로드해 실행하게 함. 필요 시 질문을 반복함
-
Review & test: 다시 컨텍스트를 초기화하고 계획이 잘 반영됐는지 검토시킴. 이때 테스트 코드나 린트, 타입체크 등을 추가함
이 루프를 20~30분 돌리면 꽤 쓸만한 결과가 나옴. 결국 핵심은 컨텍스트 관리와 테스트 피드백 루프를 만드는 일임
- 2025년 12월 기준으로 Sonnet/Opus, GPTCodex 같은 모델은 이미 subagent 탐색 기능이 내장되어 있음
“explore”라는 키워드로 탐색을 시작하면 별도의 계획 작성이나 컨텍스트 초기화 없이도 Research가 가능함
다만 코드 언어를 C나 Python으로 가정하는 경향이 있어, 상태를 객체로 캡슐화하지 않고 함수 다섯 개로 나누는 식의 비구조적 코드를 생성하곤 함
또 claude는 종종 CLAUDE.md를 무시하므로, 먼저 그 파일을 읽게 한 뒤 “explore”를 시키면 안정적임
최신 모델들은 쓸모없는 컨텍스트를 잘 버리지만, 예전 모델은 여전히 계획 문서 기반 접근이 더 나은 경우가 있음 - 모델이 기본적인 추론 능력에서 실패하면 어떤 워크플로도 소용없음
계획과 가이드라인을 정반대로 수행하거나, 같은 문장을 다시 읽고도 반대 결론을 내리는 경우가 많았음
한동안 LLM을 중심으로 한 프로세스를 만들 수 있다고 믿었지만, 지금은 확신이 줄었음
모델이 “좋은 상태”일 때는 괜찮지만, 그 상태로 만드는 게 여전히 운에 좌우됨 - 나도 비슷한 접근을 하고 있음. HumanLayer의 Advanced Context Engineering 가이드라인에서 영감을 받았음
/research_codebase,/create_plan,/implement_plan같은 커스텀 명령어를 Claude Code에 추가했음
세심하게 리뷰하고 수정하면 매우 잘 작동하지만, 팀 전체로 확산되지는 못했음 - 나는 이런 절차를 거의 안 씀. GitHub Copilot과 Claude Sonnet 4.5를 쓰면 명확한 지시만으로도 충분히 잘 처리함
완전히 새로운 기능을 만들 때만 컨텍스트를 초기화함. Codespaces에서는 괜찮지만 Tasks 기능은 거의 쓸모가 없음
큰 작업을 시키면 엉뚱한 방향으로 가버리기도 해서 항상 주시해야 함 - 나도 거의 같은 워크플로를 씀. 계획 md 파일을 저장소에 남겨두고 새 기능 추가 시 참조하게 함
컨텍스트 리프라이밍에도 유용해서 자주 활용함
-
프롬프트 라이브러리를 유용하게 만들려면 반복적 개선이 필요함
LLM이 살짝 빗나갈 때마다 “무엇을 더 명확히 해야 했을까?”를 스스로 묻고, 그 답을 프롬프트에 추가함
단순히 엔터만 누르거나 자동 승인하면 토큰만 낭비함. 대신 LLM이 어디서 막히는지 관찰하고 CLAUDE.md에 짧게 기록함
컨텍스트 파일이 커지면 작업 유형별로 분리함
내 주요 사용 사례는 코드베이스 탐색과 실행 경로 추적, 필요한 파일 요약 제공임. 질문 유형별로 결과 전달 방식을 명시해두면 훨씬 효율적임- “무엇을 명확히 해야 했을까?”보다 더 나은 방법은 LLM에게 스스로 묻게 하는 것임
나는 이를 “Student Pattern (Fresh Eyes)”라 부름. 서브에이전트가 초보자의 시선으로 문서나 코드를 읽고 혼란점·모순·누락 정보를 찾아내는 방식임
개발자가 놓치는 암묵적 지식을 잡아내는 데 탁월함. 새 문서 검토나 프롬프트 평가 전 단계로 유용함 - 나도 그렇게 하지만, Claude Code는 종종 프롬프트나 문서 무시가 심함
CLAUDE.md를 읽으라고 여러 번 지시해도 무시할 때가 많고, 세션 시작 직후에도 랜덤하게 빠짐
문서와 명령을 다 준비해도 여전히 “잊어버리는” 경우가 많음
- “무엇을 명확히 해야 했을까?”보다 더 나은 방법은 LLM에게 스스로 묻게 하는 것임
-
큰 코드베이스를 에이전트 친화적으로 구조화하는 실험을 하고 있음
프로젝트를 nix flakes 기반 방향성 그래프로 분해해 각 노드가 독립된 개발 환경을 가짐
Claude Code를 flake devshell에서 실행하면 해당 범위만 인식해 컨텍스트 과부하를 막을 수 있음
flakes 간 입출력으로 협업하게 하고, 서로에게 기능 요청과 테스트를 주고받게 함
이런 컨텍스트 분할이 토큰 폭증 문제를 줄이는 핵심이라 생각함- 이런 접근이 흥미로움. 나도 소프트웨어 구조와 속성의 상관관계를 고민 중임
단순히 기존 워크플로를 개선하는 대신, LLM이 쉽게 확장할 수 있는 구조를 새로 설계해야 함
“테스트 가능성·확장성·보안성” 같은 속성과 코드 구조의 관계를 탐구하고 있음 - 컨텍스트 분할뿐 아니라 재현성과 의존성 고정 측면에서도 흥미로움
나도 Docker 기반 프로젝트에서 비슷한 실험을 해봤는데, 이를 자동화해주는 도구가 있으면 좋겠음
- 이런 접근이 흥미로움. 나도 소프트웨어 구조와 속성의 상관관계를 고민 중임
-
LLM은 내가 잘 모르는 주제는 훌륭히 설명하지만, 전문 분야에서는 자신감 있게 틀림
- 이건 Gell-Mann Amnesia 효과 같음
- 주제에 따라 편차가 큼. 나는 웹 분야에서는 정확했지만, Hare 같은 희귀 언어에서는 엉망이었음
- 우리가 모르는 분야에서는 단순한 질문을 하고, 아는 분야에서는 훨씬 어려운 질문을 하니까 그렇게 느껴지는 것 같음
- 사실은 우리가 모르는 분야에서는 헛소리를 구분 못 하는 것일 수도 있음
-
나는 다른 관점을 가짐. LLM 성능을 높이는 가장 좋은 방법은 코드베이스 자체에 의미를 내장하는 것임
즉, DDD(Domain-Driven Design)처럼 구조화된 코드가 LLM에게도 이해하기 쉬움
복잡한 코드를 도구로 억지로 다루려는 건 돈 낭비임
결국 LLM이 증명한 건 “언어와 의미”를 강조한 DDD 철학이 옳았다는 사실임
관련 글: DDD & the Simplicity Gospel- 나도 두 개의 대형 코드베이스를 관리하는데, 구조화된 쪽은 LLM이 훨씬 잘 이해함
-
“LLM은 왜 그린필드 프로젝트에서 잘 작동하나?”라는 질문에, 나는 반대 경험을 함
코드베이스에 패턴이 2~3번 반복되면 LLM이 그걸 학습해 일관성 있게 복제함
하지만 “일관성”이 곧 “품질”은 아님. 기준 없이 일관성만 추구하면 유지 불가능한 코드가 됨 -
“엔지니어가 이해 못하는 코드는 LLM도 이해 못한다”는 말은 맞지만, 그 반대는 성립하지 않음
인간은 이해하지만 에이전트는 못하는 경우가 많음
코드베이스를 인간보다 에이전트가 이해하기 쉽게 만드는 게 더 어려움
“피드백을 컴퓨터로 옮기면 원샷 성공률이 높아진다”는 주장도 있지만, 이는 P=NP를 주장하는 것과 비슷함
검증이 쉽다고 해서 해답 찾기가 쉬운 건 아님
ATS나 Idris 같은 언어는 정확성 증명을 코드와 함께 작성할 수 있음
만약 그 주장이 맞다면, 이런 언어에서 LLM이 최고의 성능을 보여야 함
하지만 현실은 그렇지 않음. 결국 지금은 모델 개선을 기다리는 편이 낫다는 생각임 -
이런 문제들 때문에 나는 의견이 강한 프레임워크가 AI 코딩 생산성을 높일 거라 봄
프레임워크의 규칙을 LLM이 이미 알고 있으니 별도의 가이드라인이 필요 없음- 160만 라인짜리 코드베이스에서 일하는데, 패턴 불일치가 심한 곳에서는 LLM이 거의 쓸모없었음
- 같은 앱을 여러 언어로 만들어봤는데, Rails는 거의 즉시 완성됐고 Bun도 괜찮았음
반면 Go, Rust, Elixir, C#은 훨씬 많은 의존성과 지시가 필요했음
Rust는 결과는 좋았지만 200개 넘는 패키지를 끌어와서 부담이 컸음
-
“Garbage in, garbage out” 원칙은 맞지만, LLM에는 완전히 적용되지 않음
인터넷 전체의 노이즈 데이터로 학습했음에도 꽤 잘 작동함
환각(hallucination) 은 단순한 노이즈보다 부정확한 컨텍스트에서 더 자주 발생함
구조가 엉망인 코드베이스라도 정보량이 많으면 여전히 유용한 컨텍스트를 제공함 -
결국 사람들은 기본 원칙을 다시 배우는 중임
문서화(=프롬프트 라이브러리)와 정돈된 코드 구조가 개발 속도를 높인다는 사실을 새삼 깨닫는 중임- 그래도 그 덕분에 더 많은 사람들이 테스트 작성을 시작한다면 나쁠 건 없다고 생각함