육아밸 (yougabell)

개요
육아밸은 워킹맘/워킹대디를 위한 육아 정보 검색·기록·AI 챗봇 서비스입니다. 사용자가 입력한 아이 정보(연령, 발달 단계, 알레르기 등)를 맥락으로 합성해 AI가 맞춤 답변을 제공하고, 일상 미션 기록을 모아 AI 주간 리포트로 정성 회고를 돌려줍니다.
배포 형태는 Expo(React Native) 네이티브 셸 안에 Next.js 웹뷰를 띄우는 하이브리드 구조이며, 도메인 로직과 모든 LLM 호출은 NestJS API가 전담합니다. 기획자 1명, 디자이너 1명, 개발자 2명으로 구성된 four-lovely-fairies 팀에서 진행했습니다.
주요 기능
- 육아 정보 큐레이션: 발달 단계·마일스톤·미션 데이터를 기반으로 한 정보 탐색과 콘텐츠 제공
- 아이 성장·미션 기록: 아이별 미션 수행/피드백, 심리 에너지(멘탈 배터리) 기록을 누적
- RAG 기반 AI 챗봇: 아이 정보·최근 기록을 system prompt로 합성하고, 임베딩 검색으로 지식 청크를 결합해 SSE 스트리밍으로 응답
- AI 주간 리포트: 요일별 미션 완료·키워드·심리 에너지 집계를 LLM이 격려 문장·베스트 모먼트 서사·다음 행동 제안으로 변환
- 운영자 CMS: 미션/콘텐츠/사용자 관리를 위한 어드민
- 푸시·인증: Supabase Auth 기반 인증과 Expo 네이티브 푸시 알림
기술 스택
| 분류 | 기술 |
|---|---|
| Language | TypeScript 5 (전 영역 strict: true) |
| API | NestJS 11, Prisma 7, @nestjs/swagger (OpenAPI) |
| AI | Vercel AI SDK 6 (ai), Gemini 2.5 Flash, gemini-embedding-001 (768d) |
| Web / Admin | Next.js 16.2 (App Router), React 19, Tailwind CSS 4, shadcn/ui |
| Mobile | Expo SDK 54, expo-router 6, React Native 0.81 |
| DB / Auth | Supabase Postgres, Supabase Auth |
| Validation | Zod 4 |
| 배포 | web/admin → Vercel, mobile → Expo EAS, api → Render |
| Pkg Manager | pnpm |
프로젝트 구조
단일 모노레포가 아니라, umbrella 레포가 5개 레포의 큰 그림을 인덱싱하는 멀티레포 + umbrella 전략을 씁니다.
youth/ (워크스페이스 루트)
├── yougabell/ # umbrella — 기획·스키마·결정의 진실의 소스 (docs only)
│ └── docs/
│ ├── design/ # 레포 전략·인증·크론 결정
│ ├── schema/ # 도메인 스키마 11개
│ └── features/ # 기능 기획서 (신규 기능은 여기서 시작)
├── yougabell-api/ # NestJS + Prisma — 구현 anchor (도메인·챗봇·LLM)
├── yougabell-web/ # Next.js — 사용자 웹 (Expo WebView 타깃)
├── yougabell-admin/ # Next.js — 운영자 CMS
├── yougabell-mobile/ # Expo — RN 셸 (푸시·인증·WebView 컨테이너)
└── yougabell-cron/ # tsx + node-cron — 임시 크론 (웜업 + 알림 디스패치)주요 설계 패턴
- Umbrella / Anchor 분리: 기획·결정은 umbrella(
yougabell)에서, 도메인 구현은 anchor(yougabell-api)에서. Prisma schema와 OpenAPI 스펙이 코드의 단일 진실 - 타입 공유 = OpenAPI codegen: 별도 공유 패키지 대신 NestJS OpenAPI 스펙을 클라이언트 3개(web/admin/mobile)로 코드젠
- 에이전트 문서 단일 소스: 5개 레포가 동일하게
AGENTS.md(진실의 소스) +@AGENTS.md만 import하는 얇은CLAUDE.md패턴 유지 - 파일 크기 규칙:
.ts/.tsx는 400줄을 넘기면 동일 이름 디렉토리로 전환하고index.ts에서 re-export — 호출부 경로는 불변
RAG 챗봇 / AI 주간 리포트
모든 LLM 호출은 yougabell-api가 처리하고 web/admin/mobile은 결과만 소비합니다.
- 사용자 질문이 들어오면 아이 정보·최근 미션/기록을
context-builder로 system prompt에 합성 gemini-embedding-001(768d)로 질문을 임베딩,KnowledgeDocument/KnowledgeChunk에서 유사 청크를 검색(RAG)- 합성된 컨텍스트 + 검색 결과로 Gemini 2.5 Flash 호출, SSE 스트리밍으로 토큰 단위 응답
- 응답 완료 후 카드/출처 링크를 노출하고, 사용자/어시스턴트 메시지를 DB에 영속화 (사용자당 단일 세션)
주간 리포트는 월요일 cron이 집계를 마친 뒤, 3개 AI 필드(headline body / bestMoment body / action suggestion)를 1회 호출로 생성합니다. LLM 실패 시 fallback 텍스트로 row는 항상 생성되도록 설계했습니다.
담당 역할
개발 2인이 기능별로 나눠 풀스택으로 구현했고, 본인은 API·웹·모바일·어드민·인프라 전반에 참여했습니다.
- API/백엔드 (~80% 기여): NestJS 도메인 모듈, Prisma 스키마(30+ 테이블), RAG 챗봇·AI 주간 리포트 파이프라인, Supabase Auth Guard
- 웹 프론트 (~50% 기여): Next.js 사용자 웹 UI/UX, 챗봇·미션·온보딩 화면
- 모바일: Expo RN 셸, 푸시·딥링크, WebView 컨테이너 및 네이티브 ↔ 웹 메시지 연동
- 어드민/인프라·기획: 운영자 CMS, 임시 크론(Render free cold start 웜업 겸용), umbrella 문서·아키텍처 설계
- 배포 운영: iOS App Store 심사 신청, Google Analytics 등록
트러블슈팅
문제 1: Render Free 플랜 cold start
- 상황: API가 Render Free 플랜에 올라가 있어 15분 무트래픽 시 인스턴스가 슬립, 다음 요청이 cold start로 30초 안팎 대기
- 원인: 무료 플랜의 자동 슬립 정책 + 별도 스케줄러 인프라 미도입
- 해결: 어차피 5분 단위로 도래하는 미션 알림 디스패치 호출 자체를 웜업 핑으로 겸용. 별도 헬스체크 호출을 burn 하지 않고 한 번의 호출이 웜업과 알림 발송을 동시에 수행하는 임시 크론(
yougabell-cron)으로 처리
문제 2: 멀티레포에서의 타입·계약 동기화
- 상황: api/web/admin/mobile 4개 클라이언트가 독립 레포라 도메인 타입·API 계약이 흩어질 위험
- 원인: 모노레포가 아니라 공유 패키지로 타입을 묶을 수 없음
- 해결: NestJS OpenAPI 스펙을 단일 계약으로 두고 클라이언트 코드젠으로 전파. 생성 파일은 수정 금지로 못 박아 진실의 소스를 anchor 레포로 고정
회고
- umbrella/anchor로 기획과 구현의 진실의 소스를 분리하니, 여러 AI 에이전트(Claude Code·Codex)와 2인 개발이 동시에 움직여도 컨텍스트 충돌이 적었습니다.
- RAG 챗봇·AI 주간 리포트를 프롬프트/모델 설정과 도메인 코드로 분리한 덕분에, 모델을 환경변수로 교체하거나 프롬프트만 갱신해도 서버 구조 변경이 거의 없었습니다.
- 임시 크론은 "최소한으로 단순하게" 원칙이 잘 맞았지만, 정식 스케줄러 인프라로의 이관이 다음 과제로 남았습니다.