Hook
AI 디자인 도구로 페이지를 만들었습니다. 멋진 그라데이션, 깔끔한 카드 레이아웃, 완벽한 폰트. 그런데 막상 개발을 시작하려 보니 — “이 카드에 들어갈 데이터는 어디서 오지?”, “이 버튼을 누르면 무슨 API를 호출하지?”, “데이터가 없으면 뭘 보여주지?”.
프리뷰는 답을 주지 않습니다. 성공 상태의 시각적 아름다움만 보여줄 뿐입니다. 이 글에서는 프리뷰가 개발 순서를 지배하는 문제를 4단계 게이트로 통제한 방법을 공유합니다.
TL;DR
- 근본 원인은 “순서 역전” — 프리뷰(결과)를 먼저 만들고 데이터 구조(원인)를 나중에 맞추면 데드 버튼, 목 데이터, 상태 누락이 필연적으로 발생합니다
- 4단계 게이트로 올바른 순서를 강제합니다: 도메인 모델 → 데이터 스키마 → API 컨트랙트 → UI 구현 → 디자인 적용
- 3가지 불변 규칙: Data First, Contract First, No Dead Buttons
- 게이트를 우회하는 유일한 예외는
prototype/브랜치뿐입니다
Background: 프리뷰 역설이 만드는 부채
금지된 흐름
✗ 금지: Open Design 프리뷰 → 코드 작성 → 데이터 구조 설계
이 흐름을 따르면:
├── 하드코딩된 목 데이터 (const workingOut = 3; 대신 fetch('/api/active-users'))
├── 데드 버튼 (onclick={}, href="#", TODO 인터랙티브 요소)
├── UI를 위한 임시 필드 (badge_color 대신 subscription_tier)
├── 에러 무시 (프리뷰에 에러가 없으니 성공 케이스만 구현)
└── 상태 누락 (loading, error, empty, unauthorized 없음)
왜 AI 도구가 이 문제를 유발하는가
AI 디자인 도구는 완성된 화면을 생성하도록 훈련되어 있습니다. 완성된 화면에는:
- 데이터가 있습니다 (실제로는 하드코딩된 목 데이터)
- 에러가 없습니다 (성공 케이스만 렌더링)
- 모든 버튼이 작동합니다 (시각적으로는)
개발자는 이 “완성된” 프리뷰를 보고 “남은 건 API만 연결하면 되겠네”라고 착각합니다. 하지만 API가 없고, 에러 상태가 없고, 권한 모델이 없습니다.
Solution: 4단계 게이트 프로세스
필수 흐름
도메인 모델 데이터 스키마 API 컨트랙트 UI 구현 디자인 적용
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 비즈니스 │ → │ D1 / │ → │ 엔드포인트│ → │ 핸들러 │ → │ 스타일링 │
│ 도출 │ │ Storage │ │ 정의 │ │ 연결 │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
Gate 1 Gate 1 Gate 2 Gate 3 Gate 4
게이트별 통과 조건
| 게이트 | 통과 조건 | 리뷰어 | 핵심 질문 |
|---|---|---|---|
| Gate 1 | 도메인 모델 + DB 스키마 + 마이그레이션 SQL | 테크 리드 | 데이터가 도메인을 반영하는가? |
| Gate 2 | API 컨트랙트 + 검증 스키마 + 라우트 + 테스트 | 테크 리드 | API가 데이터를 노출하는가? |
| Gate 3 | UI 구현 + 모든 인터랙티브 요소 API 연결 + 상태 처리 | PR 리뷰어 | 데드 버튼이 없는가? |
| Gate 4 | 디자인 적용 + 시각적 정합성 | 디자인 리뷰 | 의도된 UX가 구현되었는가? |
3가지 불변 규칙
① Data First — 프리뷰에서 역추론 금지
스키마는 도메인에서 도출합니다. 프리뷰에 “뱃지 색상” 필드가 있다고 해서 badge_color 컬럼을 만들지 않습니다. 대신 의미 있는 도메인 속성(subscription_tier)에서 색상을 파생합니다.
// ✗ 안티패턴: 프리뷰에 맞춘 임시 필드
{ badge_color: "gold" }
// ✓ 올바름: 도메인에서 파생
{ subscription_tier: "premium" }
// → UI에서 tier에 따라 색상 매핑
② Contract First — 인터랙티브 요소는 API 정의 선행
모든 클릭 가능한 요소는 구현 전에 API 엔드포인트와 조작이 정의되어야 합니다. “나중에 연결할게”라는 버튼은 존재하지 않습니다.
③ No Dead Buttons — 데드 버튼 프로덕션 배포 금지
<!-- ✗ 데드 버튼 -->
<button onclick="{}">저장</button>
<a href="#">더 보기</a>
<button>삭제</button> <!-- TODO: 구현 예정 -->
<!-- ✓ 올바름: 미구현은 명시적으로 -->
<button disabled title="다음 버전에서 사용 가능">저장</button>
게이트 우회 금지
| 우회 패턴 | 결과 |
|---|---|
| Gate 1 없이 Gate 3부터 시작 | ❌ 데이터 구조가 UI에 종속됨 |
| “나중에 데이터 구조 고칠게”라며 UI부터 | ❌ 전면 재작업 필요 |
| 프리뷰 예시 데이터를 실제 DB에 | ❌ 목 데이터 잔류 |
유일한 예외:
prototype/브랜치에서만 스파이크 허용 (main 머지 금지).
필수 상태 처리
프리뷰는 성공만 보여주므로, 반드시 4가지 상태를 직접 구현해야 합니다:
| 상태 | 설명 | 프리뷰에 표시되는가? |
|---|---|---|
| loading | 데이터 로딩 중 | ❌ |
| error | 에러 발생 | ❌ |
| empty | 데이터 없음 | ❌ |
| unauthorized | 인증 실패 | ❌ |
인터랙티브 요소 추적 표
페이지별로 모든 클릭 가능 요소를 추적 표에 등록합니다:
| 요소 | 타입 | API 엔드포인트 | 상태 처리 |
|------------|--------|------------------------|-----------------|
| 저장 버튼 | button | POST /api/v1/workouts | loading/error |
| 삭제 버튼 | button | DELETE /api/v1/workouts| confirm/error |
| 더 보기 | link | GET /api/v1/feed?page=2| loading/empty |
프리뷰 만료 규칙
구현 완료 후 30일 경과 시 프리뷰는 보관 자료로 전환됩니다. 이후 프리뷰와 코드가 충돌하면 항상 구현 코드가 우선입니다.
Result: 게이트 도입 전후
| 지표 | 게이트 도입 전 | 게이트 도입 후 |
|---|---|---|
| 데드 버튼 (PR당) | 5~8개 | 0개 (Gate 3에서 차단) |
| 상태 누락 | loading/error/empty 중 평균 2개 누락 | 4가지 상태 모두 구현 |
| 목 데이터 잔류 | 프로덕션에서 mock 변수 발견 | 0건 (정적 탐지) |
| 프리뷰-코드 불일치 | 빈번 (프리뷰가 stale) | 30일 후 코드 우선 |
Takeaway
프리뷰는 “완성된 화면”이라는 환상을 줍니다 — AI 디자인 도구가 만든 프리뷰에는 데이터가 하드코딩되어 있고, 에러가 없고, 모든 버튼이 작동하는 것처럼 보입니다. 이 환상에 속아 “API만 연결하면 끝”이라고 생각하면 데드 버튼, 상태 누락, 목 데이터 잔류라는 부채가 누적됩니다. 프리뷰를 디자인 참고 자료로만 사용하고, 개발 순서의 기준으로 삼지 않아야 합니다
게이트는 “순서 강제” 장치입니다 — 도메인 모델 → 스키마 → 컨트랙트 → UI → 디자인 순서를 강제하는 것이 게이트의 본질입니다. Gate 1을 통과해야 Gate 2를, Gate 2를 통과해야 Gate 3를 진행할 수 있습니다. 이 강제가 귀찮게 느껴질 수 있지만, 순서를 우회해서 생기는 재작업 비용이 훨씬 큽니다
프리뷰는 30일이면 만료됩니다 — 프리뷰는 구현을 안내하는 임시 자료이지 영구 명세가 아닙니다. 구현이 완료된 후 코드와 프리뷰가 충돌하면 항상 코드가 이깁니다. 이 규칙이 없으면 stale 프리뷰를 근거로 AI가 이미 변경된 코드를 과거 상태로 되돌리는 일이 발생합니다
| ← 이전 | 시리즈: AI 주도 개발 거버넌스 | 다음 → |
|---|---|---|
| (1) 10가지 함정 | (2) 4단계 디자인 게이트 | (3) 모놀리식 HTML 분할 |
게이트웨이 On-promise 제품 팀에서 시스템 모니터링 및 관리를 쉽게 다가갈 수 있도록 하기 위한 업무를 하고 있습니다.
Contact: lhjnano@gmail.com