📦 이 문서는 v1 이전 자료(deprecated)입니다 — 최신 정보는 문서 허브를 보세요
🏗 BACKEND ARCHITECTURE · v1

밀프레드 편식도감
백엔드 아키텍처

평가 엔진 · 추천 엔진 · OCR · 데이터 인프라 — 프로덕션 4-6주 MVP 설계 문서

📅 2026-05-23 🎯 care.html → 프로덕션 💰 월 ₩300k (100 사용자)

한눈에 보기

3 LAYER
데이터 + 평가 + 추천
평가 엔진
Rule-based
8축 × 36 영양소 결정론
추천 엔진
Hybrid
Rule(190) → LLM 정제
MVP
4-6주
enrich 2주 + 백엔드 2주 + 통합 1-2주
월 운영비
~₩300k
100 사용자 기준
기존 자산
4,432 레시피
KDRI 2025 · 농진청 DB

전체 아키텍처

클라이언트(PWA·iOS·Android) → 엣지 API(Vercel) → 3개 코어 엔진(평가·추천·OCR) → 데이터 레이어(Supabase Postgres) + ETL/배치(GitHub Actions)

flowchart TD CLIENT["📱 CLIENT
PWA · iOS · Android
care.html · daycare-eval.html"] GATEWAY["⚡ EDGE / API GATEWAY
Vercel Serverless · Supabase Auth
Rate limit · CORS · 캐시"] EVAL["📊 평가 엔진
(Pure Rule)
8축 · 36 신호등 · BMI"] RECO["🍳 추천 엔진
(Hybrid LLM)
4원칙 매칭 · 자연어 생성"] OCR["📷 OCR 엔진
(CLOVA OCR + Claude 분해)
식단표 전사 · 메뉴→식재료"] DB[("🗄 DATA LAYER
Supabase Postgres
users · meal_logs · recipes
ingredients · kdri_rni · cache")] ETL["🔄 ETL / BATCH
GitHub Actions cron
4,432 레시피 enrich · 운영 큐"] CLIENT <-->|HTTPS / WebSocket| GATEWAY GATEWAY --> EVAL GATEWAY --> RECO GATEWAY --> OCR EVAL --> DB RECO --> DB OCR --> DB ETL --> DB classDef client fill:#FFE8D1,stroke:#E89244,color:#1F2D3D,stroke-width:2px classDef edge fill:#E3F2FD,stroke:#3498DB,color:#1F2D3D,stroke-width:2px classDef engine fill:#E8F5E9,stroke:#16A085,color:#1F2D3D,stroke-width:2px classDef data fill:#F3E5F5,stroke:#9C27B0,color:#1F2D3D,stroke-width:2px classDef batch fill:#FFF8E1,stroke:#F9A825,color:#1F2D3D,stroke-width:2px class CLIENT client class GATEWAY edge class EVAL,RECO,OCR engine class DB data class ETL batch

데이터 모델 (Supabase Postgres)

4개 도메인으로 분리: 사용자/아이 · 식단 기록 · 마스터 데이터 · 캐시

📋 14개 핵심 테이블

도메인테이블역할
사용자users보호자 계정 (카카오·구글 OAuth)
children아이 프로파일 (생년월일·알레르겐·어린이집 유형)
growth_stats주 1회 키·몸무게·BMI·또래 percentile
식단 기록meal_logs끼니별 기록 (시간·장소·식감·자율성·반응)
meal_log_ingredients끼니 ↔ 식재료 다대다
daycare_evals어린이집 식단표 평가 (24h TTL)
마스터ingredients식재료 + 농진청 영양 36종 (~1,000)
recipes레시피 + 메타 enrich (4,432종)
kdri_rniKDRI 2025 연령별 RNI/AI/UL
캐시recommendationsLLM 추천 결과 24h 캐시
ocr_cache이미지 해시 → OCR 결과 영구

🔑 핵심 컬럼 — meal_logs (식단 기록)

CREATE TABLE meal_logs (
  id UUID PRIMARY KEY,
  child_id UUID REFERENCES children(id),
  eaten_at DATE,
  meal_type ENUM('아침','점심','저녁','간식'),
  approx_hour INT,           -- 8,10,12... 대략적
  duration_min INT,           -- 10,15,20,30
  place ENUM(...),
  texture_level ENUM('미음·페이스트·매시·다진·큐브·스틱·통째'),
  autonomy ENUM('떠먹여줌·반자율·스스로'),
  reaction ENUM('거부·남김·잘먹음·또달라')
);

평가 엔진 — Rule-based

모든 평가는 결정론적 계산. LLM 미사용. 빠르고 일관적·재현 가능.

3-1. 8축 식단 진단

flowchart LR L["📝 meal_logs
최근 3-7일"] P["👶 child profile
age · allergens"] A1["1. 다양성
WHO MDD"] A2["2. 식감 단계
HabEat"] A3["3. 메뉴 반복도
HabEat"] A4["4. 식사 환경
Satter DOR"] A5["5. 식욕 응답
CEBQ"] A6["6. 새 시도
Solid Starts"] A7["7. 자율성
Satter DOR"] OUT["📊 8축 자연어 진단
+ 종합 등급"] L --> A1 & A2 & A3 & A4 & A5 & A6 & A7 P --> A2 & A7 A1 & A2 & A3 & A4 & A5 & A6 & A7 --> OUT classDef input fill:#E3F2FD,stroke:#3498DB,color:#1F2D3D classDef axis fill:#E8F5E9,stroke:#16A085,color:#1F2D3D classDef output fill:#FFF8E1,stroke:#F9A825,color:#1F2D3D class L,P input class A1,A2,A3,A4,A5,A6,A7 axis class OUT output

3-2. KDRI 36종 신호등 (빈도 기반)

핵심 아이디어 — 그램 단위 정확 추정 불가 → 끼니 등장 빈도로 KDRI 충족률 추정

# 의사코드
def signal_36_nutrients(child_id, days=7):
    logs = get_meal_logs(child_id, days=days)
    age_band = get_age_band(child_id)  # '1-2y'

    nutrient_meals = defaultdict(int)
    for log in logs:
        for ing in log.ingredients:
            for nutrient, amount in ing.nutri_per_100g.items():
                contribution = (amount * ing.estimated_g/100) / KDRI_RNI[age_band][nutrient]
                if contribution >= 0.3:
                    nutrient_meals[nutrient] += 1

    for nutrient in KDRI_36_NUTRIENTS:
        freq_pct = nutrient_meals[nutrient] / weekly_target * 100
        status = 'green' if freq_pct >= 80 else 'orange' if freq_pct >= 50 else 'red'

3-3. 탄·단·지 + BMI 종합

빈도 단독 평가 부족 → 키·몸무게·BMI percentile 가미. 같은 "매일 먹음"도 BMI 위치에 따라 다른 권고.

빈도BMI 위치탄수화물단백질지방
매일정상 (5-85p)✅ 적정✅ 적정✅ 적정
매일저체중 (<5p)✅ 적정✅ 보강⚠️ 양 부족 가능
매일과체중 (85p+)⚠️ 양 조절✅ 유지⚠️ 양 조절
드물게저체중⚠️ 양 늘리기⚠️ 양 늘리기⚠️ 양 늘리기

추천 엔진 — Hybrid (Rule + LLM)

Rule 단독: 자연어 빈약. LLM 단독: 4,432종 컨텍스트 X. 하이브리드: Rule이 후보 압축, LLM이 정제·자연어 생성.

━ PHASE 0 ━
식습관 프로파일 자동 집계
ingredient_stats · base_stats · texture_distribution · nutrient_status (KDRI 신호등)
━ PHASE 1 ━
4가지 원칙별 Rule 필터링 (SQL)
4,432 레시피 → 각 원칙별 50-100개 → 합계 ~190개 후보
━ PHASE 2 ━
LLM 정제 (Claude Sonnet 4.7)
프로파일 + 4원칙 + 190 후보 → 6-8 최종 + "왜 우리 아이에게?" 자연어 생성
━ PHASE 3 ━
캐싱 + 응답
recommendations 테이블 24h TTL · profile_hash 기반 캐시 hit 시 LLM 호출 스킵

4-1. 4가지 원칙 (사용자 정의)

#원칙트리거 데이터적용
1선호 베이스 + 도전 식재료잘먹는 베이스 × 거부·미만남 식재료베이스 유지, 도전 식재료는 가림
2잘먹지만 오랜만 → 원물accept_rate↑ AND 미만남↑ AND 가린 형태만통째·스틱·큐브로 식감 ↑
3극도 거부 → 형태 안 보이게accept_rate < 30% AND exposure ≥ 5즙·페이스트 (인지 차단)
4부족 영양 보강KDRI 신호등 빨강 영양소nutrient_highlights 매칭

4-2. LLM 프롬프트 구조 (Phase 2)

system: |
  당신은 만 0-5세 영유아의 식습관 데이터를 분석해
  4가지 원칙에 따라 레시피를 추천하는 영양 전문가입니다.

  원칙: 1) 잘먹는 베이스 + 도전, 2) 잘먹지만 오랜만 → 원물,
        3) 극도 거부 → 가림, 4) 부족 영양 보강

  각 원칙별 1-2개 선택 + 자연어 "왜 우리 아이에게?" (3-4문장).

