---
title: "D-4. 근력운동 v2 저장/루틴 + 운동종목 등록 개편"
notion_id: "364229620868815d8686cadbbafee4cb"
notion_url: "https://app.notion.com/p/364229620868815d8686cadbbafee4cb"
category: "workreport"
parent: "Claude Code 작업보고"
updated: "2026-05-16"
priority: "High"
purpose: "근력운동 v2 — 저장된 루틴/운동 기능 신설 및 카테고리 분류·베이직 프리셋·체중부하·볼륨계산 등 운동종목 등록 시스템 전면 개편"
read_when: ["기능파악"]
---

## 📋 스터디 요약
- **파트-섹션**: D-4
- **작업 주제**: 근력운동 v2 — 저장/루틴 기능 신설(작업3-1) + 운동종목 등록 시스템 전면 개편(작업3-2). 카테고리 분류(맨몸/웨이트/머신/아이소메트릭/기타), 베이직 프리셋 30종, 체중 부하율, 맨몸+추가중량 토글, 카테고리별 볼륨 분기 계산, 저장된 루틴/운동 app_settings JSON 저장, 기록행 수정/삭제/저장 액션
- **작업일**: 2026-05-16
- **에이전트**: Claude Code (Opus 4.7 1M, win32 PowerShell)
- **브랜치**: main
### 작업 흐름 요약
사용자가 근력운동 v2 전체 개편(저장/루틴 + 운동종목 등록 시스템)을 위해 파일경로 `D:\dallog\dallog_git`와 함께 작업3-1(저장된 루틴/저장된 운동 개념 분리, 설정-근력운동에 루틴 등록 섹션·기록탭에 저장된 운동 등록·기록 수정/삭제), 작업3-2(카테고리 5종 분류·베이직 프리셋 내장·맨몸 부하율·맨몸+추가중량 토글·볼륨 계산 분기·체중 자동연동)를 한 번에 지시.
→ Claude Code가 코드베이스 탐색으로 `StrengthLog.tsx`가 데드 코드이고 실제 입력은 `LogEntry.tsx` strength 탭, 종목 관리는 `Settings.tsx`임을 확인. 저장된 루틴/운동을 `app_settings` JSON(fitness_projects와 동일 패턴)으로 저장하기로 결정. 사용자에게 수정 예정 파일 목록(migrations 신규 + supabase.ts/Settings.tsx/LogEntry.tsx/History.tsx 수정) 제시 후 확인.
→ 사용자가 "History.tsx에서 근력운동 기록 행 수정/삭제/저장 액션 부분만 작업. 탭 통합·기록하기 버튼(섹션4-A), 달력(섹션4-C) 코드는 건드리지 말 것" 주의사항 추가.
→ 8단계 TodoWrite 등록 후 1) 마이그레이션 SQL 작성(ADD COLUMN IF NOT EXISTS 멱등 + 베이직 30종 INSERT), 2) supabase.ts 타입 확장(ExerciseCategory, ExerciseConfig·StrengthSet 컬럼·SavedRoutine·SavedExerciseTemplate), 3) Settings.tsx 근력운동 섹션 전면 개편(카테고리 필터·베이직 추가 폼·커스텀 등록·저장된 루틴/운동 CRUD), 4) LogEntry.tsx 근력 탭 개편(카테고리별 입력 분기·루틴 적용·저장된 운동 등록·볼륨·체중 자동연동), 5) History.tsx 근력 기록 수정/삭제/루틴 저장/저장된 운동 등록 액션 추가(탭/달력 미수정 준수). vite build 통과(727KB / gzip 210KB).
→ 의미 단위 커밋 4건(b30311d 스키마·타입 / b32c29e Settings / 269cd9b LogEntry / 118e18b History) + `git push origin main` 성공(`deb36fa..118e18b main -> main`). 사장님 액션은 Supabase 콘솔에서 마이그레이션 SQL 수동 실행 1건.
달록의 근력운동 기록 UX(카테고리별 입력·볼륨 자동 산출·저장된 루틴/운동 재사용)를 완전체로 만들기 위해 작업되었으며, 빌드·커밋·푸시까지 모두 정상 완료로 종결.
---
## 📄 원본 대화 전체 (raw)
\<!-- 대화 추출 — 근력운동 v2 (저장/루틴 + 운동종목 등록 개편) — 2026.05.18 추출 --\>
# 04. 근력운동 v2 — 저장/루틴 기능 + 운동종목 등록 시스템 개편
- 작업일: 2026.05.16
- 작업 환경: Claude Code (Opus 4.7 1M, win32 PowerShell)
- 작업 디렉토리: `D:\dallog\dallog_git`
- 결과 커밋 4건 + GitHub push 완료
- 관련 마이그레이션 SQL: `migrations/2026-05-16_strength_v2.sql` (Supabase 콘솔 수동 실행 필요)
---
## 1. 사용자 첫 프롬프트
> 파일 경로: D:dallogdallog_git
>
> 작업 3-1. 근력운동 저장/루틴 기능 신설
>
> 개념 정의 (혼동 금지):
> - 저장된 루틴: 설정탭에서 등록/관리하는 운동 템플릿
> - 저장된 운동: 기록탭에서 저장하는 운동 템플릿
> - 기록(히스토리): 기존 기록 리스트 (변경 없음)
>
> 설정-근력운동:
> ㄴ 기존 운동종목 목록 유지
> ㄴ '루틴 등록' 기능 섹션 추가 개설
> ㄴ 저장된 루틴 등록/수정/삭제/덮어쓰기 → 설정-근력운동에서만 접근
> ㄴ 자동 덮어쓰기 구조 아님 — 유저 명시적 액션 필요
>
> 기록-근력운동:
> ㄴ 저장된 루틴/저장된 운동 드롭다운 또는 검색으로 불러오기
> ㄴ 불러온 템플릿 기반 세트/반복/종목 추가 등 기존 기록 UX 그대로 활용
> ㄴ 저장된 운동 등록: 기록탭 기록섹션에서 가능
> ㄴ 저장된 운동 수정/삭제/덮어쓰기: 설정-근력운동 또는 기록-근력운동 양쪽 접근 가능
> ㄴ 기존 기록(히스토리)에 수정/삭제/저장 기능 추가
>
> 작업 3-2. 운동종목 등록 시스템 전면 개편
> ㄴ 카테고리 분류: 맨몸 / 웨이트 / 머신 / 아이소메트릭 / 기타
> ㄴ 카테고리별 베이직 종목 프리셋 내장:
> 맨몸: 풀업·친업·푸쉬업·다이브봄버·레그레이즈 등
> 웨이트: 벤치프레스·스쿼트·데드리프트 등
> ㄴ 베이직 종목: 드롭다운 또는 자동검색으로 선택 추가
> ㄴ 커스텀 종목: 별도 등록 템플릿으로 추가 가능
> ㄴ 맨몸 종목: 체중 부하율(%) 기본값 내장, 사용자 수정 가능
> ㄴ 맨몸+추가중량 토글 신설 (조끼·디핑벨트 등 대비)
> ㄴ 볼륨 집계 로직 카테고리별 분기:
> 웨이트/머신: 입력중량 × 렙수
> 맨몸: (사용자 체중 × 부하율) × 렙수
> 맨몸+추가중량: (사용자 체중 × 부하율 + 추가중량) × 렙수
> ㄴ 사용자 체중: 인바디 최근 기록 자동 연동, 없으면 수동 입력
>
> 완료 후: 빌드 확인 → 커밋 → 푸시
---
## 2. 어시스턴트 — 코드베이스 초기 탐색
(StrengthLog.tsx, Settings.tsx, History.tsx, LogEntry.tsx, supabase.ts, App.tsx, dallog_[CLAUDE.md](http://CLAUDE.md) 등 다중 파일 Read·Grep 수행)
- `StrengthLog.tsx`는 App.tsx 라우트에 연결되지 않은 데드 코드임을 확인.
- 실제 근력운동 기록 입력은 `LogEntry.tsx`의 strength 탭, 표시·삭제는 `History.tsx`, 종목 관리는 `Settings.tsx`임을 확인.
- DB 스키마는 `exercise_configs`, `strength_logs`, `strength_exercises`, `strength_sets`로 분리 저장.
- 저장된 루틴/운동은 `app_settings` 테이블의 key-value(JSON) 패턴을 활용하기로 결정 (기존 `fitness_projects`와 동일 패턴).
---
## 3. 어시스턴트 — 사용자에게 수정 예정 파일 목록 제시
작업 명세를 분석한 결과, 수정 예정 파일을 다음과 같이 정리했습니다. 진행 전 확인 부탁드립니다.
### 📁 신규 생성
**1. `migrations/2026-05-16_strength_v2.sql`** (신규)
- `exercise_configs` 컬럼 추가: `category` (맨몸/웨이트/머신/아이소메트릭/기타), `bodyweight_ratio` (numeric), `is_basic` (boolean), `sort_order` (int)
- `strength_sets` 컬럼 추가: `weight_kg` (numeric), `additional_weight_kg` (numeric), `use_additional` (boolean)
- 베이직 종목 INSERT (카테고리별 프리셋)
- ⚠️ DB 적용은 Supabase 콘솔에서 사장님이 수동 실행 필요 — SQL 파일만 작성합니다.
### 📁 수정
**2. `src/lib/supabase.ts`**
- `ExerciseConfig` 타입 확장: `category`, `bodyweight_ratio`, `is_basic`, `sort_order` 추가
- `StrengthSet` 타입 확장: `weight_kg`, `additional_weight_kg`, `use_additional` 추가
- `SavedRoutine`, `SavedExercise` 타입 신설 (app_settings JSON 저장 방식)
**3. `src/pages/Settings.tsx`**
- 사이드 메뉴: `exercises` → `근력운동` 섹션으로 확장
- 운동 종목 섹션 전면 개편: 카테고리 분류, 베이직 프리셋, 커스텀 등록, 맨몸 부하율 편집
- 저장된 루틴 섹션 신설: 등록/수정/삭제/덮어쓰기
- 저장된 운동 섹션 신설: 수정/삭제 (등록은 기록탭)
**4. `src/pages/StrengthLog.tsx`**
- 상단에 "저장된 루틴 불러오기" + "저장된 운동 불러오기" 드롭다운/검색 추가
- 종목 행: 카테고리 인식 → 입력 UI 분기 (웨이트/머신·맨몸·맨몸+추가중량)
- 볼륨 자동 계산 표시
- 사용자 체중 자동 연동
- "이 운동을 저장된 운동으로 등록" 버튼
**5. `src/pages/History.tsx`**
- 근력운동 기록 행: 수정/삭제 버튼 추가
- 기록을 "저장된 운동" 또는 "저장된 루틴"으로 저장하는 액션 추가
### 📋 영향만 확인 (수정 없음 예상)
- `src/pages/Dashboard.tsx`, `src/components/SummaryBrief.tsx`, `src/pages/CoachNotes.tsx`, `src/pages/LogEntry.tsx`
### 확인 필요 사항
1. **저장 방식**: 저장된 루틴/저장된 운동을 `app_settings` JSON에 저장하는 방식 — 기존 `fitness_projects` 패턴. 이 방식으로 진행할까요?
2. **마이그레이션 SQL**: SQL 파일은 작성하되, 실제 Supabase DB 적용은 사장님이 콘솔에서 수동 실행하시는 방식 맞나요?
3. **빌드/커밋/푸시**: 완료 후 `npm run build` → 커밋 → push origin 순서로 진행. 작업 단위 커밋은 의미 단위로 분할 (예: 마이그레이션 / 타입 / Settings / StrengthLog / History) — 혹은 한 번에 묶을까요?
위 3가지 확인되면 바로 착수하겠습니다.
---
## 4. 사용자 — 추가 주의사항
> History.tsx에서 근력운동 기록 행 수정/삭제/저장 액션 부분만 작업.
> 탭 통합/기록하기 버튼(섹션4-A 담당) 및 달력 관련(섹션4-C 담당) 코드는 건드리지 말 것.
>
> 이상 주의사항에 유의해서 착수할것
---
## 5. 어시스턴트 — 착수 (단계별 진행)
> 확인했습니다. History.tsx는 근력운동 기록 행의 수정/삭제/저장 액션만 작업하고, 탭 통합/기록하기 버튼/달력 관련은 절대 건드리지 않겠습니다.
>
> 먼저 History.tsx의 근력운동 렌더링 부분과 LogEntry.tsx의 근력운동 부분을 확인하고 착수합니다.
### 5-1. TodoWrite 등록 (8단계 체크리스트)
1. 코드베이스 추가 탐색 (History strength 부분, LogEntry strength 부분)
2. 마이그레이션 SQL 파일 작성
3. `src/lib/supabase.ts` 타입 확장
4. `Settings.tsx` 근력운동 섹션 전면 개편
5. `LogEntry.tsx` 근력운동 탭 개편 (StrengthLog.tsx는 데드 코드라 미수정)
6. `History.tsx` 근력운동 기록 행 수정/삭제/저장 액션만 추가
7. 빌드 확인 (npm run build)
8. 의미 단위 커밋 + 푸시
(탐색 중 `StrengthLog.tsx`가 라우팅되지 않은 데드 코드임을 재확인 → 작업 대상에서 제외하고 LogEntry.tsx로 변경)
### 5-2. 마이그레이션 SQL 작성
> 마이그레이션 SQL 파일을 작성합니다.
파일: `migrations/2026-05-16_strength_v2.sql`
```sql
-- 근력운동 v2: 카테고리·체중부하·맨몸+추가중량 + 저장된 루틴/운동 지원 — 2026.05.16
-- 적용: Supabase SQL Editor에서 전체 복사·실행
-- 안전: ADD COLUMN IF NOT EXISTS / NOT EXISTS 가드로 멱등 실행 가능

-- ───────── exercise_configs 확장 ─────────
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS category TEXT;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS bodyweight_ratio NUMERIC;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS is_basic BOOLEAN DEFAULT FALSE;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS sort_order INTEGER DEFAULT 0;

-- ───────── strength_sets 확장 (중량 / 추가중량 / 토글) ─────────
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS weight_kg NUMERIC;
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS additional_weight_kg NUMERIC;
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS use_additional BOOLEAN DEFAULT FALSE;

-- ───────── 기존 미분류 종목은 '기타' 처리 ─────────
UPDATE exercise_configs SET category = '기타' WHERE category IS NULL;

-- ───────── 베이직 종목 프리셋 INSERT (이미 동일명 존재 시 스킵) ─────────
-- 맨몸 9종 / 웨이트 9종 / 머신 8종 / 아이소메트릭 4종
-- (VALUES + LEFT JOIN NOT EXISTS 패턴으로 멱등 보장)
```
(맨몸: 풀업·친업·푸쉬업·다이브봄버·레그레이즈·딥스·인버티드로우·피스톨스쿼트·한손푸쉬업
웨이트: 벤치프레스·스쿼트·데드리프트·오버헤드프레스·바벨로우·덤벨컬·덤벨플라이·루마니안데드리프트·덤벨숄더프레스
머신: 렛풀다운·시티드로우·레그프레스·레그익스텐션·레그컬·체스트프레스머신·펙덱플라이·카프레이즈머신
아이소메트릭: 플랭크·사이드플랭크·월싯·데드행)
### 5-3. `src/lib/supabase.ts` 타입 확장
추가된 타입:
- `ExerciseCategory = '맨몸' | '웨이트' | '머신' | '아이소메트릭' | '기타'`
- `ExerciseConfig` 확장: `category`, `bodyweight_ratio`, `is_basic`, `sort_order`
- `StrengthSet` 확장: `weight_kg`, `additional_weight_kg`, `use_additional`
- `SavedSet`, `SavedExerciseRow`, `SavedRoutine`, `SavedExerciseTemplate` 신규
### 5-4. `src/pages/Settings.tsx` 근력운동 섹션 개편
- 사이드 메뉴 `exercises` → `strength` (`◧ 근력운동`)
- `fetchAll`에서 `saved_routines`, `saved_exercises` JSON 로드 추가
- `EXERCISE_CATEGORIES`, `CATEGORIES_WITH_RATIO`, `BASIC_PRESETS` 상수
- 운동 종목 목록 섹션:
	- 카테고리 필터 칩 (전체/맨몸/웨이트/머신/아이소메트릭/기타)
	- 인라인 종목 수정 (이름/카테고리/부하율)
	- 종목 삭제 (확인 모달)
	- 베이직 종목 추가 폼 (카테고리 + 종목명 드롭다운, 이미 등록된 항목 자동 제외)
	- 커스텀 종목 추가 폼 (이름 + 카테고리 + 맨몸 시 부하율 입력)
- 저장된 루틴 섹션:
	- "+ 새 루틴" 버튼 → 폼 오픈
	- 루틴 폼: 이름 + 운동 행 N개 (종목 선택 + 카테고리별 세트 입력 행 N개)
	- 카테고리별 세트 행: 웨이트/머신은 중량+렙, 맨몸은 추가중량 토글+렙, 아이소메트릭은 초
	- 저장/덮어쓰기/취소 (덮어쓰기는 동명 루틴 존재 시에만 노출)
	- 루틴 수정/삭제 액션
- 저장된 운동 섹션:
	- 등록은 기록탭에서 가능 안내
	- 인라인 수정 / 삭제 액션
- `detailFor()` switch 분기에 `'strength'` 추가
빌드 확인 통과: 712 kB (gzip 207 kB).
### 5-5. `src/pages/LogEntry.tsx` 근력 탭 개편
- `SetRow` 타입 확장: `reps`, `weight_kg`, `additional_weight_kg`, `use_additional`
- `ExerciseOption` 타입 신설 (`name`, `category`, `bodyweight_ratio`)
- `emptySet()` 헬퍼
- 상태 추가: `savedRoutines`, `savedExercises`, `latestBodyWeight`, `manualBodyWeight`, `routinePick`, `savedExPick`
- `reloadStrengthMeta()` 함수 (app_settings 재로드)
- useEffect:
	- `exercise_configs` select: `name, category, bodyweight_ratio` (sort_order, created_at 정렬)
	- `body_records` 최근 1건 weight_kg 자동 연동
	- 저장된 루틴/운동 JSON 로드
- `exerciseMap`, `effectiveBodyWeight` 메모화
- 카테고리별 볼륨 계산: `setVolume()`, `exerciseVolume()`, `totalVolume`
- 루틴 적용: `applyRoutine()` (선택 시 종목+세트 일괄 로드)
- 저장된 운동 추가: `appendSavedExercise()` (마지막 빈 행 교체 또는 append)
- 종목별 "저장된 운동으로 등록": `saveAsTemplate()` (동명 존재 시 덮어쓰기)
- `handleStrSave` 업데이트: `weight_kg`, `additional_weight_kg`(토글일 때만), `use_additional` 컬럼 함께 insert
- JSX 전체 교체:
	- 날짜 + 구분 (기존 유지)
	- 운동 시각 (기존 유지)
	- 사용자 체중 (인바디 자동 연동 표시 / 없으면 입력)
	- 저장된 루틴/운동 드롭다운 (좌우 2열)
	- 운동 목록 헤더에 총 볼륨 표시
	- 종목 행: 종목 선택(카테고리·부하율 표기) + 볼륨 + 저장 버튼 + 삭제
	- 세트 행 카테고리 분기:
		- 웨이트/머신: 중량 입력 + kg
		- 맨몸: 추가중량 체크박스 → 체크 시 추가중량 입력
		- 공통: ± 버튼 + 렙 입력 + 회/초
	- 세트 추가 / 운동 추가 / 저장 버튼
### 5-6. `src/pages/History.tsx` 근력운동 기록 수정/삭제/저장 액션 추가
⚠️ 주의: 탭 통합·기록하기 버튼·달력 코드는 건드리지 않음.
- import 추가: `SavedRoutine`, `SavedExerciseTemplate`, `SavedSet`
- `StrengthSetRow` 타입 신설 (set_index + reps + 신규 컬럼들)
- `StrengthSession` 내부 sets 타입을 `StrengthSetRow`로 교체
- 상태 추가:
	- `editingStrId`
	- `editStrForm: { recorded_at, label, exercises: [{ exercise_name, sets: [{...}] }] }`
	- `savedRoutines`, `savedExercises`
- `fetchSavedTemplates()` 함수 추가, `fetchData()`에서 호출
- 컨텍스트 메뉴 '수정' 액션에서 strength 선택 시 `setDetailStr(s); startEditStr(s)`로 변경
- 신규 핸들러:
	- `startEditStr(s)`: 폼 데이터 셋업
	- `doSaveEditStr()`: strength_logs update + 자식(strength_exercises, strength_sets) 캐스케이드 삭제 후 재삽입
	- `requestSaveEditStr()`: 확인 모달
	- `editStrSetField/AddSet/RemoveSet/SetExName/AddEx/RemoveEx`
	- `saveSessionAsRoutine(s)`: 루틴명 prompt → 동명 시 덮어쓰기, 아니면 신규 추가 → `app_settings.saved_routines`
	- `saveExerciseAsTemplate(s, ei)`: 종목별 저장된 운동 등록 (동명 덮어쓰기 지원)
	- `upsertSetting(key, value)` 헬퍼
- `renderStrSetDisplay(st)`: 세트 표시 문자열 (중량 / +추가중량 / 회)
- `renderDetailStr` 전면 교체:
	- 편집 모드(`editingStrId === s.id`): 날짜/구분/종목·세트 전체 편집 UI + 저장/취소
	- 표시 모드: 헤더에 "수정 / 루틴으로 저장 / 삭제" 버튼, 각 종목 우측에 "저장된 운동으로" 버튼, 세트는 카테고리별 표시 문자열
- `renderStrCard`: 카드 클릭 시 `openStrDetail()` (detail 열기 + 폼 사전 준비 + editing은 null 유지하여 표시 모드부터 시작)
### 5-7. 최종 빌드 확인
```javascript
npm run build
✓ 103 modules transformed
dist/assets/index-BzEDu4TM.js   727.22 kB │ gzip: 210.58 kB
✓ built in 1.94s
```
### 5-8. 의미 단위 커밋 4건 + 푸시
각 파일 헤더 주석에 `// 작업3: ...` 한 줄 추가.
```javascript
b30311d feat: 근력운동 v2 DB 스키마·타입 - 카테고리/체중부하/저장된루틴·운동 (작업3)
  - migrations/2026-05-16_strength_v2.sql
  - src/lib/supabase.ts

b32c29e feat: 설정 근력운동 섹션 - 운동 종목 분류/저장된 루틴/저장된 운동 (작업3-1·3-2)
  - src/pages/Settings.tsx

269cd9b feat: 기록 근력 탭 - 카테고리별 입력/루틴 불러오기/볼륨·체중 자동연동 (작업3-1·3-2)
  - src/pages/LogEntry.tsx

118e18b feat: 히스토리 근력운동 기록 수정/삭제/저장 액션 (작업3-1)
  - src/pages/History.tsx
```
`git push origin main` →
`deb36fa..118e18b  main -> main` (성공)
---
## 6. 어시스턴트 — 최종 보고
끝났습니다. 작업 요약:
### 완료 항목
**작업 3-1 · 3-2: 근력운동 v2 — 4단위 의미 커밋 + 푸시 완료**
<table header-row="true">
<tr>
<td>커밋</td>
<td>파일</td>
<td>내용</td>
</tr>
<tr>
<td>`b30311d`</td>
<td>`migrations/2026-05-16_strength_v2.sql`  • `src/lib/supabase.ts`</td>
<td>DB 스키마(컬럼 추가 + 베이직 종목 프리셋) + 타입 정의</td>
</tr>
<tr>
<td>`b32c29e`</td>
<td>`src/pages/Settings.tsx`</td>
<td>설정 근력운동 섹션 — 카테고리 분류/베이직/커스텀/부하율 + 저장된 루틴/운동</td>
</tr>
<tr>
<td>`269cd9b`</td>
<td>`src/pages/LogEntry.tsx`</td>
<td>기록 근력 탭 — 카테고리별 입력 + 루틴/저장된 운동 불러오기 + 볼륨/체중 자동연동</td>
</tr>
<tr>
<td>`118e18b`</td>
<td>`src/pages/History.tsx`</td>
<td>근력운동 기록 행 수정·삭제·저장 액션 (탭/달력 미수정)</td>
</tr>
</table>
### 사장님 액션 필요
**Supabase 콘솔에서 SQL 적용**
파일: `migrations/2026-05-16_strength_v2.sql`
Supabase SQL Editor에서 전체 복사·실행하시면 됩니다. ADD COLUMN IF NOT EXISTS·NOT EXISTS 가드라 멱등 실행 안전합니다.
### 빌드 결과
`npm run build` ✓ 통과 (727 kB, gzip 210 kB)
### 주의 사항 준수
- History.tsx에서 탭 통합·기록하기 버튼(섹션4-A) 및 달력(섹션4-C) 코드는 손대지 않았습니다.
- 근력운동 기록 행의 수정/삭제/저장 액션만 추가했습니다.
- `StrengthLog.tsx`는 라우팅되지 않은 데드 코드라 그대로 두었습니다.
---
## 7. 후속 사용자 메시지
> Continue from where you left off.
어시스턴트 응답:
> No response requested.
---
## 8. 본 추출 요청
> 현재 컨텍스트(대화방)의 첫 프롬프트부터 마지막 보고까지 한글자의 누락도 없이 모두 MD로 추출해줘
> 저장 위치는 D:dallogdallog_gitConversation_Log
> 파일명 템플릿은 04.{이 컨텍스트(대화방)에서의 주요작업 주제}.md
> 파일명의 넘버링은 준수해줘 {}영역만 니가 적어야할 내용이야.
→ 본 문서 작성·저장.
---
## 부록 A. DB 마이그레이션 SQL 전문
파일 위치: `D:\dallog\dallog_git\migrations\2026-05-16_strength_v2.sql`
```sql
-- 근력운동 v2: 카테고리·체중부하·맨몸+추가중량 + 저장된 루틴/운동 지원 — 2026.05.16
-- 적용: Supabase SQL Editor에서 전체 복사·실행
-- 안전: ADD COLUMN IF NOT EXISTS / NOT EXISTS 가드로 멱등 실행 가능

-- ───────── exercise_configs 확장 ─────────
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS category TEXT;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS bodyweight_ratio NUMERIC;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS is_basic BOOLEAN DEFAULT FALSE;
ALTER TABLE exercise_configs ADD COLUMN IF NOT EXISTS sort_order INTEGER DEFAULT 0;

-- ───────── strength_sets 확장 (중량 / 추가중량 / 토글) ─────────
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS weight_kg NUMERIC;
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS additional_weight_kg NUMERIC;
ALTER TABLE strength_sets ADD COLUMN IF NOT EXISTS use_additional BOOLEAN DEFAULT FALSE;

-- ───────── 기존 미분류 종목은 '기타' 처리 ─────────
UPDATE exercise_configs SET category = '기타' WHERE category IS NULL;

-- ───────── 베이직 종목 프리셋 INSERT (이미 동일명 존재 시 스킵) ─────────
-- 맨몸
INSERT INTO exercise_configs (name, category, bodyweight_ratio, is_basic, sort_order)
SELECT v.name, v.category, v.bodyweight_ratio, v.is_basic, v.sort_order
FROM (VALUES
  ('풀업'::text,        '맨몸'::text, 100::numeric, TRUE, 100),
  ('친업'::text,        '맨몸'::text, 100::numeric, TRUE, 101),
  ('푸쉬업'::text,      '맨몸'::text,  65::numeric, TRUE, 102),
  ('다이브봄버'::text,  '맨몸'::text,  65::numeric, TRUE, 103),
  ('레그레이즈'::text,  '맨몸'::text,  50::numeric, TRUE, 104),
  ('딥스'::text,        '맨몸'::text, 100::numeric, TRUE, 105),
  ('인버티드로우'::text,'맨몸'::text,  60::numeric, TRUE, 106),
  ('피스톨스쿼트'::text,'맨몸'::text,  85::numeric, TRUE, 107),
  ('한손푸쉬업'::text,  '맨몸'::text,  65::numeric, TRUE, 108)
) v(name, category, bodyweight_ratio, is_basic, sort_order)
LEFT JOIN exercise_configs e ON e.name = v.name
WHERE e.id IS NULL;

-- 웨이트
INSERT INTO exercise_configs (name, category, is_basic, sort_order)
SELECT v.name, v.category, v.is_basic, v.sort_order
FROM (VALUES
  ('벤치프레스'::text,        '웨이트'::text, TRUE, 200),
  ('스쿼트'::text,            '웨이트'::text, TRUE, 201),
  ('데드리프트'::text,        '웨이트'::text, TRUE, 202),
  ('오버헤드프레스'::text,    '웨이트'::text, TRUE, 203),
  ('바벨로우'::text,          '웨이트'::text, TRUE, 204),
  ('덤벨컬'::text,            '웨이트'::text, TRUE, 205),
  ('덤벨플라이'::text,        '웨이트'::text, TRUE, 206),
  ('루마니안데드리프트'::text,'웨이트'::text, TRUE, 207),
  ('덤벨숄더프레스'::text,    '웨이트'::text, TRUE, 208)
) v(name, category, is_basic, sort_order)
LEFT JOIN exercise_configs e ON e.name = v.name
WHERE e.id IS NULL;

-- 머신
INSERT INTO exercise_configs (name, category, is_basic, sort_order)
SELECT v.name, v.category, v.is_basic, v.sort_order
FROM (VALUES
  ('렛풀다운'::text,        '머신'::text, TRUE, 300),
  ('시티드로우'::text,      '머신'::text, TRUE, 301),
  ('레그프레스'::text,      '머신'::text, TRUE, 302),
  ('레그익스텐션'::text,    '머신'::text, TRUE, 303),
  ('레그컬'::text,          '머신'::text, TRUE, 304),
  ('체스트프레스머신'::text,'머신'::text, TRUE, 305),
  ('펙덱플라이'::text,      '머신'::text, TRUE, 306),
  ('카프레이즈머신'::text,  '머신'::text, TRUE, 307)
) v(name, category, is_basic, sort_order)
LEFT JOIN exercise_configs e ON e.name = v.name
WHERE e.id IS NULL;

-- 아이소메트릭
INSERT INTO exercise_configs (name, category, is_basic, sort_order)
SELECT v.name, v.category, v.is_basic, v.sort_order
FROM (VALUES
  ('플랭크'::text,      '아이소메트릭'::text, TRUE, 400),
  ('사이드플랭크'::text,'아이소메트릭'::text, TRUE, 401),
  ('월싯'::text,        '아이소메트릭'::text, TRUE, 402),
  ('데드행'::text,      '아이소메트릭'::text, TRUE, 403)
) v(name, category, is_basic, sort_order)
LEFT JOIN exercise_configs e ON e.name = v.name
WHERE e.id IS NULL;

-- ───────── app_settings 키 메모 ─────────
-- 저장된 루틴   : key = 'saved_routines'    value = JSON array of { id, name, exercises:[{ exercise_name, sets:[{ reps, weight_kg?, additional_weight_kg?, use_additional? }] }], created_at, updated_at }
-- 저장된 운동   : key = 'saved_exercises'   value = JSON array of { id, exercise_name, sets:[...], created_at, updated_at }
-- 두 키 모두 별도 테이블 없이 app_settings 의 key-value(JSON 문자열)로 보관 — fitness_projects 와 동일 패턴
```
---
## 부록 B. supabase.ts 타입 추가 전문
```typescript
export type ExerciseCategory = '맨몸' | '웨이트' | '머신' | '아이소메트릭' | '기타'

export type ExerciseConfig = {
  id?: string
  name: string
  category?: ExerciseCategory | null
  bodyweight_ratio?: number | null
  is_basic?: boolean | null
  sort_order?: number | null
  created_at?: string
}

export type StrengthSet = {
  id?: string
  exercise_id: string
  set_index: number
  reps: number
  weight_kg?: number | null
  additional_weight_kg?: number | null
  use_additional?: boolean | null
  created_at?: string
}

// 저장된 루틴/운동 — app_settings JSON 저장 (fitness_projects 와 동일 패턴) — 2026.05.16
export type SavedSet = {
  reps: number
  weight_kg?: number | null
  additional_weight_kg?: number | null
  use_additional?: boolean | null
}

export type SavedExerciseRow = {
  exercise_name: string
  sets: SavedSet[]
}

export type SavedRoutine = {
  id: string
  name: string
  exercises: SavedExerciseRow[]
  created_at: string
  updated_at: string
}

export type SavedExerciseTemplate = {
  id: string
  exercise_name: string
  sets: SavedSet[]
  created_at: string
  updated_at: string
}
```
---
## 부록 C. 변경된 헤더 주석 (작업 마커)
- `src/pages/Settings.tsx` 2행 추가
	`// 작업3: 근력운동 섹션 전면 개편 — 카테고리/베이직/커스텀/부하율 + 저장된 루틴/운동 — 2026.05.16`
- `src/pages/LogEntry.tsx` 3행 추가
	`// 작업3: 근력 탭 카테고리별 입력 + 저장된 루틴·운동 불러오기 + 볼륨 계산 — 2026.05.16`
- `src/pages/History.tsx` 3행 추가
	`// 작업3: 근력운동 기록 행 수정·삭제·저장(루틴/저장된 운동) 액션 — 2026.05.16`
---
## 부록 D. 사용자 환경·규칙 컨텍스트 (글로벌 헤더 발췌)
- 한스브릿지 글로벌 [CLAUDE.md](http://CLAUDE.md) v1.0 적용 환경
- 5대 행동 지침: Think Before Coding / Simplicity First / Surgical Changes / Goal-Driven Execution / 한국어 코드 규칙
- 종결 콜론 금지 (다·요·니다·죠 사용)
- 새 파일 첫 줄 한국어 헤더 주석 (`-- 2026.05.16`, `// 2026.05.16` 등)
- 의미 단위 커밋 (한 문장 설명 가능해야 OK)
- Phase 1: zip 패키징 유지 + 한 문장 설명 — 본 작업은 git 도입 이후이므로 Phase 2 의미 단위 커밋 방식으로 진행
- 분업 구조: 설계는 [Claude.ai](http://Claude.ai), 코드 작성은 Claude Code(본 환경), Adversarial Review는 Codex
---
(끝)
