📦 이 문서는 v1 이전 자료(deprecated)입니다 — 최신 정보는 문서 허브를 보세요
🔬 RECIPE RECOMMENDATION CRITERIA · v1

우리는 어떻게
레시피를 추천하나?

참조 데이터 6종 · 학술 이론 11종 · 4 원칙 + α · 모든 기준과 출처를 한 페이지에

📚 11 학술 출처 📊 6 데이터 소스 ⚙️ 4 핵심 원칙 🥩 메인 반찬 판단
━ TL;DR ━

한 줄 요약

"우리 아이가 잘 먹는 베이스에
학술 근거로 도전 식재료를 자연스럽게 — 매번 다른 방식으로"

밀프레드 편식도감 추천은 6 데이터 소스11 학술 이론에 따라 분석하고, 4 핵심 원칙으로 4,432 레시피에서 6-8개를 골라 "왜 우리 아이에게?" 자연어 이유와 함께 제공합니다. 추천은 매번 다릅니다 — 같은 메뉴는 4주 내 반복하지 않습니다.

━ DATA SOURCES ━

1. 참조 데이터 — 6 소스

레시피 추천은 다음 6가지 데이터를 종합 분석합니다.

📝
데이터 ①: 식단 기록 (meal_logs)
최근 30일 · 끼니 단위
필드활용
식재료 (ingredients)노출 빈도·먹은 빈도·accept_rate 계산
반응 (reaction)잘먹음/또달라 = 선호 · 거부/남김 = 도전 식재료
식감 단계 (texture_level)현재 식감 분포 → 점진 단계 ↑ 결정
자율성 (autonomy)Satter DOR · 스스로 비율 추적
식사 환경 (place·duration)20분 이내·정해진 장소 권장 체크
동적 질문 답변 (dimension_data)거부 반응·환경·새 식재료 등 차원별 시계열
📊
데이터 ②: 식습관 프로파일 (집계)
실시간 계산 · profile_hash 캐시
필드예시
ingredient_stats{시금치: {노출 22, 먹음 8, 36%, 마지막 3일 전}}
base_stats{계란찜: 43회, 죽: 32회, 부침개: 15회} (선호 베이스 TOP 5)
texture_distribution{죽 65%, 매시 25%, 다진 7%, 핑거 3%}
nutrient_status (KDRI 36){철: red, 콜린: red, 비D: red, ...}
recent_menus최근 21끼 메뉴명 (중복 회피용)
📏
데이터 ③: 아이 프로파일 (children + growth)
정적 + 주 1회 갱신
필드활용
age_months / age_band연령별 권장 (식감·자율성·KDRI band)
sexKDC 성장도표 lookup
allergens알레르겐 위배 레시피 자동 제외 (안전 필수)
care_type어린이집·유치원·가정 → 추천 톤 조정
BMI percentile탄·단·지 권장 양 조정
📚
데이터 ④: 레시피 마스터 (recipes 4,432종)
enrich 완료 후 정적
필드활용
base_type (15분류)죽·계란찜·부침개·국·볶음·찜·구이·무침·생식·면·밥·간식·찌개·전·튀김
texture_level미음/페이스트/매시/다진/큐브/스틱/통째 7단계
main_protein_type붉은고기·흰고기·생선·계란·콩 (메인 반찬 판단용 — §4 참조)
ingredients[].form즙/다진/매시/큐브/스틱/통째/페이스트/가루 — concealment 판정
nutri_total (36)레시피 단위 영양 합계 — 부족 영양 보강 매칭
nutrient_highlightsRNI 30%+ 기여 영양소 (예: ['철', '엽산'])
concealment{식재료: high/mid/low} 가림 정도
cook_minutes·difficulty현실성 (간식·간단·복잡)
🥕
데이터 ⑤: 식재료 마스터 (ingredients 1,000+)
147 자동완성 풀 + enrich
필드활용
category / subcategory16 카테고리 분류 (잎채소·뿌리·생선·계란 등)
species_id시금치 vs 케일 vs 상추 = 다른 종 (다양성 2-Layer)
nutri_per_100g농진청 국가표준식품성분표 36 영양소
allergens식약처 17대 + 추가
seasonality제철 월 (계절성 점수)
processing_level (NOVA)1=자연식 / 2=가공 / 3=초가공 / 4=울트라가공
📖
데이터 ⑥: 학술 기준 마스터 (kdri_rni + standards)
KDRI 2025·WHO·HabEat 등
필드활용
kdri_rni연령별 RNI/AI/UL — 영양 충족률 계산
who_mdd_families8 식품군 매핑
our_subcategories_14WHO MDD 세분화 (Layer 2)
habeat_texture_table연령별 권장 식감 분포
satter_dor자율성·식사 환경 기준
━ ACADEMIC THEORIES ━

