Day 1 구현기 — "어, 이게 진짜 되네?"
기획이 끝났다. 이제 실제로 만들어야 한다.
솔직히 약간 긴장됐다. 문서는 완벽하게 준비됐는데, 막상 만들려니 "이게 진짜 돌아가?" 하는 의심이 있었다. 예전 kimeunsoo.xyz 때도 처음엔 잘 모르는 채로 시작했었으니까.
context.md 준비됨. schema.md 준비됨. roadmap.md 준비됨. Step 0.1부터 시작.
Step 0.1 — 모노레포 골격
Claude Code에 context.md를 주고 첫 지시를 했다. turborepo 기반 모노레포를 만들어달라는 것. apps/mobile, apps/api, apps/core, packages/shared, packages/db, packages/llm — roadmap에 적힌 폴더 구조 그대로.
잠시 후 GitHub에 폴더가 생겼다.
몇 달 전까지 GitHub가 뭔지도 몰랐다. kimeunsoo.xyz 만들면서 처음 알게 됐다. commit이 뭔지, push가 뭔지 그때 배웠다. 지금은 자연스럽다. 그게 신기하다.
이 와중에 모노레포가 뭔지도 AI 한테 물어봤다.
성공 조건 확인: github.com/danny/pepper에 6개 폴더 보임. context.md, roadmap.md 루트에 있음. ✅
Step 0.2 — Supabase 연결
Supabase 프로젝트를 만들고 API key 3개를 받았다. 데이터베이스 주소, 공개용 키, 서버용 비밀 키.
Claude Code가 이 키들을
.env.local이라는 파일에 저장하게 만들었다. 그리고 이 파일은 GitHub에 절대 올라가지 않도록 설정했다. 키가 공개되면 누구든 내 데이터베이스에 접근할 수 있으니까. 이런 보안 처리가 자동으로 세팅되는 게 처음엔 당연한 것 같아도, 직접 해보면 "아, 이게 왜 중요한지" 체감이 된다.
성공 조건 확인: .env.local이 git에 올라가지 않음. ✅
Step 0.3~0.4 — 데이터베이스 세팅
Ep 2에서 그렇게 공들여 만든 schema.md가 여기서 쓰인다. Claude Code가 schema.md를 읽고 SQL 파일 15개를 만들었다. 가족 정보, 채팅방, Vault 아이템, AI 비용 기록 등 페퍼가 쓸 모든 테이블이 다 들어있다.
내가 한 일은 그 SQL을 Supabase에 하나씩 붙여넣고 Run을 누르는 것뿐이었다.
첫 번째 실행에서 에러가 났다. pgvector(벡터 검색 기능)라는 확장 기능이 먼저 켜져 있어야 하는데 순서가 틀렸다. Claude Code에 에러 로그를 그대로 복사해서 던졌다. 2분 만에 수정된 SQL이 나왔다. 다시 실행, 통과.
Supabase Table Editor를 열었을 때 테이블 목록이 화면 가득 나타났다. 그리고 든 생각: "아, 내가 반나절 동안 설계했던 그 구조가 진짜로 DB에 만들어졌네." 추상적인 문서가 실제 데이터베이스가 된 첫 번째 순간이었다.
그 다음엔 실제 데이터를 넣었다. 가족 4명(Danny, 소연, 은수, 은제), 채팅방 5개(가족방 1개 + 개인방 4개). 화면에 이름이 뜨는 걸 보니까 이제 진짜 "우리 가족 데이터"가 있는 시스템이 됐다는 게 실감났다.
성공 조건 확인: Table Editor에서 users 테이블에 4명 보임. chat_rooms에 5개 보임. ✅
Step 0.5 — LLM 라우터 — 이 순간이 최고였다
이게 이번 Day 1에서 가장 임팩트 있었던 순간이다.
모든 AI 호출을 하나의 진입점으로 통제하는 라우터를 만들었다. 의도 분류 같은 가벼운 작업은 Gemini Flash-Lite(가장 저렴한 모델), 코드 생성 같은 무거운 작업은 Claude Sonnet(비싸지만 성능이 좋은 모델). 그리고 모든 AI 호출은 비용까지 포함해서 자동으로 기록된다.
테스트를 돌렸다.
터미널에 "안녕하세요!"가 떴다. Gemini가 답한 거다.
그리고 Supabase cost_logs 테이블을 열었더니 row 하나가 찍혀 있었다.
model: gemini-2.5-flash-lite cost_usd: 0.0000012
그 순간 세 가지가 동시에 왔다.
"어? 진짜 내 질문에 AI가 답하네."
"이게 로그에 찍히네. 가격까지."
"가격이 찍히면 내가 컨트롤할 수 있겠구나."
비용이 기록된다는 게 단순한 기능이 아니라는 게 이때 처음으로 실감됐다. 내가 언제 어떤 모델을 얼마나 썼는지 볼 수 있으면, 폭발하기 전에 막을 수 있다. Ep 2에서 세션 한도에 터졌던 기억이 있으니까 이게 얼마나 중요한지 바로 체감이 됐다.
그리고 가장 중요한 생각: "이러면 내가 진짜 챗봇을 만들 수 있겠다."
막연하게 "만들어볼 수 있지 않을까?"였던 게 이 순간부터 "할 수 있다"가 됐다. 숫자 하나가 그 확신을 만들었다.
성공 조건 확인: Gemini 답변 나옴. cost_logs에 row 생성, cost_usd 기록됨. tier를 바꾸면 Claude Sonnet이 호출되고 model명이 다르게 찍힘. ✅
Step 0.6 — Vercel 배포
apps/api에 API 서버를 만들고 Vercel에 배포했다. Vercel은 GitHub에 코드를 올리면 자동으로 인터넷에 배포해주는 서비스다. kimeunsoo.xyz 때 처음 배웠다. 지금은 익숙하다.
배포가 끝났다. 브라우저 주소창에 URL을 입력했다.
{"ok": true, "time": "2026-05-17T..."}
페퍼의 API 서버가 인터넷에 살아있다. 내가 만든 시스템이 실제 URL로 응답하고 있다.
성공 조건 확인: /api/health에서 ok: true 반환. GitHub push 시 자동 배포 동작 확인. ✅
결과 — 반나절 기획, 2시간 구현, 세션 51%
Step 0.1부터 0.6까지. 실제 구현 시간은 2시간이었다.
세션 한도는 51%만 썼다. kimeunsoo.xyz 만들 때는 비슷한 세션으로 훨씬 적은 결과를 냈었다. 차이는 하나다. 기획에 시간을 썼느냐 아니냐.
중간에 에러가 몇 번 났다. pgvector 순서 문제, 환경 변수 누락, 타입 에러. 전부 같은 방식으로 해결했다. 에러 로그를 Claude Code에 그대로 복사해서 던진다. 수 분 안에 해결된다. 내가 뭘 고쳐야 하는지 몰라도 된다.
성공 조건을 명확히 정의하니까 진행이 명확하고 빠르다. 됐는지 안 됐는지를 내가 직접 눈으로 확인할 수 있으니, 다음 step으로 넘어가는 판단이 흔들리지 않는다. 비개발자에게 이게 생각보다 중요하다. "잘 됐나?"를 내가 판단할 수 있어야 AI한테 다음 걸 시킬 수 있으니까.
꽤 많이 왔다. 다음은 페퍼 Core 서버를 올리고, 모바일 앱 골격을 만들고, 채팅 화면을 붙인다. 다음주 쯤에는 "@페퍼 안녕"이라고 보냈을 때 페퍼가 처음으로 답하는 순간이 온다.