2탄 권2 제0·1장
2탄 권2 — 제0·1장

Phase 1.0 BUILD 시작 + API 4개 인증

매일 코드 + LogOnTable + MCP + Agent Teams의 28일 여정 시작

📑 이 챕터에서 다룰 내용
서문 권1에서 권2로

권1 (Phase 0)이 21시간으로 완성됐습니다. 5파일+ 체계가 가동되고, 5확장 5개가 등장했고, READY: YES를 받았습니다.

이제 코드 작업이 시작됩니다 — 권2 Phase 1.0 BUILD. 권1과 다른 톤이 시작됩니다.

본질문서 패턴
권1 (Phase 0)"무엇·언제·READY" (메타·계획)SPEC·PLAN·REVIEW 만드는 자리
권2 (Phase 1.0)"매일 코드 + LogOnTable" (실행)BUILD.md 매일 갱신·코드 트레이스
권3 (Phase 1.1 + 출시)"통계·알림·심사·커뮤니티" (마무리)BUILD 마지막 + 외부 노출

권2는 28일 (Phase 1.0). G1 (Day 7) + G2 (Day 28) 두 게이트.

0-1 Phase 1.0 4주 구조 — 28일 한 페이지 🔗
📘 들어가며 — "매일 펼치는" 자리

권1 제7장 끝에서 git tag v0.1-phase0-completed가 박혔습니다. 이제 매일 펼치는 챕터들이 시작됩니다. 이 첫 챕터는 권2 28일 동안 어떻게 일할 것인가의 가이드입니다. 매일 무엇을 펼치고, 무엇을 적고, 어떻게 게이트를 통과하는지 안내합니다.

  • 사전 지식: 권1 (Phase 0) 완성 / git tag v0.1-phase0-completed
  • 이 장의 목적: Phase 1.0 28일의 매일 양식 + G1·G2 게이트 흐름 + MCP·Agent Teams·E4·E2 운영 매뉴얼
  • 완료 후 결과물: Phase 1.0 첫째 날 시작 준비 완료
💻 Phase 1.0 4주 구조
┌──────────────────────────────────────────────────────────────┐
│ 1주차 (Day 1~7) — G1: API 4개 + 시딩 20개                    │
│   Day 1: API 4개 인증 신청 (개발계정 30분 자동승인)          │
│   Day 2: API 호출 테스트 + 응답 sample 저장                  │
│   Day 3: LLM 분류 prompts.ts + Haiku 첫 5건 분류             │
│   Day 4: 시딩 데이터 20개 검수 (수동) + confidence 분포 확인 │
│   Day 5: G-1 LLM JSON fallback + raw_response 보존           │
│   Day 6: G1 통과 조건 점검 + 문서화                          │
│   Day 7: G1 통과 의식 (git tag + 휴식 의무)                  │
├──────────────────────────────────────────────────────────────┤
│ 2주차 (Day 8~14) — Supabase 프로젝트 + 핵심 SQL              │
│   Day 8: Supabase 프로젝트 + 4 테이블 SQL + IP rate limit Redis│
│   Day 9: trigger 3개 (jupjups 집계 / IP rate / is_active)    │
│   Day 10: 복합 인덱스 (P1) + benefits seed 20개 INSERT       │
│   Day 11: pg_cron 매일 03:00 정상 (4 API 호출)               │
│   Day 12: 어드민 검수 큐 페이지 (Day 5 LLM JSON fallback 결과)│
│   Day 13: 운영자 검수 SLA 7일 알림 (cron 매일 09:00)         │
│   Day 14: 운영자 페이스 점검 ★ (E2 누적 시간 & 회고)         │
├──────────────────────────────────────────────────────────────┤
│ 3주차 (Day 15~21) — 카카오 인증 + 닉네임 + FCM               │
│   Day 15: 카카오 로그인 + access·refresh 토큰 자동 갱신      │
│   Day 16: 닉네임 자동 부여 (형용사 풀 20개+ + fallback)      │
│   Day 17: FCM 토큰 발급·만료 처리                            │
│   Day 18: User profile 자발적 필터 (소상공인 / 개인)         │
│   Day 19: 어드민 페이지 (검수 + 신고 처리 SLA 24h)           │
│   Day 20: 신고 처리 자동 알림                                │
│   Day 21: 토요일 작업 X (E2 의식)                            │
├──────────────────────────────────────────────────────────────┤
│ 4주차 (Day 22~28) — G2: DB·인증·pg_cron 완성                 │
│   Day 22~23: 통합 테스트 (4 테이블 + 트리거 + 카카오 + pg_cron)│
│   Day 24: 통계 조작 시도 시뮬 (UNIQUE + IP rate limit 검증)  │
│   Day 25: G2 통과 조건 검증 (★ 페이스 점검 포함)             │
│   Day 26: G-3 is_active 트리거 SQL 검증                      │
│   Day 27: G2 통과 점검 (모든 항목 PASS)                      │
│   Day 28: G2 통과 의식 (git tag + 1일 휴식)                  │
└──────────────────────────────────────────────────────────────┘