2. 적용하는 학술 이론 — 11종

모든 추천 결정은 다음 11개 학술 이론에 근거합니다. 임의 룰 X.

2-1. 다양성 평가 (5종)

① WHO Minimum Dietary Diversity (MDD)
World Health Organization, 2010 (revised 2017)
영유아 보완식의 식품군 다양성 평가 — 8 식품군 중 5개 이상 = 통과 (Pass/Fail 이분법)
활용: Layer 1 — 통과 기준 (D 등급 cutoff). 5/8 미만 시 강제 다양성 보강 추천
② Dietary Species Richness (DSR)
Lachat et al. 2018, PNAS (Lancet)
MDD 한계 보완 — 종(species) 단위 카운트로 영양 품질과 강한 상관관계 입증. 240종+/년 권장 (성인)
활용: Layer 2 — 카테고리 내 종 다양성. "시금치만 5번" = 잎채소 카테고리 통과지만 DSR 1종 → 근대·아욱 추천
③ Healthy Eating Index (HEI-2020)
USDA Center for Nutrition Policy and Promotion
13 components 점수화 (whole grains·dark green vegetables·seafood 등 세분화)
활용: 우리 14 sub-카테고리 설계의 모델. 특히 "통곡물 별도 분리", "진한 잎채소 별도" 채택
④ Food Variety Score (FVS)
Hatløy et al. 1998
7일간 unique 식재료 종 수 카운트. 30-40종+/주 권장. 영양 적정성과 강한 상관
활용: 주간 unique 종 수 추적. S 등급 cutoff (≥ 25종/주 + 새 시도 ≥ 2종)
⑤ NOVA Food Classification
Monteiro et al. 2019, Brazil
가공도 4단계 (1=자연식 / 2=요리 재료 / 3=가공식품 / 4=초가공). 4 비율 ≤25% 권장
활용: 레시피·식단표 평가 시 가공식품 비율 계산. 어린이집 식단표 평가 7축 中 1개

2-2. 식습관 형성 이론 (4종)

⑥ Food Chaining (Toomey)
Cheri Fraker, Sibyl Cox, Mark Fishbein, 2007 "Food Chaining"
아이가 좋아하는 음식을 "체인 시작"으로 점진적으로 비슷한 식감·맛·색으로 확장. 예: 닭너겟 → 닭볶음탕 → 닭다리 구이 → 닭가슴살
활용: 원칙 1 — 잘먹는 베이스에 도전 식재료 (계란찜 + 시금치). 단계적 chain 설계
⑦ Toomey SOS Approach to Feeding
Kay Toomey & Erin Ross, 2017 "SOS Approach to Feeding"
거부 식재료 점진 친해지기 6단계: 보기 → 만지기 → 냄새 → 핥기 → 씹기 → 삼키기
활용: SOS 6단계 모달 (도감) + 극도 거부 식재료 (accept_rate < 30% AND exposure ≥ 5) 자동 추천
⑧ Repeated Exposure Theory
Cooke 2011 / Birch & Marlin 1982
새 식재료 평균 12-30회 반복 노출 후 수용. 거부 자체가 정상 단계 — 부모 압박은 거부 강화
활용: 도감 노출 카운트·SOS 거부 대처 5가지·편지 답장 자연어 근거 ("연구에 따르면 12-30번 노출 후 수용")
⑨ Satter Division of Responsibility (DOR)
Ellyn Satter, 2007 "Eating Competence Model"
부모는 무엇·언제·어디서 / 아이는 얼마나·먹을지 결정. 압박 X · 자율성 ↑
활용: 7축 中 환경(식사 시간·장소)·자율성 평가. "20분 이내·정해진 자리" narrative

2-3. 영양·식감 이론 (2종)

⑩ HabEat Project
EU FP7, 2010-2014, Schwartz et al.
EU 7개국 영유아 코호트. 식감 단계·메뉴 반복도가 식습관 형성에 결정적
활용: 7축 中 식감·메뉴반복. 연령별 권장 식감 분포 lookup 테이블 (AGE_TEXTURE_TABLE)
⑪ KDRI 2025 (한국인 영양소 섭취기준)
보건복지부 · 한국영양학회, 2025
한국인 36 필수 영양소 RNI/AI/UL. 콜린 2025 신규 제정 (만 1-2세 160 mg/일)
활용: 36 영양 신호등·부족 영양 보강 추천·탄·단·지 권장량. 한국 영유아 표준
━ 4 PRINCIPLES ━

3. 추천 4 원칙 + 사용 데이터

원칙 1

⭐ 선호 베이스 + 도전 식재료

잘먹는 베이스(계란찜·죽·부침개)에 거부 식재료를 자연스럽게 — 거부감 ↓

