저는 지금 개인 프로젝트를 하나 만들고 있어요. 시작부터 TDD로 구현하고 있는데요. 테스트가 700개를 넘겼어요.
700개요. 사이드 프로젝트치고 좀 많죠.
근데 이게 귀찮아서 그만둘까 싶은 적은 한 번도 없었어요. 오히려 테스트가 쌓일수록 개발이 빨라져요. 새 기능을 붙일 때 겁이 안 나요. 뭔가 깨지면 테스트가 바로 잡아주니까요. AI한테 "이거 구현해줘"라고 시키고, 테스트가 전부 통과하는지만 확인하면 돼요. 통과하면 커밋, 실패하면 수정. 이게 전부예요.
그러다가 Daniel Saewitz라는 사람이 쓴 글을 읽었는데요. 제목이 "Tests Are The New Moat"예요. 테스트가 새로운 해자라는 거예요. 읽으면서 소름이 좀 돋았어요. 제가 개인 프로젝트에서 체감하고 있는 것을, 오픈소스 생태계 전체로 확장한 글이었거든요.
오늘은 이 글을 따라가면서, 테스트라는 걸 처음부터 끝까지 한번 깊이 파보려고 합니다. 개인 프로젝트 수준에서 시작해서, 오픈소스 전쟁, 그리고 AI 시대의 개발까지.

