📦 이 문서는 v1 이전 자료(deprecated)입니다 — 최신 정보는 문서 허브를 보세요
🧭 ENGINE STRATEGY · v1 (2026-05-25)

룰 vs LLM 결정 매트릭스
— 어디서 어디까지 LLM 쓸까

점수 계산은 룰. 자연어 인사이트는 LLM. 부모 정성 노트는 LLM 코어. 44개 핵심 기능을 3 카테고리(Pure Rule · Rule+LLM Narration · LLM-Primary)로 분류 + 비용·가드레일까지. ★ served ≠ consumed 분리 카운팅 포함.

🟢 Pure Rule 24개 🟡 Rule + LLM 11개 🟣 LLM-Primary 9개 💰 월 ₩1.85M (1만 MAU)
━ 1. CORE PRINCIPLE ━

1. 핵심 원칙 — "점수는 룰, 멘트는 LLM"

❌ 잘못된 접근

모든 자연어를 코드로 생성

if(diversity < 50) text = `다양성 부족 — ${missing.join('·')}이 비어요`;
if(texture_puree_pct > 60) text += `죽 비중 ${puree_pct}%로 핑거푸드 부족`;

결과: 부자연스러운 템플릿 문장. 부모 맥락·뉘앙스 X. 매번 똑같은 표현 → 식상.

✅ 우리 방식

룰로 데이터, LLM으로 자연어

: diversity = {pct: 62.5, missing: ['콩','유제품','과일']}
LLM (Haiku): "이번 주 콩·유제품·과일을 못 만났어요. 두부 한 컵 + 우유 한 잔이면 +25점 가능해요."

결과: 정확한 숫자(룰) + 따뜻한 자연어(LLM). 매번 다른 표현. 부모 맥락 반영.

왜 이 분리가 중요한가

━ 2. THREE CATEGORIES ━

2. 3 카테고리 분류

🟢 A · PURE RULE

코드만 (LLM 0%)

정형 데이터 + 결정론적 로직. 자연어 생성 X. 점수·등급·매칭·카운트.
22개 기능 · p95 50ms · 비용 ₩0
  • 도감·레시피 정적 조회
  • WHO MDD 다양성 카운트
  • KDRI 36 영양 신호등
  • BMI·점수·등급 계산
  • 마일리지·챌린지·룰렛
🟡 B · RULE + LLM

룰로 데이터, LLM으로 자연어 표현

점수·통계는 룰. 그 결과를 사람한테 설명하는 자연어 멘트만 LLM (Haiku).
11개 기능 · Haiku 95% · 비용 ₩4/회
  • 각 축 진단 멘트 ("죽 65%...")
  • 식단표 5문장 자연어 가이드
  • 주간 리포트·카톡 알림톡
  • 도감 식재료 친해지기 팁
  • 결핍 영양 보충 제안 멘트
🟣 C · LLM-PRIMARY

LLM 코어 (룰은 검증·가드만)

자유 텍스트 이해·생성·맥락 추론. 정성 시계열 분석. 양방향 체이닝.
8개 기능 · Haiku+Sonnet · 비용 ₩4~₩50/회
  • 자유 텍스트 → MealLog 전처리
  • 정성 노트 시계열 분석
  • 다음 입력 시 역질문 생성
  • 편지·동적 질문 회전
  • 식단표 OCR + 메뉴 매핑
━ 3. DECISION TREE ━

3. 결정 트리 — 신규 기능 어디에 넣을까

