Skip to content

✨ [Feat] Watch 홈 통합 글랜스 · 네비게이션 IA 골격 (#1206) - #1280

Open
JEONG-J wants to merge 3 commits into
developfrom
feat/1206
Open

✨ [Feat] Watch 홈 통합 글랜스 · 네비게이션 IA 골격 (#1206)#1280
JEONG-J wants to merge 3 commits into
developfrom
feat/1206

Conversation

@JEONG-J

@JEONG-J JEONG-J commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

🔗 관련 이슈

Closes #1206

⚠️ 선행 PR 의존: 이 PR 은 #1275 (feat/1205 — 워치 디자인 토큰·공통 컴포넌트) 위에 쌓여 있습니다.
feat/1205develop 에 아직 없어 그 브랜치를 base 로 분기했으므로, #1275 를 먼저 머지한 뒤 이 PR 을 머지해 주세요.
(아래 커밋 목록 중 feat: watchOS 디자인 토큰·공통 컴포넌트 레이어 구축#1275 의 커밋이며, #1275 머지 후에는 diff 에서 사라집니다.)

✨ PR 유형

Feature — 워치 앱의 진입 화면(홈 통합 글랜스)과 NavigationStack 기반 내비게이션 IA 골격을 새로 세웁니다.
기존 UMCWatchAppWindowGroup { ContentView() } + Text("Hello Watch") 뿐이라 IA 가 존재하지 않았습니다.

📷 스크린샷 or 영상(UI 변경 시)

watchOS 26.5 시뮬레이터(Apple Watch Series 11 46mm)에 설치·실행해 확인했습니다.
데이터 공급원(#1210 WatchConnectivity 페이로드 계약)이 아직 없어 빈 상태가 현재의 실제 화면입니다.

┌─────────────────────────┐
│ 홈                2:05  │  navigationTitle, 순수 블랙 배경(#000000)
│ ┌─────────────────────┐ │
│ │ 오늘 세션 없음        │ │  standard 카드(solid #16181C) — 세션 없음 빈 상태
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ The Ping          › │ │  tap-chip → The Ping 플로우
│ │ 새 공지 없음          │ │
│ └─────────────────────┘ │
└─────────────────────────┘
  • 세션이 있을 때는 hero 카드(#1B2140)에 오늘 세션 라벨 → 시작 시각(.metric, 대형 등폭) → 세션명(.cardValue) → WatchStatusBadge 순으로 위계가 잡힙니다.
  • 세션 있음 / 세션 없음 / .dynamicTypeSize(.accessibility3) 3종 #PreviewHomeGlanceView.swift 에 넣어 두었습니다.
  • GitHub CLI 로는 이미지 첨부가 안 되어 캡처 파일을 붙이지 못했습니다. 리뷰 시 프리뷰 3종으로 확인 부탁드립니다.

🛠️ 작업내용

라우팅 골격 (Sources/Routing/)

파일 내용
WatchRoute.swift 목적지 enum 6종 — attendanceList / attendanceSession(scheduleID:) / attendanceResult(scheduleID:) / pingList / pingDetail(noticeID:) / fallback(_:). WatchFallbackRoute 3종(권한 거부·연결 끊김·큐잉)
WatchRouter.swift 앱 셸이 소유하는 @Observable 단일 스택. push / pop / popToRoot
WatchRootView.swift NavigationStack(path:) 셸. navigationDestination(for:)한 곳에서 등록
WatchRoutePlaceholderView.swift 경로 도달 가능성만 증명하는 최소 화면. #1207·#1208·#1209 가 실제 화면으로 교체

케이스는 플로우 진입점 + 식별자만 담습니다. 정시/지각/지오펜스 이탈, 결과 4종 같은 화면 내부 상태는 목적지 화면이 데이터에서 파생하므로 케이스로 쪼개지 않았습니다. 그래서 후속 이슈는 enum 을 건드리지 않고 WatchRootView 의 destination 만 바꿔 끼우면 됩니다.

경로를 NavigationPath(타입 소거)가 아니라 [WatchRoute] 구체 배열로 뒀습니다. iOS PathStore 가 타입 소거를 택한 이유는 "모든 Feature 목적지를 한 enum 에 모으면 Core → Feature 역방향 의존이 생긴다"인데, 워치는 단일 타겟·단일 스택이라 그 논거가 성립하지 않습니다. 구체 배열이어야 스택 내용을 테스트로 검증할 수 있습니다.

홈 통합 글랜스 (Sources/Home/)

  • WatchGlance / GlanceSession — 표시 모델. 샘플 3종은 #if DEBUG 가드(절대 규칙 ✨ Feature: MainButton Component 제작 #5).
  • HomeGlanceViewModel@Observable. 출석 진입 경로(세션 있음 → attendanceSession, 없음 → attendanceList), 승인 대기 문구, 미확인 공지 문구, 빈 상태 문구를 파생.
  • HomeGlanceView — 세션 카드와 공지 행을 각각 Button + .buttonStyle(.plain) tap-chip 으로 승격. .plain 이어야 카드 표면이 solid 로 남습니다(Glass 버튼 스타일을 쓰면 카드가 Glass 금지 구역을 침범).

Tuist

  • watchAppProject 헬퍼에 includesTests: 추가 → UMCWatchAppTests unitTests 타겟(watchOS 26.4).
  • 같은 헬퍼에 WKApplication: true 보강. 지시 범위 밖이지만 없으면 빌드는 통과해도 설치가 거부됩니다:
    App installation failed: WatchKit app being installed is missing either the WKWatchKitApp or WKApplication key set to true in its Info.plist
    유닛 테스트가 앱을 호스트로 설치해야 돌기 때문에 이 키 없이는 테스트 실행 자체가 불가능했습니다. 워치 앱을 한 번도 설치·실행한 적이 없어 지금까지 드러나지 않던 매니페스트 누락입니다.
  • Sources/ContentView.swift(Text("Hello Watch")) 삭제.

⚠️ 이슈 AC 와 다르게 구현한 부분 1건 — 승인 대기 색

이슈 본문 AC 는 "승인 대기 상태를 앰버(#FFB340)로 표시" 인데, 그렇게 하지 않고 WatchStatus.pending(중립 #B2B8BF 점 + 인디고 링)을 썼습니다.

  • 설계 스펙이 2026-08-26 개정되면서 승인 대기 = 중립 + 인디고 링, 앰버 #FFB340 = 지각(warning) 전용으로 갈렸습니다. 스펙 본문도 "대기 상태를 중립 링으로 표시"라고 씁니다.
  • 선행 이슈 ✨ Feature: Watch 디자인 토큰·공통 컴포넌트 구축 (Glass 절제 규칙) #1205 의 디자인시스템이 이미 그 개정판을 코드로 굳혔습니다 — WatchColor.statusPending = #B2B8BF, statusWarning = #FFB340.
  • 이슈 AC 는 개정 이전 값을 물고 있는 것으로 보입니다. 앰버를 쓰려면 WatchStatusBadge 를 우회해 색을 직접 칠해야 해서 디자인시스템 계약을 깨게 됩니다.
  • 판단 근거를 HomeGlanceView.swiftpendingApprovalBadge 에 주석으로 남겼습니다. 개정 전 값이 맞다면 .pending.warning 한 글자만 바꾸면 됩니다.

📋 추후 진행 상황

📌 리뷰 포인트

1. 검증 결과 (전부 통과)

커맨드 결과
make test (기본 SCHEME=UMCApp, iOS 시뮬레이터) ** TEST SUCCEEDED ** — 32 tests / 5 suites
xcodebuild -scheme UMCWatchApp -destination 'platform=watchOS Simulator,name=Apple Watch Series 11 (46mm)' test ** TEST SUCCEEDED **13 tests / 2 suites
make test SCHEME=CoreWatchDesignSystem ** TEST SUCCEEDED ** — 회귀 없음
시뮬레이터 설치·실행 정상 (홈 글랜스 렌더 확인)

2. 디자인시스템 계약 준수 (docs/claude/watch-design-system.md §9 체크리스트)

grep -rn "glassEffect\|ultraThin\|Material\|buttonStyle(\.glass" UMCApp/UMCWatchApp/Sources   # 매치 0
grep -rn "Font.system(size:" UMCApp/UMCWatchApp/Sources                                       # 매치 0
grep -rn "@StateObject\|@ObservedObject\|@Published" UMCApp/UMCWatchApp                       # 매치 0
  • 카드·행·화면 배경 전부 solid. Glass 는 한 군데도 쓰지 않았습니다(이 화면엔 CTA 가 없습니다).
  • 텍스트 전부 .font(.watch(...)), 고정 pt·고정 높이 0건. 줄 길이 99자 초과 0건.
  • 좌우 인셋 WatchLayout.screenHorizontalPadding, 세로 간격 stackSpacing/tightSpacing.

3. 집중해서 봐주실 곳

  • 승인 대기 색 결정 (위 ⚠️ 항목) — 이 PR 에서 유일하게 이슈 AC 와 어긋나는 지점입니다.
  • WKApplication 추가 — 헬퍼가 만드는 모든 워치 앱 타겟에 영향을 줍니다. 릴리스 빌드·업로드 경로에 부작용이 없는지 확인 부탁드립니다.
  • [WatchRoute] vs NavigationPath — iOS PathStore 와 의도적으로 다른 선택입니다. 근거는 WatchRouter 타입 주석에 적어 뒀습니다.
  • 카운트를 String 으로 들고 있는 것 — 절대 규칙 💄 [Design] 디자인 시스템 기반 토큰 및 스타일 구현 #2 를 따랐고, Int 변환은 HomeGlanceViewModel.count(_:) 비교 시점 한 곳에서만 합니다. 숫자가 아니거나 음수인 값은 0건으로 떨어집니다("", "abc", "-1", "3건" 케이스를 테스트로 잠갔습니다).
  • @Observable 전역 관리자 예외WatchRouter 가 절대 규칙 🎉 [Chore] 프로젝트 초기 설정 및 환경 구성 #1 의 "앱 생명주기 전역 관리자" 예외에 해당한다고 판단했습니다. iOS PathStore 와 같은 성격입니다.

✅ Checklist

PR이 다음 요구 사항을 충족하는지 확인해주세요!!!

- Core/WatchDesignSystem 모듈 신설 — iOS 카탈로그는 watchOS 에서 항상 dark 를
  resolve 하므로(브랜드색이 #4869F0 대신 #4264F0 이 됨) 워치 전용 레이어로 분리
- 토큰 3종 추가: WatchColor(표면·브랜드·상태·텍스트 20종) ·
  WatchTypography(WatchTextRole 5종) · WatchLayout(코너·보더·패딩·스페이싱)
- 표면 API 추가: watchCard(_:leadingAccent:) · watchScreenBackground() ·
  watchListRowBackground(isSelected:)
- 컴포넌트 2종 추가: WatchActionButton(역할 3종) · WatchStatusBadge(상태 5종)
- Glass 절제 규칙을 API 로 강제 — WatchCardStyle 을 닫힌 enum 으로 두어 카드에
  Glass 를 넣을 경로 자체를 없애고, Glass API 는 WatchActionButton 한 파일로 한정
- 토큰 드리프트 가드 테스트 추가 — iOS Colors.xcassets 를 파싱해 브랜드색 불일치 시 실패
- docs/claude/watch-design-system.md 신규 · CLAUDE.md 레퍼런스 인덱스 갱신
- WatchRoute/WatchFallbackRoute — 출석·The Ping 두 축 진입점과 폴백 3종(권한 거부·연결 끊김·큐잉)을 경로로 확보
- WatchRouter — 앱 셸이 소유하는 @observable 단일 스택. NavigationPath 대신 [WatchRoute] 구체 배열로 두어 스택 내용을 테스트로 검증
- WatchRootView — navigationDestination 단일 등록. 목적지는 후속 이슈가 교체할 플레이스홀더
- HomeGlanceView/HomeGlanceViewModel — solid 카드 글랜스, 세션·공지 행을 tap-chip 으로 승격, 세션 없을 때 "오늘 세션 없음" 빈 상태
- 승인 대기는 WatchStatus.pending(중립+인디고 링). 앰버 #FFB340 은 개정 스펙상 지각 전용이라 쓰지 않음
- 카운트·식별자는 전 레이어 String, Int 변환은 비교 시점에만 (규칙 #2). 숫자 아닌 값은 0건 처리
- watchAppProject 헬퍼에 includesTests 추가 + WKApplication 키 보강 — 없으면 테스트 호스트 설치가 거부됨
- ContentView("Hello Watch") 제거
- "오늘 세션" 라벨 + "오늘 세션 없음" 값이 같은 말을 두 줄로 반복해 위계가 흐려지던 문제
- 빈 상태는 문구 한 줄만 카드에 남긴다. 세션이 있을 때의 라벨/값 위계는 그대로
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능을 추가합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Feature: Watch 홈 통합 글랜스 + 네비게이션 IA 골격

1 participant