사용 데이터: base_stats (선호 베이스 TOP 5) · ingredient_stats (accept_rate < 0.5 OR 미만남 > 60일) · recipe_ingredients.form ('즙'/'다진'/'매시')
학술 근거: Food Chaining (Toomey 2007) + Repeated Exposure (Cooke 2011)
원칙 2

🥗 잘먹지만 오랜만 → 원물 노출

accept_rate ≥ 0.7 AND 30일+ 안 만났고 항상 가린 형태(즙·다진)였던 식재료를 통째·스틱·큐브로 식감 단계 ↑

사용 데이터: ingredient_stats.preferred_forms · texture_distribution (현재 max texture) · recipe.texture_level (한 단계 위)
학술 근거: HabEat (식감 점진 단계) + Satter DOR (자율성·핑거푸드)
원칙 3

🥄 극도 거부 → 형태 안 보이게

accept_rate < 30% AND exposure ≥ 5회인 식재료를 즙·페이스트로 — 노출 누적 우선

사용 데이터: ingredient_stats (accept_rate·exposure_count) · recipe.concealment (high만) · preferred_bases (베이스도 친숙해야)
학술 근거: Toomey SOS Approach (단계 4-5) + Cooke 12-30회 반복 노출
원칙 4

💊 부족 영양 보강

KDRI 신호등 빨강 영양소 (철·콜린·비타민D·EPA+DHA 등)를 잘먹는 베이스 + 영양 highlight 매칭으로 자연 보강

사용 데이터: nutrient_status (red) · recipe.nutrient_highlights · recipe.nutri_total · KDRI_RNI[age_band]
학술 근거: KDRI 2025 (보건복지부) + 농진청 식품성분표

3-1. 4 원칙 가중합 랭킹

final_score = (
  0.30 × base_preference_score +    # 잘먹는 베이스 (Food Chaining)
  0.20 × concealment_score +        # 가림 정도 (Toomey SOS)
  0.30 × nutrient_boost_score +     # KDRI 부족 영양
  0.10 × novelty_score +            # 최근 21끼 미등장 (반복 회피)
  0.10 × age_appropriateness_score  # 연령 fit (HabEat)
)
━ MAIN DISH DETECTION ━

4. 고단백 메인 반찬 판단 로직 (NEW)

🚨 문제 — "고기 있다 = 고단백" 단순 체크의 한계

예시: "소고기 미역국"은 메인이 미역국 (수프). 소고기는 5g 정도 들어간 육수용 — 실제 고단백 메인 X.

vs "갈비찜": 메인이 소고기 (50g+). 고단백 메인 ✓

단순 ingredients.contains('소고기') 체크로는 두 경우를 구분 못함. 메인 반찬 판단 알고리즘 필요.

4-1. recipes.main_protein_type 필드 (NEW)

레시피 enrich 단계에서 LLM으로 결정:

type MainProteinType =
  | 'red_meat'      // 소·돼지·양 (메인, ≥30g)
  | 'white_meat'    // 닭·오리 (메인, ≥30g)
  | 'fish'          // 생선 (메인, ≥30g)
  | 'seafood'       // 새우·오징어·조개 (메인)
  | 'egg'           // 계란 (메인, ≥1개)
  | 'soy'           // 콩·두부 (메인 단백질 ≥40g)
  | 'mixed'         // 2종 이상 메인 (혼합)
  | 'aux_protein'   // 보조 단백 (소고기 미역국·멸치육수)
  | 'no_protein';   // 단백질 미포함 (밥·면 위주)

4-2. 판단 알고리즘 (LLM 분류 + 검증)

async function classifyMainProtein(recipe: Recipe): Promise<MainProteinType> {
  // 1차: 룰 기반 — 단백질 식재료 g 기준
  const proteinIngs = recipe.ingredients.filter(i => PROTEIN_INGREDIENTS.has(i.name));
  if (proteinIngs.length === 0) return 'no_protein';

  // 단백질 식재료의 분량 합 (메인 판정)
  const mainProtein = proteinIngs.filter(i => i.amount_g >= PROTEIN_MAIN_THRESHOLD[i.type]);
  if (mainProtein.length === 0) return 'aux_protein';  // 보조만

  // 메인 단백 분류
  if (mainProtein.length > 1) return 'mixed';
  const main = mainProtein[0];
  return PROTEIN_TYPE_MAP[main.type];
}

// 임계값 (메인 판정 기준)
const PROTEIN_MAIN_THRESHOLD: Record<string, number> = {
  'red_meat': 30,    // 소고기·돼지고기 ≥30g
  'white_meat': 30,
  'fish': 30,
  'seafood': 20,
  'egg': 50,         // 달걀 1개 ≈ 50g
  'soy': 40,          // 두부 1/4모 ≈ 40g
};