flowchart TD START{새 기능 요구} Q1{입력이 정형 데이터?
(숫자·enum·매핑)} Q2{출력이 정형 데이터?
(점수·등급·매칭 결과)} Q3{출력에 자연어
인사이트·멘트 필요?} Q4{입력이 자유 텍스트
또는 사진?} Q5{시계열 맥락
또는 정성 분석 필요?} A[🟢 A · Pure Rule
코드로만] B[🟡 B · Rule + LLM Narration
룰 계산 → Haiku 자연어] C[🟣 C · LLM-Primary
Haiku/Sonnet 코어] START --> Q1 Q1 -->|"YES"| Q2 Q1 -->|"NO (자유 텍스트·사진)"| Q4 Q2 -->|"YES"| Q3 Q2 -->|"NO"| C Q3 -->|"NO (숫자만)"| A Q3 -->|"YES"| B Q4 -->|"단순 정규화 필요"| C Q4 -->|"시계열 분석·체이닝"| Q5 Q5 -->|"YES"| C classDef start fill:#FFE8D0,stroke:#C45A00,color:#1a2b4a classDef qst fill:#FFFBF5,stroke:#FF8A47,color:#1a2b4a classDef cata fill:#E8F5E9,stroke:#16A085,color:#1a2b4a,font-weight:bold classDef catb fill:#FFF8E1,stroke:#F9A825,color:#1a2b4a,font-weight:bold classDef catc fill:#F3E5F5,stroke:#9C27B0,color:#1a2b4a,font-weight:bold class START start class Q1,Q2,Q3,Q4,Q5 qst class A cata class B catb class C catc

판단 예시 (3가지)

요구판단분류
"이번 주 다양성 8/8 채우면 +12점"Q1=YES → Q2=YES → Q3=NO (숫자만)🟢 A
"콩·유제품 비어있어요. 두부 한 컵..." 안내 문장Q1=YES (룰 결과) → Q2=YES → Q3=YES🟡 B
"오늘 시금치 한 입 먹고 거부, 평소보다 덜 화냄" 노트 분석Q1=NO → Q4=YES → Q5=YES🟣 C
━ 4. FULL MATRIX ━

4. 41개 핵심 기능 매트릭스

#기능분류LLM 모델이유
1식재료 도감 정적 조회 (147→650)ADB 조회
2레시피 카드 표시·검색ADB 매칭·필터
3도감 식재료 친해지기 팁 (자녀 맥락)BHaiku 4.5자녀 거부도 + 자연어
4SOS 6단계 표시 (정적 가이드)A정적 콘텐츠
5제철 식재료 매핑 (12개월)A월별 룩업
6자유 텍스트 → MealLog 정규화CHaiku → Sonnet자유 입력 해석
7식재료 fuzzy 매칭 (147 풀)ALevenshtein 룰
8미매칭 식재료 → ai_estimated 추론CHaiku새 식재료 분류
9사진 → 식사 분석 (Vision)CClaude Vision이미지 이해
10meal_chip·시간·자율성 룰 검증Aschema validation
10b★ served vs consumed 자유 텍스트 분리 ("소고기 골라냄")CHaiku거부 표현 인식 + state 분류
10c★ 식재료 chip [✓ 먹음] / [✗ 남김] 토글 UIAUI 상태 토글 (룰)
11WHO MDD 다양성 카운트 (consumed/partial만)Aset 카운트 + state 필터
1214 sub-카테고리 깊이 카운트 (consumed/partial만)Aset 카운트 + state 필터
13KDRI 36 영양소 빈도 추정 (partial=50% 환산)Astate 가중 frequency
13b★ 시도 vs 섭취 진전도 (Toomey SOS 단계 자동 산출)AsuccessCount / attemptCount + 임계치
14영양 신호등 색상 결정 (green/orange/red)A임계치 룰
15BMI·백분위 계산 (성장 곡선)AWHO 차트 룩업
168축 점수·총점·등급 산출A가중 평균 + 매핑
17메뉴 반복도 (4주 동일 카운트)Agroup by + 카운트
18NOVA 가공식품 비율ANOVA 1-4 카운트
19알레르겐 표시 검증Atext grep
20각 축 진단 멘트 ("죽 65% — 핑거푸드 부족")BHaiku 4.5점수 → 자연어 설명
21식단표 5문장 자연어 가이드BHaiku 4.5여러 축 통합 자연어
22결핍 영양 보충 제안 멘트BHaiku 4.5자녀 베이스 + 자연어
23주간 리포트 자연어BHaiku 4.57일 통계 → 한 문단
24이번 주 추천 식재료 Top 5 (룰 필터)A4 원칙 + 빈도 룰
25레시피 후보 1차 필터 (Phase 0-1)A알레르겐·식감·식재료 룰
26레시피 LLM 정제 (Phase 2, "이유" 생성)BSonnet 4.7자녀 맥락 + 자연어 이유
27Food Chaining 기회 추출CSonnet 4.7정성 + 정량 결합 추론
28이미지 전처리·해시 (sharp)A이미지 처리 룰
29OCR 식단표 → 텍스트CClaude Vision이미지 → 텍스트
30OCR 메뉴 → recipe_id fallbackCHaikufuzzy 실패 시 LLM
31정성 raw_note 시계열 분석CSonnet 4.7맥락·뉘앙스·진전 추론
32다음 입력 시 역질문 생성CHaiku 4.5thread 기반 동적 질문
33주간 편지 답장CSonnet 4.7raw_text 인용 + 자연어
34동적 질문 회전 (7 차원)BHaiku 4.5차원 룰 + 자연어 표현
35감정 추정 (parent_emotion)CSonnet 4.7자유 텍스트 감정 추론
36마일리지 적립·반감기·일일 한도AStage 룰 + 검증
3790일 챌린지 완주 판정A일수 카운트
38친구 초대·룰렛 (IP·fingerprint·확률)A검증 + 분포 룰
39카톡 알림톡 메시지 personalizationBHaiku 4.5템플릿 + 자연어 채움
40매일 +50종 도감 enrich (분류·메타)CHaiku 4.5새 식재료 분류·메타 생성
41도감 댓글 모더레이션 (광고·욕설 필터)CHaiku 4.5자연어 분류

