작은 변경마다 Blast Radius가 생길 때
Vibe coding의 첫 구간은 믿기 어려울 만큼 생산적으로 느껴집니다. Cursor와 Claude Code는 login, dashboard, settings, push notifications까지 순식간에 만들어 내기 때문에, founder 혼자서도 며칠 만에 MVP를 live로 올릴 수 있습니다.
문제는 두 번째나 세 번째 변경 라운드에서 드러납니다. settings를 조금 손봤더니 auth가 깨집니다. profile update 하나가 notifications를 흔듭니다. bug들은 서로 무관해 보이지만, 코드에 강제된 boundary가 없어서 앱의 모든 부분이 다른 모든 부분에 영향을 줄 수 있습니다.
이게 Vibe coding의 함정입니다. Cursor와 Claude Code는 동작하는 코드를 생성하는 데는 놀랍도록 강합니다. 유지보수 가능한 코드를 생성하도록 설계되지는 않았습니다. settings screen을 만들어 달라고 하면 settings screen을 만들어 줍니다. service boundary를 지키고, theme contract를 따르고, input을 validate하는 settings screen을 자동으로 만들어 주지는 않습니다. prompt는 Claude가 채우고, 빈틈은 당신이 메웁니다.
왜 Vibe Coding은 Scale 단계에서 깨지는가
사용자가 10명일 때는 edge case 하나가 DM 한 통으로 끝납니다. 1,000명일 때는 churn event가 됩니다. 10,000명일 때는 회사를 위협하는 outage가 됩니다. MVP 단계에서 마법처럼 느껴지던 같은 코드베이스가, 성장 단계에서는 손대기 무서운 대상이 됩니다.
전통적인 개발은 이 문제를 code review, ADR, 그리고 “안 된다”고 말할 수 있는 senior engineer로 해결합니다. 하지만 Cursor와 Claude Code로 10배 속도로 배포하는 상황에서, 모든 verification loop마다 human bottleneck을 넣을 수는 없습니다. 그러면 속도 이점이 사라집니다. 사람 리뷰보다 빠르면서도, 그만큼 신뢰할 수 있는 무언가가 필요합니다.
Declarative Constraint가 답이다
guardrail, 즉 milliseconds 안에 끝나는 deterministic declarative check입니다. 피곤해하지 않습니다. context를 잃지 않습니다. 토큰 비용도 들지 않습니다. Vibe-coded 앱의 architectural immune system입니다.
// These aren't suggestions. They're non-negotiable gates.
// They run on every commit. Human or AI code — doesn't matter.
1. TypeScript strict mode catches interface violations
- noUncheckedIndexedAccess
- exactOptionalPropertyTypes
2. ESLint catches architectural violations
- no className outside primitive seam
- import from barrels, not implementations
3. dependency-cruiser catches boundary violations
- Circular dependencies caught at commit time
- Never reach production
4. Pre-commit hooks run ALL checks before code lands
pnpm run verify → lint, typecheck, depcruise, test
If any check fails, the commit is blocked.
Guardrail을 건너뛸 때의 비용
guardrail이 없으면 AI-generated code는 예측 가능한 죽음의 나선으로 들어갑니다. 빠르게 생성하고, 빠르게 배포하고, 빠르게 망가지고, production에서 패닉 디버깅하고, 사용자를 잃고, 잠을 잃고, 자신감도 잃습니다. 저는 ESLint rule 하나면 막을 수 있었던 문제 때문에 팀이 스프린트 전체를 날리는 모습을 여러 번 봤습니다. 1주 차에 Vibe coding으로 절약한 시간은 10주 차에 10배로 되돌려 갚게 됩니다.
guardrail이 있으면 lifecycle이 완전히 달라집니다. 빠르게 생성하고, 빠르게 검증하고, 안전하게 배포합니다. Cursor와 Claude Code는 여전히 코드를 씁니다. boundary는 guardrail이 강제합니다. AI의 속도와 아키텍처 안전성을 동시에 가져갈 수 있습니다.
Production-Ready Guardrail은 어떤 모습인가
production-ready React Native architecture에서 guardrail은 foundation의 일부입니다.
- TypeScript strict mode를 첫날부터 켠다.
noUncheckedIndexedAccess,exactOptionalPropertyTypes - dependency-cruiser로 폴더 boundary를 강제하고 circular dependency를 막는다.
- ESLint로 styling primitive seam 바깥의
className을 막는다. - Pre-commit hook으로 모든 commit마다 전체 verification suite를 돌린다.
결과는 이렇습니다. AI-generated code가 boundary 안에서만 움직입니다. Cursor는 component, screen, hook을 전속력으로 생성합니다. 하지만 service boundary를 깨뜨릴 수는 없습니다. styling engine을 누수시킬 수도 없습니다. circular dependency를 만들 수도 없습니다. 무엇이 허용되는지는 guardrail이 말하고, AI는 그 빈칸을 채웁니다.
매끄러운 전환
Autotomy Expo Starter Pack은 바로 이 전환을 위해 만들어졌습니다.
- barrel export를 포함한 명확한 service interface
- dependency 관리를 위한 composition root
- theme contract를 갖춘 semantic UI primitive
- hard dependency boundary와 optional dependency boundary
- 중앙집중식 lifecycle 관리
- route boundary에서의 URL validation
- deterministic pre-commit guardrail
- 안전한 리팩터링을 위한 autotomy pattern
Vibe coding을 멈출 필요는 없습니다. 확장 가능한 foundation 위에서 Vibe coding 하면 됩니다. Cursor는 theme contract에 맞춰 screen을 생성합니다. Claude Code는 service interface에 맞춰 feature를 생성합니다. pre-commit hook은 production에 닿기 전에 boundary violation을 막습니다. 앱은 사용자 10명에서 10,000명으로 커져도, 대부분의 AI-coded 프로젝트를 무너뜨리는 아키텍처 붕괴를 피할 수 있습니다.
속도는 중요합니다. 구조는 그 속도를 지속 가능하게 만듭니다. Cursor와 Claude Code로 React Native 앱을 배포하고 있다면, guardrail은 필요해진 뒤가 아니라 필요해지기 전에 투자해야 합니다. 리팩터링보다 싸고, 깨지는 변경을 사용자의 손이 아니라 commit 시점에서 잡아내는 첫 순간 바로 본전을 뽑습니다.