온보딩에서 [시작하기]를 누르면 기존 저장 데이터를 초기화한 뒤 입력 화면으로 이동합니다.
추천 화면의 [정보 수정]으로 입력 화면에 재진입하면 저장된 값이 프리필됩니다.
네트워크가 없는 상태에서 페이지를 열면 서비스워커가 /offline 폴백 화면을 제공합니다.
4.기능 명세
FR-101온보딩 화면
서비스 컨셉("오늘의 기운에 맞는 한 끼")을 소개하는 3장의 음식 카드가 스프링 애니메이션으로 등장합니다.
[시작하기] 클릭 시 localStorage의 사용자 데이터를 삭제(clearUserData)하고 /setup으로 이동합니다.
FR-201생년월일 입력
8자리 숫자(YYYYMMDD) 텍스트 입력. 숫자 외 문자는 입력 즉시 제거되고 8자로 제한됩니다.
검증 규칙 (React Hook Form + Zod, onChange 즉시 검증):
8자리 숫자 형식 → 위반 시 "생년월일은 8자리 숫자로 입력해주세요."
실제 존재하는 날짜(예: 2월 30일 불가) → 위반 시 "존재하지 않는 생년월일입니다."
허용 범위: 오늘로부터 100년 전 ~ 오늘 → 위반 시 "입력 가능한 생년월일 범위를 벗어났습니다."
FR-202성별 · 양력/음력 선택
성별(여성/남성), 달력(양력/음력)을 세그먼트 토글로 선택합니다. 기본값은 여성 · 양력입니다.
FR-203태어난 시간 선택
십이시(자시~해시) 기준 12개 구간의 드롭다운으로 선택합니다. 예: 07:00 - 08:59 진시
[모름] 체크 시 드롭다운이 비활성화되며 unknownBirthTime으로 저장됩니다. 체크 해제 시 직전에 선택했던 시간이 복원됩니다.
시간 미선택 + 모름 미체크 상태로는 제출할 수 없습니다("태어난 시간을 선택해 주세요.").
FR-204제출 및 저장
모든 필드가 유효할 때만 [결과 보기] 버튼이 활성화됩니다.
제출 시 선택된 시간 옵션에서 시작 시각(HH:mm)만 추출해 저장하고 /today로 이동합니다.
저장 키: energy-meal:user-data. 재진입 시 저장값으로 폼이 프리필됩니다.
FR-301시간대 탭 및 접근 정책
아침 / 점심 / 저녁 3개 탭. 접속 시각에 따라 초기 탭과 접근 가능 여부가 결정됩니다.
접속 시각
초기 탭
차단 탭 (토스트 문구)
00:00 – 04:59
저녁
아침 · 점심 — "오전 5시부터 확인 가능해요"
05:00 – 10:59
아침
없음 (전체 열람 가능)
11:00 – 16:59
점심
아침 — "내일 오전 5시부터 확인 가능해요"
17:00 – 23:59
저녁
아침 · 점심 — "내일 오전 5시부터 확인 가능해요"
차단된 탭 클릭 시 화면 하단에 토스트가 2초간 노출됩니다.
FR-302내 정보 요약 카드
생년월일(YYYY.MM.DD) · 양/음력 · 성별 · 태어난 시간을 표시합니다.
태어난 시간은 십이시 명칭(자시~해시)으로 변환 표기하며, 모름인 경우 "시간 모름"으로 표시합니다.
[정보 수정] 클릭 시 /setup으로 이동합니다.
FR-303추천 결과 카드
선택된 시간대의 추천 리스트 중 현재 순번의 메뉴를 카드로 노출합니다.
구성: 음식 이미지(14종 WebP) + 이미지별 그라데이션 팔레트 + 메뉴명 + 한 줄 설명.
카드 하단에 추천 이유 문구를 표시합니다. 문구는 이미지 키별 고정 텍스트입니다.
저장된 사용자 정보가 없으면 기본값(1996.08.22 · 07:00 · 여성 · 양력)으로 동작합니다.
FR-304다시 추천하기
클릭 시 추천 리스트의 다음 순번으로 순환 이동합니다.
다음 메뉴의 이미지를 프리로드 + 디코딩 완료한 뒤 전환하여 이미지 플리커를 방지합니다. 로딩 중에는 버튼이 "불러오는 중..."으로 비활성화됩니다.
추천 리스트는 인접한 추천 간 동일 이미지가 반복되지 않도록 재정렬됩니다(§5.4).
FR-401PWA · 오프라인
웹 앱 매니페스트와 서비스워커를 제공하여 홈 화면 설치를 지원합니다.
서비스워커 전략: 페이지 이동은 network-first(실패 시 /offline 폴백), 정적 자원은 캐시 우선입니다.
FR-402WebView 임베드 대응
React Native WebView 등 임베디드 환경을 감지하면 데스크톱용 모바일 프레임(테두리 · 그림자)을 제거하고 전체 화면 레이아웃으로 전환합니다. (v3.1.4 부터는 브라우저에도 프레임이 없고, 웹뷰에선 열 폭 제한과 좌우 경계선만 풀립니다 — spec-v3 FR-612)