🟢 A (Pure Rule) 24개 · 🟡 B (Rule + LLM) 11개 · 🟣 C (LLM-Primary) 9개 · 합 44개

━ 5. CATEGORY A DETAIL ━

5. 🟢 Pure Rule (22개) — 결정론·재현·빠름

왜 룰만으로 충분한가

  • 입출력 모두 정형: 식재료 ID·점수·등급·일수 등 숫자/enum
  • 학술 정합성: KDRI·WHO·NOVA는 명확한 룩업 테이블 + 카운트 룰
  • 재현 가능: 같은 입력 → 같은 출력 (RCT 검증 가능)
  • 빠름·무료: p95 50ms · 비용 ₩0
  • 오프라인 작동: LLM 장애에도 핵심 기능 유지

대표 알고리즘

// WHO MDD 다양성 카운트 (식품군 1-2단계)
function axisDiversity(logs) {
  const groups = new Set();
  logs.forEach(log => log.ingredients.forEach(i => groups.add(i.foodGroup)));
  const mddSatisfied = groups.size >= 5;        // WHO 기준 8 중 5 이상

  // 14 sub 깊이 (1번 축 2단계)
  const subCats = new Set();
  logs.forEach(log => log.ingredients.forEach(i => subCats.add(i.subCategory)));
  const subDepth = subCats.size;

  return {
    pct: !mddSatisfied ? 60 : subDepth >= 10 ? 95 : subDepth >= 6 ? 90 : 85,
    grade: !mddSatisfied ? 'C' : subDepth >= 10 ? 'A+' : subDepth >= 6 ? 'A' : 'B+',
    raw: { mddGroups: groups.size, subDepth }
  };
}

이 카테고리에 추가 안 되는 것

  • ❌ "다양성이 부족해요" 같은 자연어 — 카테고리 B로 (Haiku)
  • ❌ 자녀별 맥락 추론 — 카테고리 C로 (Sonnet)
  • ❌ 부모 노트 해석 — 카테고리 C로 (Sonnet)
━ 6. CATEGORY B DETAIL ━

6. 🟡 Rule + LLM Narration (11개) — 점수는 룰, 멘트는 LLM

패턴 — 두 단계 호출

