fix: 컴포넌트 binding audit + 14건 수정 + props 11개 추가#157
Merged
Conversation
전체 컴포넌트의 props ↔ SDK 결합 audit 결과를 반영. SDK 옵션 저장 패턴(A: _mapOptions 분리, B: setX 부수효과, C: _<key>_changed, D: 일방향/static)을 정밀화하고, 발견된 버그를 수정하며, 누락 옵션을 노출. 핵심: - useControlledKVO/useKVO가 Map의 _mapOptions로 정확히 라우팅 - setter 우선순위 setX > setOptions > set (mapTypeId registry 전환 등) - onInit/onIdle/onTilesloaded을 useLayoutEffect로 이동 (init 발화 누락 방지) - InfoWindow를 per-key useControlledKVO로 전환 (인라인 객체 폭주 방지) - TrafficLayer autoRefresh undefined 가드 - GroundOverlay 미발화 이벤트 타입 축소 - CustomOverlay equality + pane static 명시 - logoControl 일방향 controlled 분류 정밀화 - 신규 props: cursor/tilt/rotation/tileDuration/repeatX/gl/customStyleId/useStyleMap/crossOrigin/simplifyLevel - 신규 internal hook useStaticProp (dev mode warn) 회귀 테스트 121 -> 147개. CompassControl은 SDK 클래스 정의 부재로 보류 결정. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
리뷰에서 도출된 6개 Major + 8개 Minor 항목 처리. Group A (안전한 즉시 수정): - useStaticProp: dev/prod 함수 선택 패턴으로 production DCE 가능 (effect 슬롯 0) - kvoEquals: 양쪽 어느 쪽이든 equals 있으면 시도 (literal vs instance 비대칭 해소) - logoControl false warn: ref 가드로 한 번만 출력 + 회귀 테스트 3건 Group B (측정으로 검증): - NaverMap 38개 useControlledKVO: 단일 토글 36 get + 8 set, 16ms 내. 이론적 우려 대비 실측 OK - InfoWindow per-key 동등성: bulk vs per-key 모두 1 draw + 1 resize. SDK _drawRequired 플래그가 중복 draw 방지 Group C (이벤트 등록 시점 정책): - InfoWindow onOpen/onClose를 useLayoutEffect로 이동. open() 호출 직후 동기 발화하는 'open' 이벤트를 listener 미등록으로 놓치는 timing bug 해소 (fix-13과 동일 원리) - 정책 명문화: SDK 동기 발화 = useLayoutEffect, 사용자 인터랙션 = useEffect Group D (Minor 정리): - console.warn 메시지 영문 우선 + 한국어 보조 (한영 병기) - StrictMode 더블 마운트 회귀 테스트 7건 추가 - *Options 런타임 audit (zoomControlOptions 등 5개) — 모두 controlled 동작 확정 - useNaverMapStatic vs satisfies 패턴 정책 명시 (3+ 호출 wrapper, 1-2 satisfies) 테스트: 147 -> 160 (+13). 빌드 통과. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2차 리뷰 결과 처리:
1. fix-16 회귀 테스트 catch 능력 보강 — 분석 결과 솔직히 인정
- test-utils의 mock InfoWindow.open()이 'open' 이벤트 동기 발화하도록 수정
- 테스트가 onOpen 호출을 직접 검증
- 결론: 같은 컴포넌트 내 hook 등록 순서가 React에서 보장되므로 useLayoutEffect/useEffect
차이가 사실상 없음. fix-16은 방어적 보수책. fixes/16에 한계 + 의도 명시
2. vitest.config.ts headless 잠금
- PWDEBUG/HEADED 환경변수 명시 차단
- browser.headless: true + launch.headless: true 이중 명시
- launch.devtools: false
3. SDK 동기 발화 이벤트 audit (미커밋, research/bindings/sync-fired-events-audit.md)
- OverlayView의 added/removed/draw 이벤트는 우리 prop으로 미노출 → 영향 없음
- 모든 노출 props가 적정 timing으로 처리됨 확인
테스트: 25 files, 160 tests 통과.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vitest browser mode가 Playwright Chromium 헤드리스 브라우저를 launch하므로, 'pnpm exec playwright install --with-deps chromium' 단계 추가. 이전엔 호스트 환경에 설치되어 있어서 우회됐을 가능성. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SDK는 GROUND_DOMEVENTS의 click 리스너에서 250ms·5px 이내 연속 클릭을 _getSingleClickType(maps.beautified.js:9014)으로 합성 dblclick으로 trigger한다. 이전 타입 축소 라운드가 이를 미발화로 오판해 onDblclick을 제거했으나, deobfuscation 재검증 결과 실제 발화하므로 복원한다. mouseover/mouseout/mousemove 제거는 유지(GROUND_DOMEVENTS 미포함). - GroundOverlayEvent에 'dblclick' 추가 + 근거 주석 - ground-overlay.tsx onDblclick 리스너/deps 복원 - 5종 이벤트 바인딩 런타임 회귀 테스트 추가 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
event-types.spec.ts는 expectTypeOf/toEqualTypeOf만 쓰는 순수 타입 테스트인데, vitest.config에 typecheck 설정이 없고 tsconfig가 spec을 exclude해 런타임 no-op로 0 assertion 통과해왔다(dead test). GroundOverlay dblclick 오제거를 잡지 못한 원인. - vitest.config.ts: test.typecheck 활성화 (event-types.spec.ts 평가) - tsconfig.test.json: typecheck 전용 (메인 tsconfig는 빌드 위해 spec exclude 유지) - event-types.spec.ts: dblclick 포함 5종으로 단언 정정 검증: dblclick 임시 제거 시 typecheck가 FAIL 처리함을 확인. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- tileDuration: controlled → static. SDK에 setTileDuration/tileDuration_changed가
없고 타일 생성 시에만 읽히므로(maps.beautified.js:5568/5642/6468) 런타임 변경이
무시된다. useControlledKVO → useNaverMapStatic으로 이동.
- cursor: SDK가 드래그 중 손 커서로 자체 토글하고 초기화 시 setCursor("open")을
호출해 controlled 값과 충돌할 수 있음을 JSDoc에 명시.
- TrafficLayer autoRefresh: undefined는 정지가 아니라 'SDK 기본값 위임'이며,
true→undefined로는 멈추지 않고 false를 명시해야 정지함을 JSDoc에 명시.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
코드 리뷰에서 '수정 전에도 통과 / 얕은 검증'으로 지적된 테스트를 실제 회귀를 잡도록 보강. 각 항목은 대상 코드를 임시로 깨뜨려 FAIL을 확인한 뒤 복원해 검증했다. - custom-overlay: fromCoordToOffset을 좌표 의존으로 + setPosition spy (기존엔 고정 좌표라 draw 미호출에도 통과하는 동어반복) - info-window: open/close 분기를 toBeDefined 대체 → spy로 호출/인자 단언. fix-16 ordering은 hook 선언 순서상 layout/passive 구분 불가라 검증 범위를 'onOpen 호출 + 정확한 인스턴스/event명 등록'으로 정직하게 하향. - use-kvo: removeListener no-op mock → 실제 제거 + unmount 후 콜백 미호출 단언 - strict-mode: 더블마운트 발생(toHaveLength(2)) + 첫 인스턴스 cleanup 단언 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- use-controlled-kvo / use-kvo: 읽기 경로와 쓰기/구독 경로의 대상 선택 조건이 서로 달라 'SDK가 동일 슬롯에 라우팅한다'는 전제에 의존함을 주석화. (동작 변경 아님 — 미래 SDK 변경 시 주의점 명시) - changeset: dblclick 유지/tileDuration static/테스트 카운트 정정 - gitignore: __screenshots__ (vitest 산출물), examples/ (작업 중 — stash 보관, 별도 브랜치로 분리 예정. lint 오염 방지) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
playwright install --with-deps(sudo apt-get install)가 GitHub 호스티드 ubuntu에서 apt 락으로 무한 hang(35분+ 누적 run 다수). 호스티드 러너는 chromium 런타임 라이브러리를 대부분 사전 포함하므로 --with-deps 제거. - --with-deps 제거 (필요 lib 누락 시 test 단계에서 빠르게 드러남) - step timeout-minutes: 10 (재발 시 빠른 실패) - concurrency cancel-in-progress: 중복/hang run 자동 취소 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI의 playwright install이 chromium 다운로드 직후(extract 추정) 무한 hang하는 문제가 반복됐다(--with-deps 무관, 다운로드 자체는 2초). 단위 테스트 171개는 vi.mock 기반이라 real 브라우저가 불필요하므로 happy-dom으로 전환한다. - vitest.config.ts: browser mode → environment 'happy-dom', smoke 제외 - vitest.browser.config.ts: smoke 전용 분리(실제 SDK 로드 통합 테스트, real 브라우저 필수) - package.json: test:browser 스크립트 + happy-dom devDep - ci.yml: playwright install 단계 완전 제거 (hang 원천 차단, CI 고속화) 검증: 기본 test 181 passed(happy-dom) + typecheck OK, test:browser 3 passed(smoke). smoke 통합 테스트는 로컬/릴리스 시 pnpm test:browser로 검증. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
전체 컴포넌트의 props ↔ SDK 결합 audit + 2회 코드 리뷰 후속 + 외부 패턴 리서치까지 반영한 종합 라운드.
SDK 옵션 저장 패턴(A:
_mapOptions분리 / B:setX부수효과 / C:_<key>_changed/ D: 일방향·static) 정립.3 commits:
2480ec0(1차) →f470669(1차 리뷰 후속 A-D) →7348f88(2차 리뷰 후속).라이브러리 코어
setX > setOptions > set. Map은 옵션을 별도_mapOptionsKVO에 저장하므로 직접target.set()으론 SDK가 무시. setMapTypeId처럼 부수효과 있는 setX는 우선 사용._mapOptions로 라우팅.useKVO(map, 'draggable')등 정상 발화 수신.컴포넌트 버그 수정
minZoom/maxZoomstatic → controlledonInit/onIdle/onTilesloadeduseEffect→useLayoutEffect(init 박빙 방지)logoControl일방향 controlled 분류 정밀화 + dev warn (한 번만, ref 가드)autoRefreshundefined 시endAutoRefresh호출 안 함setOptions(obj)일괄 → per-keyuseControlledKVOonOpen/onCloselistener를useLayoutEffect로 (방어적, fix-16)panestatic 명시 + position/anchorkvoEquals비교신규 controlled props (11개)
cursor,tilt(+onTiltChanged),rotation(+onRotationChanged),tileDurationrepeatX,gl,customStyleId,useStyleMapcrossOriginsimplifyLevel개발자 경험
useStaticProp: dev/prod 함수 선택 패턴으로 production DCE 시도. undefined grace period로 비동기 prop 패턴({config?.gl}) false positive 방지.'static prop','useStaticPropDev', 한국어 dev warn 모두 0건 ✓이벤트 등록 시점 정책 (
fixes/16)useLayoutEffect로 등록useEffect등록 OKresearch/bindings/sync-fired-events-audit.md)회귀 테스트
121 → 160 tests 통과 (25 files, +39 신규).
신규 회귀 테스트:
naver-map-controlled.spec.tsx— fix 01/02/05/13/15info-window.spec.tsx보강 — fix 08, fix-16 timingtraffic-layer.spec.tsx보강 — fix 07event-types.spec.ts강화 — fix 09 (toEqualTypeOf)custom-overlay.spec.tsx보강 — fix 10hooks/__tests__/use-kvo.spec.tsx— fix 06hooks/__tests__/use-static-prop.spec.tsx— fix 14 + grace period__tests__/strict-mode.spec.tsx신규 — StrictMode 더블 마운트 7건__tests__/kvo-equals.spec.ts— 양쪽 비교외부 패턴 리서치 (
related-patterns-research.md)4개 라이브러리 비교 (
@react-google-maps/api,react-map-gl,react-leaflet,@react-three/fiber):useIsomorphicLayoutEffect+ per-prop diffinitial-/default-prefix가 가장 깔끔 — 0.3에서 하이브리드 도입 검토initial-prefix,useKVOFrame(RAF throttle), DCE 가드 표준화Breaking 가능성 (사실상 없음)
pane변경 무시 명시Changeset
patchbump (0.2.0 → 0.2.1).보류 결정 (
decisions/)🤖 Generated with Claude Code