// 검증 사례
// 소고기 미역국: 소고기 5g → aux_protein
// 갈비찜: 갈비 100g → red_meat
// 닭볶음탕: 닭 80g → white_meat
// 두부찜: 두부 100g → soy

4-3. 고단백 메인 다양성 추적

주간 메인 반찬 단백 유형 다양성:

function mainProteinVariety(logs: MealLog[], days = 7): MainProteinReport {
  const mainTypes = logs
    .map(l => l.recipe?.main_protein_type)
    .filter(t => t && t !== 'no_protein' && t !== 'aux_protein');
  const uniqueTypes = new Set(mainTypes);
  return {
    totalMainMeals: mainTypes.length,
    uniqueTypes: uniqueTypes.size,
    distribution: countBy(mainTypes),
    // 권장: 주 7끼 메인 중 4종 이상 (red·white·fish·soy 최소)
    status: uniqueTypes.size >= 4 ? 'green' : uniqueTypes.size >= 3 ? 'orange' : 'red'
  };
}
이번 주 메인 반찬uniqueTypes등급narrative
매일 닭볶음탕·닭조림·닭구이만 (7끼)1 (white_meat)🔴 red"메인 단백질 변화 없음 — 소고기·생선·두부 도전 권장"
닭 3회 + 소고기 2회 (5끼 메인)2🟠 orange"생선·콩 메인 1회씩 추가하면 균형"
닭 2회 + 소고기 1 + 생선 2 + 두부 1 + 계란 15🟢 green"메인 단백질 다양성 우수"
━ DIVERSITY 2-LAYER ━

5. 다양성 2-Layer 시스템

5-1. Layer 1 — WHO MDD (Pass/Fail)

8 식품군 중 N개 등장 여부. 최소 통과 기준.

MDD 점수등급 cutoff
≤ 4/8D (Fail)
5-6/8C (Pass)
7-8/8B 이상 진입 가능

5-2. Layer 2 — 우리 14 sub-카테고리 + 종 다양성

#대분류sub-카테고리주간 권장 종예시
1곡물정제곡물-백미·흰밀가루
2통곡물·잡곡2종+현미·보리·귀리·메밀·찰미·기장
3콩제품2종+두부·콩나물·연두부·콩가루
4콩알류·렌틸1종+검은콩·강낭콩·렌틸·병아리콩·팥
5유제품우유·요거트·치즈2종+우유·요거트·치즈·버터
6고기·생선붉은 고기1종+소·돼지·양
7흰 고기·가금류1종+닭·오리
8생선·해산물2종+고등어·연어·새우·오징어·미역
9계란계란류1종+달걀·메추리알
10진한 채소진한 잎채소3종+시금치·근대·아욱·청경채·케일·상추
11진한 색 채소3종+당근·단호박·파프리카·토마토·비트
12기타 채소기타 채소3종+무·오이·양배추·콩나물·숙주·버섯
13과일과일3종+사과·딸기·바나나·블루베리·키위
14견과·씨견과·씨앗1종+호두·들깨·참깨·아몬드

핵심 4 카테고리는 종 다양성 필수 (Lachat DSR + HabEat). 시금치만 5번 = 잎채소 통과지만 ⭐ 미충족 → A 등급 안 됨.

5-3. 등급 cutoff (Layer 1 + 2 종합)

function finalDiversityGrade(layer1: number, layer2: VarietyMetrics): Grade {
  if (layer1 <= 4) return 'D';            // MDD fail
  if (layer1 <= 6) return 'C';            // MDD pass only
  if (layer1 === 8 && layer2.subcatCount >= 10) {
    const criticalPass = CRITICAL_4.every(c => layer2.speciesPerSubcat[c] >= MIN_SPECIES[c]);
    if (criticalPass) {
      if (layer2.totalUniqueSpecies >= 25 && layer2.newSpeciesThisWeek >= 2) return 'S';
      return 'A';
    }
  }
  return 'B';
}

5-4. 카테고리 내 다양성 narrative 예시

// 시금치만 매번 등장 시
{
  subcategory: '진한 잎채소',
  species_count: 1,
  recommendation: '시금치만 5번 — 근대·아욱·청경채로 잎채소 다양화 권장',
  recipes_to_suggest: ['근대된장국', '아욱죽', '청경채 무침']
}
━ REPETITION AVOIDANCE ━

6. 반복 회피 (시계열)

같은 메뉴를 4주 내 반복하지 않음 (HabEat 권고). 자동 제외 로직:

function noveltyScore(recipe: Recipe, recentMenus: string[]): number {
  // 최근 21끼(7일) 메뉴 vs 추천 후보
  if (recentMenus.includes(recipe.name)) return 0;          // 완전 동일
  if (similarBaseSubstr(recipe.name, recentMenus, 0.8)) return 0.3; // 유사 (찜닭 vs 닭볶음탕)
  return 1.0;                                          // 신규
}

// 베이스 반복도 추가 체크
function baseRepetitionWarning(profile: ChildProfile): Warning | null {
  const top = profile.recentBases[0];  // 가장 많이 등장한 베이스
  if (top.count >= 5) {  // 한 주에 5회 이상 같은 베이스
    return {
      type: 'base_repetition',
      message: `${top.baseType}이 한 주 ${top.count}번 — 베이스 로테이션 권장`,
      suggestion: rotateSuggestion(top.baseType)  // 죽 → 부침개·국·찜 등
    };
  }
  return null;
}
━ FINAL OUTPUT ━

7. 최종 출력 — 6-8 레시피 + 자연어 이유

flowchart TD D1[📝 식단 기록
30일] D2[📊 프로파일
집계] D3[📏 아이 정보
BMI·알레르겐] D4[📚 레시피 풀
4,432종] D5[🥕 식재료 마스터
1000+] D6[📖 학술 기준
KDRI·WHO·HabEat] PHASE0[Phase 0
프로파일 집계] PHASE1A[원칙 1
선호+도전] PHASE1B[원칙 2
오랜만 원물] PHASE1C[원칙 3
형태 가림] PHASE1D[원칙 4
영양 보강] RANK[가중합 랭킹
+ 반복 회피] LLM[LLM 정제
Claude Sonnet 4.7] OUT[📤 6-8 레시피
+ 자연어 이유] D1 --> PHASE0 D2 --> PHASE0 D3 --> PHASE0 PHASE0 --> PHASE1A & PHASE1B & PHASE1C & PHASE1D D4 --> PHASE1A & PHASE1B & PHASE1C & PHASE1D D5 --> PHASE1A & PHASE1B & PHASE1C & PHASE1D D6 --> PHASE1D PHASE1A & PHASE1B & PHASE1C & PHASE1D --> RANK RANK --> LLM LLM --> OUT classDef data fill:#E3F2FD,stroke:#3498DB,color:#1F2D3D classDef phase fill:#E8F5E9,stroke:#16A085,color:#1F2D3D classDef llm fill:#F3E5F5,stroke:#9C27B0,color:#1F2D3D classDef out fill:#FFF8E1,stroke:#F9A825,color:#1F2D3D class D1,D2,D3,D4,D5,D6 data class PHASE0,PHASE1A,PHASE1B,PHASE1C,PHASE1D,RANK phase class LLM llm class OUT out

7-1. 출력 예시 (JSON)

{
  "recommendations": [
    {
      "principleId": 1,
      "recipeName": "시금치 즙 계란찜",
      "reasoning": "우리 아이가 32회 좋아한 계란찜 베이스에 시금치 5g 즙으로 추가했어요.
                    시금치는 22번 노출 중 8번만 먹은 식재료라 거부 단계가 정상입니다.
                    학술 연구에 따르면 평균 12-30번 노출 후 수용된다고 합니다.
                    즙으로 만들면 색·향이 거의 안 보여 부담 0.",
      "factors": {
        "basePreference": 0.95, "concealment": 1.0,
        "nutrientBoost": 0.6,   "novelty": 1.0,
        "ageFit": 1.0
      },
      "academicSources": ["Food Chaining 2007", "Cooke 2011"]
    },
    ...
  ],
  "narrativeSummary": "이번 주 핵심: 시금치·콜린·EPA+DHA 3가지 부족 — 위 4 레시피로 자연 보강",
  "warnings": [
    { "type": "base_repetition", "message": "죽이 한 주 5번 — 부침개·국으로 로테이션" }
  ]
}
━ SEASONAL FOODS ━

9. 제철 식재료 기준 (한국 12개월)

제철 식재료 = 영양가 ↑ · 가격 ↓ · 농약 ↓ · 환경 ↓. 농진청 + 해양수산부 권고 기반 12개월 마스터.

9-1. 한국 12개월 제철 식재료 마스터