async function axisNarrative(axisResult, childContext) {
  // 1단계: 룰로 계산 (Cat A) — 이미 끝남
  // axisResult = { axisId:'texture', pct:55, grade:'C',
  //                raw:{pureeChunkPct:65, fingerCount:2, ageMonths:14} }

  // 2단계: 캐시 확인 (같은 raw + ageBand → 자연어 재사용)
  const cacheKey = sha256(JSON.stringify(axisResult.raw) + childContext.ageBand);
  const cached = await redis.get(`narr:${cacheKey}`);
  if (cached) return cached;

  // 3단계: Haiku로 자연어 생성 (5-7일 캐시)
  const narrative = await haiku.generate(NARRATIVE_PROMPT, {
    axis: axisResult,
    childAge: childContext.ageMonths,
    childName: childContext.nickname,
    tonePreference: childContext.parentTone || 'friendly'
  });

  await redis.setex(`narr:${cacheKey}`, 604800, narrative);
  return narrative;  // "지우 어머니, 죽이 65%네요. 14개월이면..."
}

왜 LLM이 필요한가

  • 매번 다른 표현 — 같은 데이터도 부모마다·날마다 다르게
  • 자녀 맥락 반영 — 닉네임·연령·잘먹는 베이스 등 결합
  • 학술 용어 → 자연어 번역 — "RNI 65%" → "주 3-4회 만남"
  • 톤 일관성 — design-spec §10 (권유·긍정) 자동 적용

비용 통제 — 캐시가 핵심

  • (raw 데이터 + ageBand) hash 키로 7일 캐시
  • 비슷한 식습관의 다른 부모는 캐시 재사용 (수치 동일하면 멘트 동일 OK)
  • 예상 cache hit rate: 60%+
  • 실 호출 평균: 사용자당 일 1-2회 × ₩4 = 월 ₩120 / 사용자

이 카테고리 11개 기능 한 줄 요약

기능룰 출력 (정형)LLM 출력 (자연어)
각 축 진단 멘트{axis, pct, raw}"죽이 65%네요. 14개월이면 핑거푸드도 시도해볼 때예요."
5문장 식단표 가이드8축 점수 + 결핍 영양 목록"이 식단은 한국 평균 +14점, 상위 25%... 한식 위주라..."
결핍 영양 보충 제안{nutrient: 'choline', missing: 0.6}"콜린이 부족해요. 노른자죽이나 두부찜 한 끼면 OK."
도감 식재료 친해지기 팁{ingredient, childRejection: 0.8}"시금치 거부 강하네요. 계란찜에 살짝 섞어보세요."
주간 리포트 자연어7일 통계 dict"이번 주 신호등 25→28 (+3). 잡곡 늘린 게 효과예요."
카톡 알림톡 personalization{template, vars}"지우 어머니, 90일 챌린지 7일째예요!" (템플릿 변수)
레시피 LLM "이유" (Phase 2){recipe, childMatch: 0.85}"카레라이스는 지우가 잘 먹는 베이스 + 시금치 도전 매치예요."
동적 질문 회전 표현{dimension: 'cebq', day: 'mon'}"오늘 거부 반응 어땠어요? (한 입도 X / 골라냄 / ...)"
━ 7. CATEGORY C DETAIL ━

7. 🟣 LLM-Primary (8개) — LLM 코어, 룰은 검증·가드

왜 LLM이 코어인가

  • 입력이 자유 텍스트·이미지: 정형 데이터로 환원 불가능 (의도·뉘앙스 손실)
  • 시계열 맥락 추론: 3주차 raw_note들을 연결해 진전 곡선 추출 — 룰로 불가능
  • 창의적 생성: 편지·동적 질문은 매번 새로운 문장 필요
  • cross-domain 매칭: Food Chaining 같이 식재료+거부도+선호도 결합

패턴 — LLM 출력은 항상 검증된 schema로

async function llmPrimary(input) {
  // 1. 입력 sanitize (prompt injection 가드)
  const safe = sanitizeUserInput(input);
  if (detectInjection(safe)) return { error: 'rejected' };

  // 2. LLM 호출 (Sonnet — 복잡 추론용)
  const raw = await sonnet.generate(SYSTEM_PROMPT, safe);

  // 3. zod schema 검증 (LLM 출력은 항상 정형으로 환원)
  const parsed = OutputSchema.safeParse(extractJSON(raw));
  if (!parsed.success) return { error: 'schema_fail', retry: true };

  // 4. 룰로 추가 검증 (학술 정합성 가드)
  if (parsed.data.sosStage > 6) return { error: 'invalid_sos' };
  if (parsed.data.confidence < 0.3) return { ...parsed.data, lowConfidence: true };

  return parsed.data;
}

