---
title: "기능설명서"
notion_id: "36622962086881929639e267d027054c"
notion_url: "https://app.notion.com/p/36622962086881929639e267d027054c"
category: "guide"
parent: "달록 개발 현황"
updated: "2026-05-30"
priority: "High"
purpose: "달록 소스코드 기반 페이지별·공통 기능 목록 + OCR/셀모드/주간마일리지/SNS 부록 (v0.9 클로즈베타 재정의 반영)"
read_when: ["기능파악","최신상태복구"]
---

# 달록(PaceLog) 기능설명서
작성일: 260523 | 버전: v2 (v0.9 클로즈베타 기준 재정의 반영) | 기준: 소스코드 직접 파악 (Claude Code)
> 🚧 (orange_bg 콜아웃)
> **v0.9 클로즈베타 목표 재정의 (2026-05-23)** — 달록 v0.9는 단순 개인기록앱이 아니라 **바이럴 기반 소셜형 운동기록 클로즈베타**다. 본 문서의 "v0.9 필수 예정 기능" 섹션은 v0.9 이후 로드맵이 아니라 **v0.9 발행 전 필수 충족기준**이다. 상세 충족기준은 [달록 v0.9(클로즈베타버전) 완성 충족기준](#/doc/spec-01) 참조.

달록(PaceLog)은 러닝·체성분·근력 통합 로그 + AI 코치 브리프 + (v0.9 신규) 스크린샷 OCR 자동기록 보조를 제공하는 React + Supabase PWA다. 라우터는 `react-router-dom`이며, 모든 페이지가 공통 `Layout`(데스크탑 좌측 사이드바 / 모바일 하단 탭바) 안에서 렌더링된다.
## 페이지별 기능 목록
### 대시보드 (`src/pages/Dashboard.tsx`, 경로: `/dashboard`)
- **3-섹션 통합 요약** — 체성분 / 러닝 / 근력 (데스크탑 3-column grid, 모바일 단일 컬럼 + 우측 세로 탭 nav)
- **최근 데이터 로드** — body_records 180건, running_logs 180건, strength_logs 60건 + 관련 strength_exercises/strength_sets, exercise_configs 일괄 로드
- **F-8 strength_daily_summary 뷰 활용** — 근력 30일 집계는 strength_daily_summary 뷰 기반(서버 사이드 집계)
- **AI에게 보내기 버튼** — 데모 모드(`AISendButton`)
- **빈 상태 안내** — 모든 기록 0건이면 `/log` 이동 CTA
- **컴포넌트** — BodySection, RunningSection, StrengthSection, AISendButton
- **Supabase** — body_records, running_logs, strength_logs, strength_exercises, strength_sets, exercise_configs, strength_daily_summary
### 기록·히스토리 (`src/pages/History.tsx`, 경로: `/history`)
- **3-탭 히스토리** — `?tab=body|running|strength` URL 동기화. 기본 탭은 러닝
- **4-뷰 모드** — 달력 / 큰 썸네일 / **주간보기(러닝 전용, 2026-05-30 신설)** / 한 줄 행 (`localStorage("history_view_mode")` 영속). 주간보기는 큰썸네일과 한줄자세히 사이, 대시보드와 동일한 주간 마일리지 집계표를 공용 렌더
- **달력 셀 직접 표시** — 체성분 탭: 체중·골격근·체지방·BMR / 러닝 탭: 거리·런타입·페이스·심박
- **상세·편집·삭제** — 인라인 편집 + ConfirmDialog/NotifyDialog/TypedConfirmDialog
- **다중 선택 일괄 삭제** — 우클릭/롱탭 컨텍스트 메뉴
- **검색·정렬·기간** — 키워드 검색(드롭다운 추천), 날짜/체중/거리 정렬, DateRangePicker
- **러닝/근력 수정 시각 필드 보완** — 2026-05-23 본 세션: AM/PM·시·분 분리 입력 정밀화
- **History 강도운동 로드 최적화** — 2026-05-23 본 세션: strength_daily_summary 뷰 기반 batch 조회로 페이지 진입 시 N+1 패턴 회피
- **저장 액션** — 근력 운동 행을 저장된 운동/루틴(app_settings JSON)으로 등록
- **요약 브리프 통합** — SummaryBrief 컴포넌트
- **Supabase** — body_records, running_logs, strength_logs, strength_exercises, strength_sets, strength_daily_summary, shoe_configs, run_type_configs, exercise_configs, app_settings
### 코치노트 (`src/pages/CoachNotes.tsx`, 경로: `/coach`)
- **데이터 기반 AI 브리프** — 최근 2주 데이터 + 개인 메모 + 이전 브리프를 컨텍스트로 Claude API(CF Workers 프록시) 호출 → 6-섹션 prose
- **시각화 카드** — 내일 훈련 / 영양 / 메뉴 / 주간 평가 / 코치 한마디 (brief_visual_cache JSON 캐싱)
- **수동 노트** — brief/review/milestone 카테고리별 색상 라벨
- **AI 브리프 지침 연동** — 설정 > 계정 설정의 지침 4항목(러닝 철학/부상/식이/기타)을 프롬프트에 주입
- **E-6 Markdown 렌더링** — react-markdown + DOMPurify로 h2 헤더·paragraph·코드블록 분리
- **모델** — `claude-haiku-4-5`(`claude.ts`) / `claude-sonnet-4`(`briefApi.ts`)
### 기록 입력 (`src/pages/LogEntry.tsx`, 경로: `/log`)
- **3-탭 입력 폼** — 체성분 / 러닝 / 근력, `?tab=...`로 초기 진입 탭 결정
- **체성분** — 체지방률·BMR 자동 계산(370 + 21.6 × 제지방량), 활성 피트니스 프로젝트 자동 연동, recorded_at upsert
- **러닝** — 페이스(분′초″/km)·속도(km/h) 자동 계산, TimeInput, 신발/런타입 드롭다운
- **근력** — 카테고리 → 종목 2단계 드롭다운, 세트별 중량/추가중량/반복, 카테고리별 볼륨 계산
- **저장된 루틴/운동 불러오기** — app_settings(saved_routines / saved_exercises JSON)
- **데모 모드 차단** — useDemoBlock 훅 → DemoBlockModal
- **(v0.9-rec) PostSaveDialog 도입** — 2026-05-23 본 세션 신규 (`src/components/Modal.tsx:117`)
	- primary: "(체성분/러닝/근력) 계속 기록" — 폼 reset 후 같은 유형 연속 입력
	- secondary: "기록 보기" → `/history?tab={kind}` 이동
	- tertiary: "기록 완료 (닫기)" — PostSaveDialog 닫기만
- **(v0.9-rec) 최근 러닝 메타값 유지** — `localStorage("dallog_last_run_meta_v1")`에 shoe·run_type 저장, 다음 진입/계속 기록 시 디폴트 (수치·날짜·시각은 매번 새로)
- **(v0.9-rec) 최근 근력 구분값 유지** — `localStorage("dallog_last_str_label_v1")`에 마지막 사용 label 저장
- **(v0.9-rec) 러닝/근력 수정 시각 필드 보완** — AM/PM·시·분 분리 입력 컴포넌트 정밀화
- **(v0.9 H-1/H-2) OCR 진입 버튼** — `src/pages/LogEntry.tsx:527` "📷 OCR로 불러오기"로 `OcrImportModal` 오픈
### OCR 자동기록 모달 (`src/components/ocr/OcrImportModal.tsx`, v0.9 H-1/H-2 MVP)
2026-05-23 본 세션 신규. 자세한 흐름은 §"공통 기능 — OCR 자동기록 MVP" 참조.
- **모달 단독 UI** — overlay + 단일 폼 카드 (max-width 560px)
- **상태**: idle → processing → ready / error
- **단계**: 이미지 업로드 → OCR 실행 → OCR 원문 펼치기/접기 → 기록 유형 선택 → 후보 보정 → "({유형}) 폼에 적용"
- **앱 힌트 드롭다운** — 삼성헬스/Garmin/Apple Health/Apple Fitness/Strava/Nike Run Club/기타/선택 없음 (APP_HINT_OPTIONS)
- **OCR 처리** — tesseract.js 7.x (kor+eng)
- **자동 저장 금지** — 사용자가 적용 후 기존 LogEntry 저장 버튼으로 최종 저장
### 근력운동 (`src/pages/StrengthLog.tsx`)
- **F-6e 이후 라우트는 리다이렉트 처리** — App.tsx에서 `/strength-log` 진입 시 `<Navigate to="/log?tab=strength" replace />`. 컴포넌트 자체는 코드베이스에 남아 있으나 진입 경로 없음.
### 설정 (`src/pages/Settings.tsx`, 경로: `/settings`, 서브 경로: `/settings/changelog`)
- **6개 섹션** — 피트니스 목표 / 개인 러닝 / 근력운동 / 개인 메모 / 계정 설정 / 수정 로그
- **F-9 사이드바 통합** — 데스크탑 좌측 aside 제거, 메인 사이드바 '설정' 클릭 시 6 sub-items 아코디언으로 펼침, URL hash 라우팅(`#goals`/`#account`/`#changelog` 등)
- **C-6 수정로그 fallback** — updated_at 쿼리 실패 시 recorded_at 기준 fallback (UI 정상, 콘솔 400은 잔존)
### 프로필 (`src/pages/Profile.tsx`, 경로: `/profile`, I-0)
- **더미 페이지** — "프로필 기능은 준비 중입니다" 안내
- **사이드바·모바일 하단 4탭 진입점 노출**
- **본 개발은 v0.9 필수 충족기준 "작업 7. 프로필 폼 개발"에서 진행**
### 로그인 (`src/pages/LoginPage.tsx`)
- **데스크탑 split / 모바일 stack 레이아웃**
- **마스터 ID/PW 로그인 폼** — MasterLoginForm → signInMaster → 로컬 세션 저장
- **Gmail OAuth 버튼** — signInWithGoogle → Supabase OAuth → `/auth/callback`
- **(v0.9-block) C-7 재진입 가드** — 세션 존재 상태에서 `/login` 진입 시 `/dashboard`로 즉시 리다이렉트 (App.tsx 라우트 레벨 `<Navigate to="/dashboard" replace />`)
## 공통 기능
### App.tsx — 인증 가드 & 라우터 구조 (2026-05-23 본 세션 개선)
```javascript
SplashScreen (1.2s)
  ↓
/auth/callback 진입?
  → Yes: AuthCallbackPage 직행
  → No:
    IS_DEMO?
      → Yes: AppRouter 직행 (인증 우회)
      → No:
        loading → "로딩 중..."
        isLoggedIn?
          → Yes: AppRouter (Layout + 자식 라우트)
          → No: GuestRouter  ← v0.9 신규 (이전: LoginPage 직접 렌더)
```
- **GuestRouter (신규)** — `/login`은 `LoginPage`, 그 외 모든 경로는 `/login`으로 정규화
- **AppRouter — 안전 폴백 라우트** — `<Route path="*" element={<Navigate to="/dashboard" replace />} />`로 알 수 없는 경로 빈 root 방지
- **AppRouter — /login 가드** — `<Route path="/login" element={<Navigate to="/dashboard" replace />} />` (C-7)
- **AppRouter — /strength-log 리다이렉트** — `<Route path="strength-log" element={<Navigate to="/log?tab=strength" replace />} />` (F-6e)
### 마스터 계정 로그인 (`lib/auth.ts`)
- `mster` + 고정 비밀번호 → `localStorage("dallog.masterSession") = "1"` + 커스텀 이벤트
- Supabase 비의존 로컬 세션. useAuth가 Supabase 세션과 OR로 통합
- 가상 사용자 `master@dallog.internal`
### Google OAuth
- `supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: '/auth/callback' } })`
- AuthCallbackPage가 세션 확정 후 메인 이동
- 헤더/사이드바 'AI에게 보내기' 버튼은 소유주 계정 일치 시 노출
### 데모 잠금 시스템 (`lib/demo.ts`, `hooks/useDemoBlock.ts`)
- `VITE_IS_DEMO === 'true'` 단일 플래그
- 데모 모드: App.tsx가 인증 가드 우회, LogEntry submit 가로채 DemoBlockModal, Dashboard AISendButton 노출
### AI에게 보내기 / AI 브리프 (`lib/briefApi.ts`, `lib/claude.ts`, `lib/aiHandoff.ts`)
- buildAIHandoffMarkdown() — 30일 체성분/러닝/근력 + 최근 브리프 + 개인 메모 + AI 지침을 한 장 마크다운으로 직렬화
- buildLatestBriefMarkdown() — 최근 코칭 브리프 1건
- callClaude(prompt, maxTokens) — CF Workers 프록시(`dallog-brief-proxy.ccy4848.workers.dev`) 경유 Anthropic Messages API
### OCR 자동기록 MVP (v0.9 H-1/H-2, 2026-05-23 본 세션 신규)
**모듈 5종**:
- `src/lib/ocr/types.ts` — OcrSourceApp / OcrRecordType / OcrConfidence / OcrCandidate\<T\> / OcrNormalizedCandidates / OcrParseResult / APP_HINT_OPTIONS / emptyCandidates()
- `src/lib/ocr/extractText.ts` — `extractTextFromImage(file, options)` — tesseract.js 7.x 기반 브라우저 OCR (kor+eng), 진행률 콜백
- `src/lib/ocr/normalize.ts` — normalizeDate / normalizeDistanceKm / normalizeDurationSec / normalizePaceSecPerKm / extractInt / extractFloat / computePaceFromDistanceDuration / isPaceMismatch / fatKgFromPct
- `src/lib/ocr/parseCandidates.ts` — `parseOcrCandidates(rawText, sourceAppHint)` — 키워드 근처 탐색 + 정규식으로 공통 후보 추출, `detectSourceApp` 보조, `inferRecordType` 유형 추론
- `src/components/ocr/OcrImportModal.tsx` — UI 모달
**후보 필드 (OcrNormalizedCandidates)**:
- 공통: detectedType, recorded_at, workout_type, note
- 러닝: distance_km, duration_sec, pace_sec_per_km, avg_bpm, max_bpm, calories
- 체성분: weight_kg, fat_pct, muscle_kg, bmr
- 근력: routine_name
- 기타: project_name
**OcrCandidate\<T\> 구조**: `{ value, rawText?, confidence: 'high'|'medium'|'low', needsReview, reason? }`
**UI 흐름**:
1. 이미지 업로드 + 앱 힌트(선택)
2. OCR 실행 → tesseract.js (logger로 진행률 표시)
3. parseOcrCandidates로 공통 후보 추출
4. OCR 원문 펼치기/접기 (lines.length 표기)
5. 기록 유형 선택 (자동 추정 유형 표시 + 수동 변경)
6. 후보 보정 — needsReview 항목에는 `● 확인 필요` 마크 + reason tooltip
7. "(유형) 폼에 적용" → onApplyBody/onApplyRunning/onApplyStrength 콜백으로 LogEntry 폼 상태 주입
8. 모달 닫고 기존 LogEntry submit 흐름으로 사용자가 최종 저장
**원칙**:
- 자동 저장 금지 (사용자 확인 없이 DB 쓰기 금지)
- 이미지 원본은 호출 측 상태에서만 사용, 외부/서버 미전송
- 기존 저장 버튼 / ConfirmDialog / useDemoBlock / PostSaveDialog 흐름 그대로 재사용 (별도 저장 버튼 신설 없음)
- 근력 자동 종목/세트/반복/중량 구성은 v0.9.1 보류 — 루틴명/날짜만 주입
## 데이터 흐름
### Supabase 연결 구조 (`lib/supabase.ts`)
- `createClient(url, anonKey)` 단일 모듈 export
- 환경변수: `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY`
- 주요 테이블/뷰:
	- body_records (project_id 외래연결)
	- running_logs
	- coach_notes (brief/review/milestone)
	- shoe_configs, run_type_configs, exercise_configs
	- strength_logs → strength_exercises → strength_sets (3단 정규화)
	- **strength_daily_summary** (F-8 서버 사이드 집계 뷰)
	- app_settings (key/value JSON — saved_routines/saved_exercises/fitness_projects/personal_memo/ai_brief_instructions/brief_visual_cache 등)
### 인증 흐름 (위 §"App.tsx — 인증 가드 & 라우터 구조" 참조)
### CF Workers 프록시 흐름
```javascript
브라우저 (lib/claude.ts | lib/briefApi.ts)
  ↓ POST { model, max_tokens, messages }
CF Workers (dallog-brief-proxy.ccy4848.workers.dev)
  ↓ Anthropic API Key 주입 + CORS 헤더
Anthropic Messages API
  ↑ response
프록시 → 브라우저
```
- `import.meta.env.PROD` 분기로 dev 환경에서는 `http://localhost:8787` 사용
- 워커 소스는 `workers/` 디렉토리, 배포는 `wrangler.toml`
## 컴포넌트 의존 관계 (2026-05-23 갱신)
```javascript
App
├─ SplashScreen
├─ AuthCallbackPage (단독, /auth/callback)
├─ GuestRouter  ← 2026.05.23 신규
│  └─ LoginPage
│     └─ MasterLoginForm
└─ AppRouter
   └─ Layout (Outlet)
      ├─ Dashboard
      │  ├─ BodySection / RunningSection / StrengthSection
      │  └─ AISendButton (데모 한정)
      ├─ LogEntry
      │  ├─ TimeInput (내부)
      │  ├─ ConfirmDialog / NotifyDialog (Modal)
      │  ├─ PostSaveDialog (Modal) ← 2026.05.23 신규
      │  ├─ DemoBlockModal
      │  └─ OcrImportModal ← 2026.05.23 신규
      ├─ History
      │  ├─ SummaryBrief
      │  ├─ ConfirmDialog / NotifyDialog / TypedConfirmDialog
      │  └─ DateRangePicker
      ├─ CoachNotes (자체 시각화 + callClaude + MarkdownBriefRenderer)
      ├─ Profile (I-0 더미)
      ├─ Settings
      │  ├─ ColorPicker (내부)
      │  ├─ ConfirmDialog / NotifyDialog / TypedConfirmDialog
      │  └─ ChangeLog (서브 라우트)
      └─ Settings/ChangeLog
```
추가 공통 의존:
- `lib/auth.ts` / `lib/supabase.ts` / `lib/claude.ts` / `lib/briefApi.ts` / `lib/aiHandoff.ts` / `lib/demo.ts` / `lib/theme.ts`
- `hooks/useAuth.ts` / `hooks/useDemoBlock.ts`
- **`lib/ocr/*` (신규)** — types / extractText / normalize / parseCandidates
## v0.9 필수 예정 기능 (구현 예정 — v0.9 이후 로드맵 아님)
> ⚠️ (yellow_bg 콜아웃)
> 아래 항목은 **v0.9 클로즈베타 발행 전 필수 충족기준**이다. 일부는 현재 미구현 또는 구현 예정 상태지만, **v0.9 이후 로드맵으로 분리되는 것이 아니다**. 자세한 작업순번은 [달록 v0.9(클로즈베타버전) 완성 충족기준 §5](#/doc/spec-01) 참조.

| 분류 | 항목 | 작업번호 |
|---|---|---|
| 데이터·보안 | 기존 개발자 개인계정 데이터 귀속 정리 | 1 |
| 데이터·보안 | 타 사용자 데이터 분리관리 및 보안관리 (user_id·RLS·역할 분리) | 2 |
| 환경 | 테스트버전 분리 (도메인·staging/production·CF Pages·Supabase) | 3 |
| 로그인 | E-mail 컨펌 / 카카오 OAuth / 네이버 OAuth / 역할 정리 | 4 |
| 운영 | CS대책 및 보안대책 수립 (개인정보처리동의·약관·관리자 접근 범위) | 5 |
| 인증 | 개인정보 인증 필요성 검토 및 연동 대책 (SMS·모바일신분증·민간인증서) | 6 |
| 소셜 | 프로필 폼 개발 (공개범위·프로필 사진·자기소개·팔로우·드래그 배치) | 7 |
| 소셜 | 타임라인 코멘트 기능 (한 줄 코멘트 + 단일/범위/단위/키워드/프로젝트 첨부) | 8 |
| 소셜 | SNS탭 신설 (팔로잉/친구 피드, 실시간/준실시간 최신화) | 9 |

## 운영 규칙
기능설명서 최신화 흐름:
```javascript
Claude.ai 업데이트 필요 판단
  → 사용자 승인
  → Claude.ai 프롬프트 산출
  → 사용자가 Claude Code에 전달
  → Claude Code 수행
```
Claude Code는 임의로 자동 업데이트하지 않는다.
본문 교체 시 보관 규칙: 기존 본문은 하위 페이지 `기능설명서_yymmdd_n` 형식으로 보관 후 교체.
## 이전버전 (보관)
- 📄 [기능설명서_260523_1](#/doc/archive-guide-03) — versions/기능설명서_v_260523_1.md
- 📄 [기능설명서_260530_1](#/doc/archive-guide-04) — 2026-05-30 주간 마일리지 반영 직전 본문 스냅샷 — versions/기능설명서_v_260530_1.md
---
## 갱신 부록 A — 셀 입력 모드 + 후속 기능 (2026-05-26)
본 부록은 2026-05-26 본 컨텍스트에서 추가·확정된 기능을 본문 §"기록·히스토리" / §"공통 기능" / §"컴포넌트 의존 관계" 항목에 합쳐서 읽기 위한 보강 섹션. 본문 교체 없이 부록 형태로 추가.
### A-1. 셀 입력 모드 (History `/history` 한줄 자세히 진화)
`history_view_mode = 'row'` (한줄 자세히)가 단순 카드 리스트에서 **구글시트 스타일 셀 입력 모드**로 진화. 체성분·러닝 탭만 적용(근력은 Phase 5 세컨드 페이즈 보류).
**커밋 시퀀스:** `c6b6090` (Phase 1~4 본구현) → `a453a0c` (1차 fix 9건) → `78b027b` (2차 fix + 추가 3건) → `737c460` → `9a14d3f` (3·4차 fix) → `a772bb5` (컬럼명 조정)
**컴포넌트 구조 (`src/components/cellmode/`)**:
- `CellModeBody.tsx` — 체성분 8컬럼 (날짜·프로젝트·체중·골격근·체지방량·체지방률·BMR·메모)
- `CellModeRunning.tsx` — 러닝 14컬럼 (날짜·시간·거리·런타입·루틴·신발·소요시간·페이스·속도·평균심박·최대심박·케이던스·칼로리·거리반영)
- `CellModePagination.tsx` — 10/30/50 페이지네이션 (Settings 프로젝트 펼침 패턴 차용)
- `useColumnConfig.ts` — 컬럼 토글·순서 hook (localStorage 영속 `cellmode_columns_body_v1` / `cellmode_columns_running_v1`)
- `ColumnConfigDropdown.tsx` — 컬럼 표시·순서 popover (fixed center overlay)
- `useColumnFilters.ts` — 컬럼별 카테고리 필터 hook (활성 필터 AND 결합)
- `ColumnFilterPopover.tsx` — 필터 popover (검색·전체선택/해제·체크박스·적용/해제)
**기능:**
- **페이지네이션** — 10/30/50행 + 페이지 이동, 정렬·검색·기간 필터와 결합
- **컬럼 커스텀** — 토글(체크박스) + 순서 변경(↑↓ 화살표), localStorage 영속
- **컬럼 필터 (MS 엑셀 패턴)** — filterable 컬럼: 체성분(프로젝트), 러닝(런타입·루틴·신발·거리반영)
- **수정 모드** — 행별 체크박스 + 페이지 전체선택, 인라인 셀 편집(input/select)
- **모두저장 / 취소 / + 새 행 / 선택 삭제** — 한 액션바에 통합
- **자동계산 유지** — 체지방률(`fat_kg/weight_kg*100`) · BMR(`370+21.6*(weight-fat)`) · 페이스(`duration/distance`) · 속도(`distance/duration*3600`)
- **자세히 모달 클릭 복원** — 수정 모드 비활성 시 행 클릭 → setDetailBody/setDetailRun (기존 모달 재사용)
- **단위 표시** — 보기 모드 셀에 "110.8 kg" / "138 bpm" / "31.1 %" / "2019 kcal" / "5.01 km" 형식
- **자동계산 셀 색상** — `#d4e8a0` 연 라임 (가독성 ↑)
- **삭제 강화** — ConfirmDialog + NotifyDialog (성공/실패 모달 강제), `supabase.delete({ count: 'exact' })` 명시 검증, 실패 시 RLS 안내 메시지
**저장 흐름:**
- 체성분: `body_records.upsert(payload, { onConflict: 'recorded_at' })`
- 러닝: 신규는 `insert`, 수정은 `update(...).eq('id', id)`. `routine_id` 컬럼 저장 활성화(마이그레이션 완료)
**정렬 보강 (이슈 6b):**
- History.tsx `filteredRuns` sort에 `runTimeKey(r) = (period==='PM'?12:0)*60 + hour%12*60 + minute` 보조키 추가
- 같은 날 더블런: date_desc면 AM이 PM보다 위, date_asc면 반대
### A-2. 러닝 루틴(프로젝트) 기능 신설 (`app_settings.run_routines` JSON)
`fitness_projects`와 동일 패턴의 사용자 정의 루틴 — `src/lib/supabase.ts`에 `RunRoutine` 타입 신설. Settings § "개인 러닝 설정" 에 CRUD UI 추가.
**필드:** id / name / goal / description / default_run_type / mileage_plan (period+target_km, MVP UI 미노출) / default_shoes (배열, MVP UI 미노출) / period_start / period_end / is_active / created_at / updated_at
**마이그레이션 동반:** `running_logs.routine_id` 컬럼 추가 (`migrations/2026-05-26_running_logs_routine_id.sql` — 사용자 적용 완료)
### A-3. 신발별 누적 마일리지 (대시보드 + Settings 양쪽)
- **대시보드 RUNNING 섹션 (`RunningSection.tsx`):** 가로 막대(indexAxis: 'y') Bar 차트 신설. `is_record !== false`인 기록만 합산. 신발 수 비례 동적 높이.
- **Settings 개인 러닝 신발 목록 (`Settings.tsx`):** 각 신발 카드에 "신발명 · N.N km" 병기. 새 신발은 0.0 km 명시. fetchAll에 `running_logs.select('shoe,distance_km,is_record')` 추가.
### A-4. 컬럼명·단위 조정 (`a772bb5`)
| 컬럼 ID | 이전 라벨 | 새 라벨 | 단위 |
|---|---|---|---|
| `avg_bpm` | 평심 | **평균심박** | **bpm** |
| `max_bpm` | 최심 | **최대심박** | **bpm** |
| `is_record` | 마일리지 | **거리반영** | — |

- 셀 값에 자동 단위 표기 ("138 bpm" / "158 bpm")
- 헤더에 자동 단위 표기 ("평균심박 (bpm)")
- `is_record` 필터 옵션: '포함'/'제외'
### A-5. History.tsx 통합 변경
- `import { CellModeBody, CellModeRunning } from '../components/cellmode/...'`
- `viewMode === 'row'` 분기를 셀 컴포넌트로 교체 (체성분·러닝). 근력은 기존 카드 유지(Phase 5 보류).
- `runRoutines` state 추가 + `fetchSavedTemplates`에 `run_routines` app_settings 로드 추가
- `<CellModeBody ... onDetailOpen={setDetailBody} />`, `<CellModeRunning ... onDetailOpen={setDetailRun} />` props 전달
### A-6. v0.9 충족기준 신규 등재
본 페이지 §"v0.9 필수 예정 기능"에 작업 10 추가 (v0.9 충족기준 §8 작업 10 본문 참조):
- **작업 10. 셀기능 추가 (Phase 1~4 완료, Phase 5 세컨드 페이즈)** — 체성분·러닝 셀 모드 완료. 근력 Phase 5는 옵션 B(1행=1세트, 세션 stripe) 추천안 인계.
### A-7. v0.9 작업 2 (멀티유저 보안) RLS 검증 항목 신규 추가
본 세션에서 발견된 OAuth 계정 런타입 차단 + 셀 모드 삭제 차단의 공통 원인 의심 — Supabase RLS 정책. v0.9 작업 2 수행 시 다음 점검 필수 (체크리스트 4중 리마인드 등재됨):
- `run_type_configs` / `shoe_configs` / `exercise_configs` / `app_settings` RLS 진단
- `body_records` / `running_logs` / `strength_logs` DELETE 정책 진단
- master 계정 vs OAuth 계정 권한 동일성
### A-8. 관련 작업보고
- [260526-월요일_셀기능 추가 Phase 1~4 구현](#/doc/workreport-61)
- [260526-월요일_셀기능 시각검증 후속 fix](#/doc/workreport-62)
- [260526-월요일_셀기능 시각검증 2차 fix + 컬럼 필터 + 신발 마일리지](#/doc/workreport-63)
- [260526-월요일_셀기능 시각검증 3·4차 fix + 컬럼명 조정](#/doc/workreport-64)

- 📄 [기능설명서_260530_1](#/doc/archive-guide-04) (이전버전 보관 — 본문 §"이전버전" 참조)
---
## 갱신 부록 B — 주간 마일리지 집계 + 주간보기 (2026-05-30)
본 부록은 2026-05-30 추가된 주간 마일리지 기능을 §"대시보드" / §"기록·히스토리" 항목에 합쳐 읽기 위한 보강. (PR #17, main `d51d7f4`)

### B-1. 대시보드 RUNNING — 주간 마일리지 표 신설 + 레이아웃 재배치
- RUNNING 탭 최하단에 주간 마일리지 집계 표 신설. 11컬럼: 주간·누적마일리지·러닝횟수·전주대비·평균시간·평균페이스·평균속도·케이던스·평균심박·칼로리/주·칼로리/km
- 행 수 5/10/30 + 페이지네이션(CellModePagination 재사용) + 현재주 강조(좌측 인디케이터·라임 배경·'현재' 배지)
- 섹션 재배치: 상단 통계표 → 심박·케이던스·페이스 추이(30회) → 신발별 누적 → 월별(3개월) → 주간(신규). 기존 그래프·데이터 보존
- 컴포넌트: `src/components/WeeklyMileageTable.tsx`(공용) + `src/components/dashboard/RunningSection.tsx`

### B-2. 기록>러닝 — 주간보기 모드 신설
- History 보기 모드 3-뷰 → 4-뷰: 달력 / 큰썸네일 / **주간보기** / 한줄 자세히
- 주간보기는 러닝 탭 전용(체성분·근력 미노출). 큰썸네일과 한줄자세히 사이
- 대시보드와 동일 공용 WeeklyMileageTable 재사용. `filteredRuns`(검색·기간 필터 반영) 전달
- `ViewMode = 'calendar' | 'large' | 'weekly' | 'row'`

### B-3. 공용 주간 집계 로직 (`src/lib/weeklyMileage.ts`)
- 주차 기준(E단계 사장님 명시): 주 시작일(월)이 속한 연도 · 그 해 첫 월요일=1주차. 연말 경계는 본문 데이터 기준(현재주 26-21주차 일치)
- 집계: 누적마일리지(주 합계)·러닝횟수(건수)·평균시간(총시간/횟수)·평균페이스·속도(가중)·케이던스·심박(단순평균)·칼로리/주(합)·칼로리/km
- is_record=false 제외(null 포함). date-fns/dayjs 미사용 순수 Date

### B-4. 관련 작업보고
- 260530_주간 마일리지 집계표 + 기록 러닝 주간보기 신설
---
## 갱신 부록 C — SNS 운동기록 첨부 + 엑셀 편집기 러닝·근력 (2026-05-30)
본 부록은 같은 날 후속 작업(PR #18, main `27eda24`)을 §"기록 입력/SNS" / §"기록·히스토리"에 합쳐 읽기 위한 보강. (오늘 시작 전 원본은 부록 B 작성 시 만든 `_260530_1` 보관본에 보존됨 — 동일자 연속 작업으로 중복 보관본 미생성)

### C-1. SNS 게시 — 운동 기록 첨부 (`SocialFeedPage.tsx` · `social.ts`)
- 게시 작성 모달(PostComposer)에 운동 기록 첨부 신설: 유형 선택(러닝/체성분/근력) → 본인 최근 기록 목록 → 선택 → 미리보기·첨부취소
- `social.ts`: `getRunningCandidates`·`getBodyCandidates`·`getStrengthCandidates`(근력은 exercises·sets 조인해 세트·볼륨 집계). 선택 기록은 snapshot(값 복사·불변)으로 `createPost`에 전달
- DB/스키마 변경 없음 — posts.attached_type/attached_id/attached_snapshot 컬럼·buildSnapshot 유틸·AttachmentPreview 기존 자산 활용

### C-2. 엑셀 편집기 — 러닝·근력 탭 (`pages/HistoryExcel.tsx` · `components/excel/*`)
- **ExcelGrid**(신규 범용 엔진): config 주입형 컬럼·키보드 네비·셀 편집·다중선택·저장·자동계산 컬럼·readOnly·allowAdd. 체성분 기존 코드는 미변경(회귀 방지)
- **러닝 탭**: running_logs 단일 테이블. 날짜·거리·런타입·신발·소요시간(M:SS)·평균/최대심박·케이던스·칼로리·거리반영·메모 + 페이스·속도 자동계산. 신발·런타입 동적 select
- **근력 탭**: strength_logs+exercises+sets 3-테이블 평탄화(1행=1세트). 세션 구조(날짜·구분·운동·세트#) 읽기전용 + 세트 수치(반복·중량·추가중량·추가사용) 편집·삭제 + 볼륨 자동계산. 새 세션·운동 추가는 셀모드/입력폼(allowAdd=false)