제철 채소·과일제철 수산물하이라이트
1월시금치·당근·우엉·연근·미나리·콩나물·배추·무·귤·딸기굴·새조개·아귀·꽃게·과메기겨울 시금치·굴
2월시금치·미나리·달래·냉이·연근·딸기·한라봉굴·미더덕·황태·과메기·꼬막겨울 끝물 + 봄나물 시작
3월봄나물(달래·냉이·쑥·취나물)·시금치·딸기·앵두주꾸미·꼬막·도다리·바지락·소라봄나물 본격
4월봄나물·근대·아욱·상추·딸기·앵두·미나리주꾸미·도다리·꼬막·소라·바지락잎채소 다양
5월죽순·아스파라거스·근대·완두콩·딸기·체리·매실멸치·갑오징어·새우·해삼·다슬기죽순·새우 최성기 🌟
6월매실·복숭아·살구·자두·블루베리·옥수수·오이·가지·호박농어·갈치·전복·붕장어여름 시작
7월옥수수·오이·가지·애호박·복숭아·자두·수박·참외민어·갈치·장어·전복여름 채소 절정
8월옥수수·가지·오이·토마토·수박·참외·복숭아·자두·포도전복·장어·갈치·민어과일 풍성
9월가지·고구마·호박·사과·배·포도·대추·밤전어·꽃게·갈치·고등어·새우전어·꽃게 절정 🍂
10월고구마·단호박·무·당근·시금치·사과·배·홍시·석류꽃게·고등어·전어·삼치·갈치가을 영양 풍부
11월시금치·무·배추·생강·연근·우엉·당근·귤·사과·배고등어·갈치·삼치·전어·과메기김장 채소
12월시금치·무·배추·당근·우엉·연근·미나리·귤·사과굴·꼬막·과메기·아귀·복어겨울 영양 회복

9-2. 평가 알고리즘 — 제철 비율 점수

const SEASONAL_TABLE: Record<number, string[]> = {
  1: ['시금치', '당근', '우엉', '연근', '굴', '꽃게', ...],
  2: [...], ...
};

function seasonalScore(logs: MealLog[], currentMonth: number): SeasonalResult {
  const seasonalIngs = new Set(SEASONAL_TABLE[currentMonth]);
  const allUsed = logs.flatMap(l => l.ingredients.map(i => i.name));
  const uniqueUsed = new Set(allUsed);
  const seasonalUsed = [...uniqueUsed].filter(i => seasonalIngs.has(i));
  const ratio = seasonalUsed.length / uniqueUsed.size;
  return {
    pct: ratio * 100,
    seasonalCount: seasonalUsed.length,
    totalUnique: uniqueUsed.size,
    matched: seasonalUsed,
    missing: [...seasonalIngs].filter(i => !uniqueUsed.has(i)).slice(0, 5),
    status: ratio >= 0.30 ? 'green' : ratio >= 0.15 ? 'orange' : 'red',
    recommendation: buildSeasonalRecommendation(seasonalUsed, currentMonth)
  };
}

9-3. 임계치

제철 비율등급narrative
≥ 30%🟢 green"제철 식재료 잘 챙기고 있어요"
15-30%🟠 orange"이번 달 죽순·새우 같은 제철 식재료 1-2종 추가 권장"
< 15%🔴 red"제철 식재료 거의 없음 — 가격·영양·환경 모두 ↓"

9-4. 레시피 추천 가산점

// 4 원칙 final_score에 0.05 가산
final_score += seasonal_bonus(recipe, current_month);

function seasonalBonus(recipe, currentMonth) {
  const seasonalIngs = SEASONAL_TABLE[currentMonth];
  const recipeIngs = recipe.ingredients.map(i => i.name);
  const matchCount = recipeIngs.filter(i => seasonalIngs.includes(i)).length;
  return matchCount >= 2 ? 0.05 : matchCount === 1 ? 0.03 : 0;
}

9-5. 데모 적용

━ CUISINE DIVERSITY ━

10. 조리 스타일 다양성 (한식 외 다채로움)

14 sub-카테고리는 식재료 단위. 그러나 매번 한식만이면 식습관 폭이 좁아짐. HabEat·EU 코호트 연구는 cuisine variety가 식습관 형성에 결정적이라고 보고.

10-1. 6 cuisine 카테고리 (제안)

#Cuisine대표 메뉴핵심 영양
1한식 (기본)밥·국·찌개·반찬·김치·전·찜·무침발효식품·미네랄·식이섬유
2양식파스타·스튜·샐러드·오믈렛·리조또·수프유제품·올리브유·치즈
3중식볶음밥·짜장·만두·딤섬·당면·동파육다양한 채소·콩·견과
4일식덮밥·우동·소바·돈가스·계란말이·미소국해산물·다시·간단 조리
5동남아·아시아쌀국수·카레·팟타이·나시고렝·반미향신료·코코넛·향초
6지중해·중동후무스·팔라펠·쿠스쿠스·타불레올리브유·통곡물·콩

10-2. 평가 알고리즘

