Skip to content

feat: URL 직렬화 · Minetti 대안 모델 · PR CI - #3

Merged
cbcruk merged 1 commit into
mainfrom
claude/project-start-b3jyjo
Aug 14, 2026
Merged

cbcruk merged 1 commit into
mainfrom
claude/project-start-b3jyjo

Conversation

@cbcruk

@cbcruk cbcruk commented Aug 14, 2026

Copy link
Copy Markdown
Owner

세 가지를 함께 올립니다. 테스트 26 → 52개.

1. PR CI

.github/workflows/ci.yml — pull_request에서 test → typecheck → build. 지금까지 main 푸시에서만 돌아 머지 전에 걸리는 게 없었습니다. 이 PR이 새 워크플로의 첫 검증 대상입니다.

concurrency는 cancel-in-progress: true입니다. 배포와 달리 중간에 잘려도 남는 상태가 없습니다.

2. v0.4 상태 공유 — core/serialize.ts

입력 5개를 쿼리스트링에 싣습니다: ?g=20&v=5&b=b&m=70&h=1000

  • 슬라이더 조작마다 replaceState로 갱신합니다. pushState면 한 번 끌 때 히스토리가 수십 개 쌓여 뒤로가기가 못 쓰게 됩니다.
  • 디코딩은 방어적입니다. URL은 잘리고, 손으로 고쳐지고, 채팅앱을 거치며 망가집니다. 없는 키는 오류가 아니라 폴백이고(링크에 일부만 실릴 수 있음), 모르는 키(utm_source 등)는 무시합니다.
  • 도메인 밖 값을 클램프하지 않습니다. 클램프는 "45%로 보정했다"는 사실을 감춘 채 사용자가 요청하지 않은 계산 결과를 보여주는 짓입니다. 그 필드만 폴백으로 되돌리고 rejected[]에 무엇을 왜 버렸는지 남깁니다. UI가 배너로 노출합니다.
  • URLSearchParams를 쓰지 않습니다. 코어에 DOM 타입을 끌어오는 대가가 스칼라 5개 파싱보다 큽니다.

3. Minetti et al. (2002) 대안 모델 — core/minetti.ts

−45%+45% 실측 기반 5차 다항식을 넣고 ACSM 추정과 나란히 내보냅니다. 우리 경사 도메인(145%)이 통째로 적합 범위 안에 듭니다.

비교하려면 축을 맞춰야 합니다. 비용은 벨트 거리 1 m당이고 안정시를 뺀 순수 증분이라 3.5를 되돌려 더합니다. 안 하면 모델 차이가 아니라 기준선 차이를 보게 됩니다. 검증 앵커는 절편입니다: Cw(0) = 2.5, Cr(0) = 3.6 J/(kg·m) — 평지 실측값과 일치.

대조 결과가 통설 두 개를 뒤집습니다

MET 차이, 양수 = ACSM이 크게 나옴.

경사 5 km/h (보행식) 8 km/h (주행식)
15% +19.3% −5.2%
20% +19.8% −11.2%
30% +18.2% −21.0%
45% +7.4% −34.0%
  1. 과대추정 폭은 경사에 비례하지 않습니다. 20% 부근이 최대고 45%로 갈수록 오히려 좁아집니다. "가파를수록 더 틀린다"가 아닙니다.
  2. 주행식 구간에서는 부호가 뒤집힙니다. 주행식의 경사 계수가 보행식의 절반(0.9 vs 1.8)인데 Minetti의 주행 비용은 계속 가파르게 오릅니다. 7 km/h 위에서는 ACSM이 과소추정합니다.

처음 작성한 테스트는 "경사가 높을수록 두 모델이 더 벌어진다"였고 실패했습니다. 가정이 틀렸던 것이라 실제 거동에 맞춰 다시 썼습니다. README의 "10~20% 과대추정"이 보행 구간에만 해당한다는 점도 문서에 반영했습니다.