24일 작업 + 4일 의식·휴식·점검 = 28일.

0-2 매일 양식 — BUILD.md 5분 entry 🔗

권1에서 사용한 양식이 그대로 이어집니다. BUILD.md에 매일 5분 양식 + LogOnTable 트레이스를 작성합니다.

💻 BUILD.md 매일 entry 양식
## Phase 1.0 Day N (날짜)

[계획]
- [PLAN.md Day N 작업 항목 그대로]

[실행]
- [실제 산출물 — 코드 줄 수·파일·기능]

[LogOnTable 트레이스]
> 결정: [핵심 결정 한 줄]
> 근거: [왜 그 결정인가]
> 대안 비교: [다른 옵션과 트레이드오프]
> 부작용: [부산물·미래 부담]

[이슈]
- [발생/해결, 없으면 "없음"]

[누적 시간] [Phase 0 21h] + [Phase 1.0 누적] = [총합]

[E2 페이스 점검]
- 누적 시간 60h 트리거 거리: [Xh 여유]
- 토요일 작업 X 준수
- 주간 회고: 일요일 30분
💡 매일 5분 양식의 본질

매일 4시간 일하면 그 중 5분만 BUILD.md 갱신합니다. 그러나 그 5분이 6개월 후 동업자가 펼치는 답 + R4 1인 번아웃 트리거 자동 감지 + 권3 회고 자료가 됩니다.

5분 × 28일 = 약 2.3시간. 2시간으로 28일 운영 매뉴얼이 박힙니다.

0-3 MCP 활용 — 매일 시간 절감 🔗

1탄 v2 새 14장에서 본 MCP가 권2부터 작동합니다.

줍줍 권2의 MCP 조합 (3개 필수)

💻 MCP 설정
[필수 3개]
- Supabase MCP: DB 자연어 조작 (Day 8 이후 매일)
- GitHub MCP: commit + PR 자동 (매일)
- Playwright MCP: E2E 테스트 (Day 14·21·28 게이트)

[선택 1개 — 출시 전 도입]
- Slack MCP: 운영 알림 (Day 19~)

MCP 시간 절감 측정

작업MCP 없이MCP 있을 때절감
DB 조회3분 (Supabase 대시보드)10초 (자연어)-90%
commit2분 (수동)5초 (자연어)-95%
E2E 테스트5분 (Playwright 직접)30초 (자연어)-90%
매일 약30~50분MCP 활용-80%

권2 끝 (Day 28)까지 누적 약 24시간 절감. R4 1인 번아웃 핵심 완화책입니다.

0-4 Agent Teams 적용 시점 — Day 5+ 🔗

권2의 Agent Teams 적용은 Day 5+부터. 처음 4일 (Day 1~4)은 순차 작업을 권장합니다. 이유: BUILD 첫 흐름 이해가 우선이기 때문입니다.

CLAUDE.md §6 File Structure 분배 기준

💻 폴더 구조 분배
src/
  app/(tabs)/business/      ← 에이전트 A (소상공인)
  app/(tabs)/individual/    ← 에이전트 B (개인 복지)
  components/               ← 공통 (직접 수정)
  lib/                      ← 공통 (직접 수정)
  api/                      ← 에이전트 C (Day 8 이후 가능)
tests/                      ← 통합 단계

Agent Teams 적용 vs 순차 결정 트리

💻 Day N 작업 시작 전 자기 질문
질문 1: 작업 A의 결과를 작업 B가 기다려야 하는가?
  YES → 순차 (독립성 X)
  NO → 다음 질문