user: |
  ## 프로파일
  - 만 28개월 · 잘먹는 베이스: 계란찜 43회·죽 32회·부침개 15회
  - 거부 식재료: 시금치(36%)·가지(37%, 1년 9개월 미만남)
  - 신호등 빨강: 철 38%·콜린 42%·비타민D 35%·EPA+DHA 30%

  ## 190개 후보 (Phase 1)
  [JSON 데이터]

cache_control: ephemeral (1시간)

OCR — 어린이집·유치원 식단표 인식

네이버 CLOVA OCR로 식단표 전사 → Claude Haiku가 메뉴를 식재료로 분해. 표 추출(table extractor) 미지원 도메인은 일반 OCR로 자동 폴백.

sequenceDiagram participant U as 부모 participant API as Edge API participant Cache as ocr_cache participant V as Claude Vision participant DB as daycare_evals U->>API: POST /api/ocr-menu (이미지) API->>API: sha256(이미지) → hash API->>Cache: 캐시 조회 alt 캐시 hit Cache-->>API: 기존 JSON else 캐시 miss API->>V: Vision API (메뉴·날짜·알레르겐 추출) V-->>API: JSON 결과 API->>Cache: 저장 (영구) end API->>DB: 평가 결과 + expires_at (24h) API-->>U: 8축 점수 + 가정 보충 가이드

