지난 글에서 비개발자가 Claude Code로 SaaS를 만들기 시작한 이야기를 썼다. 이번 글은 그 다음 단계 — 본격적으로 코딩 들어가기 전에 스펙 문서부터 만든 이야기다.
처음엔 정말 단순하게 생각했다.
아이디어가 있다.
나는 제품 개발을 잘 모른다.
그러면 스펙 문서 하나 잘 만들어서 Claude에게 던지면 되는 거 아닌가?
아니었다.
스펙 문서는 끝이 아니라 시작이었다. 그리고 바이브 코딩에서 가장 오래 걸린 일은 코딩이 아니라, AI가 만든 걸 계속 의심하고 줄여가는 일이었다.
여기서 말하는 바이브 코딩은, 내가 직접 코드를 한 줄씩 쓰는 대신 자연어로 의도와 제약을 설명하고 AI가 코드를 만들게 하는 방식에 가깝다. 문제는 자연어로 말한다고 해서 설계 책임까지 사라지는 건 아니라는 점이다.
시작: “스펙 하나 던지면 끝 아닌가?”
머릿속 아이디어를 일단 말로 풀었다. “주식·코인 예측 리더보드를 만들고 싶다”, “사람도 AI도 같은 보드에서 경쟁한다”, “한 번 제출하면 수정 못 한다” 이런 식으로. Claude에게 정리해달라고 했다.
Claude가 약 760줄짜리 스펙 문서를 만들어줬다. 데이터 모델, 점수 공식, API 엔드포인트, RLS 정책까지. 처음 보는 단어가 절반이었지만 어쨌든 큰 그림은 잡혔다.
“오, 이제 이거 그대로 개발 들어가면 되겠네.” 라고 생각하고, 다음 며칠을 그 문서에 질문하는 데 썼다.
스펙이 부풀어오른다
질문을 하나씩 던질 때마다 스펙이 늘어났다.
- “베이스라인 봇은 어떻게 매일 제출하지?” → 새 섹션, 30줄 추가
- “한국 시장 휴일은 어떻게 처리해?” → 새 섹션, 50줄
- “cron이 실패하면?” → 또 30줄
대충 이런 식으로 760 → 860 → 1,150 → 1,350 → 1,490 → 결국 약 1,600줄까지 갔다. 코딩은 한 줄도 안 했는데 문서만 두 배가 됐다.
솔직히 중간엔 불안했다. 서비스를 만드는 건지 문서만 쓰다 끝나는 건지 헷갈렸다.
며칠 더 지나서 알았다. 바이브 코딩의 90%는 코딩이 아니라 설계 토론이었다. 코드가 빠르게 나오기 때문에 잘못된 설계로 가면 빠르게 잘못된 게 만들어진다. 코드 짜기 전에 스펙 단계에서 미리 걸러내는 게 결국은 빠른 길이었다.
AI가 만든 복잡한 설계를 지운 순간
스코어링 공정성 얘기를 하다가 Claude가 “클러스터 시스템”이라는 걸 제안했다.
예를 들어 어떤 사용자가 엔비디아·AMD·TSMC·반도체 ETF에 전부 “상승”을 찍었다고 하자. 겉으로는 예측을 네 번 한 것 같지만 사실상 “반도체 오른다”는 한 가지 주장에 가깝다. Claude는 이 문제를 해결하려고 자산을 클러스터로 묶고, 같은 클러스터 안의 예측엔 점수 가중치를 깎는 방식을 제안했다.
여기서 더 나아갔다. 클러스터를 세 단계로 나누는 설계까지 갔고, 자동 상관관계 클러스터링도 추가했다. 합리적으로 들렸고, 스펙에 수십 줄이 더해졌다.
며칠 지나서 다시 보다 보니 뭔가 찜찜했다. 무엇이 같은 클러스터인지 누가 정하지? 시장이 바뀌면 클러스터도 바뀌어야 하나? 결정이 너무 주관적이었다. 그래서 물어봤다.
나: 아무리 생각해도 클러스터를 설정하는 게 주관적이고 문제가 있을 것 같아. 클러스터가 꼭 필요할까?
답은 의외로 빨랐다. Claude도 설계하면서 그 부분이 걸렸다고 했다. 클러스터 시스템은 공정성을 높이려는 의도는 있지만 기준이 주관적이고, MVP 단계에서는 과하다는 결론으로 정리됐다.
수십 줄 전부 삭제. 대체한 건 “동일 자산엔 timeframe당 1건만 제출 가능” 같은 단순한 rate limit 5줄이었다.
수십 줄을 지우는 건 이상하게 아까웠다. 며칠 동안 그럴듯해 보였던 설계였기 때문이다. 그런데 지우고 나니 오히려 제품이 선명해졌다.
이때 처음 느꼈다. AI는 복잡한 해결책을 꽤 자신 있게 제안한다. 그리고 내가 멈추지 않으면 그 복잡도는 계속 쌓인다.
“이거 진짜 필요해?”
바이브 코딩에서 이 질문은 생각보다 강력했다.
비개발자의 질문 세 개가 설계를 바꿨다
기술 디테일은 Claude가 다 채워줬지만, “이거 이상하지 않아?”라는 감각은 내가 제공해야 했다. 그렇게 던진 질문 중 지금도 기억에 남는 게 셋 있다.
(a) “항상 상승만 찍으면 이기는 거 아니야?”
주식은 장기적으로 오른다. 그러면 매번 “상승”만 찍는 봇은 적중률이 꽤 높게 나오지 않을까? 그게 그냥 시장 평균이지 실력은 아닌데.
Claude가 표까지 그려가며 설명했다. 어떤 시장이 장기적으로 대부분 상승하는 구간에 있다면, 매번 “상승”만 찍는 봇도 꽤 높은 적중률을 얻을 수 있다. 하지만 그건 실력이라기 보다 시장의 기본 방향을 따라간 결과다. 단순 적중률(accuracy)로 랭킹 매기면 운만 좋은 봇이 1등 한다. 그래서 base rate(시장 자체의 상승 확률)를 빼고, 시장 트렌드 위에서 실제로 얼마나 잘했는지를 보는 점수 체계가 필요하다 — 라는 흐름을 그때 처음 이해했다.
스펙에는 Elo 기반의 “Skill Rating”으로 결론을 냈다. (그런데 실제 구현에 들어가서 이걸 또 한 번 갈아엎게 된다. 그 이야기는 다음 글들에서.)
어쨌든 이 질문 하나가 점수 공식의 방향 자체를 잡았다.
(b) “장 끝나기 직전에 예측하면 치팅 아니야?”
장 마감 5분 전에 예측하면 종가를 거의 아는 셈이다. 너무 쉬운 거 아닌가?
이 한 줄이 Dormant Window라는 섹션이 됐다. 결론 — 장 마감 후부터 다음 날 장 시작 전까지만 “안전 시간대”로 예측을 받고, 장중 제출은 자동으로 다음 세션 종가 기준으로 이월. 크립토는 24시간 거래라 다음 UTC 자정 close로 잡힘. 이 단락이 결국 스펙에서 50줄 가까운 섹션이 됐고, 코드에서도 timing 로직 핵심이 됐다.
(c) “배당금은 어떻게 처리하지?”
SCHD 같은 고배당 ETF는 배당락일에 주가가 뚝 떨어진다. 그런데 주가만 보면 “아, 하락이네” 라고 판정되어버린다. 배당 받는 사람은 손해 본 게 아닌데.
여기서 처음 adj_close(배당과 액면분할까지 반영한 조정 종가)라는 개념을 들었다. 단어 하나 도입하니 DB 스키마에 컬럼 추가, 점수 공식 전체에서 close → adj_close로 갈아엎기, 데이터 fetch 로직 수정 — 도미노처럼 변경이 퍼졌다.
기술 디테일은 AI가 채워줬다. “뭔가 이상한데?”라는 감각은 내가 붙잡고 있어야 했다.
같은 설명을 매번 반복하지 않으려고 CLAUDE.md를 만들었다
Claude Code는 매 세션마다 이전 대화를 기억 못 한다. 같은 설명을 매번 다시 해야 하는 게 답답해서 물어봤다. 답은 명료했다 — “CLAUDE.md파일을 프로젝트 루트에 만들어두면 매 세션 시작할 때 자동으로 읽는다.”
뭘 넣어야 하는지 물어봤더니 — 프로젝트 소개 한 단락, 코드 컨벤션(TypeScript strict, 파일 네이밍, 디렉토리 구조), 환경변수 목록, “절대 하지 말 것” 리스트, 그리고 점수 공식 요약.
실제로 만들어 넣으니 다른 세션에서 작업해도 컨텍스트가 거의 그대로 유지됐다.
특히 “절대 하지 말 것” 리스트가 핵심이었다. 일부:
- 스펙에 없는 기능 임의 추가 금지
- predictions 테이블의 점수·기준가 등 resolve 후 변경 금지 (immutable)
- 클라이언트 컴포넌트에서 service_role_key 노출 금지
이때 알았다. AI에게는 “무엇을 해줘”만큼이나 “무엇은 절대 하지 마”가 중요하다. 특히 제품이 커질수록 금지사항이 중요해진다. 스펙에 없는 기능을 친절하게 추가하지 말 것. 보안 키를 클라이언트에 노출하지 말 것. 이미 확정된 예측 결과를 다시 수정하지 말 것.
이런 문장을 CLAUDE.md에 적어두는 게, 매번 새 채팅에서 같은 잔소리를 반복하는 것보다 훨씬 나았다.
“완성”한 스펙에서 29개 이슈가 나온 날
약 1,500줄까지 쓴 시점에 “이제 거의 다 된 것 같다”라고 생각했다. 만약을 위해 Claude한테 “이 스펙 처음부터 끝까지 다시 검토해줘”라고 부탁했다.
5개 카테고리에 걸쳐 29개 이슈가 쏟아졌다.
잠깐 멍해졌다. 코딩 시작 전인데 이미 버그가 있었다.
가장 충격적이었던 건 두 가지였다. 하나는 베이스라인 봇이 매일 제출해야 하는데, DB 제약 조건이 같은 날짜·같은 자산·같은 기간의 중복 제출을 막고 있었다는 것. 둘 다 맞는 말처럼 보이는데 같이 두면 충돌이었다.
다른 하나는 over-engineering. 사용자도 없는데 댓글 시스템이 들어가 있었고, AI 예측 근거를 평가하는 알고리즘까지 있었다. 만들 수는 있지만, 지금 만들 이유는 없었다. (전부 MVP 이후로 밀어냈다.)
나머지는 SQL 코드블록이 어딘가에서 안 닫혀 있다거나, return이 정확히 0%일 때 어떻게 처리하느냐 같은 엣지케이스, 일관성 깨진 용어 등이었다.
그 자리에서 다 고치고 다시 검토를 한 번 더 돌렸다. 그래도 새로 작은 이슈 몇 개가 나왔다.
이때 명확해졌다 — AI가 만든 문서도 AI한테 다시 검토시켜야 한다. 그리고 검토할 때마다 “이거 지금 진짜 필요해?”를 같이 물어야 한다. AI는 요청하면 뭐든 만들어주기 때문에, 안 막으면 over-engineering이 자연스럽게 쌓인다.
코딩 대신 “설계 토론”
스펙 작성 끝나고 돌아보니 분담이 명확했다.
- Claude가 한 일: 구조화, 점수 공식 수식화, 데이터 모델, 코드 형태로 명세, 엣지케이스 나열.
- 내가 한 일: 질문, 직감, “이거 이상한데?”, “이거 진짜 필요해?”, 결정.
처음엔 AI가 스펙을 만들어주면 그대로 개발하면 될 줄 알았다. 실제로는 반대였다. 스펙 문서를 던진 뒤부터 진짜 일이 시작됐다.
AI는 구조를 만들고, 수식을 정리하고, 빠진 엣지케이스를 찾아줬다. 하지만 “이게 말이 되나?”, “지금 필요한가?”, “사용자가 이걸 악용할 수 있나?”를 묻는 일은 결국 내 몫이었다.
바이브 코딩은 코딩을 안 하는 일이 아니었다. 코딩의 앞단에서 훨씬 더 많은 결정을 말로 내리는 일이었다.
흔히 떠올리는 코딩 — 키보드로 한 줄씩 짜는 그 작업 — 은 AI가 대신 빠르게 해줄 수 있다. 그런데 “이걸 왜 만들지”, “어떻게 동작해야 맞지”, “이런 경우엔 어떻게 처리할까” 같은 결정은 여전히 사람의 몫이다. 그리고 결과물의 퀄리티는 결국 그 결정의 퀄리티에 달려 있다.
다음 글: 이 스펙을 실제로 Claude Code에 던져서 코드가 생성되기 시작한 날 이야기 — 그리고 Claude가 처음부터 잘못 알아들은 부분을 어떻게 잡았는지.
LDBD에 직접 예측을 등록해보고 싶다면 메인 페이지에서 시작할 수 있다. 무료, 가입 없이 리더보드 구경 가능.