Skip to content

공용 controller에서 기능별 binding으로 — A15 shell 책임 정리 (2026-09-10)

  • 기간: 2026-09-10, 한 작업. 오너 지시: “바벨릭 #1531 진행해줘. 나한테 물어보지 말고 phase 끝까지 완주”.
  • 랜딩: 앱 PR #1550 → release/v0.18.0, merge 290eadb9b808cbbc85d69fde09600e79b925ee9c, 2026-09-10 20:39:38 KST. 문서 PR #57. DB·마이그레이션·Edge·배포 변경 없음. staging/Production 미승격.
  • 설계서: #1531 실행계획, 채택/기각과 예상 효과 포함.
  • 정본: feature 통합 §10, 앱 src/react/features와 app.tsx/appController.tsx/양 플랫폼 root.
  • 도구: 앱 tests/browser/a15·u02·u03, 기존 node test/타입/경계 검사. work의 추출·분류·mutation 스크립트는 임시 산출물이며 앱 실행 코드가 아니다.
  • 게이트: Phase별 npm run check, A15 실제 Chromium 7개, U02 22개, U03 25개+실제 browser zoom 4개. 최신 release 00a93ad3 실제 통합 후 precheck 2분24초·3,579 pass/0 fail/88 조건부 skip, 정적·미사용·앱 빌드·산출물 통과. Merge Check 성공·release HEAD/merge ancestry 확인. 일반 Merge Check이며 full CI 성공을 뜻하지 않는다. DB 변경이 없어 이번 작업의 별도 pgTAP 실행은 없음.
  • 버그리포트: 없음. 운영 장애를 새로 단정하지 않은 구조 이식 작업이다. owner 정리·늦은 응답은 회귀 경계로 확인했다.
  • 계약: feature-resource-integration §10, 소유권 장부 A15 증거.

Phase 현황

Phase내용상태
Phase 1선행 7개 포함·실제 책임/기준선완료 · 기준 b0fa93ec
Phase 2기능별 상태·명령·구독·화면 이전완료 · 151217fd
Phase 3공용 context·재수출·root 정책 제거완료 · eb17ad1c
Phase 4실제 화면·편집·수명주기 검증과 문서 인계완료 · 2147101c, precheck 정리 7aebc993

네 Phase를 앱 PR #1550 하나로 통합했다. 각 Phase check와 실제 브라우저 증거를 보존하고, 최신 release를 합친 최종 커밋 7aebc993에서 필수 precheck를 통과했다. 큐는 검사한 merge 290eadb9를 그대로 반영했다.

1. 배경

홈에서 기록 상세를 열고 수정한 뒤 돌아오는 기능은 owner resource와 공통 편집기로 이미 분리돼 있었다. 그런데 화면 연결은 appController 2,650줄·mobileApp 2,213줄·desktopApp 1,193줄과 하나의 넓은 context가 계속 맡았다. 직접 선행 A10/A11/A12/A13/A14/U02/U03의 최종 PR가 착수 release에 포함됨을 확인했다.

2. 문제 제기

한 화면 변경이 공용 전달 객체와 양 플랫폼 root에 전파됐다

실제 전달 필드는 계획의 197개보다 늘어난 206개였다. 운동 진입·카탈로그·직접 기록·온보딩·달력 선택·표시 데이터와 UI 구독이 root에 함께 있었다. 파일 줄 수보다 이 책임 혼합이 문제였다. Production 공개 페이지 HTTP 200은 확인했지만 로그인 사용자 성능이나 운영 장애의 재현으로 보지 않았다.

3. 해결 방안

원칙

D1(2026-09-10): 기존 owner resource·editor·명령을 재사용해 모든 Phase와 release 통합까지 진행한다. 전역 event bus나 service locator를 만들지 않는다.

접근

방안판단
기능별 명시적 계약과 실제 hook/selector/command 소유 이전채택. feature의 변경 위치와 해제 책임이 일치한다.
공용 context를 여러 중첩 객체로 다시 포장기각. 정책·구독을 root에 남긴다.
범용 등록기/전역 event bus기각. 의존성과 해제 순서를 숨긴다.

4. 적용한 내용

Phase 1 — 실제 기준선

착수 release b0fa93ec에서 check 3,572 pass/0 fail/70 조건부 skip. 기존 원본·편집·owner 계약과 직접 선행 SHA를 고정했다.

Phase 2 — 기능별 실제 소유 이전

active/completed workout·plan·calendar·home·catalog·PR/volume·profile/onboarding/import·social/group binding으로 이전했다. 플랫폼 root는 구독을 개별 binding에 넘기고 route/overlay를 조립한다. store/runtime를 중복 생성하지 않는다. 직접 기록은 owner 전환 때 비우고 늦은 답장을 거부한다. 초안 lifecycle은 준비된 owner만 복원하고 background listener를 해제한다.

