AI에게 기능을 맡기면 첫 구현은 빨리 나온다. 비용은 다음 변경에서 드러난다. 중복된 로직과 어긋난 타입이 쌓이고, 같은 개념을 여러 이름으로 표현하면 다음 에이전트가 코드의 뜻부터 추측해야 한다. 팀은 코드를 생성하는 시간보다 생성된 코드를 이해하고 고치는 데 더 많은 시간을 쓴다.
DORA는 2025년 연구에서 AI를 조직의 강점과 약점을 확대하는 증폭기로 설명했다. 명확한 피드백 시스템을 갖춘 팀은 AI로 검증 주기를 줄일 수 있다. 기준이 흐린 팀은 결함과 재작업도 빠른 속도로 쌓는다. Claude Code와 Codex를 여러 작업에 붙이면서 나도 같은 장면을 봤다. 모델을 바꾸기 전에 코드베이스와 검증 절차를 정리해야 했다.
이 블로그에서 상태값이 문제를 드러냈다
이 블로그에는 공개 글과 검토 중인 글이 함께 있다. 처음에는 published 값만으로 노출 여부를 관리했다. 검색엔진에 보여 줄 글, 주소는 유지하되 검색에서 제외할 글, 작성 중인 글을 하나의 값으로 다루니 코드마다 published의 뜻이 달라졌다.
지금은 발행 상태와 콘텐츠 등급을 분리한다.
| 필드 | 값 | 코드가 답해야 하는 질문 |
|---|---|---|
status | draft, published | 독자가 이 글을 열 수 있는가 |
visibility | primary, archive | 검색과 광고의 주력 콘텐츠인가 |
reviewed_at | 날짜 또는 빈 값 | 사람이 최종 검토했는가 |
이 구분을 타입과 데이터베이스 제약 조건에 함께 적었다. sitemap은 published + primary만 가져온다. 광고 스크립트도 같은 조건을 사용한다. archive 페이지는 주소를 유지하면서 noindex를 반환한다. 에이전트가 새 페이지를 만들 때 자기 식으로 공개 기준을 해석할 여지가 줄었다.
한 개념을 여러 파일에서 다르게 부르면 프롬프트가 길어진다. 개념을 타입으로 고정하면 프롬프트는 짧아지고 검토자는 diff에서 규칙 위반을 찾는다. 이 정리가 다음 변경에 필요한 시간을 줄인다.
구현 전에 네 줄을 고정한다
나는 구현 판단에 필요한 네 줄부터 쓴다.
목표: 이번 변경으로 달라지는 사용자 행동
범위: 수정해도 되는 파일과 외부 시스템
불변 조건: 기존 주소, 데이터, 권한 중 지켜야 할 항목
완료 조건: 실행할 검사와 확인할 화면
“블로그 품질을 높인다”는 목표로는 구현을 시작하지 않는다. “검토한 20편만 /posts와 sitemap에 노출하고 나머지 주소는 archive에서 유지한다”처럼 결과를 관찰할 수 있어야 한다. 불변 조건에는 기존 slug 보존과 Supabase 원본 백업을 적는다. 완료 조건에는 콘텐츠 개수, canonical URL, noindex, 빌드 성공을 넣는다.
설계 결정은 코드가 쓰는 단어로 기록한다. 문서에서 “주요 글”이라고 부르고 타입에서 featured라고 부르면 에이전트가 둘의 관계를 추측한다. 문서와 타입, 테스트가 primary라는 이름을 공유하면 새로운 세션도 같은 규칙을 읽을 수 있다.
에이전트가 읽을 수 있는 코드베이스
AI 코딩에서 기본기는 타자 속도와 거리가 멀다. 나는 다음 항목을 먼저 살핀다.
- 한 개념이 한 모듈에 모여 있는가
- 타입이 허용된 상태와 금지된 상태를 구분하는가
- 테스트와 검사 명령이 성공 여부를 판정하는가
- 로그가 실패한 위치와 입력을 보여 주는가
- 실행 권한이 작업 디렉터리와 승인 범위를 지키는가
예를 들어 primary slug 목록을 페이지마다 복사하면 목록을 바꿀 때 누락이 생긴다. 한 파일에서 목록을 내보내고 sitemap, 글 목록, 광고 조건이 그 값을 읽게 하면 변경 지점이 하나로 줄어든다. 데이터베이스에도 같은 규칙을 적용하되, 배포 시점에 스키마가 아직 바뀌지 않았을 가능성까지 코드가 처리해야 한다.
오류 메시지도 인터페이스다. invalid content만 출력하면 사람과 에이전트가 원인을 찾으려고 파일을 다시 훑는다. 파일명, 빠진 필드, 기대한 값을 출력하면 다음 행동이 정해진다.
content/posts/example.md: reviewed_at is required for primary content
파일명과 빠진 필드를 담은 메시지는 설명 문서보다 실행 중에 더 자주 읽힌다. 검사 코드는 팀의 규칙을 구체적인 형태로 보관한다.
작은 변경마다 증거를 남긴다
에이전트에게 전체 기능을 한 번에 맡기면 실패 지점을 찾기 어렵다. 나는 현재 상태를 읽힌 뒤 한 종류의 변경만 맡기고 곧바로 검사한다.
npm run typecheck
npm run content:audit
npm run content:links
npm run build
명령마다 확인하는 대상이 다르다. 타입 검사는 코드 계약을 확인한다. 콘텐츠 감사는 필수 메타데이터와 20편 목록을 비교한다. 링크 검사는 독자가 열 출처를 확인하고, 프로덕션 빌드는 Next.js가 모든 라우트를 만들 수 있는지 보여 준다.
테스트가 없는 작업에도 판정 기준을 만들 수 있다. 데이터 마이그레이션은 dry-run에서 행 개수와 삭제 대상을 출력한다. UI 변경은 대표 URL의 HTML에서 canonical과 robots 값을 확인한다. 글 편집은 제목 중복, 출처 URL, 헤딩 구조를 검사한다. 작업 종류가 달라도 에이전트의 설명만으로 완료를 승인하지 않는 원칙은 유지한다.
diff에서 생성량보다 변경 이유를 본다
AI는 요청하지 않은 정리까지 한꺼번에 시도할 때가 있다. 기능이 동작해도 diff가 넓으면 다음 변경 비용이 커진다. 검토할 때 나는 파일별로 한 문장씩 이유를 붙여 본다.
lib/primary-content.ts: 주력 글 목록을 한곳에 둔다
app/sitemap.ts: 주력 글만 검색엔진에 전달한다
app/archive/[slug]/page.tsx: 기존 주소의 내용을 보존한다
이유를 설명하기 어려운 파일은 작업 범위를 벗어났을 가능성이 크다. 생성된 코드의 줄 수는 진척도를 말해 주지 않는다. 요구사항을 만족하는 데 필요한 변경만 남았는지, 되돌릴 단위가 분명한지를 함께 본다.
같은 오류를 두 번 고쳤다면 대화 기록에만 교훈을 남기지 않는다. 타입을 좁히고 검사 항목을 추가하거나 발행 기본값을 안전한 쪽으로 바꾼다. 이 블로그의 새 글 발행 기본값을 draft + archive로 둔 이유도 같다. 검토자가 published + primary를 명시해야 검색과 광고에 들어간다.
AI가 낮춘 비용은 초안 작성에 집중돼 있다. 설계 개념을 정하고, 변경 범위를 제한하고, 실행 결과로 품질을 판정하는 일은 여전히 개발자의 몫이다. 나는 새로운 모델의 출력 차이를 살피면서도 타입, 테스트, 로그, 복구 절차에 더 많은 시간을 쓴다. 그 작업이 다음 기능의 속도를 결정한다.