Doggae Log

AI가 짠 코드를 믿지 못했다

2026년 6월 20일

30분 만에 짠 코드를 이틀 동안 들여다봤다

AI가 가계부 입력 폼을 30분 만에 짜줬다. 화면은 잘 떴고, 코드도 그럴듯했다.

그런데 그게 우리 코드베이스에 맞는 코드인지 확인하는 데 이틀이 걸렸다. 날짜는 프로젝트 전체를 dayjs로 통일해놨는데, 새 컴포넌트에는 date-fns가 끌려 들어와 있었다. 폼은 shadcn <Form> 패턴으로 가기로 했는데, raw <input>을 손으로 제어하고 있었다. 하나하나는 사소했다. 진짜 문제는, 어디가 어긋났는지 내가 파일을 전부 다시 읽기 전까지는 알 수 없다는 거였다.

생성은 30분, 검증은 이틀. 그제야 알았다. 내 병목은 AI가 코드를 못 짜는 게 아니었다. AI가 짠 코드를 내가 믿지 못한다는 거였다.

순서 없이 던지던 시절

처음엔 떠오르는 대로 프롬프트를 던졌다. "가계부 입력 폼 만들어줘." 동작은 했다. 하지만 매번 어딘가 어긋났고, 결국 내가 다시 열어 고치거나 새로 썼다. AI에게 자유를 줄수록 결과는 들쭉날쭉했고, 그 들쭉날쭉을 걸러내는 일이 통째로 내 몫으로 남았다.

가계부를 만들며 한 번 적었듯이, 해결은 더 좋은 모델로 바꾸는 게 아니었다. 일하는 순서를 고정하는 거였다. 그 글에선 거기까지만 적었다. 이 글은 그 "순서"의 정체에 대한 이야기다. 들여다보니, 그건 결국 검증 게이트를 줄줄이 박은 것이었다.

일곱 단계가 사실은 검문소였다

이 프로젝트의 구현 사이클은 이렇게 고정돼 있다.

① 명세 문서 읽기 → ② E2E 테스트 작성 → ③ 실행해서 실패 확인(Red) → ④ UI 컴포넌트 추출 + Storybook 스토리 → ⑤ /storybook-review로 내 승인 → ⑥ 페이지 조립 → ⑦ 실행해서 통과 확인(Green)

한동안 이걸 "만드는 절차"라고만 생각했다. 지금은 다르게 읽는다. 이 일곱 단계는 만드는 순서가 아니라, 내가 무엇을 어디서 확인할지 박아둔 검문소들이다.

  • 명세 먼저(①) — AI가 추측하지 못하게 막는 사전 게이트. 타입과 Firestore 스키마, 화면 상태를 문서에 먼저 못 박으면, AI가 빈칸을 상상으로 채우지 않는다.
  • E2E 테스트 먼저(②③) — 합격 기준을 말 대신 코드로 박는 게이트. "돌려보니 되던데요"는 못 믿는다. 빨간 테스트가 먼저 있어야 초록에 의미가 생긴다.
  • Storybook + /storybook-review(④⑤) — 내가 눈으로 보는 게이트. 이 단계 스킬은 코드를 고치지 않는다. 컴포넌트와 스토리를 표로 정리해 보여주고, 내가 "승인"이라고 말하기 전까진 페이지 조립으로 넘어가지 않는다.
  • 조립 후 Green(⑥⑦) — 자동 게이트. 매번 손으로 클릭해 확인하지 않아도, 테스트가 어긋남을 잡는다.

두 번째 게이트가 추상적으로 들리지만, 실제로는 이런 모양이다. 가계부 자연어 입력 — 한 줄을 넣으면 미리보기 카드가 뜨고, 추가하면 목록에 박힌다 — 의 합격 기준을 구현보다 먼저 코드로 못 박아 둔다.

test('한 줄 입력 → 미리보기 카드 1개 → 추가하면 목록 반영', async ({ authedContext }) => {
  const { page } = authedContext;
  await page.goto('/inner/cashbook/history');

  await page.getByTestId('ai-parse-input').fill('점심 김치찌개 9000원');
  await page.getByTestId('ai-parse-submit').click();

  await expect(page.getByTestId('parsed-entry-card')).toHaveCount(1);
  await page.getByTestId('ai-bulk-confirm').click();
  await expect(page.getByText('김치찌개').first()).toBeVisible();
});

이 빨간 테스트가 한 번은 진짜로 일을 했다. 다건 입력이 10건에서 잘려 한 달치가 일부만 들어가던 버그를, 내가 화면을 들여다보기도 전에 먼저 잡았다.

여기에 사이클 밖 게이트가 둘 더 있다. 구현 전에 명세 충돌을 훑는 /plan-page, 구현 후에 코드가 명세에서 샜는지 한 줄씩 대조하는 /review-page. 둘 다 코드는 건드리지 않고 보고서만 낸다. 그리고 마지막 게이트는 제일 단순하다 — 내가 직접 한 달 써본다.

각 게이트는 "AI가 여기서 또 어긋날 수 있다"고 내가 의심한 자리마다 박혔다. 30분짜리 생성을 이틀 동안 손으로 검증하던 그 일을, 단계마다 잘게 쪼개 박아둔 것뿐이다.

그래도 다 막아주진 못한다

솔직히 이 설계가 만능은 아니다.

사이클은 무겁다. 한 줄 고치는 데 일곱 단계는 과하다. 그래서 자주 건너뛰고, 가끔 그 건너뛴 자리에서 사고가 난다.

AI는 게이트 안에서도 여전히 어긋난다. 명세를 줘도 raw input을 짜고, 테스트가 빨간데 엉뚱한 데를 고친다. 게이트는 어긋남을 잡아준다. 다만 미리 막아주진 못한다. 빠져나가기 전에 한 번 더 거르는 그물일 뿐이다.

게다가 /storybook-review/review-page도 결국 내가 짠 스킬이다. 명세가 부실하면 감사도 부실한 채로 "이상 없음"이라고 자신 있게 말한다. 게이트의 품질은 딱 명세의 품질만큼이다.

가장 정직한 결론은 이거다. 이렇게 해도 빨라지진 않았다. 게이트도 시간을 먹는다. 대신 30분짜리 코드를 받을 때마다 이틀씩 통째로 다시 읽던 일이 사라졌다. 내가 산 건 속도가 아니라, 매번 전부를 의심하지 않아도 되는 상태였다.

AI는 그제서야 도구가 됐다

자동화가 일을 떠넘기는 거라고 생각했다. 막상 해보니 그 반대였다. 진짜 일은, AI가 짠 걸 어디서 어떻게 확인할지 미리 정해두는 데 있었다. 그 검문소들을 깔고 나서야 AI는 비로소 도구처럼 움직였다.

그래서 무엇을 만들었는지는 다른 글에 적었다. 이 글은 그걸 어떻게 만들었는지에 대한 기록이다.

관련 글