질문 2: A·B가 서로 다른 폴더를 수정하는가?
  YES → Agent Teams 가능
  NO → 순차 (충돌 위험)

질문 3: 각 작업이 30분+ 걸리는가?
  YES → Agent Teams 효율
  NO → 순차 (위임 비용 > 절감)

3 질문 모두 YES일 때만 Agent Teams. 한 번이라도 NO면 순차입니다.

0-5 E4 LogOnTable 매일 — 4 요소 표준화 🔗

권2부터 LogOnTable이 매일 등장합니다. 권1의 제0~7장에 흩어진 7 트레이스가 매일 1~3 트레이스로 증가합니다.

4 요소 작성 표준화

요소길이내용
결정1줄핵심 결정 짧게
근거1~2줄왜 그 결정인가 (SPEC §·PLAN·CLAUDE §·표준 도구 매핑·1탄 v2 챕터 등 인용)
대안 비교1~3줄다른 옵션과 트레이드오프
부작용1줄부산물·미래 부담
💡 6개월 후 동업자 시나리오

6개월 후 비개발자 동업자가 BUILD.md를 펼치면 약 100~200개 트레이스 (권2+권3 합계)가 있습니다. 동업자가 "왜 jupjups 테이블에 UNIQUE 제약?" 질문하면 Day 8 트레이스에 답이 있습니다.

매일 5분이 "인수인계 매뉴얼"의 본질입니다.

0-6 E2 매일 페이스 점검 — 누적 시간 자동 추적 🔗

권1 제6장에서 박힌 R4 (1인 번아웃) 트리거 4가지가 매일 점검됩니다.

💻 E2 매일 자동 점검 양식
[BUILD.md 매일 entry 끝에]

[E2 페이스 점검]
- 누적 시간: [Phase 0 21h] + [Phase 1.0 누적 Xh] = [Total]
- 60h/4주 트리거 거리: [Yh 여유]
- 토요일 작업 X: [Day 21·28 점검]
- 주간 회고 (일요일 30분): [Day 7·14·21·28 작성 의무]

[트리거 발생 시]
[ ] 누적 60h+/4주
[ ] 회고 부재 2주+
[ ] 토요일 4주 연속
[ ] "피곤하다" BUILD.md 회고 등장

위 4 트리거 중 하나라도 발생 시 → 즉시 1일 휴식 + 다음 주 목표 25% 축소.

게이트 통과 의식 의무

💻 게이트 통과 의식
G1 (Day 7) 통과 후:
- git tag v0.1-G1-passed
- 1일 휴식 (Day 8 휴식 또는 Day 7 일요일 휴식)
- 1주차 회고 (BUILD.md 1KB+)

G2 (Day 28) 통과 후:
- git tag v0.1-G2-passed
- 1일 휴식 (Day 29)
- 4주 회고 (BUILD.md 2KB+, 큰 회고)

📌 제0장 정리

  • 권2 = 매일 코드 + LogOnTable + MCP + Agent Teams + E4·E2 운영. 28일 (G1·G2 두 게이트)
  • 4주 구조: 1주차 (G1 D7) API + 시딩 / 2주차 Supabase + SQL / 3주차 카카오 + FCM / 4주차 (G2 D28) 통합 테스트
  • 매일 5분 양식: 계획 + 실행 + LogOnTable + 이슈 + 누적 + 페이스
  • MCP 조합: Supabase + GitHub + Playwright (필수 3개) + Slack (Day 19~ 선택)
  • Agent Teams: Day 5+ 시작. CLAUDE.md §6 폴더 구조가 분배 기준
  • E4 매일: 4 요소 트레이스 (결정·근거·대안·부작용). 권2 누적 40~50개 예상
  • E2 매일 점검: 4 트리거 자동 + 2번 게이트 휴식 + 4번 주간 회고
1-1 Day 1 — 공공데이터 API 4개 인증 🔗
📘 들어가며 — 첫 코드의 자리