Cloudflare가 Next.js를 일주일 만에 복제한 방법
먼저 이 이야기부터 해야 해요.
Cloudflare가 Next.js의 대안을 일주일 만에 만들어냈어요. Vercel이 몇 년에 걸쳐 쌓은 프레임워크를요. 어떻게 가능했을까요?
Next.js는 엄청나게 잘 문서화된 프로젝트예요. 수없이 많은 시간을 투자해서 문서를 정성스럽게 작성하고, 소프트웨어 계약을 정의하고, 포괄적인 테스트 스위트를 만들었어요. 좋은 오픈소스의 교과서적인 모습이죠.
근데 이게 역으로 작용했어요.
AI한테 그 문서를 전부 읽게 하고, 테스트를 가져오고, 기반만 바꾸면 되니까요. Cloudflare는 Turbopack 대신 Vite를 기반으로 삼았어요. 더 성숙하고 빠른 번들러를요. 최신 버전의 API만 지원하면 되니까 하위 호환성 부담도 없었고요. 기존 사용자가 없으니까 깨질 것도 없었어요.
결과요? Next.js API의 94% 커버리지를 달성했어요. 1,700개의 vitest 테스트, 380개의 playwright 테스트. 그리고 이건 장난감이 아니에요. 미국 정부 웹사이트(cio.gov)에 이미 배포됐어요.
Daniel Saewitz가 이 사건을 보면서 한 말이 이거예요.
"문서를 잘 쓸수록, 계약을 명확히 정의할수록, 경쟁자가 당신의 작업물을 더 쉽게 복제할 수 있다."
이게 모순이잖아요. 오픈소스의 미덕이 문서화, 명확한 API, 탄탄한 테스트였는데. 그 미덕이 AI 시대에는 오히려 복제를 쉽게 만든다니요.
해자가 뒤집혔어요
전통적으로 오픈소스의 해자는 뭐였어요?
코드 자체요. 수만 줄의 코드를 처음부터 작성하는 건 엄청난 비용이었어요. 그래서 잘 만들어진 오픈소스 프로젝트는 그 자체로 해자였어요. 누가 이걸 처음부터 다시 만들겠어?
근데 AI가 코드를 짜는 비용을 거의 0으로 떨어뜨렸잖아요. 이전 뉴스레터에서 Simon Willison의 "코드가 싸졌다"를 다루면서 이 이야기를 했었죠. 코드를 짜는 비용이 0에 가까워지면, 코드는 더 이상 해자가 아니에요.
그럼 뭐가 해자가 되느냐.
Daniel Saewitz는 테스트라고 해요.
왜냐면요. Cloudflare가 Next.js를 복제할 수 있었던 건, Next.js의 테스트가 공개돼 있었기 때문이에요. 테스트가 곧 "이 소프트웨어가 뭘 해야 하는지"에 대한 실행 가능한 명세거든요. 문서는 모호할 수 있어요. API 스펙도 해석의 여지가 있어요. 근데 테스트는 아니에요. 통과하면 맞는 거고, 실패하면 틀린 거예요. 이보다 명확한 명세가 어디 있어요.
AI한테 "이 테스트를 전부 통과하는 코드를 짜줘"라고 하면요. AI가 정말 잘해요. 구현 세부사항은 달라도, 외부 동작이 동일한 코드를 만들어낼 수 있거든요. 그리고 테스트가 통과하면, 적어도 "이 소프트웨어가 해야 할 일을 한다"는 걸 보장할 수 있어요.
그래서 Cloudflare는 Next.js의 테스트 스위트를 가져다가 자기들의 구현이 "진짜로 Next.js와 호환된다"는 걸 증명한 거예요. 테스트가 명세였고, 그 명세를 충족시키는 새 구현을 만든 거죠.
SQLite는 이걸 10년 전에 알고 있었어요
여기서 Daniel Saewitz가 아주 흥미로운 사례를 가져와요. SQLite.
SQLite는 코어 소스 코드 전체가 오픈소스예요. 누구나 볼 수 있어요. 근데 테스트 스위트는요? 비공개예요.
그 규모가 장난이 아니에요. SQLite의 공식 문서에 따르면, 테스트가 9,200만 줄이래요. 코어 소스 코드의 592배예요. 소스 코드보다 테스트가 592배 많은 프로젝트. 상상이 돼요?
왜 이렇게 했을까요. SQLite 팀은 이걸 "프로젝트를 지속 가능하게 유지하는 해자"라고 설명해요. 코드는 누구나 가져갈 수 있어요. 포크하면 되니까요. 근데 그 코드가 "진짜로 모든 엣지 케이스에서 정확하게 동작한다"는 걸 검증하려면? 9,200만 줄의 테스트가 필요한 거예요. 그리고 그 테스트는 비공개니까, 경쟁자가 복제할 수 없어요.
솔직히 오픈소스 관점에서 보면 이게 좀 불편할 수 있어요. "테스트도 공개해야 진짜 오픈소스 아니냐"는 시각이 있을 수 있거든요. Daniel Saewitz도 이걸 인정해요. 사용자 입장에서는 테스트가 공개된 게 당연히 더 좋다고요. 근데 동시에 이렇게도 말해요.
"그들은 그것을 빚진 게 아닙니다. 해자를 지키는 가장 좋은 방법이 이거라고 판단했다면, 그건 그들의 선택입니다."
그리고 이제 이 흐름이 확산되고 있어요. Daniel Saewitz가 글을 올린 지 이틀 후에 추가한 내용이 있어요. tldraw — 유명한 오픈소스 드로잉 라이브러리인데요. 테스트 스위트 전체를 공개 레포에서 비공개 레포로 옮기고 있다고요.
tldraw의 GitHub 이슈(#8082)를 보면 이유가 나와요. AI가 테스트를 포함한 코드를 학습하고 복제하는 게 너무 쉬워졌다는 거예요. 자기들이 수년간 쌓은 테스트 자산이 경쟁자의 무기가 되는 걸 방지하려는 거죠.
이건 제 이야기이기도 해요
여기서 좀 개인적인 이야기를 해볼게요.
제 개인 프로젝트 이야기를 좀 더 해드릴게요. TDD로 개발하고 있고, 테스트가 700개를 넘겼다고 했잖아요. 근데 이 테스트들이 왜 이렇게 많아졌냐면요.
처음에는 솔직히 귀찮았어요. 기능 하나 만들 때마다 테스트부터 쓰는 게요. "일단 만들고 나중에 테스트 쓰지 뭐"라는 유혹이 계속 들었어요. 근데 참고 버텼어요. 그리고 어느 순간 테스트가 쌓이니까 개발 속도가 역전됐어요.
무슨 말이냐면요. 테스트 없이 개발하면 처음엔 빨라요. 근데 기능이 10개, 20개 붙으면서 "이거 고쳤는데 저거 안 깨졌겠지?"를 매번 수동으로 확인해야 해요. 확인을 안 하면 나중에 버그가 터지고, 터진 버그를 찾으려면 또 시간이 들고. 점점 느려져요.
TDD는 처음엔 느려요. 근데 테스트가 쌓이면 속도가 떨어지지 않아요. 기능이 50개든 100개든, 새 기능을 붙이는 속도가 비슷해요. 이전에 만든 것들이 깨지지 않는다는 걸 테스트가 보장해주니까요.
그리고 이게 AI 코딩과 만나니까 효과가 극적이에요.
제가 하는 방식이 이래요. 테스트를 먼저 작성해요. 그리고 Claude Code한테 이렇게 말해요.
이 테스트들을 전부 통과시키는 구현을 만들어줘.
기존 테스트는 절대 수정하지 마.이러면 뭐가 좋으냐면요. AI한테 명세를 코드로 전달한 거예요. "이렇게 동작해야 해"를 말로 설명하는 대신, 테스트로 보여주는 거죠. 이전 뉴스레터에서 "프롬프트를 잘 쓰려고 하지 말고 컨텍스트를 설계하라"고 했잖아요. 테스트가 가장 정확한 컨텍스트예요. 모호함이 없거든요.
그리고 AI가 치팅을 못 해요. 이전 뉴스레터에서 "AI는 치팅합니다 — 진짜로"라고 했던 거 기억하시죠. AI가 테스트를 수정하거나, 하드코딩으로 통과시키거나, 실패하는 테스트를 삭제하는 문제요. 근데 제가 테스트를 먼저 써놓고 "수정하지 마"라고 잠가놓으면, AI가 할 수 있는 건 진짜로 구현을 제대로 하는 것밖에 없어요.
700개의 테스트는 700개의 제약조건이에요. AI는 700개의 제약조건을 전부 만족시키는 코드를 짜야 해요. 이건 "잘 만들어줘"라는 프롬프트보다 비교할 수 없이 정밀한 지시예요.
Cloudflare가 한 것과 제가 한 것은 같은 패턴이에요
잠깐 한 발 물러서서 보면요. Cloudflare가 Next.js를 복제한 것과, 제가 개인 프로젝트에서 AI한테 구현을 시키는 건 정확히 같은 패턴이에요.
| Cloudflare | 에피의 프로젝트 | |
|---|---|---|
| 명세 | Next.js 테스트 스위트 | 700개의 테스트 |
| AI의 역할 | 테스트를 통과하는 새 구현 | 테스트를 통과하는 새 기능 |
| 기반 교체 | Turbopack → Vite | 자유롭게 리팩터링 가능 |
| 검증 | 1,700 vitest + 380 playwright | 700+ 테스트 전부 통과 |
차이가 뭐냐면요. 규모만 다를 뿐이에요. Cloudflare는 프레임워크 레벨에서 이걸 했고, 저는 개인 프로젝트 레벨에서 이걸 하고 있는 거예요. 근데 원리는 같아요.
테스트가 명세이고, AI가 그 명세를 충족시키는 구현을 만든다.
그리고 이게 왜 강력하냐면요. 구현을 갈아엎을 수 있기 때문이에요. 테스트만 견고하면, 내부 구현은 언제든 바꿀 수 있어요. "이 부분을 더 효율적으로 리팩터링해줘" 하고 시키면, AI가 리팩터링하고, 테스트가 전부 통과하면 성공인 거예요. 구현이 완전히 달라져도요.
이전 뉴스레터에서 Simon Willison이 "코드를 짜는 비용이 사라졌다"고 했잖아요. 맞아요. 근데 더 정확하게 말하면요. 코드를 짜는 비용이 사라졌기 때문에, 코드는 일회용이 될 수 있어요. 테스트만 남기면 구현은 언제든 다시 만들 수 있으니까요.
반대로 테스트 없이 만든 코드는요? 버릴 수도 없고 고칠 수도 없어요. 뭘 건드리면 뭐가 깨질지 모르니까요. "작동하니까 건드리지 마"가 되는 거예요. AI 시대에 가장 위험한 코드가 이거예요. 테스트 없이 "작동하는" 코드.
이전 뉴스레터에서 한 이야기들이 여기로 수렴해요
이 뉴스레터를 쓰면서 재미있는 걸 발견했어요. 제가 지금까지 다뤘던 주제들이 "테스트"라는 키워드로 다시 묶이더라고요.
"AI가 짠 코드를 믿지 마라" — 이 뉴스레터에서 속성 기반 테스트, Writer/Reviewer 패턴을 다뤘어요. 핵심이 뭐였어요? 테스트로 AI를 검증하라는 거였잖아요. AI의 치팅을 막는 가장 강력한 방법이 테스트였어요.
"계획 문서가 코드를 이깁니다" — Boris Tane의 plan.md를 다루면서, "계획이 좋으면 구현이 좋다"는 전제를 소개했어요. 근데 제가 한 가지 비판했던 게 있어요. "테스트 이야기가 없다"는 거요. 계획이 아무리 좋아도 엣지 케이스는 구현하면서 발견되는 경우가 많다고요. 그 구멍을 메우는 게 테스트예요.
"코드가 싸졌는데 왜 더 어려워졌을까" — Simon Willison의 "좋은 코드의 조건" 9가지를 다루면서, "테스트로 보호돼야 한다"가 핵심 조건 중 하나라고 했어요. 코드를 짜는 비용이 사라졌기 때문에, 좋은 코드와 나쁜 코드를 구분하는 기준이 더 중요해졌다고요. 그리고 그 기준을 강제하는 가장 확실한 수단이 테스트예요.
"코드를 짜면 지는 겁니다" — Every 팀의 Compound Engineering에서 "코드를 생산하는 시스템이 코드보다 가치 있다"고 했잖아요. 테스트 스위트가 바로 그 시스템의 핵심 부품이에요. 테스트가 쌓일수록 AI가 만드는 코드의 품질이 올라가는 복리 효과가 발생하거든요.
결국 제가 몇 달째 다뤘던 것들의 교차점이 테스트였어요. 개별적으로 보면 Plan Mode도 중요하고, CLAUDE.md도 중요하고, 린팅도 중요해요. 근데 이것들이 전부 "AI가 만든 코드가 올바르다는 걸 어떻게 보장할 것인가"라는 하나의 질문으로 수렴하고요. 그 질문에 대한 가장 직접적인 답이 테스트예요.
근데 테스트에도 함정이 있어요
여기서 현실적인 이야기를 좀 해야 해요. "테스트가 중요하다"는 건 다 알아요. 문제는 "AI가 짜는 테스트"를 그대로 믿어도 되느냐는 거예요.
안 돼요.
이전 뉴스레터에서 자세히 다뤘지만 다시 짚을게요. AI한테 "테스트 작성해줘"라고 하면 AI가 만드는 테스트에는 문제가 있어요.
첫째, 구현에 맞춘 테스트를 만들어요. AI가 구현을 먼저 보고 테스트를 쓰면, 그 테스트는 "이 구현이 맞다"를 확인하는 테스트가 돼요. "이 동작이 맞다"를 확인하는 테스트가 아니라요. 구현에 버그가 있어도 테스트가 통과해요.
둘째, 해피 패스만 테스트해요. 정상적인 입력, 정상적인 경로만 확인해요. "네트워크가 끊기면?", "입력이 null이면?", "동시에 두 번 호출하면?" 같은 엣지 케이스는 빠져있는 경우가 대부분이에요.
셋째, 의미 없는 테스트를 양산해요. 위에서 예시를 들었던 것처럼, 입력값을 그대로 출력값에서 확인하는 식의 테스트요. 겉보기에는 테스트가 50개인데, 실질적으로 검증하는 건 5개 수준인 거예요.
그래서 핵심은 이거예요.
테스트를 "누가" 쓰느냐가 중요해요.
- 사람이 테스트를 쓰고, AI가 구현을 한다 → 강력한 패턴
- AI가 구현하고 AI가 테스트를 쓴다 → 위험한 패턴
- AI가 테스트를 쓰고 사람이 리뷰한다 → 괜찮지만 리뷰 품질에 의존
제가 개인 프로젝트에서 하는 방식은 첫 번째예요. 테스트를 먼저 직접 쓰고, 구현을 AI한테 시켜요. 이러면 테스트가 "사람의 의도"를 담고 있으니까, AI가 그 의도대로 구현했는지를 정확하게 검증할 수 있어요.
700개 테스트를 전부 직접 썼냐고요? 네. 물론 보일러플레이트는 AI한테 도움받았어요. 근데 "이 함수가 뭘 해야 하는지"를 정의하는 건 제가 했어요. 이 부분은 양보할 수 없어요.
오픈소스의 인센티브 구조가 바뀌고 있어요
Daniel Saewitz가 깊이 파고드는 또 다른 층위가 있어요. 오픈소스의 인센티브 구조 자체가 변하고 있다는 거예요.
상업적 오픈소스(commercial OSS)라는 게 있잖아요. 코드는 공개하되, 거기서 사업을 만드는 모델이요. 예전에는 이 모델이 잘 작동했어요. 코드를 공개하면 커뮤니티가 기여하고, 그 커뮤니티의 신뢰가 사업의 기반이 됐으니까요.
근데 지금은 코드를 공개하면 AI가 학습해요. 문서를 공개하면 AI가 읽어요. 테스트를 공개하면 AI가 명세로 활용해요. 그래서 이 인센티브 구조에 균열이 생기는 거예요.
| 예전 | 지금 | |
|---|---|---|
| 코드 공개 | 커뮤니티 기여 → 프로젝트 성장 | AI 학습 데이터 → 경쟁자의 무기 |
| 문서 공개 | 사용자 신뢰 → 채택률 증가 | AI 프롬프트 소스 → 클론 용이 |
| 테스트 공개 | 품질 증명 → 신뢰 구축 | 실행 가능한 명세 → 완전한 복제 가능 |
Daniel Saewitz가 이런 말을 해요.
"AI 세계에서, 당신의 작업물을 더 잘 문서화할수록, 더 강한 계약을 정의할수록, 누군가가 당신의 작업물을 복제하기가 더 쉬워집니다."
이건 딜레마예요. 좋은 오픈소스를 만들려면 문서화하고, 테스트하고, API를 명확히 정의해야 해요. 근데 그렇게 할수록 복제당하기 쉬워지는 거예요. "잘 만들수록 잘 복제된다"는 모순이요.
그래서 SQLite처럼 테스트를 비공개로 유지하거나, tldraw처럼 테스트를 비공개로 옮기는 프로젝트가 나오기 시작한 거예요. 이게 오픈소스 정신에 부합하느냐는 논쟁이 있을 수 있지만, 비즈니스 관점에서는 합리적인 선택이에요.
Daniel Saewitz는 이걸 좀 냉정하게 정리해요.
"오랫동안 오픈소스는 순수하게 이타적인 척했지만, 규모가 커지면서 그게 깨지는 걸 여러 차례 봤습니다. 기업들이 오픈소스에 엄청나게 기여한 것도 사실이지만, 아무런 되돌림 없이 가치만 추출한 것도 사실입니다."
이게 개발자 개인에게는 무슨 의미일까요
오픈소스 생태계 이야기를 했으니, 이걸 개인 레벨로 가져와볼게요. 이 글에서 개발자가 가져가야 할 실전 교훈이 뭘까요.
테스트는 비용이 아니라 자산이에요
제가 700개 테스트를 쓰면서 가장 크게 느낀 거예요. 테스트를 쓰는 시간이 아까운 게 아니라, 테스트가 쌓일수록 개발이 빨라진다는 거요. 특히 AI 코딩을 할 때 이 효과가 극대화돼요.
테스트가 없으면 AI한테 뭘 시킬 때마다 결과를 수동으로 확인해야 해요. "이거 맞아? 저거 안 깨졌어? 엣지 케이스는?" 전부 눈으로 봐야 해요.
테스트가 있으면요? npm test를 치면 끝이에요. 전부 통과하면 OK. 실패한 게 있으면 뭐가 깨졌는지 바로 알 수 있어요. 이게 AI 시대의 진짜 생산성이에요.
테스트를 먼저 쓰면 AI에 대한 의존도를 줄일 수 있어요
모순처럼 들리죠. AI를 더 잘 쓰는 방법이 AI에 대한 의존도를 줄이는 거라니요.
근데 생각해보면 논리가 맞아요. 테스트를 먼저 쓰면, "이 코드가 뭘 해야 하는지"를 제가 정의하는 거예요. AI는 그 정의를 충족시키는 구현을 만드는 거고요. 주도권이 저한테 있어요. AI가 이상한 구현을 해도, 테스트가 실패하면 바로 알 수 있어요.
반대로 테스트 없이 AI한테 "만들어줘"라고 하면요? AI가 뭘 만든 건지, 그게 맞는 건지, 제가 판단하기 어려워요. AI의 결과물을 "대충 맞겠지" 하고 수용하게 되는 거예요. 이게 진짜 의존이에요.
테스트는 AI가 바뀌어도 살아남아요
이것도 중요해요. 오늘은 Claude Code를 쓰지만, 내일은 다른 도구가 나올 수 있잖아요. 더 좋은 모델, 더 좋은 에이전트가 나올 때마다 도구를 바꿀 수 있어요.
근데 그때마다 처음부터 다시 시작하는 건 아니에요. 테스트가 있으면 새 도구에서도 "이 테스트를 통과시켜"라고 하면 되니까요. 테스트가 도구에 종속적이지 않은 자산이에요. Claude Code에서 짠 코드는 Claude Code에 최적화돼 있을 수 있지만, 테스트는 그냥 "이 소프트웨어가 뭘 해야 하는지"를 정의하는 거니까 도구와 무관해요.
이건 오픈소스에서도 마찬가지예요. Cloudflare가 Next.js의 테스트를 Vite 위에서 돌린 거잖아요. 기반이 바뀌어도 테스트는 유효해요. 기반이 바뀌어도 유효한 건 테스트뿐이에요.
솔직히 이렇게 느꼈습니다
Daniel Saewitz의 글을 읽으면서 제일 먼저 든 생각은 "오픈소스 세계가 참 아이러니하다"는 거였어요. 잘 만들수록, 잘 문서화할수록, 잘 테스트할수록 — 복제당하기 쉬워진다니요. 개발자의 정성이 역으로 무기가 되는 거잖아요.
근데 동시에 이것도 생각했어요. "그래서 뭐?"
Vercel이 Next.js를 잘 만들어서 Cloudflare가 복제할 수 있었다고요? 맞아요. 근데 Vercel은 여전히 Next.js 생태계의 중심이에요. 문서와 테스트를 비공개로 돌려서 방어하는 것도 방법이지만, 계속 혁신해서 앞서나가는 것도 방법이잖아요. SQLite 방식만이 정답은 아닌 거예요.
그리고 개인 개발자 입장에서는 이 "해자" 논쟁이 사실 좀 먼 이야기이기도 해요. 저한테 중요한 건 오픈소스 프로젝트의 해자가 아니라, 제 코드의 품질이거든요. 그리고 그 관점에서 보면, 이 글의 메시지는 명확해요.
테스트를 쓰세요. 테스트가 자산이에요.
오픈소스 해자 논쟁과 상관없이, AI 시대에 테스트가 더 중요해진 건 확실해요. 코드를 짜는 건 AI가 해요. 근데 "이 코드가 맞는지" 확인하는 건 테스트가 해요. 그리고 테스트를 정의하는 건 사람이 해요. 이 구조에서 테스트를 쓰는 능력이 개발자의 핵심 역량이 되는 거예요.
제가 700개 테스트를 쓰면서 느끼는 건요. TDD가 "옳기 때문에" 하는 게 아니에요. TDD가 AI 시대에 가장 합리적인 개발 방식이라서 하는 거예요. 코드를 짜는 비용이 0이 됐으니까, "무엇을 검증할 것인가"를 정의하는 게 개발자의 본업이 된 거예요. 그리고 그 정의가 테스트예요.
오늘 바로 해보세요
1. 다음 기능을 테스트부터 작성해보세요 (15분)
다음에 기능을 하나 만들 때, 코드부터 짜지 마세요. 테스트부터 쓰세요.
// 예시: 유저를 생성하는 기능을 만든다면
test("유효한 이메일로 유저를 생성하면 성공한다", () => {
const result = createUser({ email: "test@example.com", name: "테스트" });
expect(result.success).toBe(true);
expect(result.user.email).toBe("test@example.com");
});
test("이메일 없이 유저를 생성하면 실패한다", () => {
const result = createUser({ email: "", name: "테스트" });
expect(result.success).toBe(false);
expect(result.error).toContain("이메일");
});
test("중복 이메일로 유저를 생성하면 실패한다", () => {
createUser({ email: "test@example.com", name: "첫번째" });
const result = createUser({ email: "test@example.com", name: "두번째" });
expect(result.success).toBe(false);
});이 3개의 테스트를 쓰는 데 5분이면 돼요. 그리고 Claude Code한테 이렇게 하세요:
이 테스트들을 전부 통과시키는 createUser 함수를 구현해줘.
테스트 파일은 수정하지 마.한 번만 해봐도 체감할 거예요. "만들어줘"라고 시키는 것과, "이 테스트를 통과시켜"라고 시키는 것의 차이를요.
2. CLAUDE.md에 테스트 규칙 추가하기 (1분)
- 테스트 파일은 절대 수정하지 마라
- 새 기능을 구현할 때 기존 테스트가 깨지면 안 된다
- 구현을 고쳐서 테스트를 통과시켜라. 테스트를 고치지 마라.이 세 줄이면 돼요. AI가 테스트를 치팅하는 걸 원천적으로 막아줘요.
3. 기존 프로젝트에 회귀 테스트 하나 추가하기 (10분)
지금 진행 중인 프로젝트에서 가장 중요한 기능 하나를 골라서, 그 기능이 "지금 이 순간 정상 동작한다"는 걸 확인하는 테스트를 하나만 추가하세요.
이건 TDD가 아니어도 돼요. 이미 있는 기능에 테스트를 붙이는 거예요. 이 테스트 하나가 나중에 AI한테 리팩터링을 시킬 때, "이전이랑 똑같이 동작하는지" 확인하는 안전망이 돼요.
700개까지 갈 필요 없어요. 1개부터 시작하세요. 1개가 2개가 되고, 2개가 10개가 되면 그때부터는 알아서 늘어나요. 테스트가 쌓이면 개발이 빨라지는 걸 체감하면 멈출 수가 없거든요.
마무리
Daniel Saewitz는 오픈소스의 해자가 테스트로 이동하고 있다고 말했어요. 거시적인 이야기예요. 근데 이걸 개인 레벨로 가져오면 훨씬 실감나요.
제가 700개 테스트를 쓰면서 깨달은 건요. 코드는 다시 짜면 돼요. AI한테 시키면 돼요. 근데 테스트는 다시 못 짜요. 정확히는, "이 소프트웨어가 뭘 해야 하는지"를 다시 정의하는 건 사람이 해야 해요. 그게 가장 비싼 작업이에요.
AI가 코드를 짜는 비용을 0으로 만들었어요. 문서를 읽고 복제하는 비용도 0에 가까워졌고요. 근데 "뭐가 맞고 뭐가 틀린지"를 판단하는 비용은 줄지 않았어요. 그 판단을 실행 가능한 코드로 표현한 게 테스트예요.
제 강의에서 "테스트를 먼저 쓰고 구현을 시켜라"를 강조하는 이유가 여기 있어요. AI 시대에 개발자의 핵심 역량은 코드를 짜는 게 아니에요. "이 코드가 맞는지"를 정의하는 거예요. 그리고 그 정의가 테스트예요.
테스트를 쓰세요. 그게 당신의 해자예요.
레퍼런스
- Tests Are The New Moat — Daniel Saewitz. AI 시대에 오픈소스의 해자가 코드에서 테스트로 이동하고 있다는 분석. Cloudflare의 Next.js 복제, SQLite의 비공개 테스트 전략, tldraw의 테스트 비공개 전환 사례 포함
의견을 남겨주세요