getpersona.md를 이어가며 — AI 에이전트용 페르소나 플랫폼, Day 3 Foundation
Day 2에서 DB·인증·시크릿을 깔았다면, Day 3는 MinIO·Redis·GitLab CI·Docker Compose·공유 Zod까지 — 카탈로그는 아직 없지만 고객에게 팔 서비스가 버틸 인프라·검증·계약 바닥을 한 번에 깔았습니다. Build in Public로, 화면 0개인 주에 왜 이 작업을 먼저 했는지 정리합니다.
이전 글부터 보시는 분
솔직히 말하면 Day 3에도 카탈로그는 없음 ㅋㅋ
/dashboard 가면 아직 “나중에 채울게요~” 느낌이고, checkout 버튼도 export 데모도 결제 화면도 없어요. 친구한테 “자 우리 서비스!” 하고 보여주기엔 좀 민망한 그대로…
근데 이번에 TASK-009 ~ 013까지 한번에 밀어 넣었습니다. MinIO 버킷 이름 네 개 코드에 박아뒀고, Redis 큐 이름도 정해뒀고, MR 올리면 lint → typecheck → test → build 돌아가고, pnpm compose:bootstrap 치면 Postgres·Redis·MinIO 한방에 뜹니다. 웹 폼이랑 API가 같은 Zod 스키마 씀. 화면은 텅 비었는데 바닥은 꽤 단단해진 상태 ㅎㅎ
Day 1에서 모노레포·제품 계약·DESIGN.md 잡았고, Day 2에서 DB·API·Web·인증·Infisical 깔았다면, Day 3는 기능 올리기 전에 플랫폼 바닥 한 층 더 깐 날에 가깝습니다.
getPersona.md가 뭔지 (또 설명함)
getPersona.md는 AI 에이전트랑 팀한테 말투·성향·판단 기준·지식 경계를 구조화해서 넘겨주는 PERSONA.md 플랫폼이에요.
getdesign.md가 “AI한테 디자인 감각 주는 DESIGN.md 모음”이면, getpersona.md는 “AI한테 페르소나 주는 PERSONA.md 모음”이 목표입니다.
카탈로그에서 고르고 → 워크스페이스에 넣고 → 태스크에 맞는 context 받고 → Codex·Claude·Cursor로 export. 이 루프가 제품의 중심이에요.
만드는 과정은 blog.goodtek.xyz에 그대로 올립니다. vibePulse 때처럼 Build in Public입니다. 예쁜 스샷 한 장 자랑보다 왜 이 순서로 깔았는지 남기는 쪽이 맞다고 봐요.
새 프로젝트 “진짜 팔고 운영”한다고 치면
getpersona.md 구상할 때부터 전제가 하나 있었어요.
사이드 프로젝트 장난감 말고, 고객한테 팔고, 장애 나면 책임져야 하는 서비스다.
그러면 카탈로그 UI부터 박는 것만으로는 부족해요. 최소 이 셋은 비즈니스 로직보다 먼저 고민하고 시간 쏟는 게 맞다고 봅니다.
1. 기본 인프라가 단단해야 함
페르소나 export, 아바타, 인증 서류, 임시 업로드 — 곧 파일이 생깁니다. 캐시, 백그라운드 작업, 레이트리밋 — 곧 Redis가 필요해요.
이걸 기능마다 따로 붙이면 버킷 이름·권한·에러 형태가 기능마다 달라져요. 나중에 “그때는 그랬는데…” 하면 데이터 이전 지옥 ㅜㅜ
2. 무중단 배포 갈 수 있게 준비돼 있어야 함
GitHub Actions Blue-Green을 GitLab CI/CD로 이전한 글에서 이미 한번 겪었거든요. 배포 파이프는 “기능 다 만들고 나서” 붙이는 게 아니라, 처음부터 merge 기준·환경 분리·시크릿 주입 패턴을 정해두는 일이에요.
Day 3의 CI(TASK-011)는 deploy job 아직 없습니다. 일부러요. 대신 MR마다 같은 품질 게이트를 통과한다는 규칙부터 만든 거예요. 무중단 배포의 **앞단(검증)**부터 맞춰두는 거죠.
3. 기능이 맨날 바뀌어도 체계적으로 관리돼야 함
에이전트랑 사람이 같이 TASK 밀면 변경 속도 빨라져요. 그때 웹 폼 검증이랑 API DTO가 따로 놀면 persona type이나 pagination 규칙이 조용히 어긋남. 고객-facing 버그보다 찾기 더 힘듦 ㅋㅋ
공유 Zod 스키마(TASK-013)는 화려한 기능이 아닙니다. 제품이 쓰는 공통 언어를 먼저 박아두는 작업이에요.
정리하면, 빌드하려면 이 부분을 사전에 고민하고 시간 쏟는 게 맞다는 쪽입니다. 화면이 비어 보여도 괜찮아요. 바닥이 단단하면 위에 뭘 올려도 덜 무너지니까요.
Day 3에서 깔아둔 다섯 가지 (TASK-009 ~ 013)
Day 2 끝에서 “다음은 compose랑 CI”라고 했는데, 그 사이에 스토리지·Redis·공유 계약까지 한번에 갔습니다 ㅎㅎ
각 TASK를 “뭘 했다”가 아니라 **“기능 넣기 전에 왜 필요했나”**로 묶으면 이렇습니다.
| 층 | TASK | 기능 넣기 전에 박아둔 것 |
|---|---|---|
| 신체 | 009 MinIO | export·아바타·인증 서류 담을 버킷·presigned URL 규칙 |
| 신경 | 010 Redis | 캐시·큐·백그라운드 맡길 자리 (Worker는 나중) |
| 면역 | 011 GitLab CI | MR마다 lint → typecheck → test → build |
| 실험실 | 012 Compose | 빈 머신에서도 같은 Postgres·Redis·MinIO |
| 언어 | 013 Shared Zod | 웹·API가 같은 pagination·error·enum·폼 스키마 |
아래는 조금만 풀어 씁니다.
TASK-009 — 파일은 “나중에”가 아니라 “어디에, 뭐라고 부를지”
페르소나 플랫폼은 결국 파일을 만듭니다. export JSON, 아바타, official 인증 서류, 업로드 임시 파일.
비즈니스 로직 넣기 전에 persona-exports, avatars, verification-docs, uploads-temp 같은 버킷 이름을 코드에 고정해 둔 이유가 있어요. 기능마다 S3/MinIO 연동을 새로 짜면, 나중에 “이 export 어느 버킷이었지?”가 마이그레이션 비용이 됩니다.
그리고 스토리지 없어도 API는 뜨고, /ready가 storage: not_configured라고 솔직히 말해요. Day 2의 DB·auth 패턴이랑 같습니다. 부분 장애·로컬·CI에서 뭐가 안 붙었는지 보이게 — 이거 반복해두면 카탈로그 붙일 때 디버깅 덜 지옥 ㅋㅋ
TASK-010 — “나중에 느려지고, 나중에 백그라운드로”
카탈로그 인기순 정렬, 런타임 캐시, 예약 포스팅, automation — 이름만 들어도 Redis·큐가 보이죠.
Worker는 아직 안 돌립니다. 일부러요. CacheService랑 BullMQ Queue 등록만 해두고, 큐 이름 네 개를 shared 패키지에 박아뒀어요. 비즈니스 기능이 첫 번째로 “Redis 어떻게 붙이죠?”부터 시작 안 하게 하려는 거예요.
스토리지랑 마찬가지로 Redis 없어도 API는 뜨고 /ready에 상태가 찍힙니다. 없어도 돌아가고, 붙으면 ready가 바뀐다 — 이 패턴 계속 쓸 예정이에요.
TASK-011 — “내 로컬에선 됐는데”를 팀 단위로 잡는 작업
TASK 늘고, 에이전트랑 사람이 MR 병렬로 내면 merge 후에야 터지는 typecheck가 제품 속도를 죽여요 ㅜㅜ
self-hosted GitLab(gitlab.goodtek.xyz)에 MR + develop push마다 시크릿 없이 lint·typecheck·test·build를 돌립니다. deploy는 TASK-071+로 미룹니다. “배포는 나중, 변경이 안전하다는 증명은 지금” — Blue-Green 무중단 배포를 맞춰둔 것과 같은 맥락이에요. 파이프 앞단부터 닫아두는 거죠.
Build in Public이라 솔직히 말하면, runner 등록하고 exec format error 뜨고, tsbuildinfo 때문에 CI typecheck 터지는 것까지 과정에 있습니다 ㅋㅋ 지루한데 나중에 카탈로그 PR 열 때 이 게이트 있냐 없냐가 체감 차이 큽니다.
TASK-012 — 온보딩이 제품 개발이 아니라 인프라 디버깅 되면 끝
Day 2까지는 Postgres를 각자 띄우거나, Redis·MinIO는 docker run 따로따로… 둘이 되면 “어? 나 5432 이미 쓰는데?”가 시작됩니다 ㅎㅎ
루트 compose.yml 하나로 Postgres 17·Redis 7·MinIO를 묶고, pnpm compose:bootstrap으로 migrate·seed·버킷까지 맞췄어요. 앱은 여전히 host-native(pnpm dev / pnpm dev:secrets) — 핫리로드 속도랑 환경 재현 둘 다 챙기려는 타협입니다.
TASK-015(코어 DB 스키마), TASK-016(카탈로그) 논의가 “DB 어떻게 띄우죠?” 말고 “personas 컬럼 뭐 넣죠?”부터 시작하려면, 이 실험실이 먼저 있어야 해요.
Infisical local env랑 compose MinIO 맞춰놓고 /ready 전부 ok 뜨면… 화면에 보이는 건 없어도 꽤 뿌듯합니다 ㅎㅎ 바닥은 연결됐다는 뜻이니까요. Day 2에 pnpm secrets:verify 했을 때 그 느낌이랑 비슷해요.
TASK-013 — 웹이랑 API가 같은 말 하게
exampleFormSchema 하나를 @getpersona/shared로 올렸습니다. 작아 보여요. 근데 TASK-014에서 persona.json 스키마가 오고, TASK-016에서 pagination·필터가 오면 형태가 폭발합니다.
persona type(inspired / personal / role / official), visibility, risk label, API error shape, pagination — 아직 DB 컬럼도 카탈로그 API도 없어요. 단어만 먼저 정해둔 겁니다. 웹 React Hook Form이랑 Nest zodBody/zodQuery가 같은 스키마 쓰면, “프론트는 됐는데 API는 400”이 설계 단계에서 잡혀요.
Zod 3에서 4로 올린 것도 같은 맥락입니다. 기능 붙일 때마다 버전이 갈라지면 shared 패키지 의미가 없어짐 ㅋㅋ
Build in Public — 왜 지루한 날도 공개하냐
vibePulse, getDesign, getpersona.md 만들면서 반복되는 패턴이 있어요.
겉모습 없는 날이 오히려 솔직하게 쓸 게 많다 ㅎㅎ
- Day 1: health page 하나 + TASK 로드맵 80개 넘음
- Day 2:
/dashboard텅 빔 + OAuth는 됨 - Day 3: 카탈로그 없음 + 버킷 이름·CI gate·compose bootstrap
이걸 숨기고 “페르소나 마켓플레이스 곧 오픈!”만 외치면 Build in Public이 아니라 그냥 마케팅이에요.
고객한테 팔 계획 있는 제품일수록 “바닥 어떻게 깔았는지”가 신뢰의 일부라고 봅니다. 페르소나·official 라벨·고위험 도메인은 “나중에 고치자”가 신뢰 사고로 이어지기 쉬워요. Day 1 PRODUCT_CONTRACT에서 이미 못 박았고, Day 3는 그 말을 인프라·CI·계약으로 이어 붙인 날에 가깝습니다.
나중에 카탈로그 첫 화면 뜰 때 “아, 그때 compose랑 shared schema 먼저 한 이유가 이거구나” 하고 Day 1·2·3 같이 읽고 싶어지면 Build in Public 제대로 된 거예요 ㅋㅋ
그래서 뭐가 달라졌나
| Day | 갖춰둔 것 |
|---|---|
| Day 1 | 말·디자인·제품 계약·SEO 골격 |
| Day 2 | DB·API·Web·인증·시크릿 |
| Day 3 | 스토리지·캐시/큐·CI·로컬 풀스택·공유 검증 계약 |
아직 없는 것 (일부러): 카탈로그 UI, personas 테이블, Polar 결제, npx getpersona CLI, deploy job
다음 후보: TASK-014 Persona Package v0.1 스키마 → TASK-015 코어 DB → TASK-016 카탈로그. 이제 Day 3 바닥 위에 도메인 로직 올릴 차례예요. 그때는 로그인된 유저, 붙은 DB, 도는 CI, 같은 Zod enum이 당연한 전제가 돼 있을 겁니다.
마치며
카탈로그 0개인 주에 TASK 다섯 개 머지한 이유, 심플합니다.
고객한테 팔고, 무중단에 가깝게 배포하고, 기능 맨날 바뀌어도 체계적으로 굴러가는 서비스는 화면보다 인프라·검증·계약이 먼저 단단해야 해요.
getpersona.md는 아직 데모 스샷 자랑할 단계가 아닙니다. **“이 순서로 쌓는다”**는 기록을 남기는 단계예요.
Day 1·2 안 읽으셨으면 거기서 왜 Foundation first인지부터 보시고, 이 글은 그 위에 인프라·CI·계약 층을 얹은 Day 3으로 읽어주시면 맥락이 이어집니다 ㅎㅎ
다음 글 예고
- TASK-014 —
persona.json, memory card, permissions: 파일 스키마를 shared에 어떻게 쌓을지 - TASK-015~016 — DB에 personas 심고, 첫 카탈로그가 “아 제품이네” 보이기 시작하는 순간
- CI deploy job — Day 3 검증 파이프에서 Blue-Green 무중단 배포까지 이어 붙이기