function cuisineDiversity(logs: MealLog[]): CuisineResult {
  const cuisineUsed = new Set<CuisineType>();
  for (const log of logs) {
    const cuisine = classifyCuisine(log.recipe);  // LLM enrich
    cuisineUsed.add(cuisine);
  }
  const uniqueCount = cuisineUsed.size;
  return {
    uniqueCount,
    distribution: countBy(logs.map(l => l.recipe.cuisine)),
    status: uniqueCount >= 3 ? 'green' : uniqueCount >= 2 ? 'orange' : 'red',
    recommendation: uniqueCount < 2 ? '매번 한식만 — 양식·일식 메뉴 1-2회 시도' : null
  };
}

10-3. 임계치 (주간 기준)

주간 cuisine 수등급narrative
3종+🟢 green"조리 스타일 다양 — 식습관 폭 ↑"
2종🟠 orange"1종 더 시도 권장 (양식 파스타·일식 덮밥 등)"
1종 (한식 only)🔴 red"매번 한식 — 양식·중식·일식 한 끼씩 도전"

10-4. 데모 적용

━ INPUT PREPROCESSING ━

11. LLM 입력 전처리 (자연어·사진 → DB 매칭)

엄마들은 비결정적 입력 (사진·자연어 메모·퀵노트)을 사용. 우리는 LLM으로 정확하게 매칭해야 영양 평가가 정확해짐.

11-1. 입력 유형 4가지

입력예시매칭 대상
📸 사진음식 사진 1장ingredients 마스터 1,070종
🏷 해시태그"시금치", "당근", "ㅂㄹㅋㄹ"INGREDIENT_POOL 147종 (정확)
📝 자연어 메모"시금치 거부했는데 김 가루로 덮으니 잘 먹음"식재료(시금치·김) + 반응(거부→잘먹음) 추출
🔘 퀵노트 chip"한 입도 안 먹음", "친구따라 먹음"CEBQ 코드·동적 질문 차원 매핑

11-2. ALG-PREP-01 — 사진 → 식재료 추출

이미 ALG-OCR-06 (engines-deep)에 명세. 신뢰도 ≥ 0.7만 자동 채택 + 사용자 수정 UI.

11-3. ALG-PREP-02 — 자연어 메모 → 구조화 데이터 (NEW)

