# 2026-04-09 — engine-bridge v3 라이브 검증 성공 **이슈**: #10 follow-up (engine-bridge v3) **소요 시간**: ~25분 **Context 사용량**: input ~40k / output ~8k tokens (Opus 4.6) ## 결과 🎉 **engine-bridge v3 라이브 end-to-end 성공**. EG-BIM Modeler 실 환경에서 4개 HTTP 엔드포인트 모두 실값 반환 확인. ``` PS> curl http://localhost:38080/health {"status":"ok","port":38080} PS> curl http://localhost:38080/scene {"object_count":4,"document_path":"NewSpace0"} PS> curl http://localhost:38080/camera {"eye":[192.97503478492047,-328.523410937345,170.7271607015133], "target":[33.03212842830112,-72.61476076675402,10.784254344893952], "up":[0,0,1], "fov":45} PS> curl http://localhost:38080/selection {"selected_ids":["ac0380a2-b493-4218-97b7-8017841151c5", "d9a287ee-8d1c-4e32-b879-92575a346ddf"]} ``` 이것이 곧 golden-file 회귀 테스트의 결정성 신호 축이 된다. ## 과정 ### 1. 배포 스크립트 `scripts/deploy-egbim-plugin.bat` 사용자가 더블클릭 한 번에 빌드→복사 가능하도록: 1. `EG-BIM Modeler.exe` 실행 여부 체크 (DLL 잠금 방지) 2. `dotnet build` Debug 3. 기존 `Plugins/Recordingtest.EgPlugin/` (legacy) + 기존 `Plugins/Recordingtest.Sut.EgBim.PluginHost/` 삭제 4. 3개 DLL(+PDB) 복사: - `Recordingtest.Sut.EgBim.PluginHost.dll` (App-specific) - `Recordingtest.Hmeg.Bridge.dll` (HmEG-aware) - `Recordingtest.Bridge.Abstractions.dll` (Generic) 5. 배포 목록 + curl 명령 안내 `HmEG.dll` / `Editor*.dll`은 SUT에 이미 있으므로 복사 안 함. ### 2. 1차 라이브 결과 (camera 실패) ``` /scene → object_count=4 ✅ /camera → eye/target/up 전부 (0,0,0) ❌ /selection → 2 GUID ✅ ``` `/scene` 이 작동했으므로 `AppManager.ViewportManager.RootSpace` 경로는 OK. 즉 `spaceProvider` 정상. `/camera` 는 default fallback. 원인: `HmEgBridgePlugin`의 `viewportProvider = () => this.View`에서 `View`가 항상 null. ### 3. 원인 분석 `EditorPlugin.View` 는 **플러그인이 `Run()` 될 때만 주입**되는 값. 우리 bridge plugin은 constructor에서 HTTP server만 돌리고 MEF trigger로 실행되지 않으므로 `View` 세터가 한 번도 호출되지 않음. 반면 `AppManager` 는 `TriggerStateService.AppManager` 를 통해 얻어지며 SUT 전역 상태라 즉시 접근 가능. 그래서 `RootSpace` 는 동작하지만 `View` 만 null이었던 것. ### 4. Fix — ViewportManager.FocusedViewport 경유 `D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\SubManager\ViewportManager.cs` (read-only) 확인: ```csharp public HashSet Viewports { get; set; } public EGViewport FocusedViewport { get; ... } public ObservableCollection SelectedModels { get; ... } ``` `FocusedViewport` 는 활성 뷰포트를 직접 노출. `viewportProvider` 를 다음으로 교체: ```csharp var vm = AppManager?.ViewportManager; if (vm is null) return null; var focused = vm.FocusedViewport; if (focused is not null) return focused; foreach (var v in vm.Viewports) if (v is not null) return v; return null; ``` ### 5. 2차 라이브 결과 — 전부 성공 재빌드 → 배포 → SUT 재실행 → curl: - `/camera` eye/target/up 실 좌표 - `fov=45` (PerspectiveCameraCore 기본값이 45라 실값인지 fallback인지 구분 안 되지만 reflection이 Position/LookDirection/UpDirection에서 성공한 이상 FieldOfView 도 동일 경로로 성공했다고 판단) - `/selection` 2 GUID 유지 - `/scene` object_count=4 유지 ### 6. 부수 관찰 - `document_path="NewSpace0"` — `FileManager.CurrentFile` 이 빈 파일에 대해 Space 이름을 반환하는 듯. 저장된 `.hmeg` 파일일 때 진짜 경로 확인 필요 (follow-up). - `HmegDirectStateProvider` 의 Selection walk (Space.Children 재귀 + `ISelectable.IsSelected`) 가 잘 동작. 대안 `ViewportManager.SelectedModels` 로 단순화 가능하나 현재 walk 방식이 결정적이고 테스트 친화적이라 그대로 유지. ## 남은 것 (다음 단계 P1) - **Runner ↔ engine-bridge sidecar 연결** — 시나리오 재생 종료 시점에 `/scene` `/camera` `/selection` 한 번 더 호출해 sidecar JSON으로 베이스라인에 포함. `.received.json` 정규화 규칙 추가. 이게 진짜 golden-file 회귀 파이프라인의 마지막 조각. - `document_path` 의 unsaved vs saved 문서 케이스 확인 - (선택) `SelectionManager.SelectedModels` 직접 사용 옵션 비교 ## 관련 - `src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/HmEgBridgePlugin.cs` (viewportProvider 교체) - `scripts/deploy-egbim-plugin.bat` (신규 — 배포 자동화) - `src/Hmeg/Recordingtest.Hmeg.Bridge/HmegDirectStateProvider.cs` (reflection 그대로 재사용) - `docs/hmeg-api-survey.md` (Q1~Q7 완성)