---
title: "📝 260523-토요일_기록UX-입력피로도개선"
notion_id: "36922962086881adbc90cdffeabcb157"
notion_url: "https://app.notion.com/p/36922962086881adbc90cdffeabcb157"
category: "workreport"
parent: "Claude Code 작업보고"
updated: "2026-05-23"
priority: "Medium"
purpose: "v0.9 전 기록 UX·로드속도 개선 — 연속 기록 액션·필드 통일·최근값 유지·History fetch batch"
---

## 📋 작업 요약
- **목적:** 달록 v0.9 클로즈베타 전 기록 UX 및 기록 로드 체감속도 개선
- **범위:** 입력 피로도 감소 + 반복 기록 사용성 (신규 대형 기능 추가 아님)
- **수정 파일 (3):** `src/components/Modal.tsx` · `src/pages/History.tsx` · `src/pages/LogEntry.tsx`
- **제약 준수:** Layout.tsx · auth/router/session · Supabase schema · AI 브리프 구조 모두 미관여
- **빌드:** `npm run build` 성공 (tsc 0 error · vite 2.45s)
---
## 🗣️ 사용자 프롬프트 1 (작업 지시)
```javascript
※ 주의: 동일 작업을 수행 중인 다른 에이전트가 있을 수 있음.
작업 시작 전 git status로 작업트리 상태를 확인한다.
미커밋 변경사항이 있으면 작업을 중단하고 보고한다.

원격 최신 여부 확인은 git fetch로만 수행한다.
git pull은 임의 실행하지 않는다.
단, 사용자가 명시적으로 최신 원격 기준 동기화를 지시한 경우에만,
작업트리가 깨끗한 상태에서 git pull --ff-only를 실행한다.
충돌 또는 merge 필요 상황이면 즉시 중단하고 보고한다.

작업 목표:
달록 v0.9 클로즈베타 전 기록 UX와 기록 로드 체감속도를 개선한다.
신규 대형 기능 추가가 아니라 "입력 피로도 감소"와 "반복 기록 사용성" 개선이 목적이다.

우선 작업 항목:
1. 연속 기록 액션 추가 ("기록 완료" / "같은 유형 계속 기록" / "히스토리 보기")
2. 기록/수정 UX 필드 불일치 점검 및 최소 통일 (시간·루틴명·프로젝트명, DB 스키마 변경 금지)
3. 최근값/최근선택 유지 (최근 프로젝트·루틴명·입력값)
4. 기록 로드속도 점검 (re-render·과도한 fetch·summary·차트)
5. 최소 최적화 적용 (memo/useMemo/lazy, 과도하지 않은 선)

주의사항:
- auth/router/session 관련 수정 금지
- OCR/H파트 구현 금지
- 신규 상태관리 라이브러리 도입 금지
- Supabase schema 변경 금지
- 디자인 전면 개편 금지
- AI 브리프 구조 수정 금지
- 대규모 리팩토링 금지

오늘 목표: "기록앱으로 쓸 때 덜 귀찮게 만들기"

완료 기준:
1. npm run build 성공
2. 연속 기록 흐름 개선 완료
3. 기록/수정 UX 필드 차이 최소화
4. 기록탭 체감속도 개선 사항 적용
5. git diff --name-only 출력
6. 수정 파일만 git add <경로>
```
---
## 🤖 응답 1 — 미커밋 변경사항 감지 후 중단 보고
워킹트리에서 `src/components/Layout.tsx`의 v0.9-stab UI polish 변경(20+/15− 라인)을 발견했고, 헤더 주석에 `v0.9-stab(2026.05.23): 데스크탑 main 하단 padding 110→40px, 모바일 헤더 압축, 모바일 하단탭 아이콘 균형 조정`이라 표기되어 **오늘 진행 중인 다른 세션의 결과물**일 가능성이 높다고 판단.
태스크 스펙("미커밋 변경사항이 있으면 작업을 중단하고 보고한다")에 따라 진행을 중단하고 4개 옵션을 제시:
1. 기존 변경 먼저 커밋 (안전, 권장)
2. 기존 변경 stash
3. 다른 세션 종료 확인 후 진행 (Layout.tsx 비건드림)
4. 그대로 진행 (위험 인지)
원격 fetch 완료 (`main` ↔ `origin/main` 동기화 상태 확인, pull 미실행).
---
## 🗣️ 사용자 프롬프트 2 (옵션 3 선택)
```javascript
3번으로 진행합니다.

다른 세션 2개 작업이 종료되었고, 기존 변경사항은 커밋/푸쉬까지 완료된 것을 확인했습니다.
현재 작업은 Layout.tsx를 수정하지 않는 조건으로 진행하세요.

진행 전 다시 git status를 확인하고,
미커밋 변경사항이 남아 있으면 어떤 파일인지 먼저 보고하세요.

이번 작업 범위는 기록 UX / 입력 피로도 개선입니다.
Layout.tsx, auth/session/router 관련 파일은 수정하지 마세요.

기록 관련 컴포넌트와 기록/수정 모달, 기록탭 데이터 로드 관련 파일만 대상으로 진행하세요.

만약 기록 UX 개선에 Layout.tsx 수정이 반드시 필요하다고 판단되면,
수정하지 말고 중단 후 사유와 필요한 변경 내용을 보고하세요.
```
---
## 🤖 응답 2 — 구현 및 완료 보고
### 재확인 결과
워킹트리 깨끗 — Layout.tsx 변경은 `fb10bea: stabilize v0.9 routing and dashboard UI`로 커밋 완료. 추적되지 않은 docs/스크린샷만 남음 (작업과 무관).
### 분석 — 느렸던 원인
<table header-row="true">
<tr>
<td>순위</td>
<td>위치</td>
<td>원인</td>
<td>영향</td>
</tr>
<tr>
<td>🔥 #1</td>
<td>History.tsx fetchData()</td>
<td>강도운동 120건 × (1 exercises 쿼리 + N sets 쿼리) **시퀀셜 직렬**</td>
<td>평균 1,000+ 라운드트립 → 수초 블락. **최대 병목.**</td>
</tr>
<tr>
<td>#2</td>
<td>History.tsx `loading=true` 게이트</td>
<td>강도운동 fetch 완료 전 전체 페이지 블락</td>
<td>체성분/러닝까지 같이 못 봄</td>
</tr>
<tr>
<td>부수</td>
<td>SummaryBrief.tsx</td>
<td>이미 `.in()` batch 처리됨 — **참고 모델로 활용**</td>
<td>영향 없음</td>
</tr>
<tr>
<td>부수</td>
<td>차트(chart.js)</td>
<td>1MB+ chunk이지만 데이터 도착 후 렌더</td>
<td>본 작업 범위 밖</td>
</tr>
</table>
### 적용한 UX 개선
**A. 연속 기록 액션** — LogEntry.tsx + Modal.tsx
- 신규 `PostSaveDialog` 컴포넌트 (Modal.tsx에 추가)
- 저장 성공 시 자동 `/history` 이동 → **3개 선택지 다이얼로그**:
	1. **같은 유형 계속 기록** (Primary, 라임) → 폼 리셋, 페이지 유지
	2. **히스토리 보기** → `/history?tab=X` 이동 (기존 동작 보존)
	3. **기록 완료 (닫기)** → 폼 리셋 + 페이지 유지