const MEMO_EXTRACT_PROMPT = `엄마가 자유 텍스트로 쓴 식사 메모에서 다음을 추출하세요.

# 입력 예시
"시금치 거부했는데 김 가루로 덮으니 잘 먹음. 오늘 아침에는 안 먹다가 점심에는 또 달라고 했어요."

# 출력 JSON
{
  "ingredients_mentioned": [
    {"name": "시금치", "reaction": "rejected", "context": "초기 거부"},
    {"name": "김", "reaction": "well_eaten", "context": "위장 식재료로 활용"}
  ],
  "behaviors": [
    {"type": "rejection", "context": "초기"},
    {"type": "conceal_success", "context": "다른 식재료로 가림"},
    {"type": "time_difference", "details": "아침 vs 점심 다른 반응"}
  ],
  "parent_emotion": "neutral_with_relief",  // 안도·기쁨·좌절·중립
  "cebq_signals": ["food_responsiveness", "food_fussiness_partial"]
}\`;

async function extractFromMemo(memo: string): Promise<MemoExtraction> {
  if (memo.length < 5) return null;
  const response = await anthropic.messages.create({
    model: 'claude-haiku-4-5',  // 분류 작업이라 Haiku로 충분
    max_tokens: 800,
    messages: [{ role: 'user', content: MEMO_EXTRACT_PROMPT + '\n\n# 입력\n' + memo }],
  });
  const extracted = parseJSON(response.content[0].text);
  // 매칭 검증: ingredients_mentioned가 INGREDIENT_POOL에 있는지
  for (const ing of extracted.ingredients_mentioned) {
    ing.matched_id = await fuzzyMatchIngredient(ing.name);
  }
  return extracted;
}

11-4. ALG-PREP-03 — 해시태그 정규화 (이미 구현)

// 한글 초성 추출 + fuzzy match (care.html 이미 구현)
function normalizeIngredientInput(raw: string): string | null {
  const trimmed = raw.trim().toLowerCase();
  // 1. 정확 매칭
  const exact = INGREDIENT_POOL.find(i => i.name === trimmed);
  if (exact) return exact.id;
  // 2. 초성 매칭 (ㅂㄹㅋㄹ → 브로콜리)
  if (/^[ㄱ-ㅎ]+$/.test(trimmed)) {
    return INGREDIENT_POOL.find(i => getChosung(i.name).startsWith(trimmed))?.id;
  }
  // 3. 부분 매칭
  return INGREDIENT_POOL.find(i => i.name.startsWith(trimmed))?.id;
}

11-5. ALG-PREP-04 — 퀵노트 chip → CEBQ 매핑 (NEW)

const QUICK_NOTE_TO_CEBQ: Record<string, CEBQDimension[]> = {
  '한 입도 안 먹음': ['food_fussiness', 'satiety_responsiveness'],
  '골라냄': ['food_fussiness'],
  '향만 맡음': ['food_fussiness_early_stage'],
  '평소보다 많이': ['food_responsiveness', 'enjoyment_of_food'],
  '또 달라': ['food_responsiveness'],
  '친구 따라 먹음': ['social_facilitation'],
  'TV 보며': ['environment_distraction'],
  '스스로 다': ['autonomy_high'],
  ...
};

function mapQuickNoteToCEBQ(quickNote: string): CEBQDimension[] {
  return QUICK_NOTE_TO_CEBQ[quickNote] || [];
}

11-6. 전체 전처리 파이프라인

async function preprocessMealInput(input: RawMealInput): Promise<StructuredMealLog> {
  const log: StructuredMealLog = { ...input };

  // 1. 사진 → 식재료 추출 (있으면)
  if (input.photo) {
    const photoIngs = await extractFromPhoto(input.photo);  // ALG-PREP-01
    log.ingredients = [...(log.ingredients || []), ...photoIngs];
  }

  // 2. 해시태그 정규화 (이미 입력 시 매칭됨)
  log.ingredients = log.ingredients.map(normalizeIngredientInput);

  // 3. 자연어 메모 분석 (있으면)
  if (input.note && input.note.length > 10) {
    const memoData = await extractFromMemo(input.note);  // ALG-PREP-02
    log.memo_extraction = memoData;
    // 메모에서 추가 발견된 식재료 병합 (중복 제외)
    log.ingredients = dedup([...log.ingredients, ...memoData.ingredients_mentioned.map(i => i.matched_id)]);
  }

  // 4. 퀵노트 → CEBQ 매핑
  log.cebq_signals = (input.quickNotes || []).flatMap(mapQuickNoteToCEBQ);

  // 5. 신뢰도 메타
  log.preprocessing_confidence = computeOverallConfidence(log);
  log.requires_user_confirm = log.preprocessing_confidence < 0.75;

  return log;
}

11-7. 사용자 수정 인터페이스

신뢰도 < 0.75일 경우 부모에게 확인 받음:

━ ACADEMIC REFERENCES ━

8. 학술 출처 풀 리스트

국제 기준 (Diversity)

  • WHO. (2017). Indicators for assessing infant and young child feeding practices: definitions and measurement methods. Geneva: World Health Organization.
  • Lachat C, et al. (2018). Dietary species richness as a measure of food biodiversity and nutritional quality of diets. PNAS, 115(1), 127-132.
  • USDA. (2020). Healthy Eating Index-2020 (HEI-2020). Center for Nutrition Policy and Promotion.
  • Hatløy A, et al. (1998). Food variety, socioeconomic status and nutritional status in urban and rural areas in Koutiala (Mali). Public Health Nutrition, 1(3), 205-211.
  • Monteiro CA, et al. (2019). Ultra-processed foods, diet quality, and health using the NOVA classification system. FAO.

식습관 형성 이론

  • Fraker C, Fishbein M, Cox S, Walbert L. (2007). Food Chaining: The Proven 6-Step Plan to Stop Picky Eating. Da Capo Lifelong Books.
  • Toomey KA, Ross E. (2017). The SOS Approach to Feeding. SOS Approach to Feeding Conference.
  • Cooke L. (2011). The importance of exposure for healthy eating in childhood. Journal of Human Nutrition and Dietetics, 20(4), 294-301.
  • Birch LL, Marlin DW. (1982). I don't like it; I never tried it: Effects of exposure on two-year-old children's food preferences. Appetite, 3(4), 353-360.
  • Satter E. (2007). Eating Competence: Definition and Evidence for the Satter Eating Competence Model. JNEB, 39(5 Suppl), S142-S153.

식감·코호트 연구

  • Schwartz C, et al. (2014). HabEat Project — Determining factors and critical periods of food habit formation. EU FP7.
  • Northstone K, et al. (2001). Are dietary patterns stable throughout early and mid-childhood? ALSPAC cohort.

한국 기준

  • 보건복지부·한국영양학회. (2025). 2025 한국인 영양소 섭취기준 (KDRI). 1·2·3권 + 국문/영문 요약본 + 3차 정오표.
  • 농촌진흥청. (2024). 국가표준식품성분표 v10.0.
  • 질병관리청. (2017). 소아청소년 표준성장도표 (LMS 파라미터).
  • 식품의약품안전처. 식품 알레르기 17대 표시 의무.

영유아 특화

  • Solid Starts. (2024). 100+ First Foods. solidstarts.com.
  • Rapley G. (2008). Baby-Led Weaning. Vermilion.
  • NHS. (2024). Your baby's first solid foods. UK National Health Service.