이 장이 줍줍의 첫 실제 코드 작업 자리입니다. Day 1~3은 외부 의존성 (공공데이터 API 4개)부터 시작합니다 — 가장 위험한 부분을 먼저 처리합니다.

  • 사전 지식: 권1 + 권2 제0장 / Claude Code 실행 + 5파일 모두 / data.go.kr 가입 의도
  • 이 장의 목적: Day 1 (API 4개 인증) + Day 2 (호출 테스트) + Day 3 (LLM 분류 prototype)
  • 완료 후 결과물: API 4개 인증키 (.env.local) + 응답 sample 4개 + LLM 분류 prompts.ts + 첫 5건 자동 분류

작업 시작

💻 Phase 0 끝 → Phase 1.0 시작
cd jupjup
git status                  # 깨끗한 상태 확인
cat REVIEW.md | tail -3     # READY: YES 확인

claude
/model claude-sonnet-4-6    # BUILD는 Sonnet (1탄 v2 새 28장)
/effort medium
/context                    # 5파일 + MCP 활성 확인

4 API 인증 신청 — data.go.kr 통합 진행

💻 Claude Code 자연어 입력
"4 API 인증 신청 가이드를 단계별로 줘. data.go.kr·기업마당·중소벤처24·
복지로 (중앙·지자체) 4개. 각각의 가입·활용신청·키 받는 시간을 명시.

CLAUDE.md §3 Critical Constraints 준수 — 키는 .env.local에만,
GitHub 노출 X."

Claude (Sonnet) 응답 — 단계별 가이드

1
data.go.kr 가입 (5분)

data.go.kr → 회원가입 (개인·일반). 휴대폰 본인인증 또는 카카오 인증.

2
4 API 활용신청 (10분)

마이페이지 → 활용신청 → API 검색. 4개 신청: 기업마당 BizinfoApi / 중소벤처24 / 복지로 중앙부처 / 복지로 지자체

3
개발계정 자동승인 (30분 안에)

4개 모두 자동승인. 운영계정은 1~2일 소요 (배포 직전 신청).

4
.env.local 저장 (필수)

키 받은 후 즉시 .env.local에 저장. .gitignore가 .env.local 차단하는지 확인합니다.

💻 .env.local 저장
PUBLIC_DATA_BIZINFO_KEY=...
PUBLIC_DATA_SMTECH_KEY=...
PUBLIC_DATA_WELFARE_CENTRAL_KEY=...
PUBLIC_DATA_WELFARE_LOCAL_KEY=...
⚠️ .env.local 주의사항

ANTHROPIC_API_KEY 등도 같은 곳에 저장합니다. .env가 아니라 .env.local이 표준입니다 (Next.js / Expo 모두 동일).

.gitignore.env*, .env.local, .env.local.* 4개 패턴 확인 필수입니다.

Day 1 첫 LogOnTable

📘 BUILD.md Day 1 entry

[LogOnTable 트레이스 ① — 첫 트레이스]

  • 결정: data.go.kr 통합 게이트웨이 사용 (4 API 모두)
  • 근거: 별도 가입 4번 X, 한 번의 회원가입으로 4 API 인증 가능
  • 대안: 각 부처 사이트 직접 가입 — 인증 4번 + 키 관리 4번 = 부담
  • 부작용: data.go.kr 장애 시 4 API 모두 영향. R7 리스크에 추가 트리거

[LogOnTable 트레이스 ② — .env.local 보안]

  • 결정: 4 API 키를 .env.local에 저장 (커밋 X)
  • 근거: CLAUDE.md §3 Critical Constraints 절대 준수
  • 부작용: 운영계정 발급 시 새 키 4개 추가 (Day 60~ EAS Secrets 분리)

누적: Phase 0 21h + Phase 1.0 Day 1 1.5h = 22.5h

💻 Day 1 commit
git add .gitignore  # .env.local 추가됐는지 확인 (수정 없으면 스킵)
git commit --allow-empty -m "Phase 1.0 Day 1: 4 API 개발계정 인증 + .env.local"
1-2 Day 2 — API 호출 테스트 + 응답 sample 저장 🔗
💻 Claude Code 자연어 입력
"4 공공데이터 API 호출 테스트 스크립트 작성 + 응답 sample 4개 저장.

요구사항:
1. lib/api/test-fetch.ts (4 API 각각 fetch 함수)
2. tests/api-samples/ 폴더에 응답 raw JSON 4개 저장
3. CLAUDE.md §3 — process.env 키 사용
4. 에러 처리 (try/catch + Sentry 호환 구조 — 실제 Sentry는 Day 56)
5. JSON.parse 실패 시 raw text 보존"

