일진 오행은 스코어링 보조 가점과 풀이 문구에 반영되어, 같은 사용자라도 날짜에 따라 추천과 풀이가 달라집니다.
결과 화면 하단 문구 "내일은 또 다른 기운이 찾아올 거예요"가 실제 동작과 일치하게 됩니다.
FR-505메뉴 오행 태깅
메뉴 전체(현행 253종 — 안주 전용 포함. 한때 1,000종까지 늘렸다가 v3.0.20에서 실제로 사 먹을 수 있는 메뉴 위주로 재정비)에 element(오행) 속성을 부여합니다. 분류 기준은 §5의 매핑표를 따릅니다.
기존 온도 · 무게 속성은 삭제하지 않고 보조 가중치 축으로 유지합니다.
FR-506오행 기반 스코어링 (확정)
시간대 후보군의 각 메뉴에 아래 가점을 합산해 정렬합니다.
가점
조건
비고
+5
메뉴 오행이 나의 부족 오행과 일치
주 가점 — 아래 가점을 모두 합쳐도 넘지 못하게 설계
+2
메뉴 오행이 오늘의 일진이 생(生)하는 오행
일진 가점 — 같은 사용자도 날마다 순위가 바뀌는 요인
+1 / +1
메뉴 온도 · 무게가 체질의 반대 속성
v1의 주 가점(+4/+3)을 보조 축으로 강등
+1 (10% 확률)
부족 오행 비일치 메뉴의 랜덤 허용치
v1과 동일한 탐색 다양성 장치
−100
최근 14일 안에 이미 보여준 메뉴
재추천 방지 — 가점 총합보다 크게 잡아 "안 본 메뉴"가 항상 앞
−3
최근 2일 안에 보여준 계열(baseMenu)
요거트 → 요거트 같은 계열 반복만 완화
동점 시 부족 오행 일치 메뉴 우선 → 날짜 시드 기반 랜덤 → 원본 데이터 순서로 정렬합니다. 랜덤 타이브레이크가 없으면 같은 조건의 날마다 동점 무리의 맨 앞 메뉴가 늘 1순위로 고정됩니다.
비일치 메뉴의 이론상 최대 점수는 +5(1+2+1+1)로, 부족 오행 일치 메뉴(최소 +5)와 동점이 되어도 타이브레이크에서 밀립니다.
이미지 다양성 재정렬은 v1 로직을 그대로 재사용합니다.
FR-507사주 풀이 노출
결과 화면의 추천 이유 영역을 이미지별 고정 문구에서 사주 기반 동적 풀이로 교체합니다.
풀이 구성: 나의 기운(일간 오행) + 부족 오행 + 오늘의 일진 + 추천 근거.
쉬운 말 원칙(v2.5 개정) — 사주를 모르는 사용자가 "임수가 뭔지, 화 기운이 부족해서 뭘 어쩌라는 건지" 묻지 않게 합니다.
풀이 문장에는 일간 · 오행 · 일진 같은 용어와 "다섯 기운" 개념 자체를 쓰지 않고, 용어는 정보 칩에만 남깁니다.
(같은 날 1차 개정은 "촛불처럼 따뜻하고 섬세한 정화(丁火)의 당신은 타고난 다섯 기운(오행) 중 나무(木)의 생기와 불(火)의 온기가 가장 적은 편이에요"처럼
용어를 괄호로 뒤에 두는 방식이었으나, "정화의 당신 · 다섯 기운 중 나무와 불이 적다는 내용 자체가 이해가 안 된다"는 피드백으로 같은 날 철회)
짧게, 한두 문장(60자 이내) — 타입 설명("촛불처럼 따뜻하고 섬세한")은 칩 "나의 기운"에 맡기고 문장에 넣지 않습니다.
문장에는 이 메뉴가 채우는 부족한 것 한 가지 + 메뉴만 담습니다. 부족 오행은 사람에게 뜻하는 것(활력 · 온기 · 든든함 · 집중력 · 여유)으로,
메뉴에는 짧은 수식(뜨끈한 · 상큼한 · 구수한 · 담백한 · 깊은 맛의)을 붙입니다.
(부족 오행은 사주에서 정해지므로 "오늘 부족한 하루"가 아니라 "부족해지기 쉬운 당신"처럼 타고난 경향으로)
메뉴마다 문장 뼈대가 달라집니다 — "다시 추천하기"로 넘겨도 메뉴 이름만 바뀌고 나머지가 똑같다는 피드백(같은 날 2회: 이름만 바뀜 → 조각만 바꿔도 여전히 비슷하고 길다)으로,
부족 보완 뼈대 5종("○○이 부족해지기 쉬운 당신, 오늘 저녁은 뜨끈한 △△ 어때요?" / "오늘 저녁은 △△로 ○○을 채워 보세요. 밤이 한결 포근해질 거예요." /
"△△이면 모자란 ○○이 채워질 거예요." / "○○ 충전이 필요한 날엔 △△만 한 게 없어요." / "○○이 모자라기 쉬운 당신에게 △△, 오늘 저녁 추천이에요.") ·
일진 가점 뼈대 3종("오늘은 날짜의 기운이 △△을 밀어주는 날이에요 …") · 균형 뼈대 3종("기운의 균형을 지켜 주는 △△ …") · 음식 수식 오행별 3종을
골라 조합합니다. 뼈대는 추천 목록 위치 순서로 돌려("다시 추천하기" 1번째 → 2번째 → …) 연달아 같은 뼈대가 나오지 않게 하고,
음식 수식은 메뉴 id 해시(FNV-1a + fmix)로 고릅니다(같은 메뉴는 같은 수식). 위치가 없는 호출은 뼈대도 해시로 고릅니다.
정보 칩은 사주 용어가 남는 유일한 자리 — "나의 기운 · 촛불 정화(丁火)" · "오늘의 기운 · 나무(木) · 갑진일"(당초 "나의 기운 · 정화(丁火)" · "오늘의 일진 · 갑진(甲辰)").
문구는 부족 오행(5) × 추천 근거(3 — 부족 보완 · 일진 상생 · 균형) × 시간대(3) 템플릿에 메뉴별 뼈대 · 수식 선택을 더해 생성합니다.
어휘표는 아래와 같고(각 칸의 첫 버전만 표기) lib/saju/interpretation.ts의 ELEMENT_PLAIN · STEM_PLAIN(칩용) · 뼈대 배열이 원본입니다.
내 정보 요약 카드의 십이시 표기(자시~해시)는 유지합니다.
오행
칩 표기(자연물)
부족해지기 쉬운 것 (문장)
음식 수식 (§5 매핑 기준, 첫 버전)
목 (木)
나무(木)
활력
상큼한
화 (火)
불(火)
온기
뜨끈한
토 (土)
흙(土)
든든함
구수한
금 (金)
쇠(金)
집중력
담백한
수 (水)
물(水)
여유
깊은 맛의
일간 (나의 기운)
칩 표기
성향 수식 (현재 문장에는 쓰지 않음 · 칩 · 확장용)
갑목 (甲木)
큰 나무
곧게 뻗은 큰 나무처럼 우직한
을목 (乙木)
풀꽃
바람에 흔들려도 꺾이지 않는 풀꽃처럼 유연한
병화 (丙火)
태양
한낮의 태양처럼 환하고 활기찬
정화 (丁火)
촛불
어둠을 밝히는 촛불처럼 따뜻하고 섬세한
무토 (戊土)
큰 산
큰 산처럼 묵직하고 든든한
기토 (己土)
기름진 땅
씨앗을 품는 땅처럼 포용력 있는
경금 (庚金)
무쇠
무쇠처럼 강직한
신금 (辛金)
보석
잘 다듬은 보석처럼 예리하고 섬세한
임수 (壬水)
넓은 바다
넓은 바다처럼 깊고 유연한
계수 (癸水)
빗물
조용히 스미는 빗물처럼 맑고 지혜로운
FR-508최근 추천 이력 (재추천 방지)
끼니 알림을 보고 들어왔을 때 어제와 같은 메뉴가 떠 있으면 열어볼 이유가 없습니다. 화면에 실제로 띄운 메뉴를 이력으로 남기고, 다음 날 스코어링에서 감점합니다.
저장 위치는 localStorage의 energy-meal:recent-menus — { date, slot, name, baseMenu } 배열이며 최대 150건까지 보관하고 넘치면 오래된 것부터 버립니다.
기록 시점: ① 화면 진입 시 세 끼니의 현재 추천(탭을 열지 않아도 기록) ② "다시 추천"으로 갈아탄 메뉴.
오늘 날짜 이력은 감점 대상에서 제외합니다. 포함하면 새로고침할 때마다 방금 본 메뉴가 밀려나 하루 고정 원칙(§FR-506 시드)이 깨집니다.
필터가 아닌 감점이므로 이력이 후보군을 다 덮어도 추천이 비지 않습니다. 끼니별 후보가 194~409종이라 14일 창(하루 3건 + 다시 추천분)에는 여유가 있습니다.
FR-509알림 진입 시 본 끼니 교체
추천은 하루 고정(§FR-506 시드)이라, 아침에 점심 · 저녁 탭까지 미리 열어본 사용자는 점심 알림을 눌러도 이미 본 메뉴를 다시 보게 됩니다. 알림을 눌러 들어온 진입에 한해 그 끼니를 다음 후보로 갈아 끼웁니다.
"봤다"의 기준(v2.4 개정): 끼니 탭을 직접 누르면 그 즉시, 화면을 열 때 펴진 첫 탭도 즉시 본 것으로 기록합니다. 당초(v2.3)는 탭이 1.5초 이상 떠 있어야 했는데, 메뉴만 빠르게 훑고 넘기는 사용 습관에서는 기록이 남지 않아 알림으로 들어와도 이미 본 메뉴가 그대로 떴습니다(사용자 피드백). 1.5초 체류 기준은 자동으로 펴진 탭(백그라운드 복귀로 끼니가 넘어간 경우, 화면을 켜둔 채 자정을 넘겨 다시 만든 경우)에만 남깁니다 — 알림 진입 직전의 자동 전환까지 본 것으로 세면 정작 처음 보는 메뉴가 교체됩니다. 저장 위치는 energy-meal:viewed-slots — { date, slots }이며 날짜가 바뀌면 비워집니다.
열어보지 않은 끼니는 그대로 둡니다. 아침 · 점심만 봤다면 저녁 알림은 그날의 원래 추천을 보여줍니다.
진입 경로는 네이티브 셸이 붙이는 쿼리 /today?entry=meal-reminder&slot=<끼니>&n=<토큰>입니다. 토큰(알림 식별자 + 발생 시각)은 energy-meal:handled-entry에 남겨, 같은 알림으로 두 번 들어와도 연달아 교체되지 않게 합니다. 처리 후 쿼리는 주소창에서 지웁니다.
교체했을 때는 "오늘 이미 본 OO 메뉴라 새로 골랐어요" 안내와 "이전 메뉴 보기"를 함께 띄웁니다. 아침에 본 메뉴로 이미 마음을 정한 경우를 되돌릴 수 있어야 합니다.
그냥 앱을 다시 열거나 백그라운드에서 복귀한 경우에는 교체하지 않습니다. 메뉴가 바뀌는 계기는 알림 진입뿐입니다. 다만 백그라운드에 있는 동안 끼니가 넘어갔다면(아침 → 점심) 복귀 시 지금 끼니 탭으로 옮겨 줍니다.
결과 화면의 첫 탭은 지금 끼니입니다 — 0~10시 아침 · 11~16시 점심 · 17~23시 저녁(추천 엔진의 시간대 구분과 같음). 당초 0~4시는 저녁 탭이었으나, 날짜 시드가 자정에 바뀌어 새 날의 메뉴가 뜨므로 자정 직후 "오늘 메뉴"를 보러 온 사용자가 지난 저녁이 아닌 다음 날 저녁부터 보게 됐습니다 — v2.4에서 아침 탭으로 변경(사용자 결정).
4.사주 계산 규칙 상세
기둥
계산 규칙
비고
년주 (年柱)
60갑자 순환. 해의 경계는 1월 1일이 아닌 입춘(立春) 기준
입춘 이전 출생은 전년도 간지 적용
월주 (月柱)
월의 경계는 절기(節氣) 기준 (입춘 · 경칩 · 청명 …). 월간은 년간에 따른 규칙(오호둔)으로 결정
절기 시각 데이터 필요 — 라이브러리 제공 여부가 선정 기준
일주 (日柱)
기준일로부터의 경과일수로 60갑자 순환 계산 (순수 산술)
오늘의 일진(FR-504)도 동일 규칙 사용
시주 (時柱)
십이시 구간으로 시지 결정, 시간은 일간에 따른 규칙(오서둔)으로 결정
진태양시 −30분 보정 후 판정. 시간 모름 시 생략
경계 케이스 정책 — 야자시/조자시 구분은 1차 범위에서 적용하지 않고 23:00~00:59를 자시로 일괄 처리합니다(현행 입력 UI의 십이시 구간과 일치).
입춘 당일 출생은 절입 시각 기준으로 판정합니다.
4.1 계산 예시 — 팔자 조합에서 추천까지
1996-08-22(양력) 진시(07:00–08:59) 출생 사용자의 실제 계산 흐름입니다.
단계
결과
① 사주팔자
년주 병자(丙子) · 월주 병신(丙申) · 일주 신묘(辛卯) · 시주 임진(壬辰)
② 오행 분포 (8자)
천간 丙화 丙화 辛금 壬수 + 지지 子수 申금 卯목 辰토 → 목 1 · 화 2 · 토 1 · 금 2 · 수 2
사주 계산은 밀리초 수준의 결정적 연산으로, 서버리스 · localStorage 구조를 바꿀 이유가 없음
만세력 라이브러리
확정: lunar-typescript + korean-lunar-calendar
lunar-typescript가 사주팔자 · 절기(입춘 경계 포함) 계산, korean-lunar-calendar가 한국 기준 음력→양력 변환 담당. 선정 전 검증: 년주 앵커 4건 · 입춘 경계 2건 · 시주 규칙(오서둔) · 일진 300건 교차검증(두 라이브러리 독립 계산, 불일치 0건) 통과
진태양시 보정
미적용으로 변경 (Draft: −30분 적용)
입력 UI가 분 단위 시각이 아닌 십이시 구간 선택이므로, 사용자가 고른 십이시가 곧 시지(時支)가 됨. 구간 시작 시각에 −30분을 적용하면 오히려 선택한 십이시와 다른 시지로 계산되는 오류가 생겨 미적용으로 확정
일주 경계 (자시)
23시 출생 = 당일 일주 + 자시
야자시 구분 없음(§4 정책). 검증에서 23:30 출생이 당일 일주를 유지함을 확인
스코어링 가중치 (확정)
부족 오행 +5 · 일진 상생 +2 · 온도/무게 반대 각 +1 · 랜덤 허용치 10% +1
부족 오행 가점이 항상 우세하도록 설계 (비일치 메뉴의 이론상 최대 +5 동점 시 부족 오행 일치가 타이브레이크 우선)
체질 판정 방식
부족 오행 보완 휴리스틱
정통 억부용신은 복잡도가 과도. 메뉴 추천 도메인에서는 "부족한 기운을 채우는 음식" 서사가 직관적이고 풀이로 설명하기 쉬움
지장간
1차 미적용 (본기만)
분포 정밀도보다 규칙 단순성 우선. 필요 시 v2.x에서 확장
8.구현 단계 계획
단계
작업
산출물
Step 1
만세력 라이브러리 검증 · 선정 + 사주팔자 계산 코어 구현
lib/saju/calendar.ts · pillars.ts + 만세력 대조 golden test
Step 2
오행 분포 · 부족 오행 · 오늘의 일진 계산
lib/saju/elements.ts + 단위 테스트
Step 3
메뉴 120종 오행 태깅
menu-data.ts element 필드 + 태깅 검수표(§5 갱신)
Step 4
추천 엔진 교체 (오행 스코어링, 기존 인터페이스 유지)
lib/recommendation.ts 재작성 + 테스트 개편
Step 5
풀이 생성기 + 결과 화면 풀이 노출
lib/saju/interpretation.ts + /today UI 반영
Step 6
테스트 정비 · E2E 갱신 · 문서 As-Built 반영
전체 테스트 그린 + 기능명세서 v2 확정판
최대 리스크는 Step 1의 사주 계산 정확도입니다. 계산이 틀리면 이후 단계 전체가 무의미하므로,
한국 만세력 서비스와의 대조 테스트를 통과한 뒤에만 Step 2로 진행합니다.
9.테스트 계획
계층
내용
Golden test (핵심)
알려진 생년월일시 케이스의 사주팔자를 한국 만세력과 대조. 케이스 구성: 일반 케이스 · 음력 입력 · 입춘 경계(전후일) · 자시 출생(진태양시 보정 경계) · 시간 모름
단위
오행 분포 집계 · 부족 오행 판정(동률 포함) · 일진 계산 · 상생 관계 가점 · 스코어링 정렬
회귀 유지
v1의 시간대 판정 · 이미지 다양성 · 메뉴 데이터 규모 테스트는 그대로 유지
컴포넌트
결과 화면 풀이 노출 — 부족 보완 · 일진 · 균형 케이스별 문장, 사주 용어 미노출, 메뉴별 뼈대 다양성(10개 중 5종 이상) · 위치 회전(연속 5개 모두 다름) · 60자 이내, 시간 모름 케이스
E2E
기존 온보딩 → 결과 흐름 유지 + 풀이 섹션 노출 확인 추가
10.개정 이력
버전
일자
내용
v2.0 Draft
2026-07-29
사주팔자 · 오행 엔진 고도화 설계
v2.0
2026-07-29
엔진 구현 완료 — 라이브러리 · 가중치 · 진태양시 결정 확정 (As-Built)
v2.1
2026-07-29
콘텐츠 확장 — 사용자 피드백("메뉴가 한정적이고 이미지가 똑같다") 대응.
메뉴 120종 → 232종(분식 · 중식 · 일식 · 양식 · 아시안 추가, 아침 50 / 점심 95 / 저녁 94),
공유 이미지 14종 → 요리별 개별 이미지 196종(AI 푸드 포토그래피로 일괄 생성, 톤 통일).
카드 배경 팔레트는 각 이미지의 평균 색상에서 자동 추출(scripts/build-menu-image-map.mjs).
메뉴 데이터는 시드 테이블 구조로 개편, 모든 이미지 키의 에셋 존재를 테스트로 강제
v2.2
2026-08-17
재추천 방지 — 사용자 피드백("알림 보고 들어와도 어제와 같은 메뉴") 대응.
최근 추천 이력(FR-508) 신설 및 스코어링 감점 추가,
동점 정렬에 날짜 시드 랜덤 타이브레이크 도입(FR-506)
v2.3
2026-08-22
알림 진입 시 본 끼니 교체(FR-509) 신설 — 사용자 피드백
("오전에 점심 · 저녁까지 다 봤는데 점심 알림에 같은 메뉴가 뜬다") 대응.
끼니 탭 열람 기록(1.5초 체류 기준)을 남겨, 알림을 눌러 들어온 진입에 한해
이미 본 끼니만 다음 후보로 교체하고 "이전 메뉴 보기"로 되돌릴 수 있게 함.
백그라운드 복귀 시 끼니가 넘어갔으면 현재 끼니 탭으로 이동
v2.4
2026-09-08
알림 진입 교체(FR-509) 개정 — 사용자 피드백("빠르게 슥 넘기며 보는데
알림에 이미 본 메뉴가 그대로 뜬다") 대응. 열람 기준을 1.5초 체류에서
탭을 직접 누르면 즉시 · 열 때 펴진 첫 탭도 즉시로 바꾸고(자동 전환된 탭만 체류 기준 유지),
결과 화면 첫 탭 규칙을 0~4시 저녁 → 아침으로 변경(자정에 날짜 시드가 바뀌므로)
v2.5
2026-09-28
사주 풀이(FR-507) 쉬운 말 개정 — 사용자 피드백("임수가 뭔지, 화 기운이 부족해서
뭘 어쩌라는 건지 사주를 모르면 알 수 없다"). 1차로 용어를 괄호로 뒤에 두는 방식("촛불처럼 따뜻한 정화(丁火)의 당신은
타고난 다섯 기운(오행) 중 …")을 시도했으나 "그 내용 자체가 이해가 안 된다"는 재피드백으로 같은 날 철회.
최종: 풀이 문장에서 일간 · 오행 · 일진 용어와 "다섯 기운" 개념을 모두 빼고 ① 당신은 어떤 타입(자연물 비유)
② 무엇이 부족해지기 쉬운지(활력 · 온기 · 든든함 · 집중력 · 여유) ③ 그래서 이 끼니엔 이 메뉴 — 3단으로 씀.
부족 오행은 "오늘 부족한 하루"가 아니라 타고난 경향으로. 사주 용어는 정보 칩에만("나의 기운 · 촛불 정화(丁火)" · "오늘의 기운 · 나무(木) · 갑진일").
이어서 "다시 추천하기를 눌러도 메뉴 이름만 바뀐다"는 피드백으로 문장 조각을 메뉴 id 해시로 골라 조합했으나 "여전히 비슷하고 너무 길다"는 재피드백 →
최종 타입 문장 삭제 · 한두 문장(60자 이내) · 메뉴마다 문장 뼈대 자체가 다르게(부족 보완 5종 · 일진 3종 · 균형 3종, 사용자 선택 ②, 같은 메뉴는 늘 같은 문구).
안주 페어링 코멘트 · 칩 · 술 카드(v3 FR-605)도 같은 원칙 적용. 어휘표 추가