feat(1.5-p4): 신기능 소개 팝업 + 도움말 전면 자연화·신규 주제 3개 + l10n 전량 완역

- WhatsNewView(버전당 1회·신규 설치 시드·온보딩 우선·全기기 업데이트 안내·-showWhatsNew)
- HelpView: 전 주제 ko 자연화(오타 '남긴 하지만'·번역투·'완료 보관함'→'완료된 목표' 정정,
  1.1 맥 일기 반영), 신규 주제 3개(행동 컴플리케이션·건강 보기·건강 다짐), 건강 그룹은
  지원 기기에서만 노출
- l10n: 4개 카탈로그 sync·en/ja 신규 208건+본문 재번역·용어 통일(Action/アクション·
  Quest/クエスト·Home/ホーム)·stale 43 청소 → 전 카탈로그 missing/stale 0,
  InfoPlist에 NSHealthShareUsageDescription 3언어
- 검증: Debug/Store/워치 빌드, 자가 검증 61·20·27·10 ALL PASS, ja 도움말 실렌더 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WtZwRRjbtM9pZJDM4FkTq6
This commit is contained in:
songyc macbook 2026-08-22 06:28:11 +09:00
parent 78bad15852
commit 9929e6631e
10 changed files with 4071 additions and 1230 deletions

View File

@ -354,7 +354,7 @@ xcrun xcstringstool sync Widgets/Localizable.xcstrings --stringsdata "${widget_f
| 측정 알림 | `-settings.longSessionAlertHours <N>`(설정 주입) `-longSessionAlertTestSeconds <N>`(시간 대신 N초 발화, provisional 권한 자동) `-alertDump`(예약 상태 → Documents/session-alerts.txt) |
| 목표 | `-goalShowEditor` `-goalShowFinished` `-goalReorder` `-goalScrollBottom` `-questShowEditor`(다짐 편집기 표시 — 마감 시각 다짐 우선, 없으면 첫 다짐. `-questShowEditorHealth`를 함께 주면 건강 다짐 우선 — 1.5) `-endGoalYesterday "제목"`(종료일을 어제로 — 실행 시점 자동 판정 검증) `-watchGoals <N>`(생성순 목표 N개를 '애플워치에서 보기'로 설정, 0=전체 해제 — 설정 후 스냅숏 즉시 푸시, 컴플리케이션 필터 검증용) `-watchActions <N>`(1.5 — 배치순 행동 N개를 '행동 기록' 컴플리케이션에 올림. 표시 기간은 하루→이번 주→이번 달→지난 7일→지난 30일 순환, 스냅숏 즉시 푸시) `-streakDump`(모든 다짐의 연속 계산 결과 → Documents/streak-dump.txt) `-progressSelfTest`(spanProgress 수치 49건 자가 검증[환산 캡·수행일 집계·마감·atMost·custom·주기 몫 완료+목표 하루 평균+과거 기준 소급 차단] → Documents/progress-self-test.txt — 인메모리 컨테이너·설정 고정이라 실데이터/기기 설정 무영향) `-seedPeriodFulfilled YES`(주기 몫을 이미 채운 주간·월간 다짐 목표 1개 시드 — '이번 주/달 달성' 표기 검증용, 기본 시드와 분리·이름 비현지화. 기록은 주/달 첫날 09:00이라 실행일이 첫날이면 오늘 기록이 되지만 완료 표기는 동일) |
| 기록/통계 | `-historyMode timetable` `-historyWeekly` `-historyDaysAgo <N>`(N일 전 날짜로 시작 — 지난주 등 과거 주 표시, 마케팅 촬영용) `-excludeActions "이름,이름"` `-historyShowFilter` `-statShowFilter` `-statSpan <day|week|month>` `-statRolling`('오늘 기준' 롤링 토글 켠 상태로 시작 — 1.4) `-statScrollBottom` `-showExport` `-exportDump`(Documents/export-dump.png) `-recordShowEditor <session|count>`(최신 기록 수정 시트) |
| 설정/기타 | `-settingsScrollGoal` `-settingsScrollPremium` `-settingsScrollMeasure`(측정 섹션 — 짧은 기록 무시 푸터 검증) `-settingsShowDayStartWheel`(하루 시작 시간 휠 펼침 시작) `-premiumPreview` `-helpPreview`(+`-helpExpandAll`: 전 주제 펼침, `-helpScrollTo <help-main 등>`: 이미지 주제로 스크롤) `-dataExportPreview`(+`-dataExportRun`이면 CSV 자동 생성 후 Documents 복사, `-dataExportRangeDays <N>`이면 최근 N일 구간 지정 상태로 시작 — 1.4) `-dataImportPreview`(가져오기 화면 직접 표시 — -premium과 함께) `-csvImportTest`(CSV 가져오기 자가 검증 20건[파서·판별·연결·중복·멱등] → Documents/csv-import-test.txt — 인메모리 컨테이너라 실데이터 무영향) `-dataResetPreview`(데이터 정리·초기화 화면 직접 표시 — 1.4. +`-dataResetBackupRun pdf|image`면 백업 3종 자동 생성 후 Documents 복사, `-dataResetRangeDays <N>`이면 날짜 지정[오늘-N일까지], `-dataResetScope diary`면 '일기만' 선택으로 시작) `-dataResetTest`(초기화 자가 검증 27건[범위 삭제 경계·보존·cascade·전체 초기화·일기만] → Documents/data-reset-test.txt — 인메모리·설정 고정, 실데이터 무영향) `-seedHealthCache`(1.5 — 건강 스토어를 합성 값으로 대체[HK 비의존]·과거 7일+운동 종목·수면 구간 키까지 캐시 기록, 건강 타일·다짐 렌더 검증용) `-seedHealthQuest`(1.5 — 건강 다짐 목표 1개 시드: 걸음 8000·수면 8h·달리기 30분·물 2L, 멱등) `-healthQuestDump`(1.5 — 건강 다짐들의 캐시 키·오늘 값·하루 진행률 → Documents/health-quest-dump.txt) `-healthShowGuide`(1.5 — 건강 안내 시트 표시) `-healthShowMetricPicker`(1.5 — 건강 지표 선택 시트 표시) `-symbolAuditDump`(아이콘 카탈로그 중 현재 OS에 없는 심볼 → Documents/symbol-audit.txt — 하위 OS QA, 1.4) `-actionShowFavoriteOrder`(행동 탭 즐겨찾기 순서 시트 — `-startTab action`과 함께) `-actionShowEditor`(1.5 — 첫 행동의 편집 시트를 열고 애플워치 섹션까지 자동 스크롤, `-startTab action`과 함께) `-showOnboarding`(온보딩 강제 표시) `-widgetPreview YES|lock|marketing`(marketing = 디버그 라벨 없는 위젯 실렌더 모아 보기 — 마케팅 스크린샷 05-widgets/06-widgets 촬영용, Marketing/README 참고) `-widgetPreviewScroll <앵커>` `-widgetRenderMode accented|vibrant`(틴트/투명 홈 렌더링 분기 검증) `cloudSync`(standard bool로 강제) `-intentSmokeTest`(시리 인텐트 10개 시나리오 실행 → Documents/intent-smoke-test.txt, -seedDemo·-premium과 함께) |
| 설정/기타 | `-settingsScrollGoal` `-settingsScrollPremium` `-settingsScrollMeasure`(측정 섹션 — 짧은 기록 무시 푸터 검증) `-settingsShowDayStartWheel`(하루 시작 시간 휠 펼침 시작) `-premiumPreview` `-helpPreview`(+`-helpExpandAll`: 전 주제 펼침, `-helpScrollTo <help-main 등>`: 이미지 주제로 스크롤) `-dataExportPreview`(+`-dataExportRun`이면 CSV 자동 생성 후 Documents 복사, `-dataExportRangeDays <N>`이면 최근 N일 구간 지정 상태로 시작 — 1.4) `-dataImportPreview`(가져오기 화면 직접 표시 — -premium과 함께) `-csvImportTest`(CSV 가져오기 자가 검증 20건[파서·판별·연결·중복·멱등] → Documents/csv-import-test.txt — 인메모리 컨테이너라 실데이터 무영향) `-showWhatsNew`(1.5 — 신기능 소개 팝업 강제 표시) `-dataResetPreview`(데이터 정리·초기화 화면 직접 표시 — 1.4. +`-dataResetBackupRun pdf|image`면 백업 3종 자동 생성 후 Documents 복사, `-dataResetRangeDays <N>`이면 날짜 지정[오늘-N일까지], `-dataResetScope diary`면 '일기만' 선택으로 시작) `-dataResetTest`(초기화 자가 검증 27건[범위 삭제 경계·보존·cascade·전체 초기화·일기만] → Documents/data-reset-test.txt — 인메모리·설정 고정, 실데이터 무영향) `-seedHealthCache`(1.5 — 건강 스토어를 합성 값으로 대체[HK 비의존]·과거 7일+운동 종목·수면 구간 키까지 캐시 기록, 건강 타일·다짐 렌더 검증용) `-seedHealthQuest`(1.5 — 건강 다짐 목표 1개 시드: 걸음 8000·수면 8h·달리기 30분·물 2L, 멱등) `-healthQuestDump`(1.5 — 건강 다짐들의 캐시 키·오늘 값·하루 진행률 → Documents/health-quest-dump.txt) `-healthShowGuide`(1.5 — 건강 안내 시트 표시) `-healthShowMetricPicker`(1.5 — 건강 지표 선택 시트 표시) `-symbolAuditDump`(아이콘 카탈로그 중 현재 OS에 없는 심볼 → Documents/symbol-audit.txt — 하위 OS QA, 1.4) `-actionShowFavoriteOrder`(행동 탭 즐겨찾기 순서 시트 — `-startTab action`과 함께) `-actionShowEditor`(1.5 — 첫 행동의 편집 시트를 열고 애플워치 섹션까지 자동 스크롤, `-startTab action`과 함께) `-showOnboarding`(온보딩 강제 표시) `-widgetPreview YES|lock|marketing`(marketing = 디버그 라벨 없는 위젯 실렌더 모아 보기 — 마케팅 스크린샷 05-widgets/06-widgets 촬영용, Marketing/README 참고) `-widgetPreviewScroll <앵커>` `-widgetRenderMode accented|vibrant`(틴트/투명 홈 렌더링 분기 검증) `cloudSync`(standard bool로 강제) `-intentSmokeTest`(시리 인텐트 10개 시나리오 실행 → Documents/intent-smoke-test.txt, -seedDemo·-premium과 함께) |
| 버블 | `-radialExpanded` `-radialDoubleTapTest`(2초 뒤 FAB 더블탭 재현 — `-settings.radialDoubleTapTab <탭>`과 함께) |
| 일기 | `-diarySeed`(필기 획 2줄 포함 — 달력 화면에서 먼저 시드 후 재실행으로 열 것) `-diaryDrawMode`(맥 분기에서 '그리기' 토글 켠 채 시작) `-diaryOpenToday`(**`-startTab diary` 필수**) `-diaryPage <N>` `-diaryZoomScale <배>`(2.5초 뒤 fit×N 프로그램 줌 — 획 유지·선명도 확인) `-diaryShowConfig` `-diaryShowExport`(달력 화면 onAppear — `-diaryOpenToday`와 함께 쓰면 안 뜸) `-diaryExportRun pdf|image`(결과를 Documents로 복사) `-diaryShowCalendarPicker` `-diarySeedEvents` `-diaryHideFirstAction` `-diaryShowActionFilter` `-diaryHideFirstGoal` `-diaryShowGoalFilter` `-diaryShowDefaults`(기본 표시 설정 시트) `-diaryDefaultsSeed`(첫 목표·첫 행동 기본 숨김+캘린더 기본 '모든 캘린더' 주입 — 기본값 적용 검증용) `-diarySeedPhoto`(오늘=이모지+사진·어제=사진만 기분 사진 시드 — 달력 백레이어 검증) `-diarySeedTemplate`(2쪽 샘플 PDF 양식 생성) `-diaryLockScreen`(잠금 게이트 화면 강제 — 인증 없이 표시) `-diaryShowTemplates`(양식 관리 시트) `-diaryShowPageChooser`(페이지 추가 선택 시트) `-diaryApplyTemplate`(첫 양식으로 페이지 추가) `-diaryShapeEdit`(활성 노트 페이지 첫 도형 선택+'도형 수정' 시트 — `-diarySeed`·`-diaryOpenToday`·`-diaryPage 1`과 함께. 시드 요소들이 스타일 필드[굵기·둥글기·채움·자유 비율·회전·글자 크기·정렬·테두리 색]를 전부 렌더) `-diaryTextEdit`(첫 텍스트 상자를 제자리 입력 상태로 — 같은 조합) `-diaryStrokeTest`(3초 뒤 프로그램 획 추가로 디바운스 저장 파이프라인 검증 → Documents/diary-stroke-test.txt[strokes·delivered] — `-diaryOpenToday`와 함께, 1.4) `-diaryExportDeleteTest`(내보내기 완료 후 '삭제' 버튼과 같은 가드·경로로 삭제 실행 — `-diaryShowExport -diaryExportRun`과 함께, 재실행 후 달력 마커 소멸로 판정, 1.4) / 섹션 강제: `-diary.sectionOrder "timetable,hero,goals"` `-diary.hiddenSections "mood,todos,records,bars"` |
| 워치 | `-complicationPreview` `-complicationScroll` `-complicationScrollTo <stale\|quest\|actionValue>` `-autoRunFirstAction` `-autoStopRunning`(측정 중 행 탭과 동일 경로로 대표 측정 종료 — 페어링 심 E2E용) `-watchOpenActionDetail`(1.5 — 스냅숏 수신 후 첫 옵트인 행동의 미니 행동 화면을 딥링크와 같은 경로로 push, 착지·뒤로가기 검증) |

View File

@ -91,7 +91,7 @@
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)
## 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)와 동일 내용의 확장판.
@ -152,7 +152,7 @@
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 사용자 추가)
## 11. 기능 4 — 업데이트 신기능 소개 팝업 (2026-08-22 사용자 추가) ✅구현 완료(2026-08-22 — WhatsNewView·버전당 1회·신규 설치 시드·온보딩 우선·-showWhatsNew, 3언어)
- 업데이트 후 첫 실행 시 스플래시 뒤 **버전당 1회** 표시되는 신기능 소개 시트(통상의 "이번 업데이트" 팝업 관습): 1.5 신기능 카드(행동 컴플리케이션·건강 타일·건강 다짐·도움말 개편) + **"여러 기기를 쓰신다면 모든 기기에서 업데이트해 주세요" 안내 1줄**(혼합 버전 완화 — §10 R16 연계).
- 표시 규칙: standard defaults에 마지막 표시 버전 기록(예: `whatsnew.lastShownVersion`) — 저장된 버전과 현재 버전이 다르고 소개 콘텐츠가 있는 버전일 때만 표시, 닫는 즉시 기록(재표시 없음). **신규 설치는 미표시**(첫 실행 안내는 온보딩 담당 — 최초 실행 시 현재 버전을 시드). 온보딩과 동시 표시 금지(온보딩이 우선).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ struct ContentView: View {
@AppStorage(SettingsKeys.theme, store: AppGroup.defaults) private var theme: String = "system"
@State private var showSplash = true
@State private var showOnboarding = false
@State private var showWhatsNew = false
var body: some View {
ZStack {
@ -54,6 +55,10 @@ struct ContentView: View {
.fullScreenCover(isPresented: $showOnboarding) {
OnboardingView { showOnboarding = false }
}
// 1.5: ( 1 , plan §11)
.sheet(isPresented: $showWhatsNew, onDismiss: WhatsNewView.markShown) {
WhatsNewView()
}
// (Designed for iPad)
// .
.transformEnvironment(\.dynamicTypeSize) { size in
@ -106,6 +111,15 @@ struct ContentView: View {
((try? context.fetchCount(FetchDescriptor<Action>())) ?? 0) == 0 {
showOnboarding = true
}
// 1.5:
// ( ), (plan §11)
WhatsNewView.markSeededIfFreshInstall(context: context)
#if DEBUG
if UserDefaults.standard.bool(forKey: "showWhatsNew") { showWhatsNew = true }
#endif
if !showOnboarding, WhatsNewView.shouldShow {
showWhatsNew = true
}
}
}
}

View File

@ -1,122 +1,145 @@
{
"sourceLanguage" : "ko",
"strings" : {
"CFBundleDisplayName" : {
"comment" : "홈 화면에 표시되는 앱 이름",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Haru Danim"
"sourceLanguage": "ko",
"strings": {
"CFBundleDisplayName": {
"comment": "홈 화면에 표시되는 앱 이름",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Haru Danim"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "Haru Danim"
"ja": {
"stringUnit": {
"state": "translated",
"value": "Haru Danim"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "하루 다님"
"ko": {
"stringUnit": {
"state": "translated",
"value": "하루 다님"
}
}
}
},
"CFBundleName" : {
"comment" : "Bundle name",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Haru Danim"
"CFBundleName": {
"comment": "Bundle name",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Haru Danim"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "Haru Danim"
"ja": {
"stringUnit": {
"state": "translated",
"value": "Haru Danim"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "하루 다님"
"ko": {
"stringUnit": {
"state": "translated",
"value": "하루 다님"
}
}
}
},
"NSCalendarsFullAccessUsageDescription" : {
"comment" : "Privacy - Calendars Full Access Usage Description",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Needed to show your calendar events alongside the diary timetable."
"NSCalendarsFullAccessUsageDescription": {
"comment": "Privacy - Calendars Full Access Usage Description",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Needed to show your calendar events alongside the diary timetable."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "日記のタイムテーブルにカレンダーの予定を一緒に表示するために必要です。"
"ja": {
"stringUnit": {
"state": "translated",
"value": "日記のタイムテーブルにカレンダーの予定を一緒に表示するために必要です。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "일기 타임테이블에 캘린더 일정을 함께 표시하기 위해 필요해요."
"ko": {
"stringUnit": {
"state": "translated",
"value": "일기 타임테이블에 캘린더 일정을 함께 표시하기 위해 필요해요."
}
}
}
},
"NSFaceIDUsageDescription" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Used to unlock your diary."
"NSFaceIDUsageDescription": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Used to unlock your diary."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "日記のロック解除に使用されます。"
"ja": {
"stringUnit": {
"state": "translated",
"value": "日記のロック解除に使用されます。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "일기 잠금을 해제하는 데 사용돼요."
"ko": {
"stringUnit": {
"state": "translated",
"value": "일기 잠금을 해제하는 데 사용돼요."
}
}
}
},
"NSPhotoLibraryAddUsageDescription" : {
"comment" : "이미지 내보내기 → 사진에 저장 권한 문구",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Needed to save exported report images to your photo library."
"NSHealthShareUsageDescription": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Health data like steps, exercise and sleep is read to display it in the app and to calculate quest achievement. It never leaves this device."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "書き出したレポート画像を写真アルバムに保存するために必要です。"
"ja": {
"stringUnit": {
"state": "translated",
"value": "歩数・運動・睡眠などのヘルスデータを、アプリでの表示とクエスト達成の計算のために読み取ります。データがこの端末の外に出ることはありません。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "내보낸 리포트 이미지를 사진 앨범에 저장하기 위해 필요해요."
"ko": {
"stringUnit": {
"state": "translated",
"value": "걸음·운동·수면 같은 건강 데이터를 앱에서 보여주고 다짐 달성 계산에 쓰기 위해 읽어요. 데이터는 이 기기 밖으로 나가지 않아요."
}
}
}
},
"NSPhotoLibraryAddUsageDescription": {
"comment": "이미지 내보내기 → 사진에 저장 권한 문구",
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Needed to save exported report images to your photo library."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "書き出したレポート画像を写真アルバムに保存するために必要です。"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "내보낸 리포트 이미지를 사진 앨범에 저장하기 위해 필요해요."
}
}
}
}
},
"version" : "1.0"
"version": "1.0"
}

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,12 @@ private struct HelpGroup: Identifiable {
struct HelpView: View {
private var groups: [HelpGroup] {
var result: [HelpGroup] = [basics, mainTab, goals, history, sync]
var result: [HelpGroup] = [basics, mainTab, goals, history]
// (1.5) (·)
if HealthDataStore.isAvailable {
result.append(health)
}
result.append(sync)
if DeviceLayout.isPad {
result.append(diary)
} else {
@ -155,18 +160,18 @@ struct HelpView: View {
HelpGroup(title: "기본 개념", topics: [
HelpTopic(
symbol: "figure.walk",
title: "행동 — 추적의 최소 단위",
body: "행동 탭에서 습관이나 할 일을 '행동'으로 등록해요. 만들 때 시간 추적형(시작·종료 토글로 소요 시간 측정) 또는 횟수 추적형(탭할 때마다 +1) 중 하나를 골라요. 이름·아이콘·꼬리표는 나중에 언제든 바꿀 수 있지만 추적 방식은 바꿀 수 없으니 처음에 잘 골라 주세요."
title: "행동 — 기록의 기본 단위",
body: "습관이나 할 일을 행동 탭에서 '행동'으로 등록해요. 만들 때 시간 측정(시작·종료를 눌러 걸린 시간을 재요)과 횟수 기록(누를 때마다 +1) 중 하나를 골라요. 이름·아이콘·꼬리표는 나중에 언제든 바꿀 수 있지만, 추적 방식만은 바꿀 수 없으니 처음에 잘 정해 주세요."
),
HelpTopic(
symbol: "tag.fill",
title: "꼬리표 — 색으로 분류",
body: "꼬리표는 행동을 묶는 태그예요. 고유의 색을 가지며, 행동 버튼의 색은 지정한 꼬리표의 색을 따라요. 하나의 행동에 여러 꼬리표를 붙일 수 있고, 꼬리표 탭이나 행동 만들기 화면에서 바로 추가할 수 있어요."
body: "꼬리표는 행동을 묶어 주는 이름표예요. 꼬리표마다 고유한 색이 있어서 행동 버튼의 색도 지정한 꼬리표의 색을 따라가요. 한 행동에 꼬리표를 여러 개 붙여도 되고, 꼬리표 탭이나 행동 만들기 화면에서 바로 새로 만들 수도 있어요."
),
HelpTopic(
symbol: "flag.fill",
title: "목표와 다짐",
body: "목표는 '토익 700점 받기'처럼 큰 방향이고, 다짐은 그 아래 실천 단위예요. 다짐은 특정 행동(또는 꼬리표)을 골라 주기(하루·주간·월간·특정 기간)와 목표량을 정해요. '이상 달성'뿐 아니라 '이하 유지'(예: 영상 시청 하루 1시간 이하)도 만들 수 있어요."
body: "목표는 '토익 700점 받기'처럼 큰 방향이고, 다짐은 그 목표를 이루기 위한 실천 약속이에요. 다짐을 만들 때는 행동(또는 꼬리표)을 고르고 주기(하루·주간·월간·특정 기간)와 목표량을 정해요. '이상 달성'뿐 아니라 '이하 유지'(예: 영상 시청 하루 1시간 이하)도 만들 수 있어요."
),
])
}
@ -178,38 +183,38 @@ struct HelpView: View {
HelpTopic(
symbol: "play.circle.fill",
title: "측정 시작·종료",
body: "행동 버튼을 탭하면 시간형은 측정이 시작되고 다시 탭하면 종료돼요(테두리로 측정 중을 구분). 횟수형은 탭할 때마다 1씩 올라가요. 여러 행동을 동시에 측정해도 되고, 측정 중인 행동은 상단 '현재 진행 중' 영역에서도 종료할 수 있어요. 끄는 걸 자주 잊는다면 설정 → 측정의 '장시간 측정 알림'을 켜 보세요 — 정한 시간을 넘기면 알려줘요.",
body: "행동 버튼을 누르면 시간형은 측정이 시작되고, 다시 누르면 끝나요 — 측정 중인 버튼은 노란 테두리로 표시돼요. 횟수형은 누를 때마다 1씩 올라가요. 여러 행동을 동시에 측정해도 되고, 측정 중인 행동은 위쪽 '현재 진행 중'에서도 끝낼 수 있어요. 끄는 걸 자주 잊는다면 설정 → 측정에서 '장시간 측정 알림'을 켜 보세요. 정해 둔 시간을 넘기면 알려줘요.",
imageName: "help-main"
),
HelpTopic(
symbol: "clock.badge.xmark",
title: "짧은 기록 무시",
body: "설정 → 측정에서 '짧은 기록 무시'를 켜면 설정한 시간보다 짧게 측정하고 끝낸 기록은 저장하지 않아요. 행동 버튼을 실수로 눌렀다가 바로 껐을 때 의미 없는 기록이 쌓이는 것을 막아 줘요. 기준 시간은 3초부터 1분까지 고를 수 있고, 위젯·워치·시리로 종료할 때도 적용돼요. 이 설정은 iCloud를 통해 모든 기기에서 동일하게 적용되고, 애플워치에서 끝낸 측정은 페어링된 아이폰의 설정을 따라요."
body: "설정 → 측정에서 '짧은 기록 무시'를 켜면, 정한 시간보다 짧게 재고 끝낸 기록은 저장하지 않아요. 버튼을 실수로 눌렀다가 바로 껐을 때 의미 없는 기록이 쌓이는 걸 막아 줘요. 기준 시간은 3초부터 1분까지 고를 수 있고, 위젯·워치·시리로 끝낼 때도 똑같이 적용돼요. 이 설정은 iCloud를 통해 모든 기기에 같이 적용되고, 애플워치에서 끝낸 측정은 짝지어진 아이폰의 설정을 따라요."
),
HelpTopic(
symbol: "star.fill",
title: "즐겨찾기 모아 보기",
body: "행동을 즐겨찾기로 지정하면 모음 탭 행동 목록이 '즐겨찾기'와 '나머지 행동' 두 부분으로 나뉘고, 즐겨찾기가 항상 맨 위에 와요. 새로 지정한 행동은 즐겨찾기 맨 뒤에 붙어요. 순서를 바꾸려면 '배치 편집'에서 버튼을 길게 눌러 끌어 옮기거나, 행동 탭 오른쪽 위 ⋯ 메뉴의 '즐겨찾기 순서 변경'을 쓰면 돼요 — 이 순서는 애플워치의 즐겨찾기 목록에도 똑같이 적용돼요. 배치 편집에서는 즐겨찾기와 나머지 사이로 끌어서 지정·해제할 수도 있어요. '나머지 행동' 제목을 누르면 그 아래를 접어 둘 수 있어서 행동이 많아져도 자주 쓰는 것만 남길 수 있어요(접힘은 기기마다 저장). 지정은 행동 버튼을 길게 눌러 '즐겨찾기'를 선택하거나, 행동 탭에서 행동을 열어 별 아이콘을 눌러도 돼요."
body: "행동을 즐겨찾기로 지정하면 모음 탭이 '즐겨찾기'와 '나머지 행동' 두 부분으로 나뉘고, 즐겨찾기가 늘 위에 와요. 새로 지정한 행동은 즐겨찾기 맨 뒤에 붙어요. 지정하는 방법은 두 가지예요 — 행동 버튼을 길게 눌러 '즐겨찾기'를 고르거나, 행동 탭에서 행동을 열어 별을 눌러요. 순서는 '배치 편집'에서 끌어 옮기거나 행동 탭 오른쪽 위 ⋯ 메뉴의 '즐겨찾기 순서 변경'으로 바꿀 수 있고, 이 순서는 애플워치의 즐겨찾기에도 그대로 적용돼요. 배치 편집 중에는 즐겨찾기와 나머지 사이로 끌어서 지정하거나 해제할 수도 있어요. '나머지 행동' 제목을 누르면 아래를 접어 둘 수 있어서, 행동이 많아져도 자주 쓰는 것만 남겨요(접힘은 기기마다 따로 저장돼요)."
),
HelpTopic(
symbol: "arrow.clockwise",
title: "새로고침 버튼",
body: "모음 탭 왼쪽 위의 새로고침 버튼은 화면을 앱을 껐다 켠 것처럼 처음부터 다시 불러오고, 홈 화면 위젯과 애플워치도 함께 갱신해요. iCloud 동기화로 다른 기기에서 바꾼 내용이 아직 안 보이거나 위젯이 늦게 갱신되는 것 같을 때 눌러 보세요. iCloud 동기화·위젯이 프리미엄 기능이라 이 버튼은 프리미엄을 사용 중일 때만 표시돼요."
body: "모음 탭 왼쪽 위의 새로고침 버튼은 앱을 껐다 켠 것처럼 화면을 처음부터 다시 불러오고, 홈 화면 위젯과 애플워치도 함께 새로 고쳐요. 다른 기기에서 바꾼 내용이 아직 안 보이거나 위젯이 굼떠 보일 때 눌러 보세요. iCloud 동기화와 위젯이 프리미엄 기능이라, 이 버튼도 프리미엄을 쓸 때만 보여요."
),
HelpTopic(
symbol: "hand.tap.fill",
title: "길게 누르기 메뉴",
body: "행동 버튼을 길게 누르면 기록 직접 입력·수정, 즐겨찾기 지정·해제, 기록 확인, 통계 보기, 행동 설정 수정, 삭제를 할 수 있어요. 시간형은 시작·종료 시각을, 횟수형은 횟수와 시각을 직접 고칠 수 있어요."
body: "행동 버튼을 길게 누르면 메뉴가 열려요 — 기록 직접 입력·수정, 즐겨찾기 지정·해제, 기록 확인, 통계 보기, 행동 설정 수정, 삭제까지 여기서 다 돼요. 시간형은 시작·종료 시각을, 횟수형은 횟수와 시각을 직접 고칠 수 있어요."
),
HelpTopic(
symbol: "note.text",
title: "기록에 메모 남기기",
body: "행동 탭에서 행동을 열어 '기록 시 메모 창 표시'를 켜면, 시간 측정을 종료할 때·횟수를 추가할 때마다 메모 창이 떠서 그 기록에 한 줄을 남길 수 있어요(건너뛰기 가능). 이 설정은 기기마다 따로 저장돼요. 남긴 메모는 기록 탭 목록에 노란 글씨로 표시되고, 기록을 탭해 수정할 때 메모를 고치거나 새로 붙일 수도 있어요."
body: "행동 탭에서 행동을 열어 '기록 시 메모 창 표시'를 켜 보세요. 시간 측정을 끝내거나 횟수를 더할 때마다 메모 창이 떠서 그 기록에 한 줄을 남길 수 있어요(건너뛰어도 돼요). 이 설정은 기기마다 따로 저장돼요. 남긴 메모는 기록 탭에 노란 글씨로 보이고, 기록을 눌러 수정할 때 메모를 고치거나 새로 붙일 수도 있어요."
),
HelpTopic(
symbol: "square.grid.3x3.fill",
title: "배치 편집",
body: "오른쪽 위 '배치 편집'을 누르면 버튼이 흔들리며 드래그로 순서를 바꿀 수 있어요. 한 줄에 몇 개를 놓을지도 고를 수 있어요. '완료'를 누르면 저장되고, 이 순서는 위젯의 기본 표시 순서에도 쓰여요."
body: "오른쪽 위 '배치 편집'을 누르면 버튼들이 흔들리기 시작하고, 끌어서 순서를 바꿀 수 있어요. 한 줄에 몇 개를 둘지도 여기서 골라요. '완료'를 누르면 저장되고, 이 순서는 위젯이 행동을 고르는 기본 순서로도 쓰여요."
),
])
}
@ -221,28 +226,28 @@ struct HelpView: View {
HelpTopic(
symbol: "chart.bar.fill",
title: "진행률 읽는 법",
body: "다짐마다 하루·주간·월간 진행률이 표시돼요. 주간·월간 진행률은 하루 초과 달성이 다른 날의 미달을 가리지 않도록 하루 목표량까지만 인정해 합산하고, 요일·날짜를 정한 다짐은 수행일의 기록만 집계해요 — 수행일이 아닌 날의 '하루' 진행률은 '수행일 아님'으로 표시돼요. 주간·월간 다짐이 이번 주(이번 달) 몫을 이미 채웠다면 '하루' 진행률은 0% 대신 '이번 주 달성'·'이번 달 달성'으로 표시되고 목표의 하루 진행률에도 달성으로 반영돼요 — 오늘은 더 하지 않아도 된다는 뜻이에요. 다짐 자신의 주기(하루 다짐의 '하루' 등)에서는 100%를 넘는 초과 달성이 그대로 표시되지만, 주간 다짐의 '하루'처럼 주기와 다른 기간은 목표를 그 기간 몫으로 나눈 페이스 기준이라 최대 100%까지만 표시돼요. '이하 유지' 다짐은 한도 안이면 100%, 넘으면 0%예요 — 주간·월간 한도 다짐의 '하루'는 하루 몫 한도 기준이라, 주간 한도가 남았어도 오늘 많이 쓰면 먼저 알려주는 경고 역할을 해요.",
body: "다짐마다 하루·주간·월간 진행률이 보여요. 주간·월간 진행률은 어느 하루의 초과 달성이 다른 날의 미달을 가리지 않도록 하루 목표량까지만 인정해서 더하고, 요일·날짜를 정한 다짐은 수행일의 기록만 세요 — 수행일이 아닌 날의 '하루' 칸에는 '수행일 아님'이 표시돼요. 주간·월간 다짐이 이번 주(이번 달) 몫을 이미 채웠다면 '하루'는 0% 대신 '이번 주 달성'·'이번 달 달성'으로 바뀌고 목표의 하루 진행률에도 달성으로 들어가요 — 오늘은 더 하지 않아도 된다는 뜻이에요. 다짐 자신의 주기(하루 다짐의 '하루' 같은)에서는 100%를 넘는 초과 달성이 그대로 보이지만, 주간 다짐의 '하루'처럼 주기와 다른 기간은 목표를 그 기간 몫으로 나눈 페이스 기준이라 100%까지만 보여요. '이하 유지' 다짐은 한도 안이면 100%, 넘으면 0%예요 — 주간·월간 한도 다짐의 '하루'는 하루 몫 기준이라, 주간 한도가 아직 남았어도 오늘 많이 쓰면 먼저 알려 주는 경고 역할을 해요.",
imageName: "help-goal"
),
HelpTopic(
symbol: "clock.badge.checkmark",
title: "마감 시각까지 달성하기",
body: "하루 단위 다짐을 만들 때 '마감 시각까지만 집계'를 켜면 하루 시작부터 그 시각까지의 기록만 다짐에 집계돼요. 예를 들어 '일어나기 1회 · 오전 8:00까지'로 만들면 8시 이후에 누른 기록은 남긴 하지만 그날 달성으로는 인정되지 않아요. '이하 유지' 다짐은 마감 시각까지 한도를 지키면 그날 달성이에요. 시간 측정 행동이 마감을 걸치면 마감 전까지의 시간만 인정돼요. 하루 시작 시간보다 이른 시각을 고르면 다음 날 새벽(그 하루가 끝나기 전)을 뜻해요."
body: "하루 단위 다짐을 만들 때 '마감 시각까지만 집계'를 켜면, 하루 시작부터 그 시각까지의 기록만 다짐에 세요. 예를 들어 '일어나기 1회 · 오전 8:00까지'로 만들면 8시가 지나 누른 기록은 기록 탭에는 남지만 그날 달성으로는 치지 않아요. '이하 유지' 다짐은 마감까지 한도를 지키면 그날 달성이에요. 시간 측정이 마감에 걸치면 마감 전까지의 시간만 인정돼요. 하루 시작 시간보다 이른 시각을 고르면 다음 날 새벽(그 하루가 끝나기 전)을 뜻해요."
),
HelpTopic(
symbol: "flame.fill",
title: "연속 달성 표시",
body: "다짐을 끊기지 않고 지키면 불꽃과 함께 '연속 3일'처럼 표시돼요. 요일이나 날짜를 정한 다짐은 그 수행일만 세고, 주간·월간 다짐은 '연속 N주·N달'로 세요. 계산은 목표 시작일부터 시작하고, 아직 끝나지 않은 오늘(이번 주기)은 못 채웠어도 끊기지 않아요. '이하 유지' 다짐은 한도를 넘는 순간 끊겨요."
body: "다짐을 끊기지 않고 지키면 불꽃과 함께 '연속 3일'처럼 표시돼요. 요일이나 날짜를 정한 다짐은 그 수행일만 세고, 주간·월간 다짐은 '연속 몇 주·몇 달'로 세요. 세는 건 목표 시작일부터고, 아직 끝나지 않은 오늘(이번 주기)은 못 채웠어도 끊긴 걸로 치지 않아요. '이하 유지' 다짐은 한도를 넘는 순간 끊겨요."
),
HelpTopic(
symbol: "checkmark.seal.fill",
title: "목표 종료와 판정",
body: "종료일이 있는 목표는 날짜가 지나면 자동으로 달성 여부가 판정되고(앱을 며칠 뒤에 열어도 종료일까지의 기록 기준이라 결과가 달라지지 않아요), 종료일이 없는 목표는 수동 종료 버튼으로 마칠 수 있어요. 판정은 다짐들의 평균 달성률이 목표를 만들 때 정한 '달성 판정 기준'(기본 100%) 이상이면 달성이에요 — 예를 들어 기준을 80%로 두면 조금 못 채워도 달성으로 기록돼요. 다짐 없이 종료일이 지난 목표는 '달성했나요?' 상태가 되어 직접 선택해요. 완료된 목표의 종료일을 수정하면 다시 판정돼요 — 미래로 옮기면 진행 중으로 돌아와요. 완료된 목표는 목록 하단의 완료 보관함에서 볼 수 있어요."
body: "종료일이 있는 목표는 날짜가 지나면 저절로 달성 여부가 판정돼요 — 앱을 며칠 뒤에 열어도 종료일까지의 기록으로 판정하니 결과가 달라지지 않아요. 종료일이 없는 목표는 수동 종료 버튼으로 마쳐요. 판정 기준은 다짐들의 평균 달성률이에요. 목표를 만들 때 정한 '달성 판정 기준'(기본 100%) 이상이면 달성이라, 기준을 80%로 두면 조금 못 채워도 달성으로 남길 수 있어요. 다짐 없이 종료일이 지난 목표는 '달성했나요?' 상태가 되어 직접 고르면 되고, 완료된 목표의 종료일을 고치면 다시 판정돼요 — 미래로 옮기면 진행 중으로 돌아와요. 완료된 목표는 목록 아래 '완료된 목표'에서 볼 수 있어요."
),
HelpTopic(
symbol: "pin.fill",
title: "모음 탭에 목표 카드 띄우기",
body: "설정 → '모음 탭 목표 진행 현황'에서 원하는 목표를 선택하면 모음 탭 상단에 진행 현황 카드가 떠요. 여러 개를 선택하면 옆으로 쓸어 넘길 수 있어요."
body: "설정 → '모음 탭 목표 진행 현황'에서 목표를 고르면 모음 탭 맨 위에 진행 현황 카드가 떠요. 여러 개를 고르면 카드를 옆으로 쓸어 한 장씩 넘겨 볼 수 있어요."
),
])
}
@ -254,38 +259,55 @@ struct HelpView: View {
HelpTopic(
symbol: "calendar",
title: "기록 보기·고치기",
body: "기록 탭은 하루의 모든 기록을 보여줘요. 상단 화살표로 날짜를 이동하고, 기록을 탭하면 바로 수정할 수 있어요. 타임테이블 보기로 전환하면 하루 또는 일주일을 24시간 축 위에서 한눈에 볼 수 있어요.",
body: "기록 탭에는 하루의 모든 기록이 모여요. 위쪽 화살표로 날짜를 옮기고, 기록을 누르면 바로 고칠 수 있어요. 타임테이블 보기로 바꾸면 하루나 일주일을 24시간 축 위에서 한눈에 볼 수 있어요.",
imageName: "help-history"
),
HelpTopic(
symbol: "line.3.horizontal.decrease.circle",
title: "필터와 하루 기준",
body: "필터 버튼으로 보고 싶은 행동만 골라 볼 수 있어요. 설정의 '주 시작 요일'과 '하루 시작 시간'이 모든 집계에 적용돼요. 예를 들어 하루 시작을 06:00으로 두면 새벽 기록은 전날 실적으로 계산돼요."
body: "필터 버튼으로 보고 싶은 행동만 골라 볼 수 있어요. 설정의 '주 시작 요일'과 '하루 시작 시간'은 앱의 모든 집계에 적용돼요. 예를 들어 하루 시작을 06:00으로 두면, 새벽에 남긴 기록은 전날의 실적으로 세요."
),
HelpTopic(
symbol: "square.and.arrow.up",
title: "이미지로 내보내기",
body: "기록·통계 탭 오른쪽 위 내보내기 버튼으로 지금 보고 있는 기간의 리포트를 고해상도 이미지로 만들 수 있어요. 라이트·다크 테마를 골라 공유하거나 사진에 저장해요."
body: "기록·통계 탭 오른쪽 위의 내보내기 버튼을 누르면, 지금 보고 있는 기간이 그대로 고화질 이미지 리포트가 돼요. 라이트·다크 테마를 골라 공유하거나 사진에 저장해 두세요."
),
HelpTopic(
symbol: "calendar.badge.clock",
title: "지난 7일·30일로 보기",
body: "통계 탭 주간·월간에서 '지난 7일/30일 단위로 보기'를 켜면 달력 주·월 대신 오늘로 끝나는 최근 7일·30일을 볼 수 있어요 — 주초·월초에 그래프가 거의 비어 보일 때 유용해요. 화살표는 7일·30일씩 이동하고, 맨 위 비교 카드도 바로 앞 7일·30일과의 비교로 바뀌어요. 홈 화면 통계 위젯에도 '지난 7일'·'지난 30일' 기간이 있어요."
body: "통계 탭 주간·월간에서 '지난 7일/30일 단위로 보기'를 켜면, 달력 주·월 대신 오늘로 끝나는 최근 7일·30일을 봐요. 주초나 월초라 그래프가 텅 비어 보일 때 특히 좋아요. 화살표는 7일·30일씩 움직이고, 맨 위 비교 카드도 바로 앞 7일·30일과의 비교로 바뀌어요. 홈 화면 통계 위젯에도 '지난 7일'·'지난 30일' 기간이 있어요."
),
HelpTopic(
symbol: "arrow.up.arrow.down",
title: "이전 기간과 비교",
body: "통계 탭 맨 위의 비교 카드는 지금 보고 있는 하루·주·월의 총 시간과 횟수를 바로 앞 기간(어제·지난주·지난달)과 비교해 보여줘요. 필터를 걸면 필터된 행동들만으로 비교돼요."
body: "통계 탭 맨 위의 비교 카드는 지금 보는 하루·주·월의 총 시간과 횟수를 바로 앞 기간(어제·지난주·지난달)과 견주어 보여줘요. 필터를 걸어 두면 고른 행동들만으로 비교해요."
),
HelpTopic(
symbol: "tablecells",
title: "CSV로 백업·복원하기",
body: "설정 → 데이터 → '데이터 내보내기'에서 모든 기록(행동·시간·횟수·목표·다짐)을 CSV 파일로 만들 수 있어요. 구간을 직접 정해 그 기간의 기록만 내보낼 수도 있어요. 스프레드시트에서 열어 분석하거나 백업으로 보관해 두세요. 내보내기는 무료에서도 사용할 수 있어요.\n'데이터 가져오기'(프리미엄)를 쓰면 내보냈던 CSV에서 행동과 시간·횟수 기록을 다시 불러올 수 있어요. 기존 데이터는 바꾸지 않고 새 기록만 추가하며, 이미 있는 기록은 자동으로 건너뛰어서 같은 파일을 여러 번 가져와도 안전해요. CSV에 없는 행동은 새로 만들어지고, 이름과 방식이 같은 행동이 이미 있으면 그 행동으로 이어져요."
body: "설정 → 데이터 → '데이터 내보내기'에서 모든 기록(행동·시간·횟수·목표·다짐)을 CSV 파일로 만들 수 있어요. 구간을 직접 정해 그 기간의 기록만 내보낼 수도 있고요. 스프레드시트에서 열어 살펴보거나 백업으로 보관해 두세요 — 내보내기는 무료에서도 쓸 수 있어요.\n'데이터 가져오기'(프리미엄)를 쓰면 내보냈던 CSV에서 행동과 시간·횟수 기록을 되살릴 수 있어요. 기존 데이터는 건드리지 않고 새 기록만 더하며, 이미 있는 기록은 알아서 건너뛰니 같은 파일을 여러 번 가져와도 안전해요. CSV에만 있는 행동은 새로 만들어지고, 이름과 방식이 같은 행동이 이미 있으면 그 행동으로 이어져요."
),
HelpTopic(
symbol: "trash",
title: "데이터 정리·초기화",
body: "기록이 오래 쌓여 정리하고 싶다면 설정 → 데이터 → '데이터 정리·초기화'를 써 보세요. 전체를 비우거나, 처음부터 지정한 날짜까지의 기록·일기만 삭제할 수 있어요 — 이때 행동·꼬리표·목표·다짐과 일기 양식은 남고, 지정한 날짜에 걸쳐 있는 측정과 진행 중인 측정도 남아요. '일기만'을 고르면 기록은 그대로 두고 일기만 지울 수도 있어요 — 사진·필기로 커진 일기 용량만 줄일 때 좋아요. 삭제 전에 CSV 백업과, 삭제할 기록을 한눈에 정리한 '정리 리포트'(PDF·이미지)를 만들어 보관할 수 있어요 — 백업을 켰다면 파일을 만든 뒤에만 삭제로 진행할 수 있어요. 과거 기록이 사라지면 다짐의 연속 달성 기록은 끊겨요. iCloud 동기화를 켰다면 다른 기기에서도 함께 삭제되니 백업을 꼭 저장해 두세요."
body: "기록이 오래 쌓여 정리하고 싶다면 설정 → 데이터 → '데이터 정리·초기화'를 써 보세요. 전체를 비우거나, 처음부터 정한 날짜까지의 기록·일기만 지울 수 있어요 — 이때 행동·꼬리표·목표·다짐과 일기 양식은 남고, 그 날짜에 걸쳐 있는 측정과 진행 중인 측정도 남아요. '일기만'을 고르면 기록은 그대로 두고 일기만 지워요 — 사진과 필기로 커진 일기 용량만 줄이고 싶을 때 좋아요. 지우기 전에 CSV 백업과, 지울 기록을 한눈에 담은 '정리 리포트'(PDF·이미지)를 만들어 보관할 수 있어요. 백업을 켰다면 파일을 만든 뒤에만 삭제로 넘어갈 수 있고요. 과거 기록이 사라지면 다짐의 연속 달성은 끊겨요. iCloud 동기화 중이면 다른 기기에서도 함께 지워지니 백업을 꼭 저장해 두세요."
),
])
}
// MARK: (1.5)
private var health: HelpGroup {
HelpGroup(title: "건강 데이터", topics: [
HelpTopic(
symbol: "heart.fill",
title: "모음 탭에서 건강 데이터 보기",
body: "모음 탭의 '건강 데이터' 줄에서 '애플 건강 연결하기'를 누르면 걸음 수·운동 시간·수면 같은 오늘 데이터를 타일로 볼 수 있어요. 어떤 지표를 어떤 순서로 보여줄지는 줄 오른쪽의 지표 버튼에서 고르고, 설정 → '모음 탭 건강 데이터'에서 줄 자체를 끄거나 즐겨찾기·나머지 행동과의 순서를 바꿀 수 있어요.\n값은 애플 건강 앱이 가진 데이터를 읽어 온 거예요 — 하루 다님이 서버로 보내거나 iCloud에 저장하는 일은 없어요. 걸음·운동 같은 값은 설정의 '하루 시작 시간' 기준으로 오늘 하루를 더한 것이고, 수면만은 전날 밤부터 오늘 아침까지의 수면 구간을 통째로 '오늘 잔 것'으로 세요. 애플워치에 쌓인 데이터는 워치와 아이폰이 동기화된 뒤에 보이니 값이 안 맞아 보이면 잠시 뒤 다시 확인해 주세요. 값이 계속 0이라면 아이폰의 설정 → 개인정보 보호 및 보안 → 건강에서 하루 다님의 읽기 권한을 확인해 주세요."
),
HelpTopic(
symbol: "figure.run",
title: "건강 데이터로 다짐 만들기 (프리미엄)",
body: "다짐을 만들 때 대상에서 '건강'을 고르면 '매일 8,000보', '수면 8시간 이상', '달리기 30분'처럼 건강 데이터를 목표로 삼을 수 있어요. 걸음 수·운동 시간·수면·활동 에너지·이동 거리·일어서기·마음챙김·물 섭취를 고를 수 있고, 운동은 달리기·수영·근력 운동 같은 종목별로도 만들 수 있어요 — 종목별 시간은 건강 앱에 기록된 운동 기준이라 활동 링의 '운동 시간'과는 값이 다를 수 있어요. 건강 다짐은 하루 단위로만 반복돼요(요일 지정은 돼요).\n수면 다짐은 '수면 구간'(기본 저녁 9시~다음 날 아침 9시)을 정해 두면 그 안에서 잔 시간을 통째로 그날 것으로 세요 — 자정을 넘겨 자도 하루가 쪼개지지 않아요. 진행률·연속 달성·목표 판정은 다른 다짐과 똑같은 규칙으로 계산돼요. 건강 데이터가 없는 기기(맥 등)에서는 건강 다짐이 '데이터 없음'으로 보이고 목표 진행률에서 빠져요 — 아이폰에서 보면 정상으로 보여요."
),
])
}
@ -297,12 +319,12 @@ struct HelpView: View {
HelpTopic(
symbol: "icloud.fill",
title: "동기화 켜기",
body: "설정 → 프리미엄에서 '기기 간 동기화'를 켜면 iCloud로 아이폰·아이패드의 데이터가 하나로 합쳐져요. 켠 뒤에는 앱을 완전히 종료했다가 다시 실행해야 적용돼요. 모든 기기가 같은 Apple 계정으로 로그인되어 있어야 해요."
body: "설정 → 프리미엄에서 '기기 간 동기화'를 켜면 아이폰·아이패드·맥의 데이터가 iCloud로 하나가 돼요. 켠 뒤에는 앱을 완전히 껐다가 다시 열어야 적용돼요. 모든 기기가 같은 Apple 계정으로 로그인되어 있어야 하고요."
),
HelpTopic(
symbol: "arrow.triangle.2.circlepath",
title: "반영이 늦을 때",
body: "iCloud 동기화는 네트워크 상황에 따라 몇 초에서 몇 분까지 걸릴 수 있어요. 다른 기기의 기록이 안 보이면 모음 탭 왼쪽 위 새로고침 버튼을 눌러 주세요. 그래도 안 보이면 잠시 후 다시 시도하거나 두 기기 모두 인터넷 연결을 확인해 주세요."
body: "iCloud 동기화는 네트워크 상황에 따라 몇 초에서 몇 분까지 걸릴 수 있어요. 다른 기기의 기록이 안 보이면 모음 탭 왼쪽 위의 새로고침 버튼을 눌러 보세요. 그래도 안 보이면 잠시 뒤 다시 시도하거나, 두 기기 모두 인터넷에 연결되어 있는지 확인해 주세요."
),
])
}
@ -314,38 +336,38 @@ struct HelpView: View {
HelpTopic(
symbol: "book.closed.fill",
title: "하루 정리 첫 페이지",
body: "일기 탭 달력에서 날짜를 누르면 그날의 일기가 열려요. 첫 페이지는 오늘 기분·요약·목표 진행 현황·할 일·24시간 타임테이블·기록으로 하루를 정리해 보여줘요. 오른쪽 위 ⋯ 메뉴의 '첫 화면 구성'에서 섹션 표시 여부와 순서를 바꿀 수 있어요. 기분 사진을 남긴 날은 달력의 그 날짜 칸에 사진이 배경으로 함께 보여요. 일기는 아이패드와 맥에서 쓸 수 있고, iCloud 동기화를 켜면 두 기기에서 같은 일기가 이어져요.",
body: "일기 탭 달력에서 날짜를 누르면 그날의 일기가 열려요. 첫 페이지는 오늘 기분·요약·목표 진행 현황·할 일·24시간 타임테이블·기록이 모여 하루가 한눈에 정리돼요. 오른쪽 위 ⋯ 메뉴의 '첫 화면 구성'에서 섹션을 보일지 말지와 순서를 바꿀 수 있어요. 기분 사진을 남긴 날은 달력의 그 칸에 사진이 배경으로 깔려요. 일기는 아이패드와 맥에서 쓸 수 있고, iCloud 동기화를 켜면 두 기기에서 같은 일기가 이어져요.",
imageName: "help-diary"
),
HelpTopic(
symbol: "calendar.badge.plus",
title: "타임테이블에 일정·필터",
body: "첫 페이지 타임테이블 카드 오른쪽 위 캘린더 버튼으로 그날의 캘린더 일정을 골라 함께 표시할 수 있어요(일정은 점선 테두리로 구분돼요). 필터 버튼을 누르면 그날 기록된 행동 중 타임테이블에 보여줄 것만 고를 수 있고, '이 날의 목표' 카드에도 필터 버튼이 있어 보고 싶은 목표만 골라 둘 수 있어요. 선택은 모두 날짜마다 따로 저장되고, 타임테이블 선택은 일기를 내보낼 때도 그대로 반영돼요.\n매일 같은 선택을 반복하지 않으려면 달력 화면 오른쪽 위 '기본 표시 설정'을 써 보세요 — 캘린더 일정을 기본으로 넣을지(모든 캘린더/선택한 캘린더만), 어떤 목표·행동을 기본으로 숨길지 한 번만 정하면 모든 날짜에 적용돼요. 날짜별 필터로 다르게 고른 날짜는 그 선택이 우선하고, 필터의 '기본값으로 되돌리기'로 언제든 기본값으로 돌아갈 수 있어요."
body: "첫 페이지 타임테이블 카드 오른쪽 위 캘린더 버튼으로 그날의 캘린더 일정을 골라 함께 보여줄 수 있어요 — 일정은 점선 테두리라 행동과 바로 구분돼요. 필터 버튼으로는 그날 기록된 행동 중 타임테이블에 올릴 것만 고르고, '이 날의 목표' 카드의 필터로는 보고 싶은 목표만 남겨요. 이 선택들은 날짜마다 따로 저장되고, 타임테이블 선택은 일기를 내보낼 때도 그대로 담겨요.\n매일 같은 선택을 되풀이하기 싫다면 달력 화면 오른쪽 위의 '기본 표시 설정'을 써 보세요. 캘린더 일정을 기본으로 넣을지(모든 캘린더/선택한 캘린더만), 어떤 목표·행동을 기본으로 숨길지 한 번만 정하면 모든 날짜에 적용돼요. 날짜별 필터로 다르게 고른 날은 그 선택이 우선하고, '기본값으로 되돌리기'로 언제든 돌아갈 수 있어요."
),
HelpTopic(
symbol: "applepencil",
title: "펜슬 노트 페이지",
body: "페이지를 넘기면 자유 노트가 이어져요. 애플 펜슬로 자유롭게 쓰고 그려요 — 손가락은 페이지 이동과 확대·축소에 쓰여요. 두 손가락 핀치로 페이지를 확대하면 세밀하게 쓸 수 있고, 줄 노트를 켜면 줄 간격도 고를 수 있어요. 페이지가 여러 장이면 오른쪽 위 ⋯ 메뉴의 '페이지 순서 변경'으로 순서를 바꿀 수 있어요. 맥에서는 텍스트 입력이 기본이고, 도구 줄의 '그리기'를 켜면 마우스나 트랙패드로도 그릴 수 있어요 — 평소에는 클릭과 스크롤이 이동·버튼에 쓰여요."
body: "페이지를 넘기면 자유 노트가 이어져요. 애플 펜슬로 마음껏 쓰고 그리세요 — 손가락은 페이지 넘기기와 확대·축소를 맡아요. 두 손가락으로 벌려 확대하면 작은 글씨도 또박또박 쓸 수 있고, 줄 노트를 켜면 줄 간격도 고를 수 있어요. 페이지가 여러 장이면 오른쪽 위 ⋯ 메뉴의 '페이지 순서 변경'으로 차례를 바꿔요. 맥에서는 키보드 입력이 기본이고, 도구 줄의 '그리기'를 켠 동안에만 마우스·트랙패드로 그려져요 — 평소의 클릭과 스크롤은 이동과 버튼에 쓰여요."
),
HelpTopic(
symbol: "square.on.circle",
title: "사진·도형·텍스트 꾸미기",
body: "노트 페이지 도구 줄에서 '사진'·'도형'·'텍스트'를 추가한 뒤 '배치 모드'에서 꾸며요 — 드래그로 옮기고, 핀치로 크기를(모양 비율은 그대로), 두 손가락 돌리기로 회전을 바꿔요. 선택하면 나타나는 손잡이로 모양을 다듬어요: 사각형은 꼭짓점을 끌면 그 모서리만 움직이고, 원은 상하좌우 손잡이로 늘리거나 눌러 타원을 만들고, 화살표·선은 양 끝점을 끌어 길이와 방향을 바꿔요. 조절 중에 정사각형·정원이 되는 순간 손잡이가 노란색으로 변하며 살짝 붙잡아 줘요. 텍스트 상자는 선택된 상태에서 한 번 더 탭하면 바로 그 자리에서 키보드로 입력·줄바꿈이 되고, '스타일'에서 글자 색·크기·굵게·정렬·테두리(색·굵기 포함)를 바꿀 수 있어요. 도형은 '도형 수정'에서 색·선 굵기·채우기·모서리 둥글기·회전을 바꿔요."
body: "노트 페이지 도구 줄에서 '사진'·'도형'·'텍스트'를 더한 뒤 '배치 모드'에서 꾸며요 — 끌어서 옮기고, 두 손가락으로 오므리거나 벌려 크기를(모양 비율은 그대로), 돌려서 회전을 바꿔요. 요소를 선택하면 손잡이가 나타나요. 사각형은 꼭짓점을 끌면 그 모서리만 움직이고, 원은 상하좌우 손잡이로 늘리고 눌러 타원을 만들고, 화살표·선은 양 끝점을 끌어 길이와 방향을 함께 바꿔요. 조절하다 정사각형·정원이 되는 순간 손잡이가 노랗게 변하며 살짝 붙잡아 줘요. 텍스트 상자는 선택된 채 한 번 더 누르면 그 자리에서 바로 키보드 입력·줄바꿈이 되고, '스타일'에서 글자 색·크기·굵기·정렬·테두리(색·굵기까지)를 바꿔요. 도형은 '도형 수정'에서 색·선 굵기·채우기·모서리 둥글기·회전을 다듬어요."
),
HelpTopic(
symbol: "doc.text.image",
title: "나만의 양식(속지)",
body: "달력 화면 오른쪽 위 양식 버튼에서 PDF나 사진을 노트 배경 양식으로 가져올 수 있어요. 가져온 양식은 페이지를 추가할 때 빈 캔버스·줄 노트와 함께 고를 수 있고, 여러 쪽 PDF는 원하는 쪽을 골라 써요. 양식은 필기 아래에 깔리고 확대해도 선명하게 보여요. 양식을 삭제하면 그 양식을 쓰던 페이지는 빈 캔버스로 표시되지만 필기는 그대로 남아요.\n양식이 깔린 페이지에서는 펜 색이 화면 모드(라이트·다크)와 상관없이 고른 색 그대로 고정돼요 — 양식은 항상 밝은 배경이라, 어떤 기기·모드에서 봐도 필기가 같은 색으로 보여요."
body: "달력 화면 오른쪽 위 양식 버튼에서 PDF나 사진을 노트 배경 양식(속지)으로 가져올 수 있어요. 가져온 양식은 페이지를 더할 때 빈 캔버스·줄 노트와 나란히 고를 수 있고, 여러 쪽짜리 PDF는 원하는 쪽만 골라 써요. 양식은 필기 아래에 깔리고, 확대해도 흐려지지 않아요. 양식을 지우면 그 양식을 쓰던 페이지는 빈 캔버스로 보이지만 필기는 그대로 남아요.\n양식이 깔린 페이지의 펜 색은 라이트·다크 모드와 상관없이 고른 색 그대로 고정돼요 — 양식은 늘 밝은 배경이라, 어느 기기 어느 모드에서 봐도 필기가 같은 색으로 보이게 하기 위해서예요."
),
HelpTopic(
symbol: "square.and.arrow.up.on.square",
title: "일기 내보내기",
body: "달력 화면 오른쪽 위 내보내기 버튼에서 기간(며칠부터 며칠까지)이나 여러 날짜를 골라 PDF 또는 이미지로 저장해요. 요약 페이지와 노트 페이지가 그대로 담겨요.\n파일을 만든 뒤에는 '내보낸 날짜의 일기 삭제'로 방금 저장한 일기들을 앱에서 지워 저장 공간을 줄일 수 있어요. 필기·사진이 함께 지워지고 iCloud 동기화 중이면 다른 기기에서도 삭제되니, 공유로 파일을 먼저 저장했는지 확인해 주세요."
body: "달력 화면 오른쪽 위 내보내기 버튼에서 기간(며칠부터 며칠까지)이나 여러 날짜를 골라 PDF 이미지로 저장해요. 요약 페이지와 노트 페이지가 보이는 그대로 담겨요.\n파일을 만든 다음에는 '내보낸 날짜의 일기 삭제'로 방금 내보낸 일기들을 앱에서 지워 저장 공간을 되찾을 수 있어요. 필기와 사진이 함께 지워지고 iCloud 동기화 중이면 다른 기기에서도 지워지니, 공유로 파일부터 저장해 뒀는지 꼭 확인해 주세요."
),
HelpTopic(
symbol: "lock.fill",
title: "일기 잠금",
body: "설정 → 일기에서 '일기 잠금'을 켜면 일기 탭을 열 때 Face ID·Touch ID 또는 기기 암호로 잠금을 해제해야 해요. 기기가 지원하는 방식이 자동으로 쓰이고, 앱을 나갔다 돌아오면 다시 잠겨요. 이 설정은 이 기기에만 적용돼요."
body: "설정 → 일기에서 '일기 잠금'을 켜면 일기 탭을 열 때마다 Face ID·Touch ID나 기기 암호로 풀어야 해요. 기기가 지원하는 방식이 알아서 쓰이고, 앱을 나갔다 돌아오면 다시 잠겨요. 이 설정은 켠 기기에만 적용돼요."
),
])
}
@ -357,28 +379,28 @@ struct HelpView: View {
HelpTopic(
symbol: "squares.leading.rectangle",
title: "탭바 구성과 더보기",
body: "설정 → '탭바 구성'에서 하단 탭바에 바로 보일 탭(최대 3개)을 고를 수 있어요. 나머지 탭은 '더보기'에서 열어요."
body: "설정 → '탭바 구성'에서 아래 탭바에 바로 둘 탭(최대 3개)을 골라요. 나머지 탭은 '더보기'에서 열면 돼요."
),
HelpTopic(
symbol: "circle.hexagongrid.fill",
title: "글래스 버블 메뉴",
body: "설정 → '내비게이션 방식'에서 '글래스 버블 메뉴'를 고르면 하단 탭바 대신 화면 아래 중앙 버튼 하나로 모든 탭을 열어요. 버튼을 누르면 유리 구슬 버튼들이 부채꼴로 퍼지고, 버블 순서도 설정에서 바꿀 수 있어요. '두 번 눌러 바로 이동'을 켜면 메뉴가 닫혀 있을 때 버튼을 빠르게 두 번 눌러 정해 둔 탭으로 바로 이동해요.",
body: "설정 → '내비게이션 방식'에서 '글래스 버블 메뉴'를 고르면 탭바 대신 화면 아래 가운데 버튼 하나로 모든 탭을 열어요. 버튼을 누르면 유리 구슬들이 부채꼴로 피어나고, 구슬 순서도 설정에서 바꿀 수 있어요. '두 번 눌러 바로 이동'을 켜, 메뉴가 닫혀 있을 때 버튼을 빠르게 두 번 눌러 정해 둔 탭으로 곧장 갈 수 있어요.",
imageName: "help-radial"
),
HelpTopic(
symbol: "square.grid.2x2.fill",
title: "홈 화면·잠금화면 위젯 (프리미엄)",
body: "홈 화면을 길게 눌러 '하루 다님' 위젯을 추가하세요. 행동 실행(눌러서 시작·+1), 현재 진행 중(측정 중인 행동의 실시간 타이머와 종료 버튼), 목표 진행률, 다짐 진행률(원형 링·누적값), 다짐 현황, 행동 통계 그래프와 잠금화면용 목표 달성률이 있어요. 위젯을 길게 눌러 '위젯 편집'에서 표시할 행동·목표·다짐과 기간, 테마를 고를 수 있어요."
body: "홈 화면을 길게 눌러 '하루 다님' 위젯을 더해 보세요. 행동 실행(눌러서 시작·+1), 현재 진행 중(실시간 타이머와 종료 버튼), 목표 진행률, 다짐 진행률(원형 링과 누적값), 다짐 현황, 행동 통계 그래프, 그리고 잠금화면용 목표 달성률까지 있어요. 위젯을 길게 눌러 '위젯 편집'을 열면 보여줄 행동·목표·다짐과 기간, 테마를 고를 수 있어요."
),
HelpTopic(
symbol: "bolt.horizontal.circle",
title: "다이나믹 아일랜드·잠금화면",
body: "시간을 측정 중이면 다이나믹 아일랜드와 잠금화면에 진행 시간이 표시돼요. 여러 개를 동시에 측정하면 설정의 '대표 시간 기준'(먼저/나중에 시작한 행동)에 따라 하나가 대표로 보이고 나머지는 +N으로 표시돼요. 잠금화면 배너의 노란 정지 버튼을 누르면 앱을 열지 않고도 대표 측정이 바로 종료돼요."
body: "시간을 재는 동안에는 다이나믹 아일랜드와 잠금화면에 진행 시간이 떠 있어요. 여러 개를 동시에 재면 설정의 '대표 시간 기준'(먼저/나중에 시작한 행동)에 따라 하나가 대표로 보이고 나머지는 +N으로 표시돼요. 잠금화면 배너의 노란 정지 버튼을 누르면 앱을 열지 않고도 대표 측정이 바로 끝나요."
),
HelpTopic(
symbol: "switch.2",
title: "제어 센터에서 실행 (프리미엄)",
body: "제어 센터를 편집해 '하루 다님'의 '행동 실행' 컨트롤을 추가하면, 화면을 켜자마자 행동을 실행할 수 있어요. 시간형은 시작/종료 토글, 횟수형은 탭마다 +1이에요. 잠금화면 하단 버튼이나 액션 버튼에도 배치할 수 있어요."
body: "제어 센터를 편집해 '하루 다님'의 '행동 실행' 컨트롤을 더하면, 화면을 켜자마자 행동을 실행할 수 있어요. 시간형은 시작/종료가 번갈아 되고, 횟수형은 누를 때마다 +1이에요. 잠금화면 아래 버튼이나 액션 버튼에 둘 수도 있어요."
),
])
}
@ -390,18 +412,23 @@ struct HelpView: View {
HelpTopic(
symbol: "applewatch",
title: "워치에서 기록하기",
body: "워치 앱을 열면 꼬리표 목록이 보여요. 꼬리표를 탭하면 그 안의 행동들이 나오고, 행동을 탭하면 시간형은 측정 시작·종료, 횟수형은 +1이 기록돼요. 아이폰 앱에서 즐겨찾기한 행동은 첫 화면 맨 위 '즐겨찾기'에서 한 탭 만에 열 수 있어요. 측정 중일 때는 첫 화면 맨 위에 측정 중인 행동과 시간이 보이고, 그 줄을 탭하면 바로 측정이 종료돼요. 기록은 아이폰과 자동으로 동기화돼요.",
body: "워치 앱을 열면 꼬리표 목록이 보여요. 꼬리표를 누르면 그 안의 행동들이 나오고, 행동을 누르면 시간형은 측정 시작·종료, 횟수형은 +1이 남아요. 아이폰에서 즐겨찾기한 행동은 첫 화면 맨 위 '즐겨찾기'에서 한 번에 열 수 있어요. 측정 중일 때는 첫 화면 맨 위에 측정 중인 행동과 시간이 보이고, 그 줄을 누르면 바로 끝나요. 기록은 아이폰과 자동으로 맞춰져요.",
imageName: "help-watch"
),
HelpTopic(
symbol: "watchface.applewatch.case",
title: "컴플리케이션 추가",
body: "워치 페이스를 길게 눌러 '편집' → 컴플리케이션 자리에서 하루 다님을 골라요. 현재 측정 중인 행동, 목표의 달성률, 다짐의 진행률을 페이스에서 바로 볼 수 있어요. 목표·다짐 달성률 목록에는 목표를 만들거나 수정할 때 '애플워치에서 보기'를 켠 목표와 그 다짐들만 하루·주간·월간으로 나타나요 — 기본은 꺼져 있고, 완료된 목표는 목록에서 자동으로 사라져요."
body: "워치 페이스를 길게 눌러 '편집' → 컴플리케이션 자리에서 하루 다님을 골라요. 현재 측정 중인 행동, 목표의 달성률, 다짐의 진행률, 행동의 누적 기록을 페이스에서 바로 볼 수 있어요. 목표·다짐 달성률 목록에는 목표를 만들거나 수정할 때 '애플워치에서 보기'를 켠 목표와 그 다짐들만 하루·주간·월간으로 나타나요 — 기본은 꺼져 있고, 완료된 목표는 목록에서 알아서 사라져요."
),
HelpTopic(
symbol: "applewatch.watchface",
title: "행동 기록 컴플리케이션",
body: "행동의 누적 기록을 워치 페이스에 두고 싶다면, 아이폰의 행동 편집에서 '애플워치 컴플리케이션'을 켜고 표시 기간(하루·이번 주·이번 달·지난 7일·지난 30일)을 골라 주세요. 그러면 페이스 편집의 '행동 기록' 목록에 그 행동이 나타나요. 측정 중일 때는 컴플리케이션이 노란 타이머로 바뀌고, 컴플리케이션을 누르면 워치 앱의 그 행동 화면이 열려 큰 버튼으로 바로 시작·종료(횟수형은 +1)할 수 있어요 — 뒤로 가면 워치 앱 첫 화면이에요. 페이스의 숫자는 몇 분 간격으로 갱신되니 방금 누른 기록이 바로 안 보여도 잠시만 기다려 주세요."
),
HelpTopic(
symbol: "arrow.triangle.2.circlepath",
title: "워치와 폰이 안 맞을 때",
body: "워치와 아이폰은 근처에 있을 때 자동으로 맞춰져요. 값이 다르게 보이면 워치 앱을 아래로 당겨 새로고침하거나, 아이폰에서 앱을 한 번 열어 주세요. 모음 탭의 새로고침 버튼도 도움이 돼요."
body: "워치와 아이폰은 서로 가까이 있을 때 자동으로 맞춰져요. 값이 다르게 보이면 워치 앱을 한 번 열어 두거나, 아이폰에서 앱을 열어 주세요. 모음 탭의 새로고침 버튼도 도움이 돼요."
),
])
}
@ -413,17 +440,17 @@ struct HelpView: View {
HelpTopic(
symbol: "crown.fill",
title: "무료와 프리미엄의 차이",
body: "무료로는 행동 10개·목표 2개·목표당 다짐 3개까지 만들 수 있어요. 프리미엄은 개수 제한이 풀리고 위젯, 애플워치 앱, 기기 간 동기화, 아이패드 일기를 쓸 수 있어요. 월·년 구독 또는 한 번만 결제하는 평생 이용권 중에 고르면 돼요. 구독이 끝나도 기록한 데이터는 그대로 남고, 이미 쓴 일기는 계속 열람할 수 있어요."
body: "무료로는 행동 10개·목표 2개·목표당 다짐 3개까지 만들 수 있어요. 프리미엄에서는 개수 제한이 풀리고 위젯, 애플워치 앱, 시리 단축어, 기기 간 동기화, 아이패드·맥 일기, CSV 가져오기, 건강 다짐을 쓸 수 있어요. 월·년 구독이나 한 번만 결제하는 평생 이용권 중에 고르면 돼요. 구독이 끝나도 기록한 데이터는 그대로 남고, 이미 만든 건강 다짐도 계속 보이고, 쓴 일기도 계속 열람할 수 있어요."
),
HelpTopic(
symbol: "mic.fill",
title: "시리·단축어로 기록하기",
body: "단축어 앱에서 '하루 다님'을 검색하면 측정 시작·종료, 횟수 추가, 행동 누적값 가져오기, 목표·다짐 진행률 가져오기 동작을 조합해 나만의 자동화를 만들 수 있어요. \"시리야, 하루 다님에서 측정 시작\"처럼 말로도 실행할 수 있고, 어떤 행동인지 물으면 이름으로 대답하면 돼요. 진행률 동작은 값을 돌려주므로 다른 단축어와 이어 붙일 수 있어요."
body: "단축어 앱에서 '하루 다님'을 찾으면 측정 시작·종료, 횟수 추가, 행동 누적값 가져오기, 목표·다짐 진행률 가져오기 동작으로 나만의 자동화를 만들 수 있어요. \"시리야, 하루 다님에서 측정 시작\"처럼 말로도 되고, 어떤 행동인지 물으면 이름으로 답하면 돼요. 진행률 동작은 값을 돌려주니 다른 단축어와 이어 붙이기 좋아요."
),
HelpTopic(
symbol: "arrow.counterclockwise.circle",
title: "구매 복원과 구독 관리",
body: "기기를 바꿨거나 앱을 다시 설치했다면 설정 → 프리미엄의 '구매 복원'을 눌러 주세요. 구독 해지나 플랜 변경은 '구독 관리'에서 언제든 할 수 있고, 해지해도 결제한 기간이 끝날 때까지는 계속 쓸 수 있어요."
body: "기기를 바꿨거나 앱을 다시 깔았다면 설정 → 프리미엄에서 '구매 복원'을 눌러 주세요. 구독 해지나 플랜 변경은 '구독 관리'에서 언제든 할 수 있고, 해지해도 이미 결제한 기간이 끝날 때까지는 그대로 쓸 수 있어요."
),
])
}

View File

@ -0,0 +1,131 @@
//
// WhatsNewView.swift
// Haru_Danim
//
// (1.5 Docs/plan-1.5.md §11)
// - 1.
// ( ). .
// - " " ( plan §10 R16)
// - contentVersion
//
import SwiftData
import SwiftUI
struct WhatsNewView: View {
@Environment(\.dismiss) private var dismiss
/// (standard defaults )
static let lastShownKey = "whatsnew.lastShownVersion"
///
static let contentVersion = "1.5"
static var appVersion: String {
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
}
/// ( )
@MainActor
static func markSeededIfFreshInstall(context: ModelContext) {
guard UserDefaults.standard.string(forKey: lastShownKey) == nil else { return }
let isFresh = !UserDefaults.standard.bool(forKey: OnboardingView.doneKey)
&& ((try? context.fetchCount(FetchDescriptor<Action>())) ?? 0) == 0
if isFresh {
UserDefaults.standard.set(appVersion, forKey: lastShownKey)
}
}
/// : +
static var shouldShow: Bool {
appVersion == contentVersion
&& UserDefaults.standard.string(forKey: lastShownKey) != appVersion
}
static func markShown() {
UserDefaults.standard.set(appVersion, forKey: lastShownKey)
}
var body: some View {
VStack(spacing: 0) {
ScrollView {
VStack(alignment: .leading, spacing: 22) {
VStack(alignment: .leading, spacing: 4) {
Text("새로워진 하루 다님")
.font(.title.bold())
Text("버전 1.5에서 이런 것들이 더해졌어요.")
.font(.subheadline)
.foregroundStyle(.secondary)
}
.padding(.top, 28)
feature(
symbol: "applewatch",
tint: AppTheme.green,
title: String(localized: "워치 페이스에서 행동 기록 보기"),
body: String(localized: "'행동 기록' 컴플리케이션이 새로 생겼어요. 행동 편집에서 켜면 워치 페이스에서 누적 기록을 바로 보고, 눌러서 바로 실행할 수 있어요.")
)
feature(
symbol: "heart.fill",
tint: .pink,
title: String(localized: "모음 탭에서 건강 데이터 보기"),
body: String(localized: "걸음·운동·수면 같은 애플 건강 앱의 오늘 데이터를 모음 탭 타일로 볼 수 있어요. 값은 기기 밖으로 나가지 않아요.")
)
feature(
symbol: "figure.run",
tint: .pink,
title: String(localized: "건강 데이터로 다짐 만들기"),
body: String(localized: "'매일 8,000보', '수면 8시간', '달리기 30분'처럼 건강 데이터를 목표로 삼는 다짐을 만들 수 있어요. 운동은 종목별로도 돼요. (프리미엄)")
)
feature(
symbol: "questionmark.circle.fill",
tint: AppTheme.yellow,
title: String(localized: "도움말 새 단장"),
body: String(localized: "도움말 전체 문구를 더 자연스럽게 다듬고, 새 기능 사용법을 더했어요.")
)
Label {
Text("아이폰·아이패드·맥을 함께 쓰신다면 모든 기기에서 하루 다님을 업데이트해 주세요. 버전이 섞이면 새 기능이 일부 기기에서 다르게 보일 수 있어요.")
.font(.footnote)
.foregroundStyle(.secondary)
} icon: {
Image(systemName: "arrow.triangle.2.circlepath")
.foregroundStyle(AppTheme.green)
}
.padding(.top, 2)
}
.padding(.horizontal, 24)
.padding(.bottom, 16)
}
Button {
dismiss()
} label: {
Text("시작하기")
.font(.headline)
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
}
.buttonStyle(.borderedProminent)
.tint(AppTheme.green)
.padding(.horizontal, 24)
.padding(.bottom, 20)
}
.background(AppTheme.background)
}
private func feature(symbol: String, tint: Color, title: String, body text: String) -> some View {
HStack(alignment: .top, spacing: 14) {
Image(systemName: symbol)
.font(.title2)
.foregroundStyle(tint)
.frame(width: 34)
VStack(alignment: .leading, spacing: 3) {
Text(title)
.font(.subheadline.weight(.semibold))
Text(text)
.font(.footnote)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
}
}

View File

@ -279,6 +279,54 @@
}
}
},
"%@L": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%@ L"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%@L"
}
}
}
},
"%@kcal": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%@ kcal"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%@kcal"
}
}
}
},
"%@km": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%@ km"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%@km"
}
}
}
},
"%@까지": {
"localizations": {
"en": {
@ -295,6 +343,22 @@
}
}
},
"%@보": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%@ steps"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%@歩"
}
}
}
},
"%@은(는) 이미 측정 중이에요.": {
"localizations": {
"en": {
@ -966,6 +1030,22 @@
}
}
},
"건강 데이터": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Health data"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ヘルスデータ"
}
}
}
},
"건강한 생활 습관": {
"localizations": {
"en": {
@ -982,6 +1062,70 @@
}
}
},
"걷기 운동": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Walking"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ウォーキング"
}
}
}
},
"걸음 수": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Steps"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "歩数"
}
}
}
},
"고강도 인터벌(HIIT)": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "HIIT"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "HIIT"
}
}
}
},
"골프": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Golf"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ゴルフ"
}
}
}
},
"그래프로 보여줄 행동들과 기간, 테마를 선택하세요. 크기에 따라 소형 2개·중형 4개·대형 6개까지 그려져요.": {
"localizations": {
"en": {
@ -998,6 +1142,22 @@
}
}
},
"근력 운동": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Strength training"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "筋力トレーニング"
}
}
}
},
"글래스 버블 메뉴": {
"localizations": {
"en": {
@ -1063,6 +1223,38 @@
}
}
},
"기타 운동": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Other workouts"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "その他の運動"
}
}
}
},
"나머지 행동": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Other actions"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "その他のアクション"
}
}
}
},
"날짜": {
"localizations": {
"en": {
@ -1095,6 +1287,22 @@
}
}
},
"농구": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Basketball"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "バスケットボール"
}
}
}
},
"누적값 표시": {
"localizations": {
"en": {
@ -1459,6 +1667,38 @@
}
}
},
"댄스": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Dance"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ダンス"
}
}
}
},
"데이터 없음": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "No data"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "データなし"
}
}
}
},
"독서": {
"localizations": {
"en": {
@ -1475,6 +1715,22 @@
}
}
},
"등산": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Hiking"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "登山"
}
}
}
},
"라이트 모드 고정": {
"localizations": {
"en": {
@ -1491,6 +1747,22 @@
}
}
},
"마음챙김": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Mindfulness"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "マインドフルネス"
}
}
}
},
"매달": {
"localizations": {
"en": {
@ -1930,6 +2202,22 @@
}
}
},
"물 섭취": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Water intake"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "水分摂取"
}
}
}
},
"미달성 종료": {
"localizations": {
"en": {
@ -1946,6 +2234,38 @@
}
}
},
"배드민턴": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Badminton"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "バドミントン"
}
}
}
},
"복싱": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Boxing"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ボクシング"
}
}
}
},
"사용 안 함": {
"localizations": {
"en": {
@ -1962,6 +2282,22 @@
}
}
},
"사이클": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Cycling"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "サイクリング"
}
}
}
},
"선택 안 함": {
"localizations": {
"en": {
@ -2011,6 +2347,38 @@
}
}
},
"수면": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Sleep"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "睡眠"
}
}
}
},
"수영": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Swimming"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "水泳"
}
}
}
},
"수행일 아님": {
"localizations": {
"en": {
@ -2043,6 +2411,22 @@
}
}
},
"스트레칭": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Stretching"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ストレッチ"
}
}
}
},
"시간": {
"localizations": {
"en": {
@ -2267,6 +2651,22 @@
}
}
},
"야구": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Baseball"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "野球"
}
}
}
},
"연속 %lld달": {
"localizations": {
"en": {
@ -2379,6 +2779,22 @@
}
}
},
"요가": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Yoga"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ヨガ"
}
}
}
},
"요일 지정": {
"localizations": {
"en": {
@ -2395,6 +2811,22 @@
}
}
},
"운동 시간": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Exercise time"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "運動時間"
}
}
}
},
"원형 게이지": {
"localizations": {
"en": {
@ -2460,6 +2892,22 @@
}
}
},
"이동 거리": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Distance"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "移動距離"
}
}
}
},
"이번 달": {
"localizations": {
"en": {
@ -2605,6 +3053,22 @@
}
}
},
"일어서기": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Stand hours"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "スタンド時間"
}
}
}
},
"일주일 (하루 단위)": {
"localizations": {
"en": {
@ -2781,6 +3245,22 @@
}
}
},
"줄넘기": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Jump rope"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "縄跳び"
}
}
}
},
"중형 위젯 구성": {
"localizations": {
"en": {
@ -2797,6 +3277,22 @@
}
}
},
"즐겨찾기 행동": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Favorite actions"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "お気に入りのアクション"
}
}
}
},
"지금 측정 중인 행동이 없어요": {
"localizations": {
"en": {
@ -2941,6 +3437,22 @@
}
}
},
"축구": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Soccer"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "サッカー"
}
}
}
},
"측정 종료": {
"localizations": {
"en": {
@ -3053,6 +3565,22 @@
}
}
},
"테니스": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Tennis"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "テニス"
}
}
}
},
"테마": {
"localizations": {
"en": {
@ -3230,6 +3758,22 @@
}
}
},
"필라테스": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Pilates"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ピラティス"
}
}
}
},
"하루": {
"localizations": {
"en": {
@ -3663,6 +4207,22 @@
}
}
},
"활동 에너지": {
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Active energy"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "アクティブエネルギー"
}
}
}
},
"횟수": {
"localizations": {
"en": {