LLM에게 코딩을 시킬 때 반드시 알아야 할 4가지 원칙
Andrej Karpathy가 짚은 LLM 코딩의 핵심 문제들과, 이를 CLAUDE.md 한 장으로 해결하는 방법
Claude Code로 SI 프로젝트를 5개씩 돌리다 보면, LLM이 만드는 코드에서 반복적으로 같은 문제가 나온다. Andrej Karpathy가 트위터에서 정확히 이 문제를 짚었다.
“모델이 사용자 대신 잘못된 가정을 하고 확인 없이 진행한다. 혼란을 관리하지 않고, 명확화를 요청하지 않고, 불일치를 표면에 드러내지 않는다.”
“코드와 API를 과도하게 복잡하게 만들고, 추상화를 부풀리고… 100줄이면 될 것을 1000줄로 구현한다.”
읽으면서 고개를 끄덕였다. 내가 매일 겪는 일이다.
문제를 정리하면 3가지다
1. 잘못된 가정을 기반으로 돌진한다
“모터 제어 코드 짜줘”라고 하면, CAN인지 EtherCAT인지 확인 안 하고 임의로 골라서 200줄을 써내린다. 나중에 “아 EtherCAT이었어요”라고 하면 전체를 다시 짠다. 처음에 한 줄 물어봤으면 될 일이다.
2. 과잉 엔지니어링을 한다
간단한 PID 제어기를 요청했는데, 플러그인 아키텍처에 팩토리 패턴에 설정 파일 파서까지 달린 500줄짜리 프레임워크가 나온다. 단일 모터 하나 돌리는 건데.
3. 요청하지 않은 코드를 건드린다
버그 하나 수정해달라고 했는데, 옆에 있는 주석을 “개선”하고, import 순서를 바꾸고, 변수명을 리네이밍한다. diff를 보면 실제 수정은 2줄인데 변경 파일이 5개다. 리뷰가 불가능해진다.
Karpathy 원칙 4가지
이 문제들에 대한 해법을 4가지 원칙으로 정리할 수 있다.
원칙 1: 코딩 전에 생각하라
- 가정을 명시적으로 밝혀라. 확실하지 않으면 물어봐라.
- 여러 해석이 가능하면 전부 제시하라. 혼자 골라서 진행하지 마라.
- 더 단순한 방법이 있으면 말해라. 필요하면 반박해라.
- 헷갈리면 멈춰라. 뭐가 헷갈리는지 말하고 물어봐라.
핵심은 **“모르면 모른다고 해라”**다. LLM이 가장 잘 못하는 게 “나 잘 모르겠는데요”라고 말하는 것이다. 항상 뭔가를 자신 있게 내놓는데, 그게 문제다.
원칙 2: 단순함을 우선하라
- 요청받은 것 이상을 만들지 마라.
- 한 번만 쓰는 코드에 추상화를 넣지 마라.
- 요청하지 않은 “유연성”이나 “설정 가능성”을 넣지 마라.
- 불가능한 시나리오에 대한 에러 핸들링을 넣지 마라.
- 200줄을 50줄로 줄일 수 있으면, 다시 써라.
검증 기준: “시니어 엔지니어가 보고 ‘이거 오버 아닌가?‘라고 할 것 같으면, 오버다.”
실제로 LLM에게 “간단하게 짜줘”라고 말해도 잘 안 된다. CLAUDE.md에 원칙으로 박아넣어야 비로소 따른다.
원칙 3: 외과적으로 수정하라
기존 코드를 편집할 때:
- 인접한 코드, 주석, 포맷팅을 “개선”하지 마라.
- 안 부서진 것을 리팩터링하지 마라.
- 기존 스타일을 따라라. 네가 다르게 하고 싶어도.
- 관련 없는 죽은 코드를 발견하면 언급만 하고 삭제하지 마라.
- 네 변경으로 인해 생긴 고아(미사용 import, 변수, 함수)만 정리하라.
검증 기준: 변경된 모든 줄이 사용자의 요청에 직접 연결되어야 한다.
이 원칙이 없으면 diff가 오염된다. SI 프로젝트에서 고객에게 PR을 보여줘야 할 때, “이건 AI가 자기 마음대로 바꾼 겁니다”라고 설명할 수는 없다.
원칙 4: 목표 지향으로 실행하라
명령형 작업을 검증 가능한 목표로 바꿔라:
| 대신에… | 이렇게 변환 |
|---|---|
| ”유효성 검사 추가해줘” | 잘못된 입력에 대한 테스트를 먼저 짜고, 통과시켜라 |
| ”버그 수정해줘” | 버그를 재현하는 테스트를 짜고, 통과시켜라 |
| ”리팩터링해줘” | 리팩터링 전후로 테스트가 통과하는지 확인해라 |
복잡한 작업이면 계획을 먼저 세워라:
1. [단계] → 검증: [확인 방법]
2. [단계] → 검증: [확인 방법]
3. [단계] → 검증: [확인 방법]
Karpathy의 핵심 통찰이 여기 있다:
“LLM은 특정 목표를 충족할 때까지 반복하는 데 매우 뛰어나다… 뭘 할지 말해주지 말고, 성공 기준을 주고 돌려라.”
실제 적용: CLAUDE.md에 넣기
이 원칙들은 대화 중에 “이렇게 해줘”라고 말해서는 잘 안 먹힌다. CLAUDE.md에 넣어야 한다. 대화 시작 시점에 시스템 컨텍스트로 로드되기 때문에, 매번 말하지 않아도 적용된다.
내가 쓰는 글로벌 ~/.claude/CLAUDE.md에 넣은 버전은 이렇다:
## Coding Guidelines (Karpathy Principles)
### 1. Think Before Coding
- State assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them.
- If a simpler approach exists, say so.
- If something is unclear, stop and ask.
### 2. Simplicity First
- No features beyond what was asked.
- No abstractions for single-use code.
- No speculative "flexibility" or "configurability".
- If 200 lines could be 50, rewrite it.
### 3. Surgical Changes
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style.
- Remove only orphans YOUR changes created.
- Test: every changed line traces to the user's request.
### 4. Goal-Driven Execution
- Transform tasks into verifiable goals.
- "Fix the bug" → Write a reproducing test, then pass it.
- For multi-step tasks, state a plan with verification steps.
글로벌 CLAUDE.md에 넣으면 모든 프로젝트에 자동 적용된다. 프로젝트별 CLAUDE.md는 프로젝트 고유 규칙만 담으면 된다.
효과가 있는가
솔직히 말하면, 극적인 변화는 아니다. 하지만 체감되는 개선이 있다.
확실히 좋아진 것:
- “이 부분이 불확실한데, A와 B 중 어떤 것을 의도하셨나요?”라는 질문이 늘었다. 예전에는 그냥 A를 골라서 달렸다.
- diff가 깔끔해졌다. 요청하지 않은 주석 변경이나 import 정렬이 줄었다.
한계:
- 컨텍스트가 길어지면 초반 지시사항의 영향력이 약해진다. 대화가 길어질수록 원칙을 잊는다.
- “단순하게 짜라”는 지시에도 복잡하게 짜는 경향은 완전히 사라지지 않는다. 줄어들 뿐이다.
- 원칙 4(목표 지향 실행)는 사용자가 성공 기준을 명확히 줘야 효과가 있다. LLM이 알아서 성공 기준을 잘 세우지는 않는다.
결론
LLM 코딩 도구를 쓰면서 “왜 자꾸 쓸데없는 코드를 만들지?”라고 느꼈다면, 그건 당연한 것이다. Karpathy가 짚은 문제들은 현재 LLM의 구조적 한계다. 완전히 없앨 수는 없지만, 가드레일을 세울 수는 있다.
4가지만 기억하면 된다:
- 코딩 전에 생각하라 — 가정을 숨기지 말고, 모르면 물어봐라
- 단순함을 우선하라 — 요청한 것만 만들어라
- 외과적으로 수정하라 — 요청과 관련된 줄만 바꿔라
- 목표 지향으로 실행하라 — “해줘”가 아니라 성공 기준을 줘라
CLAUDE.md 한 장에 넣으면 된다. 10분이면 세팅이 끝나고, 그 이후로는 자동 적용된다.
원본 저장소: forrestchang/andrej-karpathy-skills
← All posts