Phase 3 — 공용 전달 경로 폐기

renderCtx·ControllerRenderContext와 테스트 전용 재수출을 삭제했다. app children은 ReactNode이며 각 binding은 전체 controller 타입을 가져오지 않는다. root의 PR 조회/오류 처리·활성 상세 동기화를 PR binding으로 옮겼다. 공통 controller의 명시적 any 허용도 삭제했다.

Phase 4 — 실제 행동과 계약

A15 Chromium 7개는 실제 React lifecycle·owner runtime/store와 실제 Home 화면/탐색 binding을 사용한다. Home 데이터 변경, 현재 owner 명령, 탭 왕복, overlay 해제 후 지연 응답, 반복 mount/unmount, 복원 준비, 계정별 직접 기록, read failure 중 초안 보존을 확인한다. U02 22개와 U03 29개는 기존 편집/저장 fixture의 입력·포커스·원본 id·저장 중 추가 입력 보존을 확인한다. 실제 외부 API 전구간 검증과 구분한다.

주요 결정과 그 근거

feature 계약은 좁은 입력을 선언한다. Home에 남은 gymData는 홈 표시용 5종 필드로 제한했고 다른 feature가 이를 빌려 쓰지 않는다. 기록표·검색·소셜·WorkoutFlowBinding에는 필요한 데이터만 직접 전달한다. 전송/DB/원본 권위와 저장 성공 의미는 바꾸지 않았다.

작업 중 드러난 것

  • source 검사 14개가 이동 전 root만 읽어 실패했다. 실제 소유 파일로 재조준하고 기존 단언을 유지했다. 감사 목록에는 manifest에 있는 대상만 선언했다.
  • 기존 full Chromium 실행 파일에서 spawn UNKNOWN이 발생해 zoom 4개가 시작 전에 실패했다. 원인은 미확인이다. U03에서 이미 검증한 동일 Chromium 작업용 실행 파일을 기존 U03_CHROMIUM_EXECUTABLE로 지정해 4개 모두 통과했다. 공유 설치나 테스트 의미를 수정하지 않았다.
  • 필수 precheck에서 이전 후 소비가 끝난 반환값5개·import4개를 찾아 제거했고, 해결된 desktop 미사용 허용도 삭제했다(7aebc993). 최초 precheck는 갱신 전 원격 참조를 보고 중단했으며, fetch 후 최신 release 00a93ad3를 실제 merge한 최종 커밋으로 다시 실행했다.
  • 이중 키 호환은 378곳 그대로이며 이동 6곳의 위치만 갱신했다. A15 이전분 UI bridge 13곳은 A16 대상이다.
  • 이번 신규 미분류 65개는 기능/계층 규칙으로 등록했다. 전체 기존 미분류 118개·미등록 cron 1개·durable 장부 행 누락 2개는 다른 작업의 범위이며 이번 완성 증거로 세지 않는다.

5. 적용 결과

항목전 → 후 / 실제 결과
공용 전달값실제 206 → 0
appController 줄 수2,650 → 1,491
mobileApp / desktopApp 줄 수2,213 / 1,193 → 67 / 67
Phase 2·3·4 전체 check각각 3,579 pass / 0 fail / 70 조건부 skip
신규 binding 단위Desktop 4 / Mobile 3, 모두 통과. React hook harness이며 DOM 검사 아님
최종 precheck / Merge Check2분24초 · 3,579 pass/0 fail/88 조건부 skip · Merge Check 성공
release 통합앱 PR #1550 · 290eadb9, 2026-09-10 20:39:38 KST
실제 browserA15 7/7 · U02 22/22 · U03 25+4/29
잘못된 구현 검출feature mutation 7건 검출·원복·재통과
등록·소유 누락A15 신규 65 → 0; 기존 전역 누락은 별도
Production / native / 실기기미실행. 공개 응답 HTTP 200과 실제 인증/저장을 구분

6. 이번 개선으로 향상된 것

기능 변경과 해제 책임을 같은 모듈에서 확인할 수 있다

Home 데이터와 명령을 바꾸는 실제 예시는 root import 없이 해당 binding과 화면에서 실행된다. 기능의 selector·modal·요청 해제가 한 곳에 모였고, root에는 owner/auth/route·feature 조립이 남았다. 성능 개선 수치를 추정하지 않았다.

남은 것

A16은 UI bridge/mapper의 기존 느슨한 타입·dual shape를, U05는 실제 import graph와 초기 bundle·구독 비용을, R01은 제품 호출자 없는 completedWorkoutCommands.save/delete와 source 단언의 최종 퇴역을 담당한다. 자세한 실제 파일·삭제 조건은 계약 §10 표에 있다. staging/Production 승격·전체 full CI·배포·이슈 종결은 이번 release 통합 완료와 구분한다.