- 원인: 일기 버블 추가로 이웃 간격 ~9.6pt < 판정 18pt → 정지 상태에서도 이웃끼리 액체 합체가 걸려 원이 늘어남(7개 시절 ~20.7pt는 판정 밖 — '1.3까진 정상' 증언과 일치) - 펼침 상태 정원 분리 시각 QA 확인, FAB 피어남 모핑은 겹침 구간이라 유지 - 마케팅 06-bubble ko/en/ja 재촬영(구 샷에 왜곡 포함)·합성 재생성, 코드 주석에 탭 추가 시 재확인 경고 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtZwRRjbtM9pZJDM4FkTq6
185 lines
37 KiB
Markdown
185 lines
37 KiB
Markdown
# 하루 다님 1.5 확정 계획
|
||
|
||
> 상태: **구현 완료(2026-08-22)** — 전 기능·검증·스토어 자료 완료, 커밋·푸시됨. 남은 것은 사용자 절차(TestFlight 소킹·CloudKit 스키마 콘솔 확인·ASC 업로드 — §6)와 비차단 확인(운동 종목 목록).
|
||
> 작성 2026-08-20. 근거: 2026-08-20 세션에서 사용자 위시리스트 → 분석 보고 2회 → 사용자 결정 4건(아래 결정 이력)으로 확정.
|
||
> 구현 시 이 문서를 명세로 삼고, 완료된 항목은 CLAUDE.md 현행 명세에 흡수한 뒤 이 문서에 완료 표기할 것.
|
||
|
||
## 0. 범위 요약
|
||
|
||
| # | 기능 | 과금 | 규모 |
|
||
|---|---|---|---|
|
||
| 1 | 워치 행동 컴플리케이션 (기간 선택·미니 행동 화면 착지) | 프리미엄(워치 자체가 프리미엄) | 중간 |
|
||
| 2 | 건강 데이터 통합 — 모음 탭 타일(표시) + 건강 다짐 | 타일 **무료** + 건강 다짐 **프리미엄** | 큼 |
|
||
| 3 | 도움말(HelpView) 전체 문구 한국어 자연화 (2026-08-12 예약분) | — | 중간 |
|
||
| 4 | 업데이트 신기능 소개 팝업(버전당 1회, 全기기 업데이트 안내 포함) | 무료 | 소 |
|
||
|
||
**제외 확정**: 하위 OS 인하(§4 기각 기록), 워치 스마트 스택 인터랙티브 위젯, 모음 탭 스와이프 페이지 배치.
|
||
|
||
## 1. 결정 이력 (2026-08-20 사용자 답변)
|
||
|
||
1. 컴플리케이션 탭 착지 = **미니 행동 화면 신설** (기존 목록 스크롤 기각)
|
||
2. 건강 타일 배치 = **타일 줄 + 접기** (스와이프 페이지 기각 — 목표 카드 가로 페이징·배치 편집 드래그와 제스처 충돌, 발견성 낮음)
|
||
3. 건강 다짐 v1 지표 = **"지표 더 추가"** 선택 — 구체 세트는 미지정. 아래 §3.4의 8종 제안을 착수 시 사용자 확인으로 확정할 것 (수면 주간 합산은 선택지에서 미채택 → v1 제외)
|
||
4. 과금 = **타일 무료 + 건강 다짐 프리미엄**
|
||
5. (앞선 보고에서) 하위 OS 철회, 스마트 스택 불요, 표시 기간 옵션은 달력 주기+롤링 둘 다, 건강은 표시·다짐을 **한 버전에 통합**(감수사항 3건 §3.1 전제)
|
||
6. 후속 추가(2026-08-20): ①컴플리케이션 착지 미니 화면은 **뒤로가기로 워치 앱 메인 복귀**(push 구조 필수) ②모음 탭 건강 섹션은 **설정에서 표시 on/off + 즐겨찾기·나머지 행동·건강 3섹션 순서 지정**(목표 카드 선택 설정 아래 배치). 이후 추가 요구는 사용자가 그때그때 전달 예정 — 이 문서에 계속 반영할 것
|
||
7. 후속 승인·추가(2026-08-22): **§8 세 건 전부 승인**(③ 표시 전용 상속 / 데이터 없는 기기 모수 제외+판정 보류 / 건강 다짐 v1 마감 시각 미지원). 추가 요구: **건강 데이터의 특성(하루 경계·수면 귀속 규칙·값 신선도·기기별 편차)을 도움말에 상세히 설명**하고, **건강 데이터를 보는 화면(모음 탭 타일 영역)에도 해당 안내 가이드 문구를 표기**할 것. 1.5 도움말 작업은 자연화(예약분)+신규 주제+이 안내를 한 묶음으로 진행. **안정성 최우선 지시**: 1.4 기능은 오류 없이 그대로 유지된 채 1.5가 얹혀야 함 — §10 위험 검토가 그 근거 문서
|
||
8. 2차 답변(2026-08-22): ①지표 8종 확정 + **운동 세부 종목 다짐 추가**(사용자 핵심 니즈 — §3.4) ②프리미엄 만료 처리=기존 원칙(만든 건 유지 — "돈 내고 쓴 기간에 한 건 인정") ③스크린샷=기존 전부 폐기, 아이폰·아이패드 각 10장 신규(스타일 동일·구성 일신 — §6) ④whats-new에 全기기 업데이트 완화 문구 ⑤**업데이트 신기능 소개 팝업 신설**(§11) ⑥CloudKit 스키마 배포=사용자 기억 없음 → 아리나가 구현 중 확인·준비하고 콘솔 작업 필요 시 시점·절차를 사용자에게 안내(§3.5). 이 반영은 "기록 용도 문서 수정만 허용(코드·프로젝트 파일 금지)" 상태에서 수행
|
||
|
||
## 2. 기능 1 — 워치 행동 컴플리케이션 ✅구현 완료(2026-08-22 — 빌드 4종·자가 검증 106건×2심·시각 QA[워치 26.2 실데이터/11.5 빈 상태·미니 화면 뒤로가기·편집기 섹션] 통과)
|
||
|
||
### 스펙
|
||
- **옵트인+기간 설정(아이폰)**: 행동 편집 화면에 '애플워치 컴플리케이션' 토글, 켜면 표시 기간 선택: **하루 / 이번 주 / 이번 달 / 지난 7일 / 지난 30일**. 저장은 기기 로컬 `LocalPrefs` 신설 키(uuid→기간 맵, `local.watchGoals`와 동일 계열 — 워치는 이 아이폰과 페어링이므로 기기 로컬이 자연스러움).
|
||
- **표시**: 원형=행동 아이콘 + 큰 값(횟수형 "N회", 시간형 압축 표기 "47분"·"1:12"류). corner/inline/rectangular 대응(rectangular는 아이콘+이름+값). 측정 중이면 노란 시그널 + 실시간 타이머 — '현재 현황' 컴플리케이션의 tickingBase 방식(스냅숏 시점 − 누적초)과 유령 타이머 2시간 안전장치(`runningTrustInterval`) 재사용. 주·월 기간에도 tickingBase 산식 동일 적용 가능.
|
||
- **탭 동작**: `widgetURL` 딥링크 → 워치 앱 **미니 행동 화면**(신설): 아이콘+이름+설정 기간 누적값+큼직한 실행/종료(횟수형은 +1) 버튼. 실행은 기존 run 명령 재사용. 자동 실행 금지(손목 오탭 방지 — 명시적 버튼 탭 문법 유지). **뒤로가기 필수(사용자 요구 2026-08-20)**: 미니 화면은 워치 앱 첫 화면(메인 목록) 위에 push로 착지 — 뒤로가기 한 번에 기존 메인으로 복귀, 앱을 종료·재실행할 필요가 없어야 함(딥링크가 고립 화면이 되지 않게 내비게이션 스택 구성).
|
||
- **recommendations**: 옵트인 행동 전부 나열(상한 금지 — §11 규칙), 기간은 아이폰 설정값이 반영된 단일 항목. 스냅숏 적용 시 기존 `invalidateConfigurationRecommendations()` 경로로 자동 갱신.
|
||
- **갱신 특성(의도된 한계)**: 기존 컴플리케이션과 동일 — 스냅숏 push + 15분 .after 타임라인. 아이폰 +1 직후 페이스 반영은 수 분 지연 가능, 측정 시작/종료는 `transferCurrentComplicationUserInfo` 즉시 푸시 경로 기존과 동일.
|
||
|
||
### 데이터 배선
|
||
- `WatchActionInfo`에 **optional** 필드 추가: 설정 기간 식별 + 그 기간 누적값(예: `complicationPeriodRaw`, `periodValue`). 구버전 캐시 호환은 기존 optional 패턴(isFavorite·runningActionID 선례). `makeSnapshot`이 **옵트인된 행동만** 기간값을 계산(페이로드 절약).
|
||
- 롤링 창은 1.4의 `DayMath.rollingRange` 재사용. ⚠️ **StatSpan enum 케이스 추가 금지**(§6.6 경고 준수) — 기간+롤링 플래그로 처리. 수치는 통계 탭과 동일해야 함(Aggregator 경유).
|
||
|
||
### 영향 파일(예상)
|
||
`WatchShared/WatchPayload.swift`(필드) · `IOS/Core/WatchSyncManager.swift`(makeSnapshot) · `Haru_DanimWatchWidgets/`(신규 컴플리케이션 1종) · 워치 앱(미니 행동 화면 + widgetURL 라우팅) · `IOS/Views/ActionViews.swift`(편집 토글+기간 선택) · `Shared/LocalPrefs.swift`(신설 키) · l10n: IOS+워치 카탈로그.
|
||
|
||
### 검증
|
||
`-complicationPreview`에 신규 종 포함, 워치 심 11.5·26.2 렌더, 기간별 수치를 통계 탭과 대조, 구버전 스냅숏 캐시 폴백(기간 필드 없음 → 하루 폴백), 딥링크 착지·실행·오탭 시나리오, ko/en/ja.
|
||
|
||
## 3. 기능 2 — 건강 데이터 통합 (HealthKit)
|
||
|
||
### 3.1 전제 (사용자 감수 확정)
|
||
1. **위젯의 건강 수치는 실시간이 아님** — 마지막 앱 갱신+백그라운드 딜리버리(걸음 등 누적형은 시스템상 최대 시간당 1회) 기준.
|
||
2. **기기별 데이터 편차 상태 신설** — 아이폰=완전 / 아이패드=사용자가 시스템 건강 iCloud 동기화를 켠 경우만 / 맥(Designed for iPad)=HealthKit 없음 → 타일 섹션 숨김·건강 다짐은 "이 기기에선 건강 데이터 없음" 상태('수행일 아님' 패턴 계열의 새 표시 상태).
|
||
3. **큰 버전** — 내부 2단계(타일 먼저 완성 → 다짐 얹기) 순서로 구현, TestFlight 자가 테스트 기간 넉넉히.
|
||
|
||
### 3.2 아키텍처 — HealthCache (핵심)
|
||
- **메인 앱만** HealthKit을 조회해 App Group에 "지표×dayKey→값" 캐시를 유지(포그라운드 진입 + `HKObserverQuery`/백그라운드 딜리버리 때 갱신 후 위젯 리로드). **화면·위젯·시리·판정·연속 계산은 전부 이 캐시를 동기 조회** — QuestProgress의 동기 계산 세계를 깨지 않는다. 위젯 확장은 HealthKit 직접 조회 불가이므로 캐시가 유일 경로.
|
||
- **워치**: 워치에서 HK 직조회하지 않음 — 건강 다짐 값도 다른 다짐처럼 아이폰이 계산해 스냅숏에 실어 보냄(스냅숏이 곧 캐시 push, 추가 구조 불요).
|
||
- ⚠️ **심사 지침 5.1.3 — HealthKit 데이터를 iCloud(CloudKit)에 저장 금지**: HK **값**은 절대 SwiftData/CloudKit에 넣지 않는다(App Group 캐시는 기기 로컬이라 무방). 다짐의 **정의**(지표·목표량·방향·수면 구간)는 일반 데이터이므로 동기화 OK. → "정의는 동기화, 값은 기기별 자기 HealthKit"이 동기화 모델의 전부.
|
||
- 읽기 권한은 거부돼도 감지 불가(빈 값이 옴 — 애플 프라이버시 설계) → "표시할 데이터 없음" 상태로 흡수, 권한 재요청 유도 문구는 안내 수준만.
|
||
|
||
### 3.3 타일(표시 전용, 무료) ✅구현 완료(2026-08-22 — 타일·순서 설정·지표 선택·안내 시트·CTA·HealthCache·엔타이틀먼트, 26.5+18.5 시각 QA·회귀 106건 그린. ⚠️QA 도구 주의: simctl spawn defaults의 앱그룹 도메인 쓰기가 18.5 심에선 앱그룹 컨테이너에 닿지 않음 — 26.5는 닿음, 검증 시 참고)
|
||
- 형태: 가로 스크롤 컴팩트 타일 줄 + 접기(즐겨찾기/나머지 섹션과 같은 접힘 문법, 접힘 상태 기기 로컬).
|
||
- **모음 탭 배치 = 설정으로 제어(사용자 요구 2026-08-20)**: 설정 탭 모음 탭 영역(목표 카드 선택 아래)에 ①**건강 데이터 표시 on/off** ②표시 시 **즐겨찾기 / 나머지 행동 / 건강 데이터 3섹션의 표시 순서 지정** UI 신설(기기 로컬 저장 — 보기 설정 계층). 목표 현황 카드·'현재 진행 중' 영역은 기존대로 상단 고정, 순서 지정 대상은 위 3섹션만(다른 해석 원하면 착수 전 확인).
|
||
- 우상단 버튼 → **지표 선택 시트**(표시할 지표 체크+순서, 기기 로컬 — 표시 지표는 다짐 가능 8종보다 넓게 제공 가능).
|
||
- **안내 가이드 문구(사용자 요구 2026-08-22)**: 타일 영역에 정보 버튼(또는 각주) → 건강 데이터 특성 안내 시트: 값은 애플 건강 앱이 준 것(앱이 저장·전송하지 않음), 하루 경계·수면 귀속 규칙, 위젯 값의 신선도(실시간 아님), 기기별 차이(아이패드=건강 iCloud 설정 따름·맥=미지원). 도움말 해당 주제와 문구 일관 유지.
|
||
- 상태 처리: 맥=섹션 숨김 / 권한 없음·데이터 없음=상태 문구 타일 / 아이패드=자기 HK 기준.
|
||
- 운동은 종목별 집계 가능(HKWorkout.workoutActivityType) — 타일 v1은 '운동 시간(엑서사이즈 분)' 기준, 종목별은 후속 후보로만.
|
||
|
||
### 3.4 건강 다짐 (프리미엄) ✅구현 완료(2026-08-22 — 모델 3필드·값 공급 이음새 3곳·운동 종목 20종·수면 구간·판정 보류·데이터 없음 상태·편집기·자가 검증 H 시리즈 12건. 회귀 61·20·27·10 ALL PASS×2심)
|
||
- **모델**: `Quest`에 건강 대상 필드 신설(예: `healthMetricRaw` 기본 "" — §3 CloudKit 규칙 준수: 기본값+스키마 안전). 대상 3유형째(행동/꼬리표/건강 지표). 수면 구간 필드(시작·끝 분, 기본값) 추가.
|
||
- **v1 지표 세트(확정 2026-08-22)**: 걸음 수 · 운동 시간 · 수면(특수) · 활동 에너지(kcal) · 이동 거리(km) · 스탠드 시간 · 마음챙김 분 · 물 섭취(L) — 8종. 수면 외 7종은 전부 "논리적 하루 창의 누적 수량"으로 구조 동일 — 한 어댑터로 처리.
|
||
- **운동 세부 종목 다짐(확정 2026-08-22 — 사용자 핵심 니즈)**: '운동'은 전체 합산 외에 **종목별 다짐** 지원(예: 달리기 30분 이상) — HKWorkout의 종목별 duration 합산(기록된 운동 기준). `healthMetricRaw`에 종목 파라미터 인코딩(예: workout.running), HealthCache 키도 지표+종목으로 확장(위험 구조는 8종과 동일 — 누적 수량 패턴). **종목 이름은 API가 현지화를 제공하지 않음 → 자체 큐레이션 목록(20종 내외: 달리기·걷기·사이클·수영·근력 운동·요가·필라테스·등산·축구·야구·농구·테니스·배드민턴·골프·줄넘기·복싱·댄스·스트레칭·HIIT·기타)을 ko/en/ja 직접 명명** — 구현 중 목록을 사용자에게 제시해 실사용 종목 포함 확인(비차단), 이후 추가는 케이스+문자열 3개로 저비용. ⚠️'운동 시간'(엑서사이즈 분, 활동 링 기준)과 종목별(기록된 운동 시간)은 값 기준이 다름 — 가이드 문구·도움말에 차이 설명 필수.
|
||
- **수면 귀속 규칙(사용자 설계, 2026-08-20)**: 다짐 전용 **수면 구간**(기본 전날 21:00 ~ 당일 09:00, 사용자 지정 가능) 안의 수면 합을 **'깨어난 날' 하루에 통귀속**. ⚠️ 시간 세션의 겹침 분할과 다른 규칙이지만 **의도된 예외**(수면은 깬 날의 것으로 세는 도메인 관습 — deadlineMinutes와 같은 '다짐 전용 측정 창' 계열의 확장). 소스 중복(아이폰+워치 동시 기록)은 '잠듦' 구간 **합집합 병합**. 하루 단위 전용(요일 스케줄 가능), 주간 합산 다짐 v1 제외.
|
||
- **진행률 의미론 — 기존 §4.2와 완전 동일 유지**: atLeast 게이지 min(값/목표,1)·퍼센트 초과 표기, atMost, 연속 달성(하루 버킷·시작일 하한·400일 상한), 판정 시점 클리핑, 주기 몫 완료, 수행일 아님. 단 **마감 시각 옵션은 건강 다짐 v1 미지원(§8ⓔ 확정 — 캐시가 하루 단위 값이라 부분 창 미지원, 편집기에서 숨김)**. HealthProgress 어댑터가 QuestProgress와 같은 수치 계약을 지키고, 값 공급원만 HealthCache로 다름.
|
||
- **과금 게이트**: 건강 다짐 생성 = 프리미엄(`PremiumManager` 경유 — canAddQuest에 건강 분기 또는 전용 판정). 만료 정책 **확정(2026-08-22)**: 기존 원칙 그대로 — 이미 만든 건강 다짐은 만료 후에도 유지·표시, 추가 생성만 제한.
|
||
- **파급 표면(전수)**: QuestEditorView(대상 3유형째+지표 픽커+수면 구간 UI — CollapsibleTimeWheel 재사용) · 목표 탭 행 · 모음 카드 · 일기 '이 날의 목표'(과거 날짜는 캐시/실조회) · 위젯 ②③④(캐시 조회) · 워치(스냅숏 동승) · 시리 다짐 조회 · evaluateIfEnded 판정 · CSV(다짐 CSV에 건강 다짐 행 — 기록 CSV 무영향, 가져오기 비대상 유지 §6.8) · 무료 한도 카운트(목표당 3개에 귀속).
|
||
- **이중 상태 정규화(혼합 버전 대비 — 2026-08-22 검토)**: 구버전(1.0~1.4) 편집기가 "대상 없음"으로 보이는 건강 다짐에 행동/꼬리표를 지정·저장하면 지표+행동이 공존할 수 있음 → 판독 규칙은 **행동/꼬리표 우선**(사람의 최신 의도 존중), 1.5 편집기는 저장 시 반대편 필드를 정리해 일관 상태 복원.
|
||
|
||
### 3.5 심사·문서 체크리스트
|
||
- HealthKit 엔타이틀먼트 + `NSHealthShareUsageDescription`(ko/en/ja — InfoPlist.xcstrings).
|
||
- **`Docs/PRIVACY.md`에 건강 데이터 문단 추가 필수**(5.1.3 요구): 읽기 전용·기기 밖 전송 없음·iCloud 저장 없음 명시. 개인정보 라벨 "데이터 수집 안 함" 유지 가능(개발자 서버 전송 없음).
|
||
- 결제 화면(PremiumView) 기능 목록에 '건강 다짐' 추가, 도움말 신규 주제(§5), Marketing 자료 갱신(§6).
|
||
- **CloudKit 스키마 배포 체크(2026-08-22 — 사용자 기억 없음, 아리나 주도)**: 착수 첫 단계에서 개발 환경 스키마 생성·CloudKit 콘솔 상태 확인(1.3의 DiaryPageItem 필드 8개가 프로덕션에 있는지도 함께 판독 — 당시 배포 이력 불명). 출시 게이트에서 프로덕션 배포가 필요하면 콘솔 작업은 사용자 계정이 필요 → 아리나가 정확한 절차를 안내하고 사용자가 실행. 배포 없이 1.5를 내보내면 새 필드 동기화가 실패할 수 있으므로 **출시 전 필수 관문**. → ✅**배포 완료(2026-08-22)**: 개발 스키마에 Quest 필드 3종이 없어(디버그 내보내기 이력 없음) 시뮬 519869F7에 사용자 iCloud 로그인 후 `-premium YES -cloudSync YES -seedHealthQuest YES` 실행 → Sandbox 내보내기 성공(98레코드, error nil)으로 생성 → 사용자가 콘솔에서 프로덕션 배포. **판독 결과: 1.3의 DiaryPageItem 스타일 필드는 프로덕션 미배포 상태였음**(마지막 배포=1.0 출시 시점) — 이번 배포에 동승해 해소, 소킹 중 아이패드↔맥 도형·텍스트 상자 동기화 교차 확인 권장. → ✅**회복 확인(2026-08-22 밤, 사용자 실기기)**: 첫 배포에는 스타일 필드가 실리지 못했음(개발 스키마에도 미생성 — 아이템 있는 기기의 동기화 전체 정지로 실측 발각). 시뮬 내보내기로 개발 스키마에 필드 생성 → 콘솔 재배포 → 도형·텍스트 상자 있는 상태의 동기화 왕복 정상.
|
||
|
||
### 3.6 검증
|
||
- `-progressSelfTest`에 건강 어댑터 시리즈 추가(HealthCache 주입식 인메모리 — 실 HK 비의존): 수면 구간 경계(자정 걸침·구간 밖 수면·하루 시작 시간 05:00 조합), atLeast 캡·초과, 연속, 판정 클리핑.
|
||
- 시뮬 검증용 캐시 시드 인자(예: `-seedHealthCache`) 신설 — 시뮬레이터 HK 실데이터 없이 전 표면 렌더 확인.
|
||
- 기기별 상태: 맥 분기 `-forceMacLayout`, 권한 없음/데이터 없음 상태, 아이패드 심.
|
||
|
||
## 4. 하위 OS 인하 — 기각 기록 (재제안 방지)
|
||
|
||
2026-08-20 검토 후 사용자 철회. 근거(재검토 시 참고): ①iOS 17·18은 지원 아이폰 목록 동일(XS/XR 이후) → 인하해도 신규 커버 아이폰 0대, 노치 중 미지원은 iPhone X(iOS 16 최대)뿐 ②iPadOS 17 인하 시 추가는 iPad 6세대·Pro 10.5"·Pro 12.9" 2세대(2017~18) 3기종뿐 ③iOS 16 이하는 SwiftData(17+)라 데이터층 재작성 = 불가 ④17 지원 실비용: `LiveActivityIntent`(17.2 도입) 채택 인텐트 4종 구조 수술 + 제어 센터 API 3곳·ControlWidget 18 가드 + 심볼 감사·QA 라인 추가 + 17 SwiftData 안정성 리스크. **재검토 트리거**: 구형 기기 사용자의 실제 설치 문의 발생 시(아이패드만 따로 인하 불가 — 배포 타깃 공용 유의).
|
||
|
||
## 5. 기능 3 — 도움말 한국어 자연화 (예약분, 사용자 지시 2026-08-12) ✅구현 완료(2026-08-22 — 전 주제 자연화[오타·번역투·UI 명칭 정정 포함]+신규 주제 3개[행동 컴플리케이션·건강 2종], en/ja 전면 재번역·용어 통일[アクション·クエスト 등], 전 카탈로그 missing/stale 0)
|
||
|
||
- HelpView 전체 문구를 **한국어 기준으로 재작성** — 말투 컨셉(~해요체) 유지, 번역투·어색한 단어 선택·문장 구성 정리. en/ja도 함께 재검토.
|
||
- 1.5 신규 기능 주제 추가와 병행: 행동 컴플리케이션(워치 그룹), 건강 타일·건강 다짐(그룹 배치는 구현 시 결정, 이미지는 그룹당 대표 1주제 원칙 유지). **건강 주제는 특성 설명을 상세히(사용자 요구 2026-08-22)**: 하루 경계 처리, 수면 구간·깨어난 날 귀속, 위젯 신선도(실시간 아님), 기기별 편차(아이폰/아이패드/맥), 판정 보류 규칙, 데이터가 기기 밖으로 나가지 않는다는 점 — 모음 탭 가이드 문구(§3.3)와 동일 내용의 확장판.
|
||
- l10n 루틴(§2.3) 필수, 도움말 스크린샷은 문구·화면 변화 정도 보고 재촬영 판단.
|
||
|
||
## 6. 공통 마무리 작업 (출시 전) ✅구현분 완료(2026-08-22 — 버전 1.5(1)·whats-new·프로모션·설명·PRIVACY·스크린샷 원본 57장+합성 63장. 잔여=사용자 절차: TestFlight 소킹, CloudKit 콘솔 스키마 배포 확인[§3.5], ASC 업로드 → 2026-08-22 현재: 빌드 3 업로드·스키마 배포 완료[§3.5 ✅], 소킹 진행 중[약 1주] — 남은 것=심사 제출)
|
||
|
||
- MARKETING_VERSION 1.5, `Marketing/AppStore/whats-new-1.5.txt`(**"여러 기기를 쓰면 모든 기기에서 업데이트" 완화 문구 포함** — 확정 2026-08-22), 프로모션 텍스트 1.5판.
|
||
- **스크린샷 전면 신규 제작(확정 2026-08-22)**: 기존 세트 전부 폐기, **아이폰 10장·아이패드 10장 × ko/en/ja 신규**(스타일은 현재와 동일, 구성·이미지 일신 — 1.5 기능 반영. 워치 합성 1장 유지 여부는 시안 때 결정). 촬영·합성은 Marketing/README 절차 준용, 장면 구성 시안은 마무리 단계에서 사용자 확인.
|
||
- 자가 검증 전체 회귀(-progressSelfTest 확장분 포함 4종+신규 인자), Debug/Store/워치 빌드, 26+18.5 심 QA.
|
||
- CLAUDE.md 현행 명세 갱신(§6.1 모음 탭·§6.4 다짐·§11 워치·§14 인자 등) + 이 문서 완료 표기.
|
||
|
||
## 7. 구현 순서 제안
|
||
|
||
① 행동 컴플리케이션 → ② 건강 타일(권한·HealthCache 기반 공사 포함) → ③ 건강 다짐(어댑터→편집기→전 표면) → ④ 도움말 자연화(신규 주제 포함, 문구가 확정된 마지막에) → ⑤ 마무리(§6). 단계마다 빌드+심 검증+커밋.
|
||
|
||
## 8. 건강 다짐 × 위젯 전수 점검 (2026-08-20 후속 분석 — 사용자 질문 "③ 인터랙티브 위젯 처리")
|
||
|
||
- **③ 다짐 링(인터랙티브)**: 건강 다짐 셀은 **꼬리표 대상 다짐과 동일 문법** — 버튼 없는 표시 전용 셀, 탭=앱 열기(§10에 '의도된 동작'으로 기존 문서화). 누적값 줄은 HealthCache 값, atMost·주기 몫 완료 등 표시 상태 전부 상속. 셀에 지표 심볼 소표기(디자인 디테일). ✅확정(사용자 승인 2026-08-22)
|
||
- **무관(행동·세션 전용)**: ① 행동 실행 · ⑥ 현재 진행 중 · 제어 센터 · Live Activity.
|
||
- **표시 전용이라 구조 무관, 캐시 값만 소비**: ② 목표 바 · ④ 다짐 그리드 · 잠금화면(다짐 점·게이지 포함).
|
||
- **⑤ 행동 통계 = 건강 비대상 선언** — 통계 탭·⑤는 행동 기반, 건강 표시는 모음 탭 타일 담당(범위 못 박기).
|
||
- **위젯 공통**: ⓐ갱신 경로 신설 — DataChange.commit 외에 백그라운드 딜리버리→캐시 갱신→reloadAllTimelines(주기 폴링 아님, §10 예산 전략과 충돌 없음. 신선도=시간 단위 감수 §3.1) ⓑ하루 경계는 기존 타임라인 안전망으로 새 날 0 리셋 자동 ⓒ**데이터 없는 기기(맥 데스크톱 위젯·건강 미동기화 아이패드)**: 건강 다짐 셀을 '수행일 아님' 문법 재사용(② 대시·③ 문구·④ 흐림)으로 '데이터 없음' 표시 + **combinedSpanRatio 모수에서 제외**(§4.3 하루 span 선례와 동일 문법) ✅확정(사용자 승인 2026-08-22) ⓓ③④ 슬롯 픽커에 건강 다짐 표시명 노출, 과금은 위젯=프리미엄이라 자연 정합.
|
||
- **파생 결정 2건**: ⓔ건강 다짐 v1 **마감 시각 옵션 미지원(숨김)** 추천 — 캐시가 하루 단위 값이라 부분 창은 시간대 캐시가 따로 필요, 실익 대비 과함(수면은 자체 구간 보유. §3.4의 "마감 적용 가능" 표기는 이 결정으로 대체) ✅확정(사용자 승인 2026-08-22) ⓕ**판정 보류 규칙(중요)**: 건강 다짐 포함 목표의 자동 판정(evaluateIfEnded)은 건강 데이터 가용 기기에서만 실행 — 불가 기기(맥 등)에선 보류해 다음 가용 기기 실행 때 판정(빈 캐시로 미달성 오판→CloudKit 전파 방지) ✅확정(사용자 승인 2026-08-22)
|
||
|
||
## 9. 착수 시 사용자에게 확인할 것
|
||
|
||
1. §3.4 지표 8종 제안 확정(빼기/더하기 — 사용자가 "지표 더 추가"만 선택하고 세트는 미지정)
|
||
2. 건강 다짐 프리미엄 만료 시 처리(기존 원칙대로 "이미 만든 건 유지"면 확인만)
|
||
3. 도움말 스크린샷 재촬영 범위 (모음 탭 이미지에 건강 타일 반영 여부 포함)
|
||
|
||
(§8의 3건은 2026-08-22 사용자 승인으로 확정 — 결정 이력 7 참조)
|
||
|
||
## 10. 구현 전 정밀 위험 검토 (2026-08-22 — "1.4 무결 보존" 지시에 따른 전수 점검)
|
||
|
||
### 10.1 실코드로 확인된 안전 근거 3건
|
||
- **R1 크로스 버전 동기화(최대 우려 해소)**: 1.5가 만든 건강 다짐이 CloudKit으로 1.4 기기에 내려가면 targetAction·targetTag 둘 다 nil인 다짐으로 보인다 — 1.4 코드는 이미 `Quest.targetName`이 "대상 없음"(Models.swift:422, l10n 완비)·`targetSymbol`이 tag.fill로 폴백하고, 진행률은 `targetActions` 빈 배열이라 0%(atLeast) 안전 강등. **크래시 경로 없음(실코드 확인)**. 잔여 무해 오표시: atMost 건강 다짐이 1.4에서 '한도 지킴'으로 보임 + 1.4 편집기로 그 다짐을 열어 저장하면 대상이 바뀔 수 있음(사용자 규모·단일 기기 위주로 위험 수용, 구현 시 재확인)
|
||
- **R2 QuestProgress 이음새**: 값 산출이 `agg.seconds/count(for: quest.targetActions, …)` 5개 지점(current 149-150 · spanProgress 267-268 · 287-288 · streak 411)으로 수렴 — 단일 값 공급 헬퍼로 묶어 기록/건강(캐시) 분기하면 **수학 로직 무변경**. 기존 `-progressSelfTest` 49건 무변경 통과가 1.4 보존의 회귀 증거(주기 몫 완료 때와 같은 검증 문법)
|
||
- **R3 워치 스냅숏 호환**: 직렬화가 JSONEncoder/Decoder(WatchPayload.swift:57-61) — 구워치×새스냅숏=미지 키 무시, 새워치×구캐시=optional nil. 양방향 안전(isFavorite·runningActionID 선례 동일)
|
||
|
||
### 10.2 위험 등록부 (상=회귀 가능성 실재 / 중=설계로 예방 / 하=QA로 커버)
|
||
- **[상] R4 MainView 금지구역**: 건강 섹션·3섹션 순서는 §6.1 실기기 버그 이력 지대(iPad 비-lazy AdaptiveColumnsLayout·셀 높이 규칙·드래그 델리게이트)를 지나감. 완화: `actionGrid` 빌더·레이아웃 코드 무변경, 건강 섹션은 독립 뷰로 VStack 순서만 재배열, **편집(배치) 모드에서는 건강 섹션 숨김**(드롭 델리게이트 상호작용 원천 차단), 순서 기본값=즐겨찾기→나머지→건강+표시 기본 ON(첫 타일은 권한 CTA — 권한 요청은 탭에서만)
|
||
- **[상] R5 판정 무결**: evaluateIfEnded는 아무 기기에서나 실행 — 건강 다짐 **없는** 목표의 판정 경로는 분기 없이 기존 코드 그대로(래핑 금지). 건강 포함 목표만: 판정은 인앱이므로 **판정 시점 HK 실조회(권위 경로 — 캐시 희소성 무관, judgmentReference 클리핑 자연 충족)**, 가용성 없으면 보류(§8ⓕ)
|
||
- **[상] R6 l10n 대량 변경**: 자연화+신규 문자열+가이드 문구 = §2.3 오염 함정(Xcode 재저장 stale 대란 실전 이력) 최대 노출 구간. 완화: 자연화는 **마지막 단계**로 분리, 단계마다 타깃별 sync·전 카탈로그 missing/stale 0 확인, DEBUG manual 문자열 불가침
|
||
- **[중] R7 makeSnapshot 비용**: 기간값은 옵트인 행동만 계산, `-seedBulk` 1만 건으로 커밋→푸시 지연 실측(필요 시 기간 창 1회 페치+버킷 공유 — StatsData 문법)
|
||
- **[중] R8 수면 귀속 총칙**: "수면 구간의 **끝이 속한 논리적 하루**에 귀속"으로 일반화 — 하루 시작 시간이 어떤 값이어도 정의됨(끝=하루 시작 정각이면 그 하루 포함 명시). 구간 길이 UI 상한(예 20시간). 구간·하루 시작 변경 시 재계산은 기존 설정 변경 동작과 동일(값은 HK 소유라 정의만 변경)
|
||
- **[중] R9 스트릭 백필**: 권한 승인 시 과거 400일 1회 백그라운드 백필 — 완료 전 연속 과소 표시 과도기(가이드 문구 한 줄). 캐시 보존 지표당 400일
|
||
- **[중] R10 초기화 상호작용**: '전체 초기화'에 HealthCache·건강 로컬 키 청소 추가(`-dataResetTest` 확장). 범위 삭제는 무관(값이 HK 소유)
|
||
- **[중] R11 엔타이틀먼트 격리**: HealthKit 엔타이틀먼트·usage description은 **iOS 앱 타깃만** — 위젯·워치 타깃 무변경(프로비저닝·심사 잡음 방지)
|
||
- **[중] R12 컴플리케이션 구성 잔존**: 옵트아웃/삭제 행동이 페이스에 남으면 첫 옵트인 행동 폴백→없으면 빈 상태+해결 안내(§11 목표 폴백 문법). recommendations description은 Text(verbatim:)만(§11 즉사 경고)
|
||
- **[하] R13 iOS 18 하한**: 신규 API 전부 iOS 18/watchOS 10 이하 확인(HK 통계·옵저버 쿼리는 구식 API라 안전), 18.5 심 **Release 포함** 빌드 확인(플랫폼 행 컴파일러 크래시 전례 — 제네릭 중첩 코디네이터 패턴 회피)
|
||
- **[하] R14 위젯 규칙**: 건강 셀은 버튼 없음이라 invalidatable/히트 영역 함정 해당 적음, 하루 경계는 기존 타임라인 안전망으로 새 날 0 리셋 자동(§8ⓑ)
|
||
- **[하] R15 도움말 이미지**: 자연화는 문구 변경이라 예시 이미지 18장 재촬영 원칙 불필요(앱 화면 예시), 모음 탭 이미지만 건강 타일 반영 여부 판단(§9-3) — ※스토어 스크린샷은 별개로 전면 신규(§6, 2026-08-22 확정)
|
||
- **[기록] R16 혼합 버전(1.0~1.4 × 1.5) 검토 결과(2026-08-22)**: 1.0 RC(f05e611) 실코드로 `targetName` "대상 없음"·`targetActions` [] 폴백이 **1.0부터 존재** 확인 — 전 버전 안전 강등, 대상 강제 언래핑 0, KVS 무변경, 워치는 과도기만(R3). 잔여 특이 2건: ⓐ구버전 기기가 건강 포함 목표를 먼저 자동 판정하면 미달성 오판 가능 — 1.5 기기에서 종료일 재저장→재판정으로 회복, 팝업(§11)·whats-new 全기기 업데이트 안내로 완화 ⓑ구버전 편집 시 이중 상태 — §3.4 정규화 규칙으로 해소
|
||
|
||
### 10.3 안정성 실행 수칙 (전 단계 공통 — 사용자 지시 2026-08-22 "1.4 무결 보존")
|
||
1. **격리 원칙**: 신규 코드는 신규 파일 우선(HealthStore/HealthCache/HealthProgress·신규 컴플리케이션·미니 화면), 기존 파일은 가산적 분기만. 금지구역(§6.1 lazy·§6.5 휠 구조·§10 invalidatable·§11 verbatim·§6.6 StatSpan) 진입 전 재확인
|
||
2. **단계 게이트**: §7 순서의 각 단계 끝마다 자가 검증 전체(기존 49·20·27·10 + 신규) ALL PASS + Debug/Store/워치 빌드 + 26·18.5 심(워치 11.5·26.2) 시각 QA + 커밋. 이전 단계 그린 없이 다음 단계 진입 금지
|
||
3. **출시 게이트**: 전 단계 완료 후 TestFlight 실기기 소킹(사용자) → PRIVACY.md·심사 체크(§3.5) 동반 제출
|
||
|
||
## 11. 기능 4 — 업데이트 신기능 소개 팝업 (2026-08-22 사용자 추가) ✅구현 완료(2026-08-22 — WhatsNewView·버전당 1회·신규 설치 시드·온보딩 우선·-showWhatsNew, 3언어)
|
||
|
||
- 업데이트 후 첫 실행 시 스플래시 뒤 **버전당 1회** 표시되는 신기능 소개 시트(통상의 "이번 업데이트" 팝업 관습): 1.5 신기능 카드(행동 컴플리케이션·건강 타일·건강 다짐·도움말 개편) + **"여러 기기를 쓰신다면 모든 기기에서 업데이트해 주세요" 안내 1줄**(혼합 버전 완화 — §10 R16 연계).
|
||
- 표시 규칙: standard defaults에 마지막 표시 버전 기록(예: `whatsnew.lastShownVersion`) — 저장된 버전과 현재 버전이 다르고 소개 콘텐츠가 있는 버전일 때만 표시, 닫는 즉시 기록(재표시 없음). **신규 설치는 미표시**(첫 실행 안내는 온보딩 담당 — 최초 실행 시 현재 버전을 시드). 온보딩과 동시 표시 금지(온보딩이 우선).
|
||
- 구조는 OnboardingView 스타일 재사용, 향후 버전에서 콘텐츠만 교체 가능한 구조로. l10n 3언어, 검증 인자(예: `-showWhatsNew`) 신설.
|
||
|
||
## 12. 1.5 추가분 — 소킹 중 사용자 요구 5건 (2026-08-22 저녁, 빌드 4) ✅구현 완료
|
||
|
||
사용자 결정: 1.6이 아니라 **1.5에서 해결**. 세부 UX는 아리나 위임(전제: 기존 기능·사용성 무변화, 버그·충돌 없음). 확정 4결정: ①아이폰 노트 필기= b안(맥 '그리기' 토글 문법, 손가락) ②타임테이블 건강 블록 기본 켬 ③건강 카드 지표 선택= 전 날짜 공통(기기 로컬) ④새 수면 다짐 기본 구간= 타일 설정 시드.
|
||
|
||
1. **맥 일기 필터창 건강 안내** — RecordFilterSheet 건강 섹션이 맥(HealthDataStore 불가)에선 토글 대신 안내 문구.
|
||
2. **아이폰 일기 탭** — AppTab.phoneCases 개방(+radialOrder 필터 제거, 버블 8개 자동 배치). 노트 도구줄= 맥과 같은 가로 스크롤+'그리기' 토글(DeviceLayout.isPhone 신설, drawingPolicy anyInput, 배치↔그리기 상호 배타). 일기 잠금 설정 전 기기 노출, 아이패드 경로 무변경.
|
||
3. **일기 '건강 데이터' 카드** — DiarySection.health(케이스 마지막 — 기존 사용자 목록 끝 추가), DiaryHealthCard(HealthCache 하루 값, 수면은 SleepWindowPrefs 유효 구간 키), 지표 선택 diary.healthMetrics(비면 타일 상속), 맥 숨김, 인쇄 요약 DiaryPrintHealthRow 동승.
|
||
4. **일기 타임테이블 수면·운동 실구간** — **HealthIntervals 캐시 신설**(App Group health.intervals.v1, 400일, 실구간을 논리적 하루로 분할·병합 저장 — 다짐의 '깨어난 날 통귀속'과 다른 사실 그대로 표시, CloudKit 금지 동일). refreshRecent(3일)+백필이 유지, 화면·내보내기는 injectingHealthBlocks 주입(캘린더 전례, 최하층 반투명+실선 스타일), 색= 수면 인디고·운동 주황(지표 고정색), 필터 시트 '건강 데이터' 토글(diary.timetableHealthHidden — 전 날짜 공통, 기본 켬), 일기 필터 버튼 상시 노출화.
|
||
5. **수면 표시 구간 설정** — SleepWindowPrefs(전체 스펙+요일별, 시작=끝이면 24시간 창), 설정 → 건강 데이터 → '수면 표시 구간'. 평문 "sleep" 캐시 키= 그날 유효 구간 값(타일·카드 공유), 백필 서명에 요일 매핑 포함, QuestEditor 새 수면 다짐 기본값 시드. 발단: 사용자 교대·야간 근무 실측(주말 낮 수면이 기본 창 밖 → 타일 0).
|
||
6. 부수 실측 수정: 일기 목표 카드 건강 다짐 값 단위("8,432회/8,000보" → 지표 단위).
|
||
|
||
검증: 자가 검증 61/20/27/10 ALL PASS, 3종 빌드 그린, 전 카탈로그 missing/stale 0, 시각 QA 10여 장(아이폰 요약·카드·블록·노트·필터·버블8·수면 구간, 아이패드, 맥 분기[카드 숨김], 18.5). 마케팅: 아이폰 06-bubble 재촬영(버블 8개)·10-week→**10-diary** 교체·아이패드 03-diary-summary 재촬영(건강 카드·블록), whats-new-1.5/설명 3언어 갱신. 빌드 4.
|
||
|
||
### §12 후속 — 소킹 실기기 피드백 반영 (같은 날 밤, 빌드 5)
|
||
|
||
- **버그 ⓐ**: 아이폰 더보기 탭 안 일기 달력에서 날짜를 눌러도 상세로 안 들어감(터치 반응만) — `AppRouter.morePath: [AppTab]`(고정 타입)에는 Date를 푸시할 수 없어 조용히 무시. → `NavigationPath`로 변경, 회귀 감시 인자 `-morePushDiaryToday`(더보기 스택에 [일기, 오늘] 프로그램 푸시 → 상세 착지 실측 확인). 라디얼·사이드바(비타입 스택)는 원래 정상 — "처음엔 됐다"는 증언과 일치.
|
||
- **버그 ⓑ**: 수면 블록이 수면 단계(코어·렘·깸) 로우데이터 그대로 칸칸이 갈라짐 — 표시 병합 규칙 신설: **10분 이하의 깸은 이어 붙임**(`sleepDisplayMergeGap`, mergedIntervals에 tolerance 파라미터). 사용자 규칙 "연속으로 잤으면 한 칸, 20분쯤 깼다 자면 분리". 값(합계)은 실제 잔 시간 그대로 무영향.
|
||
- **추가 요구 2건**: ①**타임테이블 표시 색 설정**(설정 → 건강 데이터 — 수면·운동 ColorPicker, 기본 인디고·주황=빈 값, App Group 기기 로컬, DiaryHealthTimetable.sleepColor/workoutColor 단일 지점이라 전 타임테이블·내보내기 자동 공통) ②**기록 탭 타임테이블에도 수면·운동 블록**(하루·주간 — 같은 공급 지점·같은 숨김 토글[기록 필터 시트에 건강 섹션 추가], 내보내기 `injectingHealthBlocks(forDayKeys:)` + trim 범위 확장. plan-1.6 후보 1의 잔여분 조기 흡수 — 1.6에 남은 것은 물 점 표시뿐).
|
||
- 도움말·whats-new·팝업 문구 갱신, l10n 0/0, 빌드 5.
|
||
|
||
### §12 후속 2 — 버블 찌그러짐 (2026-08-23, 빌드 6)
|
||
|
||
- **실기기 실측(사용자)**: 버블 메뉴 펼침 상태에서 각 원이 이웃 버블 방향으로 늘어나 찌그러짐("서로 인력을 가진 느낌"). 원인: 일기 버블 추가로 8개가 되며 이웃 가장자리 간격이 ~9.6pt로 줄었는데, 유리 합체 판정 거리 `glassBlendSpacing`이 7개 시절 기준 18pt 그대로라 **정지 상태에서도 이웃끼리 합체 판정에 걸림**(1.4까지 7개는 간격 ~20.7pt로 판정 밖 — "1.3까지는 문제없었다"는 증언과 일치).
|
||
- 수정: 18→6 (펼침 상태 정원 분리 확인 — 시각 QA. FAB에서 피어나는 모핑은 겹침 구간에서 일어나므로 유지). 마케팅 06-bubble ×3 재촬영(구 샷에 왜곡 찍혀 있었음), 코드 주석에 "탭 추가 시 간격 하한 재확인" 경고.
|