종목 구 컬럼 4종 퇴역 마무리 — 진실 소스 이동·클라 독립·물리 삭제 (2026-09-01)
- 기간: 2026-09-01 (1세션 — #1060 종결 직후 후속 트랙)
- 랜딩: PR #1075 (Phase 1·2,
20260901994000) · PR #1079 (Phase 3,20260901995000) — 둘 다 Production 적용·사후 실측 완료(2026-09-01) - 설계서: 이슈 #1074 본문·쓰레드 (Phase 계획 + 오너 지시 "그냥 지금 바로 없애줘" = Phase 3 즉시 실행)
- 정본:
supabase/migrations/20260901994000_exercise_profile_normalization_v1.sql·20260901995000_legacy_exercise_columns_drop_v1.sql - 도구: Management API 쿼리 러너(pg_proc 전수 실측·사후검사) · 정본 추출-치환 빌더(schema.sql 최신 본문 → 치환 횟수 전수 검증) · 테스트 스위퍼 3종
- 게이트: pgTAP 전량(신 계약 정렬) + 빈 계정 여정 + 브라우저 여정 21종 (migration-smoke 레인, run 33509345686 그린)
- 버그리포트: 없음 (사고 전 예방 트랙 — #1060이 남긴 read 호환 창의 계획된 마무리)
- 계약: create 계열 RPC 반환·읽기 RPC 페이로드의 구 키는 파생값/
''로 유지(구 번들 호환) —exercise_legacy_read_keys_v1
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 진실 소스 이동 — 구 컬럼 3종을 파생 사본화(zy 정규화 트리거+전행 백필), 이동 분기 퇴역 | ✅ PR #1075 |
| Phase 2 | 클라이언트 독립 — 직접 select 4종 제거, 측정 단위 파생 폴백 | ✅ PR #1075 |
| Phase 3 | 물리 삭제 — 참조 함수 20종 재발행 후 컬럼 drop, 수호 v5·기록 필드 트리거 v3 | ✅ PR #1079 |
1. 배경
#1060(종목 등록·정체성 재설계)이 종결되며 남긴 마지막 잔여였다. 종목 테이블에는 같은 사실이 두 벌씩 적혀 있었다 — 부위는 타겟 부위 비율(target_muscles)과 메인/보조 부위 컬럼(primary_part·secondary_parts)에, 측정 단위는 기록 필드(recording_fields)와 요약 컬럼(measurement_type)에, 이름 식별자는 UUID와 slug에. #1060에서 등록·정체성의 진실은 한 벌로 확정됐지만 사본 컬럼 4종은 "배포 창 뒤 드롭" 예정으로 남아 있었고, 오너가 즉시 삭제를 지시했다.
2. 문제 제기
두 벌 기록은 어긋날 수 있고, 실제로 어긋났다
사본 쪽(요약 컬럼)을 고치면 진실 쪽(기록 필드)이 안 바뀌거나, 반대로 진실을 고쳐도 사본이 낡은 채 남는 구조였다. 과거 이 어긋남을 잡는 수리 트랙(20260820230000)과 감시 pgTAP가 별도로 필요했을 만큼 실제 사고 축이었다.
죽은 컬럼이 코드 전층에 뿌리내리고 있었다
Production 실측(pg_proc 전수 스캔)으로 구 컬럼을 읽는 서버 함수 20종을 확정했다. 클라이언트도 관리자 전량 읽기·소유 커스텀 목록·수정 페이로드가 컬럼을 직접 조회·기입하고 있었고, 테스트 픽스처 수십 곳이 slug 문자열로 시드 종목을 찾고 있었다.
3. 해결 방안
원칙 (오너 지시)
- 진실은 두 축뿐 — 타겟 부위 비율 + 기록 필드. 부위 표기·측정 단위가 필요한 소비자는 저장된 사본이 아니라 그때그때 파생해 쓴다.
- Phase 3은 배포 창을 기다리지 않고 즉시 실행(열린 구 번들 탭의 직접 조회 실패는 수용 — 새로고침까지).
접근
- 파생 공식을 서버 정본으로: 부위 한글 라벨 =
exercise_region_ko_v1(리포트 축과 같은 어휘), 측정 단위 =measurement_type_from_recording_fields_v1. Phase 1에서는 이 파생을 트리거로 사본에 계속 적어 어긋남 자체를 소멸시키고, Phase 3에서 사본을 물리 삭제했다. - 시드 slug 해석은 동결 해시로: 시드 시대 공식 종목의 UUID =
brid_uuid_v1(brid_for_official_exercise_v1(slug))(#1060 D4 동결 덕). slug 컬럼 없이도 slug 문자열을 UUID로 정확 해석 — 즐겨찾기 시드·리맵·수십 테스트가 계약 무변경으로 이행됐다. - 구 번들 호환은 응답 키로: 읽기 RPC와 create 계열 반환에 구 이름의 키를 파생값/
''로 계속 실어, 배포된 옛 화면이 응답을 읽다 깨지지 않는다.
4. 적용한 내용
Phase 1 — 진실 소스 이동 (PR #1075)
20260901994000: 부위 라벨 공식 신설 + zy 정규화 트리거(어떤 기입이든 사본 3종을 파생값으로 덮어씀) + 전행 백필(불일치 잔존 0 실측). 측정 단위 이동 편의 분기(사본 기입이 진실을 역으로 재유도하던 것) 퇴역, 등록 함수 멱등 비교에서 사본 3종 제거.
Phase 2 — 클라이언트 독립 (PR #1075)
앱 공용 select 목록에서 구 컬럼 4종 제거, normalizeExercise의 slug 필수 해제 + measurementTypeFromFields 파생 폴백 신설.
Phase 3 — 물리 삭제 (PR #1079)
20260901995000: 참조 함수 20종 재발행(파생식·동결 해시 치환, 정본 추출-치환 빌더로 치환 횟수 전수 검증) → 컬럼 4종 drop → 정체성 수호 v5(slug 규칙 퇴역)·기록 필드 트리거 v3(빈 프로필 23502 거부·정렬만)·zy 트리거 퇴역 → postcheck 4종. 클라는 관리자 전량 읽기 select 절제 + 부위/측정 단위 파생, 수정 페이로드의 구 컬럼 키 제거.
작업 중 드러난 것 (CI 8차 만에 그린 — 각 회차가 다른 층을 적발)
- postcheck 오탐: 부분 문자열
'e.primary_part'가 CTE 파생 컬럼(reference.primary_part)에 걸림 → 단어 경계 정규식으로. - 트리거의 행 변수 참조는 별칭 스캔에 안 걸린다: 필수 입력 기본값 트리거가
new.measurement_type을 읽고 있었음 — 실측 스캔·postcheck 둘 다 별칭 패턴만 봤다 → 파생식 재발행 +new/old패턴 등재. update of <컬럼>트리거는 그 컬럼 드롭에 연쇄 삭제된다: 기록 필드 정규화 트리거가 선언 목록에 measurement_type을 끼워 두어 컬럼 drop cascade가 트리거째로 조용히 삭제 — 정렬·빈 프로필 거부가 통째로 사라졌다 → 드롭 직후 신 컬럼 목록으로 재창설. 그 트리거의 자동 채움에 기대던 인서트 12곳(등록 엔진·모트라/워덥 인입·픽스처 9곳)에 기록 필드 명시.- 치환식은 select 목록에서 암묵 컬럼명을 잃는다:
exercise.measurement_type→ 파생식 치환 시 하류의expected.measurement_type참조가 42703 → 별칭 6곳 복원(단, concat_ws 인자 자리 2곳은 별칭 불가). - 검증 코드 자체의 구 계약: 화면 RPC 어댑터의 케밥 slug 강제, e2e 픽스처 select·종목 고르기 필터 — 전부 프로필 기반으로 이행.
5. 적용 결과
| 항목 | 전 | 후 |
|---|---|---|
| 종목 진실 축 | 부위 2벌·측정 단위 2벌·식별자 2벌 | 타겟 부위 비율 + 기록 필드 2축 (사본 컬럼 0) |
| Production 구 컬럼 | 4종 존재 | 0 (사후 실측) |
| 구 컬럼 참조 서버 함수 | 20종 (pg_proc 실측) | 0 (postcheck 상시 단언 — 별칭·행 변수 패턴) |
| 빈 기록 프로필 등록 | 측정 단위에서 무음 채움 | 거부(23502) — Production 빈 프로필 행 0 실측 |
| 구 번들 응답 호환 | — | 읽기·create RPC의 구 키 파생값/'' 유지 (exercise_legacy_read_keys_v1) |
| 게이트 | — | CI 그린(run 33509345686): pgTAP 전량·빈 계정 여정·브라우저 여정 21종 / 로컬 2,284 |
미검증: 열린 구 번들 탭의 직접 테이블 조회는 새로고침 전까지 실패한다(오너 수용 사항 — RPC 경로는 호환 키로 무파손).
6. 이번 개선으로 향상된 것
- 어긋남의 존재 가능성 자체가 소멸 — 두 벌 기록이 물리적으로 한 벌이 되어, 사본 드리프트 수리·감시 부채가 구조적으로 사라졌다.
- 파생 공식이 서버 정본 함수로 단일화 — 부위 표기·측정 단위를 쓰는 모든 소비자(리포트·관리자·카탈로그·create 반환)가 같은 공식을 지난다.
- postcheck가 트리거 행 변수·별칭 패턴까지 상시 단언 — 같은 유형의 잔존 참조가 미래 마이그레이션에서 재발하면 랜딩 단계에서 막힌다.
남은 것
- 없음 — 이슈 #1074 [반영완료] 종결. (#1060 계보의 잔여 트랙 전부 완료.)