이 카테고리 8개 기능 한 줄 요약

기능입력LLM출력
자유 텍스트 → MealLograwText + 시간Haiku → SonnetMealLog 객체
식단표 OCR이미지Claude Vision메뉴 텍스트 + recipe_id 매핑
OCR 메뉴 fallbackfuzzy 실패 텍스트Haikurecipe_id 추정
정성 노트 시계열 분석raw_note threadSonnet{진전 곡선, SOS 단계, 다음 질문}
다음 입력 시 역질문최근 5 notesHaiku"지난번 X 잘 먹었던데..." 문장
주간 편지 답장1주 thread 요약Sonnetraw_text 인용한 편지 본문
parent_emotion 추정raw_note 시계열Sonnetjoy/worry/frustration
Food Chaining 기회좋아하는 + 거부 식재료Sonnet우회 식재료·레시피 제안
매일 +50종 enrich식재료명Haiku카테고리·KDRI·SOS·이모지 메타
댓글 모더레이션댓글 텍스트Haikusafe/review/delete
━ 8. COST ANALYSIS ━

8. 비용 분석 (MAU 1만 기준)

카테고리기능 수호출 빈도모델건당월 비용
🟢 A · Pure Rule22개무제한₩0₩0
🟡 B · Rule + LLM
각 축 멘트·5문장 가이드·주간 리포트 등
11개사용자당 일 2-3회
(캐시 hit 60%)
Haiku 4.5₩4₩400k
🟣 C · LLM-Primary
전처리·OCR·정성 분석·편지
8개일 1-2 Haiku
+ 주 1 Sonnet
Haiku·Sonnet₩4-50₩800k
일회성·매일 cron
enrich 50종·댓글 모더레이션
일 1회 cronHaiku₩200/일₩6k
카톡 알림톡 SENS월 8건/사용자₩8/건₩640k
합계 (MAU 1만)~₩1.85M / 월
참고 — 매출 (월 ₩35M 보수 / ₩140M 낙관)ROI 19~76배

왜 룰 분리가 핵심인가 (비용은 부수적)

사실 도감·레시피 같은 정적 콘텐츠는 LLM으로 만들어도 한 번 생성 후 영구 캐시되므로 추가 비용 ≈ 0. 비용보다 진짜 이유는 따로 있다.

이유룰 (Cat A)LLM 사용 시왜 중요
재현성같은 입력 → 항상 같은 출력같은 입력에도 매번 다른 표현RCT 학술 데이터 검증 가능성
응답 속도p95 50msp95 800ms~2.5s홈 진입·신호등은 즉시 떠야
오프라인지하철·비행기 OK네트워크 필수식사 기록은 어디서나
장애 내성Anthropic 장애에도 작동API 다운 시 기능 중단핵심 22개 기능 항상 유지
감사·디버그코드 추적 명확black box잘못된 점수 원인 파악
비용₩0캐시 가능 시 ≈ ₩0(비용 차이는 크지 않음 — 부수적)

→ 룰 분리의 진짜 이유는 재현성·속도·오프라인·장애 내성. 비용 차이는 미미하지만, 위 4가지는 학술·UX·신뢰성에 결정적.

━ 9. GUARDRAILS ━

9. 가드레일 — LLM이 학술 정합성 깨지 않도록

⚠️ Category B·C 위험 — LLM이 룰 결과를 왜곡하면?

예: 룰이 "비타민D 결핍 위험"이라고 했는데 LLM이 "괜찮아요"라고 자연어 생성. 학술 신뢰성·법적 책임 문제.

가드레일 5종

실패 시 fallback (모든 Cat B·C)

모니터링 지표