어느 쪽이 옳다고 고르지 않습니다. 벌어지는 폭이 곧 불확실성이고, v0.2 캘리브레이션의 대조군이 됩니다.

그 외

  • toAscentInput()으로 날 숫자 → 브랜디드 타입 변환을 코어에 두고, 웹 상태를 AscentParams로 통일해 번역 계층을 없앴습니다.
  • core의 exports가 dist 대신 src를 가리킵니다. dist를 가리키면 빌드를 잊은 순간 타입체크가 낡은 산출물을 봅니다 — 이번 작업에서 실제로 걸렸습니다(새 모듈이 웹에서 안 보임).

검증

  • pnpm test 52개 통과, pnpm typecheck, pnpm build 통과
  • 브라우저(Chromium)에서 확인, 콘솔 에러 0
    • 진입 시 URL에 입력이 실림 → 슬라이더 조작 시 URL 추적 → 그 URL을 새 탭에서 열면 동일 화면 재현(30% / 8 km/h / 0:26:06)
    • "링크 복사" → 클립보드에 현재 URL, 라벨이 "✓ 복사됨"으로 전환
    • ?g=999&v=6&m=abc&b=treadmill → 거부 배너 3건, 해당 필드만 기본값, v=6은 정상 반영

Generated by Claude Code

## v0.4 상태 공유 (core/serialize.ts)

입력 5개를 쿼리스트링(g/v/b/m/h)에 싣고 링크 하나로 재현되게 한다.
디코딩은 방어적이다 — URL은 잘리고 손으로 고쳐지고 채팅앱을 거치며 망가진다.
없는 키는 오류가 아니라 폴백이고, 도메인 밖 값은 클램프하지 않는다.
클램프는 "45%로 보정했다"를 감춘 채 다른 계산을 보여주는 짓이라, 그 필드만
되돌리고 무엇을 왜 버렸는지 rejected[]로 같이 낸다. UI가 배너로 노출한다.

URLSearchParams는 쓰지 않는다. 코어에 DOM 타입을 끌어오는 대가가 스칼라 5개
파싱보다 크다.

## Minetti et al. (2002) 대안 모델 (core/minetti.ts)

−45%~+45% 실측 기반 5차 다항식을 넣고 ACSM 추정과 나란히 내보낸다.
비교하려면 축을 맞춰야 한다: 비용은 벨트 거리 1 m당이고 안정시를 뺀 순수
증분이라 3.5를 되돌려 더한다.

대조 결과가 통설 두 개를 뒤집는다.
- 과대추정 폭은 경사에 비례하지 않는다. 5 km/h에서 20% 부근 +19.8%가 최대고
  45%에서 +7.4%로 좁아진다.
- 주행식 구간에서는 부호가 뒤집힌다. 경사 계수가 보행식의 절반(0.9 vs 1.8)이라
  8 km/h · 45%에서 ACSM이 34% 과소추정한다.

어느 쪽이 옳다고 고르지 않는다. 벌어지는 폭이 곧 불확실성이다.

## PR CI

pull_request에서 test → typecheck → build. 지금까지 main 푸시에서만 돌아
머지 전에 걸리는 게 없었다.

## 그 외

- toAscentInput()으로 날 숫자 → 브랜디드 타입 변환을 코어에 두고, 웹 상태를
  AscentParams로 통일해 번역 계층을 없앴다.
- core exports가 dist 대신 src를 가리킨다. dist를 가리키면 빌드를 잊은 순간
  타입체크가 낡은 산출물을 본다(이번에 실제로 걸렸다).

테스트 52개. 브라우저에서 URL 왕복·링크 복사·거부 배너·모델 대조 확인, 콘솔 에러 없음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M2WbzDGnqbw7gxfQSqucbA
@cbcruk
cbcruk merged commit d26602b into main Aug 14, 2026
1 check passed
@cbcruk
cbcruk deleted the claude/project-start-b3jyjo branch August 14, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants