스마트 피트니스 앱: 클라이언트 아키텍처 사양
아티팩트이 아티팩트는 에피소드 3: 앱에 브레인 제공 및 에피소드 4: 노 코드 앱 구현 계획에서 사용됩니다. 이 사양을 프런트엔드 뷰, 재사용 가능한 UI 구성요소, 클라이언트 상태 관리, 일반 영어 테스트 사례의 참조로 사용할 수 있습니다.
이 아티팩트에 액세스하려면 로그인하세요.
Google 개발자 프로필로 로그인하여 Build It! 시리즈에 사용된 전체 아티팩트를 확인하고 복사하세요.
개발자 프로필 필요
개발자 프로필을 사용할 수 있는 요건을 충족하지 않습니다. Workspace 관리자에게 이 기능을 사용 설정하도록 요청하세요.
개발자 프로필 만들기
무료 Google 개발자 프로필을 만들어 모든 Build It! 프롬프트와 아티팩트를 사용해 보세요.
Builder Central에 가입하여 활용하기
클릭 한 번으로 무료 빌더 멤버십 배지를 받아 모든 Build It! 프롬프트와 아티팩트를 이용하세요.
상태: 계획 세션 준비 완료 (Client Planning Companion으로 초안 작성 중)
컴패니언 문서: 데이터 아키텍처 사양
아키텍처 목표: AI Studio 스마트 피트니스 프로토타입의 깔끔하고 유지보수 가능하며 모듈식이고 테스트 가능한 프런트엔드 재빌드
0. 핵심 기술 스택 및 엔지니어링 표준
- 런타임 프레임워크: Next.js (App Router, React 19) + 프로그레시브 웹 앱 (PWA) 오프라인 애셋 캐싱
- 스타일 지정 아키텍처: Tailwind CSS v4 + Tailwind Variants (결정론적 구성요소 스타일 지정용) + Lucide React (아이콘)
- 클라이언트 상태 구분:
- Zustand (로컬 세션): 휘발성 운동 상태 (
useActiveSessionStore), 스톱워치 타이머, 활성 세트 버퍼, 사전 계산된 단계 시퀀싱을 관리합니다. 휴대전화 잠금 복구를 위해localStorage에 동기화되었습니다. - TanStack Query (서버 상태):
weekly_plans,user_profiles,prs,workout_logs의 Firestore 문서 캐싱, 백그라운드 재검증, 낙관적 업데이트를 관리합니다.
- Zustand (로컬 세션): 휘발성 운동 상태 (
- 차트 시각화: Recharts (커스텀 어두운 체육관 모드 토큰으로 스타일 지정)
- 인증: 모든 서버 엔드포인트에 전달되는 Bearer 토큰 인증 헤더를 사용하는 Firebase 인증
1. 프런트엔드 핵심 원칙 및 설계 원칙
- 운동에 최적화된 인체공학적 설계: 운동 중 땀에 젖은 손으로도 한 손으로 쉽게 조작할 수 있도록 설계되었습니다. 넉넉한 탭 타겟 (최소 48x48dp), 높은 대비의 가독성, 최소한의 타이핑 요구사항을 제공합니다.
- 낙관적 UI 및 오프라인 복원력: 세트 기록, 휴식 타이머 시작, 운동 전환은 UI에서 즉시 (<16ms) 응답해야 합니다. 백그라운드 동기화는 사용자를 차단하지 않고
POST /api/workouts/log에 대한 서버 지속성을 처리합니다. - 엄격한 관심사 분리: 더미/프레젠테이션 구성요소는 props를 사용하여 데이터를 수신하고 이벤트를 내보냅니다. 스마트 컨테이너 뷰는 비즈니스 로직, 상태 저장소, API 조정을 관리합니다.
- 단계적 변형 UX: AI 코치 (
POST /api/coach/chat)가 생성한 제안은 애플리케이션 상태에 변경사항을 커밋하기 전에 명시적인 [계획 수락] 및 [수정] 컨트롤이 있는 대화형 단계적 미리보기 카드로 렌더링됩니다. - 동작 기반 품질: 모든 페이지, 구성요소, 상호작용은 예상 동작을 명확하게 정의하는 일반 영어 테스트 사례와 쌍을 이룹니다.
2. 애플리케이션 탐색 및 뷰 계층 구조
| 경로 | 뷰 이름 | 주요 목적 | 포함된 주요 구성요소 |
|---|---|---|---|
/ |
루트 리디렉션 | /today(으)로 즉시 리디렉션됩니다. |
(라우터 리디렉션) |
/today |
오늘의 운동 미리보기 | 기본 뷰 현재 요일로 자동 확인되고 오늘의 운동 미리보기가 표시됩니다. | <BackButton />, <WorkoutHeader />, <CoachNoteCard />, <WorkoutExerciseSummary />, <WorkoutBlock />, <ExerciseCard />, <EmptyDayPlaceholder />, <ModifyWithCoachCard /> |
/workouts/:day |
일일 운동 미리보기 | 한 주의 특정 요일 (예: /workouts/monday, /workouts/thursday)에 대한 운동 미리보기를 표시합니다. |
<BackButton />, <WorkoutHeader />, <CoachNoteCard />, <WorkoutExerciseSummary />, <WorkoutBlock />, <ExerciseCard />, <EmptyDayPlaceholder />, <ModifyWithCoachCard /> |
/workout/active |
활성 세션 추적기 | 중요한 운동 실행 화면 (세트, 반복, 타이머) 하단 탐색 메뉴는 계속 표시됩니다. | <ActiveSessionStickyHeader />, <ActiveExerciseCardHeader />, <CoachTipCard />, <CurrentSetTargetCard />, <SetNavigationRow />, <ExerciseSetsTable />, <ActiveRestTimerDock />, <SessionExerciseOverview />, <ActiveSessionControlsDock />, <SwapExerciseModal />, <FinishConfirmationModal /> |
/week |
교육 일정/주간 보기 | 주간 분량/시간 집계가 포함된 7일 전체 일정 개요입니다. | <WeekHeader />, <WeeklyStatsSummary />, <DayScheduleCardList />, <DayScheduleCard />, <ModifyWeekWithCoachCard /> |
/coach |
AI 코치 채팅 | 상황에 맞는 칩과 단계별 운동 제안을 통한 대화형 코칭 | <CoachHeader />, <ChatMessageList />, <ChatBubble />, <StagedProposalCard />, <ContextualPromptsBar />, <ChatInputDock />, <ContextBanner />, <ChatInputBar />, <SpeechToTextButton /> |
/progress |
진행 상황 및 분석 | 과거 운동 기록, PR 달성, 볼륨 차트 | <ProgressHeader />, <PRShowcaseBanner />, <VolumeProgressChartCard />, <AllTimeStatsGrid />, <WorkoutHistorySection />, <HistorySessionCard />, <EmptyHistoryPlaceholder /> |
/settings |
사용자 프로필 및 환경설정 | 장비 프로필, 알림, 3단계 메모리 관리 | <SettingsHeader />, <UserProfileCard />, <EquipmentSelectorCard />, <EquipmentToggleChip />, <PreferencesGroup />, <MemoryTierViewerCard />, <MemoryItemCard />, <AccountActionsGroup /> |
/exercises/:exerciseId |
운동 백과사전 세부정보 | 동영상 및 자세 팁이 포함된 딥 링크 가능한 운동 가이드 (예: /exercises/incline-db-bench-press) |
<BackButton />, <ExerciseVideoPlayer />, <TargetMusclesCard />, <HowToPerformCard />, <FormTipsCard /> |
3. 페이지 및 뷰 사양
보기: DailyWorkoutView (/today 및 /workouts/:day)
- 목적: 운동 중점, 시간, 목표 근육 부하 분산, 코치 메모, 스트레이트 세트 블록, 슈퍼세트 블록을 포함하여 특정 날짜에 예약된 운동 루틴을 표시합니다. 예약된 운동이 없는 경우 깔끔한 빈 상태로 대체됩니다.
- 시각적 구조:
- 상단:
<BackButton label="Back to Week" targetRoute="/week" /> - 헤더: 한 줄 제목 (
"Today"또는 요일), 조건부"✓ Completed"배지, 기간 필 ("⏱ 50 min"), 타겟 근육 강도 비율 막대 - 코치 메모: 아이콘과 녹색 테두리 색조가 있는 상황별 조언 카드입니다.
- 본문:
<WorkoutExerciseSummary />를 사용하는 운동 블록의 스크롤 가능한 목록(border-left: 3px solid #a3e635및🔁 SUPERSET BLOCK배지가 있는 스트레이트 세트 또는 슈퍼세트) - 빈 상태:
<EmptyDayPlaceholder message="Nothing scheduled for this day" /> - 하단 바닥글 작업:
<ModifyWithCoachCard />(/coach?day=:day로 라우팅)
- 상단:
보기: WeekScheduleView (/week)
- 목적: 상위 수준의 주간 운동량/시간 측정항목, 선별된 활성 운동일 목록 (예정되지 않은 날/휴식일은 엄격하게 필터링), AI 코치와 함께 주간 계획을 점검할 수 있는 바로가기를 포함하여 7일간의 전체 운동 일정 개요를 표시합니다.
- 시각적 분석:
<WeekHeader />, 3열<WeeklyStatsSummary />(총 시간, 총 볼륨, 빈도),<DayScheduleCard variant="normal" />를 사용한 예약된 날짜 목록(현재 날짜에 네온 그린TODAY배지 표시),<ModifyWeekWithCoachCard />(/coach?context=week로 연결)
보기: CoachChatView (/coach)
- 목적: Gemini 트레이너 에이전트가 있는 대화형 코칭 허브입니다. 동기 부여 및 전략적 트레이닝 조언을 제공하고, 음성 텍스트 변환 음성기록을 지원하며, 명시적인 수락 제어를 통해 휴먼인더루프 단계별 운동 조정을 렌더링합니다.
- 시각적 분석:
<CoachHeader />, 스크롤 가능한<ChatMessageList />(<ChatBubble />및<StagedProposalCard />), 조건부<ContextualPromptsBar />, 고정된<ChatInputDock />(<ContextBanner />,[✕]닫기 +<ChatInputBar />+<SpeechToTextButton />)
보기: ExerciseDetailView (/exercises/:exerciseId)
- 목적: 딥 링크가 가능한 백과사전 항목으로, 자동 반복되는 음소거 동영상 시연 (
<ExerciseVideoPlayer />), 주 타겟 근육과 보조 타겟 근육 배지 (<TargetMusclesCard />), 순서가 지정된 단계별 수행 안내 (<HowToPerformCard />), 자세 팁 (<FormTipsCard />)을 제공합니다.
보기: ActiveWorkoutView (/workout/active)
- 목적: 중요한 운동 실행 및 세트 기록 화면 실시간 세션 스톱워치 상태, 현재 설정된 목표, 반복 횟수/중량 입력, 즉각적인 체크표시 완료 (<16ms), 자동 슈퍼세트 교대, 햅틱/오디오를 사용한 휴식 타이머 카운트다운, 운동 교체,
POST /api/workouts/log에 대한 최종 지속성을 관리합니다. - 시각적 구조:
<ActiveSessionStickyHeader />,<ActiveExerciseCardHeader />,<CoachTipCard />,<CurrentSetTargetCard />, 플로팅<ActiveRestTimerDock />(+30s,Skip),<SetNavigationRow />,<ExerciseSetsTable />(+ Add Set),<SessionExerciseOverview />, 고정된<ActiveSessionControlsDock />([⇄ SWAP EXERCISE]+[▶ START WORKOUT]/[⏸ PAUSE]/[▶ RESUME]),<SwapExerciseModal />,<FinishConfirmationModal />
보기: ProgressView (/progress)
- 목적: 시간 경과에 따른 운동 진행 상황을 추적하고, 최근 개인 기록 (
<PRShowcaseBanner />)을 기념하고, 대화형 Recharts (4W | 3M | 1Y전환이 있는<VolumeProgressChartCard />)를 사용하여 주간/월간 운동량 추세를 시각화하고,<AllTimeStatsGrid />를 표시하고, 이전에 완료한 운동 세션 (<WorkoutHistorySection />/<HistorySessionCard />)을 보관처리합니다.
보기: SettingsView (/settings)
- 목적: 운동선수 프로필 정보 (
<UserProfileCard />), 장비 인벤토리 (<EquipmentSelectorCard />/<EquipmentToggleChip />), 운동 환경설정 (LBS / KG, 햅틱, 사운드의 경우<PreferencesGroup />)을 관리하고 AI 트레이너의 3단계 생활 메모리 (<MemoryTierViewerCard />/<MemoryItemCard />)를 투명하게 관리하고 삭제할 수 있습니다.
4. 재사용 가능한 구성요소 카탈로그 및 인터페이스
애플리케이션 전반의 주요 TypeScript 구성요소 속성 계약:
// src/components/workout/WorkoutExerciseSummary.tsx
interface WorkoutExerciseSummaryProps {
blocks: Array<{
label?: string;
category?: string;
exercises: Array<{
exercise_id: string; // Canonical slug (e.g. 'incline-db-bench-press')
name: string; // Denormalized display name (e.g. 'Incline DB Bench Press')
target_sets: number;
target_reps: string | number;
rest_seconds?: number;
}>;
}>;
activeExerciseId?: string;
onSelectExercise?: (exerciseId: string) => void;
}
// src/components/week/DayScheduleCard.tsx
interface DayScheduleCardProps {
dayName: string;
dayShort: string;
subtitleLabel?: string;
isToday?: boolean;
title: string;
focusBadge: string;
durationMin: number;
volumeLbs?: number;
targetMuscles: string[];
variant?: 'normal' | 'condensed';
onPress: (dayName: string) => void;
}
// src/components/coach/StagedProposalCard.tsx
interface StagedProposalCardProps {
proposalId: string;
type: 'UPDATE_DAY' | 'UPDATE_WEEK';
targetDay?: string;
summaryDiff: string;
proposalData: any;
isApplied?: boolean;
onAccept: (proposalId: string, data: any) => void;
onModify: (summaryDiff: string) => void;
}
// src/components/workout/CurrentSetTargetCard.tsx
interface CurrentSetTargetCardProps {
setNumber: number;
targetWeightLbs: number;
targetReps: number;
isCompleted: boolean;
onWeightChange: (weight: number) => void;
onRepsChange: (reps: number) => void;
onToggleComplete: () => void;
}5. 클라이언트 상태 관리 및 데이터 흐름 매트릭스
5.1 지연 시간 및 로드 정책: 투스피드 엔진
- 체육관 바닥 낙관적 UI (<16ms): 세트 체크, 중량/반복 횟수 증가, 탭 전환, 장비 전환과 같은 체육관 바닥에서의 촉각적 동작이 16밀리초 이내에 로컬 상태를 업데이트하고 다시 렌더링합니다. 사용자는 가중치를 유지하는 동안 로드 표시기에 의해 차단되지 않습니다.
- 비동기 작업에 대한 명시적 로드 스피너 및 표시기:
- AI 코치 대화 (
POST /api/coach/chat): 채팅 메시지를 제출하면 Gemini가 대답이나 운동 제안을 반환할 때까지 채팅 스트림에 애니메이션 코치 생각 중 스피너 풍선이 즉시 렌더링됩니다. - 초기 콜드 스타트 및 네트워크 동기화:
weekly_plans또는historical_workouts를 가져오는 동안 뷰 컨테이너에 스켈레톤 로더와 고대비 로딩 스피너가 표시됩니다. - 운동 제출 (
[Finish]): 세션 페이로드가 Cloud Firestore의POST /api/workouts/log에 커밋되는 동안 완료 확인 모달이 활성 로딩 스피너를 렌더링합니다.
- AI 코치 대화 (
5.2 클라이언트 스토어 아키텍처 및 도메인 경계
| 매장 레이어 | 상태 도메인 및 주요 속성 | 지속성 및 캐시 정책 | 조정된 백엔드 계약 |
|---|---|---|---|
useActiveSessionStore (Zustand) |
sessionState (unstarted, running, paused), elapsedSeconds, currentStepIndex, steps: WorkoutStep[], loggedExercises. |
완벽한 휴대전화 잠금/새로고침 복구를 위해 매초 localStorage에 동기화됩니다. |
완료된 페이로드를 POST /api/workouts/log에 제출합니다. |
useWeeklyPlanQuery (TanStack Query) |
현재 7일 일정, 일일 운동 객체, 슈퍼세트 블록, 완료 상태 (scheduled, completed) |
TanStack Query (staleTime: 5 min)에 캐시되고 실행 시 프리패치됩니다. |
사용자가 POST /api/coach/chat의 제안에 대해 [Accept Plan]를 탭하면 커밋됩니다. |
useCoachChatStore (Zustand + TanStack) |
시간순 메시지 풍선, activeContext, 프롬프트 칩, isCoachTyping: boolean, 단계별 제안 |
실시간 구독 (onSnapshot)을 사용하여 Firestore 주간 채팅 버킷과 동기화했습니다. |
POST /api/coach/chat에 쿼리를 전달합니다. |
useProgressQuery (TanStack Query) |
과거 운동 기록 보관함, PR 기록 보드 (/users/{userId}/prs), 전체 기간 운동량 집계, 기간 필터 (4W, 3M, 1Y) |
TanStack Query (staleTime: 10 min)에 캐시됩니다. |
운동 세션이 완료되는 즉시 업데이트됩니다. |
useUserProfileStore (TanStack Query) |
장비 인벤토리, 유닛 (LBS/KG), 사운드/햅틱 전환, 3단계 생활 기억 (필수품, 생리적, 상황적) |
Firestore user_profiles에 대한 양방향 동기화를 통해 TanStack Query에 캐시됩니다. |
POST /api/coach/chat의 memory_mutations을 사용하여 자동으로 업데이트되었습니다. |
useExerciseLibraryQuery (TanStack Query) |
정적 백과사전 항목, 동영상 URL, 주동근/협력근, 단계, 자세 팁 | 무한 TTL (staleTime: Infinity)을 사용하는 적극적인 캐시 우선 |
변경 불가능한 Firestore exercises 컬렉션에서 읽기 전용입니다. |
5.3 사전 계산된 활성 세션 단계 그래프
export interface WorkoutStep {
stepIndex: number; // 1-based index (e.g. 1 to 13)
totalSteps: number; // Total sets in the session
blockIndex: number; // Index of block in day plan
blockType: 'straight' | 'superset';
exerciseId: string; // Canonical slug
exerciseName: string; // Display name
setNumber: number; // Set index for this exercise (1, 2, 3...)
targetWeightLbs: number; // Target weight in lbs
targetReps: string | number; // Target reps (e.g. "8-10")
restSecondsAfter: number; // Prescribed rest duration
isCompleted: boolean; // Completion checkmark status
actualWeightLbs?: number; // User entered weight
actualRepsCompleted?: number; // User entered reps
}5.4 엔드 투 엔드 데이터 흐름 및 변형 매트릭스
| 사용자 트리거 | 발생 뷰 | 클라이언트 저장소가 변경됨 | UI 지연 시간/로드 상태 | 백엔드 조정 | 롤백/실패 처리 |
|---|---|---|---|---|---|
[✓] 세트 완료를 탭합니다. |
/workout/active |
useActiveSessionStore |
<16ms (낙관적) |
로컬 세션 버퍼가 업데이트되었습니다. 네트워크 대기가 없습니다. | 안정적인 로컬 지속성을 갖춘 로컬 메모리 쓰기 |
[Finish Workout] 탭 |
/workout/active |
useActiveSessionStore, useWeeklyPlanStore, useProgressStore |
동기화하는 동안 버튼에 로드 스피너가 표시됨 | POST /api/workouts/log에 제출합니다. Firestore 날짜를 completed로 표시합니다. |
오프라인 상태인 경우 페이로드를 로컬 동기화 버퍼에 대기열에 추가하고 다시 연결되면 재시도합니다. |
[Accept Plan] 탭 |
/coach |
useWeeklyPlanStore, useCoachChatStore |
인스턴트 UI 스탬프 ("✓ Plan Applied") |
proposal.data을 Firestore weekly_plans에 커밋합니다. |
네트워크에 장애가 발생하면 토스트 메시지 알림이 사용자에게 알리고 버튼은 다시 시도할 수 있도록 활성 상태로 유지됩니다. |
| Chat에서 메시지 보내기 | /coach |
useCoachChatStore, useUserProfileStore |
낙관적 말풍선 추가 + 코치 생각 중 스피너 | POST /api/coach/chat을 호출합니다. 커밋에서 memory_mutations를 반환했습니다. |
스피너를 '전송할 수 없습니다. 탭하여 다시 시도하세요.'라는 말풍선이 표시됩니다. |
| 장비 칩 전환 | /settings |
useUserProfileStore |
인스턴트 전환 (<16ms) |
Firestore user_profiles에 백그라운드 쓰기를 전달합니다. |
쓰기에 실패하면 칩 선택을 되돌리고 오류 토스트 메시지를 표시합니다. |
AI 메모 항목 [🗑] 삭제 |
/settings |
useUserProfileStore |
즉시 삭제 (<16ms) |
Firestore user_profiles에 삭제 변형을 전달합니다. |
메모리 항목을 복원하고 삭제에 실패하면 사용자에게 알립니다. |
6. 엔드 투 엔드 사용자 여정 및 일반 영어 테스트 매트릭스
- 도구 모음 1: 앱 셸, 탐색 및 경로 처리 (테스트 1.1~1.6: 루트 실행 리디렉션, 영구 도크, 활성 세션 빠른 복귀, 잘못된 요일 대체, 알 수 없는 경로 404 대체, 잘못된 운동 딥 링크 대체)
- Suite 2: 일일 운동 보기 및 블록 렌더링 (테스트 2.1~2.7: 동적 일일 제목 해상도, 특정 일일 경로 및 뒤로 버튼, 단일 블록과 슈퍼세트 블록 처리, 빈 일일 플레이스홀더, 완료된 운동 스탬프, 중앙 재생 버튼의 순수 탐색, 코치 컨텍스트 첨부파일로 수정)
- Suite 3: 주간 일정 보기 및 일간 일정 카드 (테스트 3.1~3.6: 주간 통계 렌더링, 예약된 날짜만 필터링, 오늘 시각적 콜아웃, 일간 카드 탐색, 주간 탐색 수정, 압축된 변형 렌더링)
- Suite 4: 코치 채팅 및 단계적 변형 (테스트 4.1~4.7: 채팅 메시지 스트림 렌더링, 컨텍스트 배너 첨부 및 닫기, 프롬프트 칩 자동 입력, 음성 받아쓰기 전환, 단계적 변형 격리, 제안 수락 및 커밋, 주간 정비 제안 미리보기 및 커밋)
- 모음 5: 운동 세부정보 및 백과사전 (테스트 5.1~5.5: 운동 세부정보 렌더링, 음소거된 재생 자동 반복, 동영상 일시중지 전환, 기본 근육 태그와 보조 근육 태그, 기록 뒤로 탐색 및 대체)
- Suite 6: Active Workout Tracker & Logging (테스트 6.1~6.7: 시작되지 않은 초기 상태 및 수동 시작, 스톱워치 일시중지 및 재개, 촉각 세트 로깅 및 체크표시 전환, 슈퍼세트 단계 교대, 운동 모달 및 교체 전환, 완료 확인 및 서버 동기화, 새로고침 시 세션 상태 복구)
- 도구 모음 7: 진행 상황 및 이전 분석 (테스트 7.1~7.5: 진행 상황 대시보드 개요 렌더링, PR 쇼케이스 배너 표시, 차트 기간 전환, 빈 기록 상태, 이전 세션 기록 탭)
- Suite 8: 사용자 설정, 장비, AI 메모리 (테스트 8.1~8.4: 설정 뷰 렌더링, 장비 전환 업데이트, 단위 전환, AI 메모리 항목 삭제)