2026-08-16 PulseUp 작업일지입니다. 오늘은 8건의 작업을 진행했습니다.
운영 모니터 이미지가 Electron에서 안 보이던 문제
현상/기능
- 서버 배포 후 운동설정에서 등록한 이미지가 웹에서는 보이지만 Electron에서 안 나온다
작업
- 웹은 `www.pulsebox.co.kr/uploads` 로 보여 주는데, Electron용 resolve가 host를 `pulsebox.co.kr` 로 바꿔 이미지가 깨졌다. 절대 URL은 유지하고, apex `/uploads` 만 www로 맞춘다
- **API / Electron**
인트로 중단 후 기본 이미지 복원
현상/기능
- 인트로 중단 시 기본 이미지가 아니라 인트로 이미지가 초기 화면에 남는다
작업
- 인트로 시작이 `currentMonitorDisplay`를 인트로 이미지로 덮어 스플래시가 그걸 재사용했다. 운동별 기본 이미지를 `initMonitorDisplay`로 따로 두고, 인트로 취소·스플래시 복귀 시 기본 이미지를 다시 적용한다
Electron 모든 창 Ctrl+Shift+Q 종료
현상/기능
- 모든 창에서 Ctrl+Shift+Q로 앱을 종료
작업
- `renderer.js`
- 모니터 창 `before-input-event`와 메뉴 종료 단축키를 `Ctrl+Shift+Q`(macOS는 `Cmd+Shift+Q`)로 맞추고, 기존 `requestCompleteShutdown`으로 정리 후 종료한다. `Ctrl+Q`만으로는 종료되지 않는다
리모컨 가로 화면 버튼 확대 + 에러 확인
현상/기능
- 가로로 보면 버튼이 길게 보인다. 상단 심박 내용을 숨기고, 버튼을 화면 가득·간격 2배로 키우며, 에러는 확인을 눌러야 사라지게
작업
- 기본 탭 안 상단 심박 진단 패널만 주석 처리(복원 가능). 상단 탭(기본/선택보기/심박)은 유지. 제어/선택보기 버튼을 기존 비율로 남은 화면을 채우게 하고 간격을 `gap-2`→`gap-4`로 확대. 선택보기 탭은 높이 체인을 맞춰 구역·번호·보기 버튼이 화면을 채운다. 선택 표시 좌우에 이전/다음을 넣어 A1→D3 순환으로 바로 확대 재생. 인트로 실행 후 폴링이 「인트로 취소」를 지우던 문제를 세션 `introPlaying` 동기화로 수정하고, 선택보기 탭에 「인트로 중단」을 추가. 인트로 취소 시 프리뷰가 아니라 스플래시 초기 화면으로 복귀. 리모컨 에러 스낵바는 자동 숨김 없이 「확인」을 눌러야 닫힘
- **Web** · ·
인트로 영상 6칸 고정 + 5분할 구역 배정 미러링
현상/기능
- 인트로 실행 시 처음엔 좌우 3개씩 6개가 아니라 3개만 나오고, 다시 실행하면 6개가 나온다. 3분할은 좌 A/B·우 C/D, 5분할은 좌좌 A/B·좌우 C/D·우좌 A/B·우우 C/D 여야 한다
- 인트로 구역 배정 규칙을 한 곳에 모으고 단위 테스트로 고정
- 인트로 화면분할 판정을 실행 이력이 아닌 권위값으로
- 3분할·5분할 모두 인트로는 창당 6개
작업
- `intro-started` 페이로드에 화면분할 정보가 없어서 renderer가 직전 운동의 `workout-setup-queue` 수신 이력(`usesFiveScreenPanelQueue`)으로 모드를 추측했다. 이 값은 새 운동에서 초기화되지 않고, `broadcastVideoQueues`가 `workout-play-ready`에서 먼저 돌기 때문에 5분할 큐를 한 번 받은 뒤 인트로를 켜면 셀이 3칸(1열)만 렌더돼 3개만 보였다. 즉 같은 인트로가 실행 시점에 따라 3칸/6칸으로 갈리는 상태 의존 버그
- (신규) · (신규)
- 화면분할 모드(3/5)와 side만으로 그 창이 담당할 두 구역(1열/2열)을 정하는 모듈 신규 분리. 3분할 좌 A/B·우 C/D, 5분할 좌좌·우좌 A/B·좌우·우우 C/D(우측 쌍이 좌측 쌍 미러링). 셀 개수 상수(2열×3행=6), `resolveIntroScreenMode`(payload 값 우선·없으면 폴백), 구역 필터·슬롯 라벨·2열 구역 판정을 함께 제공. 12 tests 추가
- main의 `intro-started` broadcast에 `screenMode: getScreenMode` 추가. `handleIntroStarted`는 이 값으로 `setFiveScreenLayout`·`render`·구역 필터를 결정하고, 값이 없을 때만 기존 플래그로 폴백한다. 스케줄러·필터 인자를 `{ fiveScreen }` 옵션에서 `screenMode`로 교체
- 인트로 셀을 모드와 무관하게 항상 2열×3행 6칸으로 통일(5분할 3칸 분기 제거)하고 열 구분선도 항상 적용. 슬롯 라벨·구역 필터는 새 배정 모듈에 위임. `mapPositionToSlot`의 `maxSlots` 폴백이 3으로 떨어지면 2열 구역(B·D)이 1열(A·C)을 덮어써 6개 중 3개만 남으므로, 인트로에서는 레이아웃 상수(6)로 폴백하도록 방어. 싱글모드는 `supportsIntro=false`로 이미 인트로가 차단되어 있어 변경 없음
- **분석**
영상 전환 멈춤 근본 원인 수정 — iframe 재로드·미버퍼링 preload
현상/기능
- Ready 5초 끝나고 화면이 바뀌면서 영상이 멈춘다. A→B 전환도 멈춘다. 싱글모드도 동일
- MULTI 그리드 A→B 전환 멈춤
- 싱글모드 Ready 5초 종료 후 영상 멈춤
- 운동시작전 카운터 창 상단 로고는 우측끝, 운동명은 좌측끝 정렬
작업
- SINGLE 로그에서 같은 videoId `iframe 로드 완료`가 운동 길이(15~20초) 간격으로 현재·다음 플레이어 **동시에** 재기록되고, 직후 Vimeo postMessage origin 오류 19회 · `STALL cur=-1` 7회(timeupdate 미수신 = SDK 연결 끊긴 죽은 플레이어) · `paused 자동 재생 복구` 74회가 이어진다. MULTI는 Ready 종료(15:07:02) 노출 순간 12개 슬롯 전부가 `버퍼링 종료 (1947~2151ms)`를 찍어, 45초 전 프리로드가 실제로는 데이터를 안 받아둔 상태였다
- 프리로드 레이어가 `display:none`이라 iframe이 렌더링 대상이 아니어서 영상 데이터를 미리 받지 않았다. `VISIBLE_LAYER_STYLE`/`PRELOAD_LAYER_STYLE`(`opacity:0`+`pointer-events:none`)로 분리하고 `showQueueEntry`는 레이어 이동 없이 style만 교체한다. AMRAP 풀이 이미 쓰던 규칙과 일치시켰고 소스 상단 summary 추가
- (신규)
- ① `attachHosts`가 이미 붙어 있는 host를 매번 `appendChild` — 같은 부모여도 제거 후 재삽입이라 iframe 재로드. 부모가 다를 때만 붙이도록 가드. ② 승격 시 host를 `#single-next-video` → `#single-main-video`로 옮겨 재로드(로그 17:17:49.134). preload player를 메인 슬롯 안 숨김(`opacity:0`) 레이어로 두고 `opacity`·`z-index`만 바꿔 승격하도록 구조 변경(current/preload/preview 3 player, preload·preview는 정지 대기). host 생명주기·승격은 로 모듈 분리. 승격 시 DOM 미이동·재삽입 방지 회귀 테스트 추가(7 tests)
- 헤더 행을 운동명(`flex:1`, 좌측 정렬) + 로고(`flex-shrink:0`, 우측 정렬)로 재배치하고 빈 스페이서 제거. stress/loop/amrap/emom 전 서킷이 이 파일을 재export하므로 한 번 수정으로 반영
- **분석** · `sent-20260815-060747.json`(MULTI) · `sent-20260815-081851.json`(SINGLE)
COMBO 운동 개수·횟수 저장 누락 수정
현상/기능
- combo 운동의 경우 운동갯수를 수정해도 저장이 안된다
작업
- 메인 목록 횟수가 패널 기본값으로 덮이던 동기화를 제거해 저장·불러오기 값을 유지한다. 이력 plan은 WorkoutSettings 레거시 3Set 확장을 타지 않게 했고, 저장 개수는 설정 MOVE가 아니라 3·6·9·12를 받는다
- **Web** ·
Electron 로그 영상 멈춤 분석 (store-3 / master-3331acfd)
현상/기능
- 영상이 멈추는 현상이 나온다. 로그 확인
작업
- DS·메인 초반에 Vimeo `STALL` 5회(진행 정지 3.8~4.6초). 워치독 1/3 복구만 있고 2회·재구축은 없다. 긴 버퍼/로드 에러는 없다. 심박 실패 큐가 4만 줄(5MB)로 남아 있으나 이벤트루프 지연은 최대 37ms라 4초 멈춤의 직접 원인은 아니다
- **분석** · `uploads/electron-logs/2026-08-15/store-3/.../sent-20260815-060747.json`