- 체성분 폼이 저장 후 리셋되지 않던 버그 함께 해결 (러닝/근력은 기존 reset 흐름 있었으나 body 폼 누락)
**B. 기록/수정 UX 필드 통일** — History.tsx
<table header-row="true">
<tr>
<td>필드</td>
<td>생성</td>
<td>수정(전)</td>
<td>수정(후)</td>
</tr>
<tr>
<td>러닝 — 달린 시각 (`run_time_period/hour/minute`)</td>
<td>✓</td>
<td>✗</td>
<td>✓ 추가</td>
</tr>
<tr>
<td>근력 — 운동 시각 (`workout_time_period/hour/minute`)</td>
<td>✓</td>
<td>✗</td>
<td>✓ 추가</td>
</tr>
<tr>
<td>체성분 — 프로젝트</td>
<td>✓</td>
<td>dropdown(상세뷰)</td>
<td>유지</td>
</tr>
</table>
DB 컬럼은 이미 존재 — 스키마 변경 없이 폼 UI만 추가. 시각 입력은 생성폼과 동일 패턴(오전/오후 + HH + MM).
**C. 최근값 유지** — LogEntry.tsx
- localStorage 키: `dallog_last_run_meta_v1` (shoe/run_type), `dallog_last_str_label_v1` (구분)
- 저장 성공 시 자동 보관 → 재진입·"계속 기록" 시 디폴트로 자동 채움
- **수치/날짜/시각은 매번 새로 입력** (의도적 — 잘못된 자동복원 방지)
- 체성분 프로젝트는 이미 활성 프로젝트 자동 선택 로직 존재
### 적용한 최적화
- 🚀 **History.tsx fetchData N+M 직렬 → 3-쿼리 batch** (SummaryBrief 패턴 차용)
	- 변경 전: `for (log of 120) { exQuery(); for (ex) { setQuery() } }` ≈ 1,000+ 라운드트립
	- 변경 후: `logs.in() → exs.in(logIds) → sets.in(exIds)` = **3 라운드트립**
