2026-06-15 PulseUp 작업일지입니다. 오늘은 10건의 작업을 진행했습니다.
EMOM 재생 멀티모니터 전환·양쪽표시·타이머(분:초) 수정
현상/기능
- DS 이후 메인 시작 시 우측 모니터(3분할 B/D, 5분할 C·D 패널)가 DS 영상에 멈춰 전환 안 됨
- EMOM 카운터가 분:초(1:30)가 아닌 초(90)로만 표시
- A1/A2 양쪽운동 횟수 아래 '(양쪽)' 표시 누락 + EMOM 타이머 분:초 전제
- GET 프로시저 재배포 후에도 '(양쪽)' 여전히 안 나옴
작업
- `renderer/`, `renderer/`
- `resolveMainPhaseSeekLabel`이 다른 구역 position('A1')을 받으면 `null`을 반환해 해당 화면이 seek 못 하던 버그 수정. 이제 같은 슬롯/세트 기준으로 각 화면 라벨(우측 B1/D1, 5분할 패널별)로 변환. 테스트 갱신
- `renderer/components/`
- EMOM-LOOP는 LoopModule로 재생돼 `getCircuitTypeForBroadcast`가 'loop'을 주입 → loop 전략(초)으로 표시되던 문제. 타이머 전용 `resolveTimerCircuitType`에서 시퀀스 `major_category`(EMOM/AMRAP)를 서킷타입보다 우선 판별하도록 변경(재생 모듈 선택용 `resolveWorkoutCircuitType`은 미변경). sticky로 라운드 중 유지
- 렌더러 `updateRepsBadge`는 `is_bilateral`이 오면 정상 표시하나, 배포된 구버전 GET 프로시저가 `is_bilateral`(및 EMOM `major_category` 오버라이드)을 반환하지 않음. `sp_get_workout_exercises_with_reps.sql` 재배포 필요(reps·is_bilateral·major_category 오버라이드 모두 포함)
- 진짜 원인 — 재생은 웹이 GET 결과를 `normalizeSequence`로 정규화해 일렉트론에 보내는데, 이 매핑이 `is_bilateral`을 누락시켜 버림. `ExerciseSequence` 타입에 `is_bilateral?` 추가하고 `normalizeSequence`에서 `is_bilateral`을 채우도록 수정. (GET 재배포 + 이 수정 둘 다 필요)
- **DB** · `database/sp_get_workout_exercises_with_reps.sql` (재배포 필요)
- **Web** · `MonthProgram/components/`
월간운동프로그램 운동상세 양쪽여부
현상/기능
- 운동기록 상세의 운동상세정보 DataTable 맨 끝에 양쪽여부 추가
작업
- `is_bilateral` 필드를 API 정규화·타입에 반영하고 상세 테이블 마지막 컬럼에 Y/N 표시. DS/CD 행은 빈 값 처리
- **Web**
월간운동프로그램 좌측 기록 테이블
현상/기능
- 좌측 기록 DataTable에 운동구분 왼쪽에 운동저장구분 정보 추가
작업
- 마스터 테이블에 `운동저장구분` 컬럼을 추가하고 `workoutScope`를 `workoutScopes` 마스터의 `scopeName`으로 표시. `MonthProgram`에서 `workoutScopes` props 전달
- **Web**
EMOM DS 누락·reps=0 근본원인 규명 및 재발 방지
현상/기능
- A/B/C/D 변경 후 EMOM 등에서 DS가 안 나오고 우측 상단 횟수가 안 뜨는 문제가 계속됨 → 전체 재점검
- 구버전 프로시저 재배포로 인한 재발 방지
- 구버전 DB일 때 데이터 조용히 유실되는 fallback 차단
작업
- API 로그에서 `sp_SaveWorkout... expected 10, got 19/18/17` 발견. DB에 구버전(10-param) 프로시저가 배포돼 있어 신버전(reps/DS) 호출이 모두 실패하고 10-param 구버전으로 fallback 저장됨. 구버전은 DS를 `exercise_type='dynamic'`로만 저장(웹앱은 'DS' 전송 → 누락)하고 reps를 INSERT하지 않아(=0) 재생 데이터에 DS·reps가 빠졌던 것. Electron 렌더러는 무관
- 구버전 `sp_save_power_circuit_new.sql`의 10-param `sp_SaveWorkout` 정의 제거(DEPRECATED 안내 스텁으로 대체). `_v2-procedures.sql`·마이그레이션 주석을 정식본 `sp_save_workout_with_reps.sql`(19-param)로 통일. (DB 적용은 사용자가 직접 수행)
- `callSaveWorkoutProcedure`의 10-param 구버전 fallback 제거 → 19/18/17 모두 실패 시 "최신 프로시저 적용 필요" 에러를 명확히 throw 하도록 변경
- **분석** · 터미널 로그(API/Electron)
- **DB** · `sp_save_power_circuit_new.sql`, `_v2-procedures.sql`, `migrations/20260614_exercise_bilateral_flag.sql`
- **API**
Electron A~D position 재생 안정화
현상/기능
- A/B/C/D 변경 후 EMOM 등에서 DS가 안 나오고 main이 바로 나오며 우측 상단 운동횟수 배지가 안 보이는 문제 수정
작업
- (1차 시도에서 추가했던) queue 모드 가드가 정상 동작하던 `handleStretchingMode`/`playVideo` 호출을 막아 회귀를 유발 → 가드를 제거하고 HEAD의 렌더링 경로(항상 playVideo, DS는 handleStretchingMode)를 복원하되 A~D position 매핑(`mapMainPositionToDisplayLabel`, 5분할 패널 prefix)은 유지. StretchingRenderer는 DS 큐 영상을 다시 재생, MainRenderer는 본운동 재생+reps 배지 갱신, ReadyRenderer는 카운트다운 중 main 프리뷰 재생. 테스트 readonly 타입 오류도 수정
양쪽운동 플래그 저장/표시
현상/기능
- 운동 마스터와 운동 등록 내 양쪽운동 여부를 별도로 저장
- 양쪽운동 값을 생성/수정/저장 API에서 전달
- 운동 목록, Vimeo 설명 편집, 통합 운동 등록 화면에서 양쪽운동 체크 지원
- 양쪽운동이면 영상 우측상단 운동카운트 아래에 `(양쪽)` 표시
- Vimeo 설명 `$Y/$N` 포맷 검증
- 메인트레이닝 행 단위 양쪽운동 체크값 저장 누락 보정
작업
- `exercises`, `workout_history_detail`, `workout_exercises`에 `is_bilateral` 저장 경로를 추가. Vimeo 설명 5번째 토큰 `$Y/$N`을 파싱하고 저장/조회 프로시저에 반영
- 운동 생성·수정 payload와 운동 저장 검증 스키마에 `is_bilateral`을 추가하고, 기존 관리자 직접 SQL 경로도 동일 필드를 처리하도록 보강
- 운동 마스터 기본값 스위치와 목록 표시를 추가. Vimeo 설명은 `운동명$자극부위$특징$기구$Y/N` 헬퍼로 편집하며, 통합 운동 등록에서는 행 단위 양쪽운동 override 체크를 저장 payload에 포함
- `sequence.is_bilateral`을 읽어 REPS 배지 아래 노란색 `(양쪽)` 라벨을 선명하게 렌더링
- 설명 파싱, 기존 4필드 N 기본 처리, 양쪽운동 토큰 변경 테스트를 추가하고 web helper 테스트로 검증
- 저장 직전 `exercises`와 `workoutExercises`의 `is_bilateral` 값을 상호 보강하도록 API 정규화를 추가. 구형 10파라미터 `sp_SaveWorkout` 정의에도 `workout_history_detail`·`workout_exercises` 저장 컬럼을 반영
- **DB** · `_v2-schema.sql`, `sp_insert_vimeo_video.sql`, `sp_save_workout_with_reps.sql`, `sp_GetWorkoutExercises_updated.sql`, `20260614_exercise_bilateral_flag.sql`
- **API**
- **Web** · `WorkoutManager`
- **Test**
- **API/DB** · , `sp_save_power_circuit_new.sql`
운동 선택 모달 A~D position 반영
현상/기능
- 메인 운동 선택 모달에도 A/B/C/D position 체계 적용
작업
- 선택 목록 라벨·position 옵션을 공통 `MAIN_GRID_POSITION_ORDER` 기준으로 연결하고, 부모에서 전달된 기존 position을 모달 내부에서 A~D 형식으로 정규화
- **Web**
Electron 메인 운동 A~D 모니터 배치
현상/기능
- 3모니터는 좌 A/C·우 B/D, 5모니터는 좌측부터 A/B/C/D 순서로 메인 운동 표시
작업
- Electron 큐 생성·seek label·인트로 필터·전후반 그룹 판단을 A1~A3/B1~B3/C1~C3/D1~D3 체계로 정리. 관련 unit test와 Electron build로 검증
통합 메인 운동 position 코드 (A~D)
현상/기능
- 메인 운동 추가 시 A1~A6/B1~B6 대신 A1~A3, B1~B3, C1~C3, D1~D3 순서로 등록
- 저장 position과 재생 파싱 일치
작업
- `positionFromMainIndex`·정렬·Stress lap 순서를 4구역×3슬롯 체계로 변경. 구 A/B(1~6)·L/R 이력은 불러올 때 `migrateOldMainGridPosition`으로 자동 변환
- web-app과 동일한 A~D position 정규화·파싱 로직 동기화
- **Web**
운동저장구분 UI 표기 통일
현상/기능
- Scope UI 표기를 운동저장구분으로 변경, Admin 테이블 정렬 컬럼 제거
- 사이드바 메뉴명도 운동저장구분으로 맞춤
작업
- 사용자 노출 문구를 운동저장구분으로 통일. WorkoutScopeManager 목록 테이블에서 정렬 컬럼 제거(등록/수정 폼의 정렬 순서 입력은 유지)
- menus 시드·마이그레이션에서 메뉴명·설명을 운동저장구분 관리로 변경
- **Web**
- **DB** · `20260614_workout_scope_menu_rename.sql`, `20260606_workout_scope_menu_seed.sql`