Claude가 작성한 lib/api/test-fetch.ts (약 80줄)

💻 lib/api/test-fetch.ts
// lib/api/test-fetch.ts
// 4 공공데이터 API 호출 테스트

interface ApiSample {
  source: string;
  url: string;
  count: number;
  raw_response: any;
  parse_success: boolean;
  error?: string;
}

const API_KEYS = {
  bizinfo: process.env.PUBLIC_DATA_BIZINFO_KEY!,
  smtech: process.env.PUBLIC_DATA_SMTECH_KEY!,
  welfare_central: process.env.PUBLIC_DATA_WELFARE_CENTRAL_KEY!,
  welfare_local: process.env.PUBLIC_DATA_WELFARE_LOCAL_KEY!,
};

export async function testFetchAll(): Promise<ApiSample[]> {
  const results: ApiSample[] = [];

  for (const [source, url] of Object.entries(ENDPOINTS)) {
    try {
      const fullUrl = `${url}?serviceKey=${encodeURIComponent(key)}&numOfRows=5&type=json`;
      const resp = await fetch(fullUrl, {
        method: 'GET',
        headers: { 'Accept': 'application/json' },
      });

      const text = await resp.text();
      let raw_response: any;
      let parse_success = true;

      try {
        raw_response = JSON.parse(text);
      } catch {
        raw_response = text;  // raw text 보존 (G-1 fallback 패턴)
        parse_success = false;
      }

      results.push({ source, url, count, raw_response, parse_success });
    } catch (err: any) {
      results.push({
        source, url, count: 0,
        raw_response: null,
        parse_success: false,
        error: err.message,
      });
    }
  }

  return results;
}

실행 결과 (실제 호출)

🎉 호출 결과
  • ✓ bizinfo: 5 items, parse_success
  • ✓ smtech: 5 items, parse_success
  • ✓ welfare_central: 5 items, parse_success
  • ⚠️ welfare_local: 5 items, parse_success — but XML 형식 (JSON 아님)

G-1 (LLM JSON fallback)이 즉시 의미 있어집니다. welfare_local API가 XML 응답하는 경우 발견 — Gemini가 선견적으로 잡은 영역입니다.

📘 BUILD.md Day 2 LogOnTable

[LogOnTable 트레이스 ① — Gemini의 선견 입증]

  • 결정: welfare_local API XML fallback — Day 3 LLM 분류 prompts.ts에 "raw text 처리 분기" 추가
  • 근거: 권1 제2장 Gemini G-1 (LLM JSON fallback)의 "실제 발생". SPEC v4 §3-2의 fallback 명세가 사용 직전에 입증됨
  • 부작용: prompts.ts에 if-else 분기 추가 (XML → text → LLM 분류)

누적: 22.5h + Day 2 (2h) = 24.5h

1-3 Day 3 — LLM 분류 prompts.ts + 첫 5건 자동 분류 🔗

Claude가 작성한 lib/parser/prompts.ts (약 90줄)

💻 lib/parser/prompts.ts — 소상공인 시스템 프롬프트
export const SYSTEM_PROMPT_BUSINESS = `
당신은 정부 지원사업 공고를 분석하는 전문가입니다.
아래 공고 원문을 읽고, 반드시 JSON 형식으로만 응답하세요.
JSON 외의 설명, 마크다운, 백틱은 절대 포함하지 마세요.

추정하지 마세요. 정보가 없으면 null로 표시하세요.

[출력 스키마 — 소상공인 타겟]
{
  "name": "사업명",
  "support_type": "GRANT | LOAN | CONSULTING | SPACE",
  "target_business_type": "식당/카페 | 도소매 | 제조업 | ...",
  "target_stage": "창업준비 | 1년미만 | 3년미만 | ...",
  "deadline": "YYYY-MM-DD 또는 null",
  "confidence": 0.0
}
`;

Claude가 작성한 lib/parser/classify.ts (약 70줄)

