토스증권에서 저는 업무 코드의 99%를 AI로 짰습니다. 처음 보는 코드베이스를 AI로 파악하고, AI로 구현하고, AI로 커밋했습니다. 그 과정에서 하나 확실히 깨달은 게 있어요. AI가 짠 코드를 그대로 믿으면 반드시 사고납니다.
근데 이게 단순히 "코드 리뷰를 잘 하자"라는 얘기가 아니에요. AI와 협업하는 방식 자체를 바꿔야 한다는 거예요. 어떤 결정을 내가 내리고, 어떤 건 AI한테 맡기고, 어떻게 검증하고, 어디서 선을 그을 건지. 이 워크플로우가 없으면 AI가 아무리 똑똑해도 소용없습니다.
이번 주에 Hacker News에서 꽤 뜨거운 글이 하나 올라왔는데요. AI와 함께 고품질 코드를 유지하기 위한 12가지 원칙을 정리한 글입니다. 댓글도 200개 가까이 달렸고, 찬반 논쟁이 꽤 격렬했어요. 읽으면서 "이건 현업에서 진짜 중요한데 아직 한국어로 제대로 된 게 없다"는 생각이 들었습니다. 그래서 오늘은 이 12가지 원칙을 제 현업 경험과 엮어서 깊이 있게 풀어보겠습니다.
단순히 원문을 요약하는 게 아니라, 제가 실제로 토스증권에서 겪은 일들, 그리고 지금 바이브 코딩을 하면서 매일 부딪히는 문제들을 기반으로 재구성했어요. 원문의 12가지를 그대로 나열하면 외우기도 어렵고 실전에 바로 적용하기도 힘들거든요.
바이브 코딩, 근데 품질은?
요즘 "AI로 코드 짜는 법"에 대한 글은 넘쳐나잖아요. 유튜브만 봐도 "AI로 SaaS 3일 만에 만들기", "Cursor로 풀스택 앱 만들기" 같은 콘텐츠가 매주 쏟아집니다. 근데 대부분 "어떻게 코드를 빨리 생성하느냐"에만 집중해요. 정작 중요한 질문은 빠져 있습니다.
"그래서 그 코드 믿을 수 있어?"
AI 코딩 도구를 좀 써본 분들은 다 공감할 거예요:
- AI가 그럴듯한 코드를 뱉었는데, 실제로 돌리면 엣지 케이스에서 터짐
- 테스트 코드를 AI한테 시켰더니, 하드코딩된 값으로 테스트를 통과시킴
- 리뷰 없이 커밋했다가 프로덕션에서 보안 이슈 터짐
- 코드가 점점 불어나면서 AI 컨텍스트 윈도우를 넘어가고, 품질이 바닥을 침
- 어제까지 잘 돌아가던 기능이 AI가 다른 기능을 추가하면서 슬쩍 깨져 있음
이건 AI 문제가 아닙니다. AI와 협업하는 워크플로우가 없는 게 문제예요.
생각해보면 당연한 얘기거든요. 신입 개발자가 팀에 들어오면 온보딩을 시키잖아요. 코딩 컨벤션 알려주고, 아키텍처 설명하고, 코드 리뷰 프로세스 알려주고. AI도 마찬가지예요. 아무 맥락 없이 "이거 만들어줘"라고 하면 교과서적인 답을 내놓습니다. 그리고 교과서적인 답은 현실에서 거의 쓸모가 없어요.
그래서 오늘은 그 워크플로우를 잡아보겠습니다. "AI한테 어떤 맥락을 줘야 하고, 어떻게 검증하고, 어디서 선을 그어야 하는지."
4가지로 정리합니다
원본 글은 12개 원칙을 나열하는데, 솔직히 12개를 다 외울 수는 없잖아요. 실무에서 "아, 원칙 7번이 뭐였지?" 하면서 문서를 찾아보는 사람은 없습니다. 그래서 제가 실무 기준으로 4가지 카테고리로 묶었어요.
| 카테고리 | 핵심 질문 |
|---|---|
| 방향 설정 | AI가 뭘 만들어야 하는지 알고 있나? |
| 품질 검증 | AI가 만든 걸 어떻게 믿을 수 있나? |
| 보안 관리 | 위험한 코드를 어떻게 관리하나? |
| 복잡도 통제 | AI가 폭주하지 않게 하려면? |
이 4가지만 기억하면 됩니다. 각 카테고리 안에서 구체적으로 뭘 해야 하는지 하나씩 들어갈게요.
방향 설정 — AI한테 지도를 줘라
"프롬프트를 잘 쓰려고 하지 마라. 컨텍스트를 설계하라."
이건 제가 강의에서 계속 강조하는 말인데요. 바이브 코딩을 처음 시작하는 분들이 가장 많이 하는 실수가 "프롬프트를 잘 쓰면 좋은 코드가 나온다"고 생각하는 거예요. 프롬프트를 아무리 다듬어봐야, AI가 여러분의 프로젝트 맥락을 모르면 결과물은 제네릭합니다.
원본 글에서도 같은 이야기를 해요. 여러분이 내리지 않은 결정은 AI가 대신 내린다고요. 근데 AI의 결정은 대부분 "교과서적"입니다.
실제로 제가 겪은 일을 하나 말씀드릴게요. AI한테 "사용자 인증 기능 만들어줘"라고 했더니 JWT 기반으로 구현하더라고요. 근데 우리 서비스는 세션 기반이었거든요. AI는 그런 맥락을 모릅니다. "요즘은 JWT가 대세니까 JWT로 하자"라는 게 AI의 판단이에요. 틀린 판단은 아니지만, 우리 상황에는 안 맞는 판단이죠.
이게 인증 같은 큰 결정이면 금방 알아차리는데, 문제는 작은 결정들이에요. 에러 처리를 try-catch로 할지 Result 패턴으로 할지, 상태 관리를 어디서 할지, API 응답 포맷을 어떻게 할지. 이런 작은 결정들을 AI가 자기 맘대로 내리면, 나중에 코드베이스가 일관성 없는 누더기가 됩니다.
그래서 중요한 결정은 미리 내려두고 문서화해야 해요. 아키텍처, 인터페이스, 데이터 구조, 코딩 컨벤션. 이런 것들이요.
CLAUDE.md가 중요한 이유
Claude Code를 쓰신다면 CLAUDE.md를 이미 알고 계실 거예요. 프로젝트 루트에 넣어두면 AI가 매번 이 파일을 읽고 시작합니다. 원본 글에서도 이걸 직접 언급해요.
근데 여기서 함정이 하나 있어요. CLAUDE.md를 너무 길게 쓰면 AI가 무시합니다. 진짜예요. 저도 처음에 CLAUDE.md에 온갖 규칙을 다 넣었거든요. 코딩 컨벤션, 아키텍처 설명, Git 커밋 메시지 규칙, 테스트 작성법... A4 3장 분량으로 써놨는데, AI가 절반은 안 지키더라고요.
CLAUDE.md는 짧게 유지하세요. 핵심 제약조건만 담으세요.
- "이 프로젝트는 DB 없이 JSON 파일로 저장한다"
- "에러는 Result 패턴으로 처리한다"
- "테스트는 Vitest를 쓴다"
- "컴포넌트 파일명은 PascalCase, 유틸 함수는 camelCase"
이 정도면 충분해요. 나머지는 상황에 따라 프롬프트로 보충하면 됩니다. CLAUDE.md에 모든 걸 담으려고 하면 오히려 역효과예요.
문서화의 ROI가 완전히 달라졌다
여기서 좀 더 넓은 이야기를 해볼게요. Hacker News 댓글에서 정말 재밌는 이야기가 나왔거든요. 한 시니어 개발자가 이런 말을 했어요.
"리드 포지션을 맡았을 때, 티켓을 매우 상세히 작성했다. 근데 관리층은 '시간 낭비'라며 반대했고, 결국 그 습관을 잃었다. 지금은 오히려 그보다 더 정교한 명세를 더 빠르게 쓰라고 요구받고 있다."
이 댓글에 공감이 엄청 많이 달렸어요. 개발자라면 다 공감하실 거예요. 과거에는 문서를 써도 아무도 안 읽었잖아요. Confluence에 열심히 써놔도 6개월 후에 보면 아무도 업데이트 안 해놓고, 현실과 동떨어진 문서만 남아있고. 그래서 "문서 안 쓰는 게 더 효율적이다"라는 문화가 자리잡은 거죠.
근데 AI는 다 읽습니다. 이게 핵심이에요. 여러분이 CLAUDE.md에 쓴 한 줄, 코드에 달아놓은 주석 한 줄, 설계 문서의 제약조건 한 줄. AI는 이걸 전부 읽고 코드에 반영해요. 문서화의 ROI가 근본적으로 달라진 겁니다.
과거: 문서 작성 → 아무도 안 읽음 → 시간 낭비 현재: 문서 작성 → AI가 전부 읽음 → 코드 품질 직결
이건 단순한 도구 변화가 아니에요. 개발 문화의 변화예요. "문서화가 선택이 아니라 필수가 됐다"는 거죠.
품질 검증 — AI가 만든 걸 AI로 검증하라
여기가 오늘 글에서 가장 중요한 부분이에요. 제가 현업에서 가장 많이 공감한 부분이기도 하고, 실전에서 바로 적용했을 때 효과가 가장 큰 부분이기도 합니다.
AI는 치팅합니다 — 진짜로
이건 AI 코딩 해본 사람이면 다 알 거예요. AI한테 "기능 구현하고 테스트도 작성해"라고 시키면, 구현과 테스트를 동시에 작성합니다. 문제는 테스트가 구현에 맞춰져 있다는 거예요. 구현이 잘못돼도 테스트는 통과합니다.
더 심한 경우도 있어요. 제가 실제로 겪은 건데, AI한테 "이 함수가 올바르게 작동하는지 테스트 작성해줘"라고 했더니 이런 코드를 만들더라고요:
test("유저 데이터 저장", () => {
const user = { name: "John", email: "john@test.com" };
const result = saveUser(user);
expect(result.name).toBe("John");
expect(result.email).toBe("john@test.com");
});언뜻 보면 괜찮아 보이죠? 근데 이건 사실상 입력값을 그대로 확인하는 것뿐이에요. 실제로 데이터베이스에 저장됐는지, 서버를 재시작해도 데이터가 남아있는지, 중복 이메일을 넣으면 어떻게 되는지는 전혀 검증하지 않아요. 그냥 "함수를 호출하면 같은 객체가 돌아온다"만 확인하는 거죠.
그리고 더 무서운 건, AI가 테스트를 통과시키기 위해 테스트 코드 자체를 수정하는 경우예요. "이 테스트가 실패하는데 고쳐줘"라고 하면, 구현을 고치는 게 아니라 테스트의 기대값을 바꿔버리는 거예요. 또는 아예 실패하는 테스트를 삭제하고 "테스트 정리했습니다"라고 보고하기도 해요.
그래서 저는 이렇게 합니다. 속성 기반 테스트를 직접 작성하고, AI가 못 건드리게 분리합니다.
// 이렇게 쓰면 AI가 치팅하기 쉬움
test("게시물 저장", () => {
const post = createPost({ title: "테스트" });
expect(post.title).toBe("테스트"); // 하드코딩된 값
});
// 이렇게 쓰면 치팅이 어려움
test("게시물을 저장하면 다시 불러올 수 있다", () => {
const post = createPost({ title: randomString() });
savePost(post);
restartServer(); // 서버 재시작 후에도 유지되나?
const loaded = loadPost(post.id);
expect(loaded.title).toBe(post.title);
});
// 더 강력하게: 엣지 케이스도 검증
test("같은 ID로 두 번 저장하면 덮어쓰기된다", () => {
const post = createPost({ title: randomString() });
savePost(post);
const updated = { ...post, title: randomString() };
savePost(updated);
const loaded = loadPost(post.id);
expect(loaded.title).toBe(updated.title);
const allPosts = loadAllPosts();
const matchingPosts = allPosts.filter(p => p.id === post.id);
expect(matchingPosts.length).toBe(1); // 중복 없이 1개만
});차이가 보이시나요? 두 번째/세 번째 테스트는 랜덤 값을 쓰고, 서버 재시작 같은 현실적인 시나리오를 포함하고, 엣지 케이스도 다뤄요. AI가 이런 테스트를 하드코딩으로 통과시키려면 꽤 복잡한 치팅이 필요합니다.
그리고 CLAUDE.md에 이 한 줄을 추가하세요:
- 테스트 파일(/tests/specs/)은 절대 수정하지 마라이거 하나만으로도 AI의 치팅을 상당 부분 막을 수 있어요. AI가 테스트를 수정하려고 하면 "이 파일은 수정 금지입니다"라고 거부하게 되니까요.
Writer/Reviewer 패턴 — 편향을 제거하는 법
제 강의 Chapter 6에서 깊이 다루는 내용인데요. 핵심은 구현한 AI 세션과 다른 세션에서 테스트를 작성하라는 겁니다.
이유는 간단해요. 같은 세션에서 구현과 테스트를 다 하면, AI가 구현의 내부 동작을 이미 알고 있으니까 테스트가 편향됩니다. 비유를 하자면, 시험 문제를 출제한 사람이 채점하는 거랑 같아요. 답을 이미 알고 있으니 "이 정도면 맞겠지" 하고 넘어가게 되거든요.
구체적으로 어떻게 하느냐면요:
세션 1 (Writer): "이 기능을 구현해줘"
- AI가 코드를 작성합니다
- 이 세션에서는 구현에만 집중합니다
세션 2 (Reviewer): "이 인터페이스의 동작을 검증하는 테스트를 작성해줘. 구현 코드는 보지 마. 인터페이스 명세만 보고 테스트를 작성해."
- AI가 구현을 모른 채로 테스트를 작성합니다
- 인터페이스 명세만 보고 "이렇게 동작해야 한다"를 검증합니다
이렇게 하면 두 가지가 좋아요.
첫째, 테스트의 객관성이 올라갑니다. Reviewer AI는 구현을 모르니까, "이 함수는 이름만 보면 이렇게 동작해야 할 것 같은데"라는 관점에서 테스트를 작성해요. Writer AI가 놓친 엣지 케이스를 잡아내는 경우가 많습니다.
둘째, 인터페이스 설계의 문제를 조기에 발견합니다. Reviewer AI가 인터페이스만 보고 테스트를 못 작성하겠다고 하면, 그건 인터페이스 자체가 모호하다는 뜻이에요. 함수 이름이나 파라미터가 직관적이지 않다는 신호죠.
저는 이 패턴을 Git Worktree와 결합해서 씁니다. 터미널 두 개를 열어두고, 하나는 구현용, 하나는 테스트용으로 돌려요. 동시에 작업하면 시간도 절약되고, 편향도 제거되고, 일석이조예요.
리뷰 상태를 추적하는 시스템을 만드세요
AI가 생성한 코드가 늘어날수록 "이 코드는 검증됐나?"를 추적하는 게 필수가 됩니다. 근데 이걸 머릿속으로 하면 안 돼요. 사람 기억력은 한계가 있으니까요. 시스템이 있어야 합니다.
원본 글에서는 AI가 작성한 함수 뒤에 //A 주석을 달아서 "아직 인간이 리뷰 안 했다"를 표시하라고 해요. 심플하지만 효과적이에요.
Hacker News에서 이걸 더 발전시킨 사람이 있었는데요. 커밋마다 코드 라인을 초록(좋음)/노랑(리팩터 필요)/빨강(재작성 필요)으로 표시하는 내부 도구를 만들었다고 해요. TODO refactor 주석보다 훨씬 체계적이죠. 곧 오픈소스로 공개한다고 했는데, 나오면 꼭 써보고 싶어요.
방법은 뭐든 상관없어요. 주석이든, 도구든, 스프레드시트든. 중요한 건 AI가 쓴 코드와 검증된 코드를 구분하는 시스템이 있어야 한다는 거예요. 없으면 코드베이스가 커질수록 "이거 누가 검증했지? 내가 했나? AI가 알아서 한 건가?" 하면서 불안해지거든요.
실전에서 가장 간단하게 시작하는 방법은 이거예요. CLAUDE.md에 이렇게 넣으세요:
- 새로 작성하는 함수에는 // AI-GENERATED 주석을 달 것
- 내가 리뷰한 후에 // REVIEWED 로 변경할 것완벽하진 않지만, 아무것도 안 하는 것보다 100배 낫습니다.
린팅은 AI 시대에 더 중요해졌다
Hacker News에서 가장 실전적인 댓글이 이거였어요. TypeScript 프로젝트에서의 린팅 세팅을 공유한 건데, 꽤 인상적이었습니다.
"tsc, eslint, sonarjs, knip, jscpd, dependency-cruiser, semgrep을 조합해서 pnpm check 하나로 통합했다. pre-commit hook으로 자동 실행시켜서 LLM이 놓친 문제를 방지한다."
각 도구의 역할을 간단히 정리하면요:
| 도구 | 역할 |
|---|---|
| tsc | 타입 체크 |
| eslint | 코드 스타일 + 잠재적 버그 |
| sonarjs | 코드 스멜, 인지 복잡도 |
| knip | 미사용 코드/의존성 탐지 |
| jscpd | 코드 중복 탐지 |
| dependency-cruiser | 의존성 규칙 위반 탐지 |
| semgrep | 보안 취약점 패턴 탐지 |
이걸 전부 pre-commit hook으로 걸면, AI가 생성한 코드가 커밋되기 전에 자동으로 검증됩니다. AI가 아무리 자신감 있게 "구현 완료했습니다"라고 해도, 린트에 걸리면 커밋이 안 되는 거예요.
AI 시대에 린팅이 더 중요해진 이유는 두 가지예요:
- AI가 생성하는 코드량이 많아졌으니, 사람이 다 읽을 수 없어요. 자동 검증의 가치가 올라간 거예요. 하루에 AI가 만들어내는 코드가 수백 줄인데, 그걸 한 줄씩 다 읽을 수는 없잖아요.
- AI 코드와 사람이 쓴 코드가 섞이면 스타일이 들쭉날쭉해져요. AI는 세션마다 미묘하게 다른 스타일로 코드를 작성합니다. 어떤 때는 arrow function을 쓰고, 어떤 때는 function 키워드를 쓰고. 린팅이 이걸 강제로 통일시켜줍니다.
근데 한 가지 꼭 말씀드려야 할 게 있어요. 린팅은 문법적 정확성만 잡아줍니다. 의미적 정확성은 못 잡아요.
Hacker News에서 이걸 정확히 짚은 댓글이 있었거든요:
"린트와 테스트를 모두 통과해도 의도와 다르게 동작하는 코드가 생긴다. 예를 들어 404를 반환해야 하는데 빈 배열을 반환하는 API처럼, 문법적으로는 맞지만 의미적으로 틀린 경우다."
맞아요. 코드에 문법 에러는 없고, 린트도 통과하고, 타입도 맞는데... "유저를 못 찾으면 404를 반환해야 하는데 빈 배열 []을 반환한다." 이런 건 린트가 잡을 수 없어요.
그래서 린팅만으로는 부족합니다. 속성 기반 테스트(치팅 방지) + Writer/Reviewer 패턴(편향 제거) + 린팅(문법 검증) — 이 세 가지가 세트로 돌아가야 해요. 어느 하나만으로는 구멍이 생깁니다.
보안 관리 — 위험한 코드를 명시적으로 표시하라
보안 이야기를 좀 더 깊이 해볼게요. 인증, 권한, 데이터 처리 같은 보안 민감 함수는 특별 관리가 필요합니다. 일반적인 UI 코드나 유틸 함수와는 차원이 다른 거예요.
왜냐하면 보안 코드의 버그는 조용히 터지거든요. UI가 깨지면 바로 알아차리지만, 인증 로직에 구멍이 나면 해킹당하기 전까지 모릅니다. 그리고 AI는 보안 맥락을 잘 모릅니다. "작동하는 코드"를 만드는 건 잘하지만, "안전한 코드"를 만드는 건 다른 문제예요.
제가 추천하는 방법은 CLAUDE.md에 이렇게 넣는 거예요:
## 보안 규칙
- auth/, middleware/auth.ts 파일의 함수는 모두 HIGH-RISK
- 이 파일들을 수정할 때는 반드시 변경 내용을 요약하고 승인을 받을 것
- 보안 함수에는 //HIGH-RISK-REVIEWED 또는 //HIGH-RISK-UNREVIEWED 주석 유지
- 함수를 수정하면 반드시 //HIGH-RISK-UNREVIEWED로 변경
- 보안 함수의 테스트는 /tests/security/ 에 별도 관리하며, 절대 수정 금지핵심은 AI가 보안 코드를 수정하면 자동으로 "미검증" 상태로 돌아가게 하는 것이에요. 개발자가 다시 확인하기 전까지는 검증된 상태로 남아있으면 안 됩니다.
Hacker News에서도 이 부분이 특히 호평을 받았어요. 한 시니어 개발자가 "보안 함수 표시 제안이 이 글에서 가장 실용적인 아이디어"라고 했거든요. 이후에 코드가 변경돼도 문맥을 유지할 수 있으니까요.
실전에서 하나 더 팁을 드리면, 보안 관련 코드는 가능한 한 적게 유지하세요. 인증/인가 로직을 여러 파일에 분산시키지 말고, 한 곳에 모아두세요. 그래야 AI가 "이 영역은 조심해야 한다"는 걸 인식하기도 쉽고, 리뷰하기도 쉬워요.
복잡도 통제 — AI가 폭주하지 않게 하라
불필요한 코드 한 줄이 AI를 망친다
제 강의에서 이런 말을 합니다. "AI 성능이 나쁜 건 AI 탓이 아니다. 네 프로젝트 구조 탓이다."
이게 무슨 뜻이냐면요. 컨텍스트 윈도우는 AI의 RAM이에요. AI가 한 번에 읽을 수 있는 코드의 양에는 한계가 있습니다. 불필요한 코드가 많으면 AI가 정말 봐야 할 코드를 못 봐요. 마치 책상 위에 관련 없는 서류가 잔뜩 쌓여있으면 정작 필요한 문서를 찾기 어려운 것처럼요.
이건 단순한 "깔끔함"의 문제가 아니에요. AI 성능에 직접적으로 영향을 주는 문제예요. 같은 AI, 같은 모델인데 프로젝트 구조가 깔끔한 프로젝트에서는 좋은 코드를 생성하고, 복잡한 프로젝트에서는 엉망인 코드를 생성하는 거예요.
Hacker News에서 40년 경력 개발자가 이런 말을 했는데, 인상 깊었어요:
"지금 내 코드가 커리어 전체 통틀어 가장 만족스럽다. LLM 덕분에 리팩터링 비용이 거의 0이 됐다. 나쁜 코드를 그대로 둘 이유가 없어졌다."
이 댓글에 달린 답글도 좋았어요:
"과거에는 '작동하니까 그냥 배포하자'는 문화가 강했다. 하지만 LLM 덕분에 리팩터링 비용이 거의 0에 가까워졌다. 효율을 높이는 도구를 품질 향상에 쓰는 게 진짜 가치다."
맞아요. 과거에는 리팩터링이 비쌌어요. 시간도 들고, 리스크도 있고, "잘 돌아가는 코드를 왜 건드려?"라는 저항도 있었고. 근데 이제 AI한테 "이 함수를 더 간결하게 리팩터링해줘"라고 하면 몇 초 만에 됩니다. 리팩터링 비용이 극적으로 줄은 거예요.
그러니까 AI를 속도에만 쓰지 마세요. 품질 향상에도 쓰세요. 기능 구현만 시키지 말고, 정기적으로 "이 파일에서 불필요한 코드를 제거해줘", "이 모듈의 복잡도를 낮춰줘"라고 시키세요. 이게 진짜 AI 코딩의 가치예요.
한 번에 다 시키면 망한다
바이브 코딩 초보자들이 가장 많이 하는 실수가 "전체 프로젝트를 한 번에 만들어줘"입니다. AI는 만들어줘요. 500줄, 1000줄씩 코드를 쏟아내죠. 근데 그 결과물은 거의 못 씁니다.
왜냐하면 AI가 한 번에 많은 코드를 생성할수록 일관성이 떨어지거든요. 앞부분에서 정한 패턴을 뒷부분에서 안 지키고, 에러 처리를 어디서는 하고 어디서는 안 하고, 변수명도 들쭉날쭉하고. 그리고 이렇게 생성된 코드를 리뷰하는 것도 거의 불가능해요. 1000줄짜리 코드를 한 줄씩 다 읽으면서 "이게 맞나?" 하는 건 사실상 직접 짜는 것보다 더 힘든 일이에요.
제 강의 Chapter 3의 핵심 메시지가 이거예요:
"한 번에 다 시키면 안 된다. 단계별 구현 + 검증 포인트 + 단계별 커밋."
탐색 → 설계 → 구현 → 커밋. 이 순서를 지키세요.
구체적으로 예를 들어볼게요. 게시판 기능을 만든다고 하면:
- 탐색: "이 프로젝트에서 게시판을 만들려면 어떤 파일을 수정해야 하고, 기존 코드와 어떻게 연결해야 할지 분석해줘" (Plan Mode)
- 설계: "게시판의 데이터 구조와 API 인터페이스를 설계해줘. 코드는 작성하지 마." (Plan Mode)
- 구현 — 1단계: "게시물 목록 조회 API만 만들어줘" → 확인 → 커밋
- 구현 — 2단계: "게시물 작성 API만 만들어줘" → 확인 → 커밋
- 구현 — 3단계: "게시물 수정/삭제 API만 만들어줘" → 확인 → 커밋
각 단계에서 AI의 출력을 확인하고, 문제가 있으면 그 단계에서 잡아야 합니다. 3단계에서 문제를 발견했는데 1단계까지 되돌아가야 하면, 그건 워크플로우가 잘못된 거예요.
원본 글에서도 이걸 경고합니다: "복잡도를 통제하지 못하면 처음부터 다시 해야 한다." 이건 과장이 아니에요. 저도 여러 번 겪었습니다. AI가 만든 코드가 엉키고 엉켜서 도저히 수습이 안 돼서, 결국 git reset --hard 하고 처음부터 다시 한 적이 한두 번이 아니거든요.
프로토타입을 3개 만들어라
반대로, AI 코드 생성이 싸다는 걸 적극적으로 활용할 수도 있어요.
Hacker News에서 Joe Armstrong(Erlang 창시자)의 프로그래밍 철학과 연결된 흥미로운 논의가 있었어요:
"AI가 코드를 싸게 만들어주니, 명세 없이 먼저 시도하고 학습한 뒤 다시 설계하는 방식이 가능해졌다. 이건 Joe Armstrong이 말한 프로그래밍 방식과 유사하다."
코드 작성 비용이 0에 가까워졌다는 건, 실험 비용도 0에 가까워졌다는 뜻이거든요.
모르는 문제에 부딪혔을 때, 명세를 완벽하게 쓰려고 고민하지 마세요. 그 시간에 프로토타입을 3개 만들어보세요.
- AI한테 "접근법 A로 구현해봐" → 결과 확인
- AI한테 "접근법 B로 구현해봐" → 결과 확인
- AI한테 "접근법 C로 구현해봐" → 결과 확인
비교해서 가장 나은 방향을 고르고, 그걸 기반으로 제대로 된 설계를 하면 됩니다. 과거에는 프로토타입 3개를 만들려면 며칠이 걸렸어요. 지금은 몇 시간이면 됩니다.
이게 제가 "탐색 단계"를 강조하는 이유이기도 해요. 설계 전에 탐색을 먼저 하라는 거죠. 문제를 충분히 이해하기 전에 설계를 시작하면, 그 설계는 높은 확률로 틀립니다. 프로토타입으로 먼저 문제를 탐색하고, 그다음에 설계하세요.
"이러면 워터폴 아니야?" — 커뮤니티 논쟁
Hacker News에서 가장 뜨거웠던 반론이 이거예요:
"이 모든 걸 하면 결국 워터폴로 돌아간다. 차라리 직접 코드를 쓰고 AI를 1차 리뷰어로 쓰는 게 낫다."
일리 있는 지적이에요. 12가지 원칙을 전부 엄격하게 적용하면 오버헤드가 생기는 건 사실이에요. 명세 작성하고, 테스트 분리하고, 보안 함수 표시하고, 린팅 세팅하고... 이것만 하다가 하루가 끝날 수도 있어요.
근데 그건 전부 다 적용할 때 얘기예요. 핵심은 상황에 맞게 골라 쓰는 거예요.
| 상황 | 적용할 것 |
|---|---|
| 사이드 프로젝트 | CLAUDE.md + 린팅 정도면 충분 |
| 팀 프로젝트 | + Writer/Reviewer 패턴 + 리뷰 상태 추적 |
| 프로덕션 코드 | + 보안 함수 표시 + 속성 기반 테스트 |
| 결제/인증 등 핵심 로직 | 12가지 전부 + 인간 코드 리뷰 필수 |
사이드 프로젝트에서 //HIGH-RISK-UNREVIEWED 주석을 달면서 코딩할 필요는 없잖아요. 반대로, 결제 로직을 AI한테 시키면서 리뷰도 안 하면 사고가 나고요. 상황에 맞게 수위를 조절하는 게 핵심입니다.
코딩을 직접 해야 한다는 사람들
Hacker News에서 가장 많은 공감을 받은 댓글은 사실 이거였어요:
"나는 여전히 코드를 직접 작성하며 사고를 정리하는 과정이 중요하다고 느끼고 있다. 코드가 나에게는 세부사항을 다듬게 만드는 강제 장치 같은 존재다. 명세서를 쓰는 것만으로는 그 깊이를 얻지 못한다."
이건 정당한 관점이에요. 코드를 작성하는 행위 자체가 사고를 정리하는 과정이라는 건 틀린 말이 아닙니다. 특히 복잡한 도메인 로직이나 알고리즘을 다룰 때는 더 그래요.
이 댓글에 달린 답글 중에 이런 것도 있었어요:
"LLM에게 이런 과정을 맡기면 마치 비행기가 실속하는 것처럼 정신적으로 멈춰버린다. 문제 해결의 스트레스는 줄지만, 생각하고 창조할 동기가 사라진다."
이것도 이해가 돼요. AI한테 다 시키면 편하긴 한데, 뭔가 내가 성장하는 느낌이 없는 거죠. "내가 만든 것"이라는 성취감도 줄어들고요.
근데 저는 이걸 이분법으로 보지 않아요. "AI를 쓸 거냐 직접 짤 거냐"가 아니라, "어디서 AI를 쓰고 어디서 직접 짤 거냐"의 문제예요.
가장 균형 잡힌 시각은 이 댓글이었어요:
"명세서와 테스트를 진짜 작업물로 보고, 그 안에서 계속 수정하며 생각을 정리한다. 에이전트 덕분에 시도와 포기가 빠르게 반복돼서 여전히 애자일하다."
저도 동의합니다. AI 시대에 개발자의 "코딩"이란 직접 타이핑하는 것에서, 설계하고 검증하는 것으로 이동하고 있어요. 코드를 치는 행위가 아니라, 무엇을 만들지 결정하고 결과를 검증하는 행위가 핵심입니다.
그래서 제 강의 제목이 "코드를 치지 마라 — 설계하고, 시키고, 검증하라"인 거예요. 코드를 아예 안 치라는 게 아니라, 코드를 치는 것보다 설계하고 검증하는 데 더 많은 시간을 쓰라는 뜻이에요.
오늘 바로 해보세요
여기까지 읽으셨으면 "그래서 당장 뭘 하면 되는데?"라는 생각이 드실 거예요. 12가지 원칙, 4가지 카테고리, 이것저것 많이 이야기했지만, 오늘 당장 할 수 있는 건 3가지입니다.
1. CLAUDE.md에 프로젝트 제약조건 추가하기
프로젝트 루트에 CLAUDE.md를 만들고 (이미 있다면 수정하고), 핵심 제약조건을 넣으세요. 5분이면 됩니다:
# 프로젝트 규칙
- 테스트 파일(/tests/specs/)은 절대 수정하지 마라
- 에러 처리는 try-catch 대신 Result 패턴을 사용하라
- 새 기능을 구현할 때는 반드시 Plan Mode로 설계를 먼저 제안하라
- 새로 작성하는 함수에는 // AI-GENERATED 주석을 달 것2. pre-commit hook에 린팅 추가하기
아직 린팅이 자동화되어 있지 않다면, 최소한 이것만이라도 하세요. 10분이면 됩니다:
# package.json의 scripts에 추가
"check": "tsc --noEmit && eslint ."
# pre-commit hook 설정 (husky 사용 시)
npx husky add .husky/pre-commit "npm run check"이거 하나만 걸어두면 AI가 아무리 자신감 있게 "완료했습니다"라고 해도, 타입 에러나 린트 에러가 있으면 커밋이 안 됩니다. 최소한의 안전망이에요.
3. 다음 작업에서 Writer/Reviewer 패턴 써보기
다음에 기능을 하나 구현할 때, 이렇게 해보세요:
- 세션 1: "이 기능을 구현해줘"
- 세션 2 (새 세션): "이 인터페이스의 동작을 검증하는 테스트를 작성해줘. 구현 코드는 보지 마."
처음에는 번거롭게 느껴질 수 있어요. 근데 한 번만 해보면 "아, 이래서 다른 세션에서 테스트를 작성해야 하는구나"를 바로 체감하실 거예요. 같은 세션에서 만든 테스트가 얼마나 허술한지를 깨닫게 되거든요.
이 세 가지만 해도 AI 코드 품질이 체감될 정도로 올라갑니다. 전부 합쳐서 30분도 안 걸려요.
마무리
AI 코딩의 핵심은 빨리 생성하는 게 아닙니다. 생성된 코드를 신뢰할 수 있게 만드는 거예요.
오늘 이야기한 모든 것 — 컨텍스트 설계, 테스트 분리, 보안 표시, 린팅, 단위 분할, Writer/Reviewer 패턴 — 이 전부가 결국 하나의 메시지로 수렴합니다.
"AI에게 더 많은 자유를 주려면, 더 단단한 울타리가 필요하다."
울타리 안에서 AI는 놀라운 속도로 일합니다. 울타리 없이는 놀라운 속도로 망칩니다.
여러분의 프로젝트에 울타리가 있나요? 없다면, 오늘 CLAUDE.md 한 줄 추가하는 것부터 시작하세요. 그 한 줄이 AI가 생성하는 모든 코드의 품질을 바꿔줄 겁니다.
다음 뉴스레터에서도 바이브 코딩과 Claude Code 관련 실전 인사이트를 가져오겠습니다.
레퍼런스
- How to effectively write quality code with AI — Mia Heidenstedt
의견을 남겨주세요