- React.lazy/메모/구조 리팩토링은 **의도적으로 미적용** ("과도하지 않은 선에서" 지시 준수)
### 남은 UX 문제 (v0.9 배포 차단 여부)
<table header-row="true">
<tr>
<td>항목</td>
<td>차단 여부</td>
<td>비고</td>
</tr>
<tr>
<td>차트 chunk 분리 (1MB+ 경고)</td>
<td>비차단</td>
<td>초기 painting 영향 없음</td>
</tr>
<tr>
<td>body 수정폼의 프로젝트 미노출</td>
<td>비차단</td>
<td>상세뷰 dropdown에서 변경 가능</td>
</tr>
<tr>
<td>강도 운동 수정 시 cascade delete-reinsert</td>
<td>비차단</td>
<td>단일 사용자 환경에서 실용상 문제 없음</td>
</tr>
<tr>
<td>LogEntry/History의 TimeInput 중복</td>
<td>비차단</td>
<td>추후 공통 컴포넌트화 후보</td>
</tr>
</table>
### 변경 파일 (`git diff --name-only`)
```javascript
src/components/Modal.tsx
src/pages/History.tsx
src/pages/LogEntry.tsx
```
스테이징: `git add src/components/Modal.tsx src/pages/History.tsx src/pages/LogEntry.tsx`
### 완료 기준 체크
- [x] `npm run build` 성공 (tsc 0 에러, vite 2.45s)
- [x] 연속 기록 흐름 개선 완료 (PostSaveDialog 3액션)
- [x] 기록/수정 UX 필드 차이 최소화 (러닝·근력 시각 추가)
- [x] 기록탭 체감속도 개선 (강도운동 fetch 1,000+ → 3 쿼리)
- [x] git diff --name-only 출력
- [x] 수정 파일만 명시적 스테이징
---
## 📐 diff 통계
```javascript
src/components/Modal.tsx  |  47 ++++++++++++++++
src/pages/History.tsx     | 137 ++++++++++++++++++++++++++++++++++++++++++-----
src/pages/LogEntry.tsx    | 105 ++++++++++++++++++++++++++++++------
3 files changed, 258 insertions(+), 31 deletions(-)
```
## 🔧 핵심 코드 변경
### Modal.tsx — PostSaveDialog 신규
```typescript
// 저장 후 후속 액션 — '같은 유형 계속 기록 · 기록 보기 · 완료' — 2026.05.23 v0.9-rec
export function PostSaveDialog({
  open, onClose, title = '저장 완료', message,
  primaryLabel, onPrimary,
  secondaryLabel, onSecondary,
  tertiaryLabel = '닫기', onTertiary,
}: BaseProps & {
  primaryLabel?: string
  onPrimary?: () => void
  secondaryLabel?: string
  onSecondary?: () => void
  tertiaryLabel?: string
  onTertiary?: () => void
}) { /* ... 3-button stacked layout ... */ }
```
### History.tsx — fetchData 강도 운동 batch (핵심 성능)
```typescript
// v0.9-rec(2026.05.23) — N+M 직렬 → 3-쿼리 일괄 batch
const rawLogs = (logs.data || [])
if (rawLogs.length === 0) { setStrengths([]) } else {
  const logIds = rawLogs.map(x => x.id)
  const { data: exData } = await supabase
    .from('strength_exercises').select('*').in('log_id', logIds).order('order_index')
  const exRows = exData || []
  const exIds = exRows.map(x => x.id)
  const { data: setData } = exIds.length > 0
    ? await supabase.from('strength_sets').select('*').in('exercise_id', exIds).order('set_index')
    : { data: [] }
  // O(N) 후처리로 setsByEx, exsByLog 인덱싱 → sessions 조립
}
```
### LogEntry.tsx — 최근값 유지 + PostSave 흐름
```typescript
const LAST_RUN_META_KEY = 'dallog_last_run_meta_v1'
const LAST_STR_LABEL_KEY = 'dallog_last_str_label_v1'
function readLastRunMeta(): { shoe?: string; run_type?: string } {
  try { return JSON.parse(localStorage.getItem(LAST_RUN_META_KEY) || '{}') } catch { return {} }
}

// 저장 성공 시
try {
  localStorage.setItem(LAST_RUN_META_KEY, JSON.stringify({
    shoe: runForm.shoe || '', run_type: runForm.run_type || '',
  }))
} catch { /* localStorage 비활성 환경 무시 */ }
setPostSave({ kind: '러닝', goTab: 'running' })
```
---
## ⏱️ 작업 리드타임
- 시작: 2026-05-23 (작업 지시 수신 시점)
- 완료: 2026-05-23
- 경과: 약 25분 (워킹트리 확인 + 분석 + 구현 + 빌드 검증 + 스테이징)
---
## 🗣️ 사용자 프롬프트 3
```javascript
1
```
노션 업로드 진행 승인 → 본 페이지 생성.