💻 lib/parser/classify.ts — Haiku 4.5 호출
export async function classifyContent(
  raw_content: string,
  tab: 'business' | 'individual',
): Promise<ClassifyResult> {
  const resp = await client.messages.create({
    model: 'claude-haiku-4-5-20251001',
    max_tokens: 1024,
    system,
    messages: [{ role: 'user', content: raw_content }],
  });

  // E5 [7] — JSON parse 실패 시 fallback
  try {
    const parsed = JSON.parse(text);
    const confidence = parsed.confidence ?? 0;

    // confidence 0.7 미만 → admin 큐 직행
    if (confidence < 0.7) {
      return { ...parsed, confidence, status: 'admin_queue', raw_response: text };
    }
    return { ...parsed, confidence, status: 'auto_active', raw_response: null };
  } catch {
    return { confidence: 0, status: 'admin_queue_parse_failed', raw_response: text };
  }
}

첫 분류 실행 결과

🎉 첫 5건 자동 분류 결과
  • "수원시 카페 청년 지원금" → confidence 0.85, auto_active ✓
  • "전국 소상공인 컨설팅 지원" → confidence 0.79, auto_active ✓
  • "서울시 중장년 창업 자금" → confidence 0.72, auto_active ✓
  • "광역지자체 음식점업 시설 개선 보조" → confidence 0.65, admin_queue
  • "일반 사업자 정책자금 안내" → confidence 0.45, admin_queue

3건 auto_active + 2건 admin 큐. SPEC v4 §3-2의 "confidence 0.7 미만 → admin 큐"가 작동 입증됐습니다.

📘 Day 3 LogOnTable

[LogOnTable 트레이스 ① — 환각 방지 명세]

  • 결정: 두 시스템 프롬프트에 "추정 X, 누락 null" 명시 + JSON 외 X
  • 근거: SPEC v4 §3-2 (E5 [7]) 코드 적용. 첫 5건 분류에서 입력에 없는 필드는 null로 처리됨
  • 부작용: confidence < 0.7인 2건 admin 큐 — Day 12 어드민 페이지 작업과 직결

[LogOnTable 트레이스 ② — Haiku 4.5 비용]

  • 결정: Haiku 4.5 사용 (분류 단순 작업)
  • 근거: 1탄 v2 새 28장 비교 우위 표 — 단순 분류는 Haiku 자리. Sonnet 비용의 1/4. 월 1,500건 약 $1.5
  • 부작용: 복잡한 한국어 공고 시 confidence 낮아질 수 있음 → admin 큐로 자연 처리

누적: 24.5h + Day 3 (3h) = 27.5h

1-4 Day 1~3 누적 — 첫 LogOnTable 6 트레이스 🔗
📘 누적 현황

권1 (Phase 0) 7 트레이스 + 권2 Day 1~3 6 트레이스 = 13 트레이스

매 트레이스가 "왜 그 결정인가" + "근거" + "대안" + "부작용" 4 요소로 구성됩니다.

권2 28일 끝 시점 누적 약 50개 트레이스 예상. 6개월 후 동업자가 펼치는 답입니다.

📌 권2 제1장 정리

  • 핵심: Day 1~3 = API 4개 인증 + 호출 테스트 + LLM 분류 prototype. 첫 5건 분류 (3 auto + 2 admin) 성공
  • Day 1 — API 4개 인증: data.go.kr 통합 게이트웨이, 30분 안에 개발계정 4개, .env.local 보안
  • Day 2 — 호출 테스트: lib/api/test-fetch.ts (80줄), 4 API sample, welfare_local XML 응답 발견 → G-1 입증
  • Day 3 — LLM 분류: prompts.ts (90줄, E5 [4]), classify.ts (70줄, E5 [7]), 첫 5건: 3 auto + 2 admin
  • 누적 시간: 27.5h (Phase 0 21h + Day 1~3 6.5h)
  • R4 트리거 거리: 60h - 27.5h = 32.5h 여유 (1주차 안전)
🎉 첫 코드가 만들어졌습니다

4 API 인증 + 호출 테스트 + LLM 분류가 6.5시간 안에 완성됐습니다. SPEC v4의 메타 명세가 코드에서 입증되기 시작했습니다 — Gemini의 G-1 (XML fallback)이 Day 2에 즉시 의미 있어졌고, E5 [4] [7]이 Day 3에 작동했습니다.

LogOnTable 트레이스 6개가 BUILD.md에 박혔습니다. 6개월 후 동업자가 "왜 data.go.kr 통합 게이트웨이?" 또는 "왜 Haiku?" 펼치면 본문에 답이 있습니다.