월 운영 비용 (100 사용자 기준)

Supabase Pro
₩30,000
Vercel Pro
₩25,000
Claude Sonnet 4.7 (추천)
₩56,000
Claude Vision (OCR)
₩20,000
Claude Haiku 4.5 (분류 배치)
₩25,000
Prompt 캐싱 절감
-₩40,000
도메인·인증서
₩5,000
월 합계 (예상)
~₩120-300k

※ 사용자 1,000명 시 ~₩1.5M 추정. 사용 패턴(추천 호출 빈도)에 따라 변동.

개발 일정 (4-6주 MVP)

W1
DB 스키마 + Supabase 셋업 + 레시피 enrich 시작
📦 DB 생성 · base_type 분류 (Haiku 4.5 배치)
W2
메타 enrich 완료 + 식재료 농진청 매핑 + KDRI 시드
📦 4,432 enrich 완료 · 매핑 표
W3
평가 엔진 (8축·36 신호등·BMI) Python 구현 + 테스트
📦 /api/eval 엔드포인트
W4
추천 엔진 (Phase 1 Rule + Phase 2 LLM) + 캐싱
📦 /api/recipe-suggest
W5
OCR + daycare-eval funnel 통합
📦 /api/ocr-menu · funnel 연동
W6
통합 테스트 + 데모 → 프로덕션 데이터 전환 + 배포
🚀 라이브 서비스 시작

보안 + 프라이버시

항목정책
어린이집 사진24h 후 자동 삭제 (expires_at 컬럼)
어린이집 이름저장 X — 식별 정보 미수집
가정 식단 사진90일 후 자동 삭제
아이 개인정보별명만 (실명 X) · 생년월일만 (주소 X)
평가 결과 공개본인만 — 공개 순위·DB 없음
공유 카드메타 자동 제거 (이름·기관·위치)
Row-Level SecuritySupabase RLS — 사용자는 자기 데이터만 접근

기술 스택

📱
FRONTEND
Next.js 15 · TypeScript · Tailwind · Pretendard
BACKEND
Vercel Serverless Functions · Supabase Edge Functions
🗄
DATABASE
Supabase Postgres (RLS) · Storage (24h TTL) · Auth (OAuth)
🤖
AI / LLM
Claude Sonnet 4.7 (추천·정제) · Haiku 4.5 (분류 배치) · Vision (OCR)
⚙️
INFRA
GitHub Actions (배포·cron) · Sentry (에러) · Vercel Analytics
🔐
CACHE
Redis (Upstash) — 추천 24h · OCR 영구

즉시 결정 필요 3가지

1

DB 인리치먼트 범위

옵션 A: 4,432종 전부 enrich (₩42k)
옵션 B: 만 1-2세 우선 131종 (₩2k)

→ MVP 시기·예산 trade-off
2

LLM 모델 분리

옵션 A: Sonnet 4.7 단일 (단순)
옵션 B: Haiku 4.5(분류) + Sonnet 4.7(정제) 분리 (-50% 비용)

→ 비용 vs 복잡도
3

MVP 범위

옵션 A: 6주 풀스택 (실제 기록·추천)
옵션 B: 3주 데이터 연결만 (기록은 mock 유지)

→ 빠른 검증 vs 완성도

성공 지표

< 500ms
API p95 응답
≥ 90%
OCR 정확도
≥ 70%
캐시 hit rate
≥ 40%
daycare → dogam 전환
2/주
식단 기록 빈도
≥ 30%
추천 실제 시도율