From bad8db5db9d825197f755f25ab10f5ac40f56f83 Mon Sep 17 00:00:00 2001 From: KangJeongmin Date: Mon, 7 Sep 2026 16:11:11 +0900 Subject: [PATCH 1/2] =?UTF-8?q?MSG-582=20fix:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EC=A7=80=EB=8F=84=20RN=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EB=B7=B0=20=EB=A7=88=EC=BB=A4=20=EB=B9=84=ED=8A=B8=EB=A7=B5=20?= =?UTF-8?q?recycle=20=ED=81=AC=EB=9E=98=EC=8B=9C=20=ED=8C=A8=EC=B9=98=20?= =?UTF-8?q?=E2=80=94=20=EC=A7=80=EB=8F=84=20=EC=A1=B0=EC=9E=91=20=EC=A4=91?= =?UTF-8?q?=20=EC=95=B1=20=EA=B0=95=EC=A0=9C=20=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=ED=95=B4=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `@mj-studio/react-native-naver-map` 2.9.0 `RNCNaverMapMarker.removeCustomView()`가 지도 아이콘이 아직 참조하는 비트맵을 먼저 `recycle()`해 GL 렌더 스레드가 `could not lock pixels` 네이티브 abort(SIGABRT)로 앱을 종료시키던 버그. 기존 pnpm 패치 파일에 recycle 제거(`customViewBitmap = null`) 1줄 추가, GC에 맡김 - Kotlin 변경이라 dev client 재빌드 필요 — MOBILE_RUNBOOK 함정 12 신설(시그니처·원인·대응) - STATUS·DECISIONS·spec/MSG-582 영속화 (앱 코드 0줄) Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P --- docs/MOBILE_RUNBOOK.md | 8 +++ docs/STATUS.md | 2 + docs/decisions/DECISIONS.md | 1 + docs/spec/MSG-582.md | 59 +++++++++++++++++++ ...studio__react-native-naver-map@2.9.0.patch | 15 +++++ pnpm-lock.yaml | 6 +- 6 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 docs/spec/MSG-582.md diff --git a/docs/MOBILE_RUNBOOK.md b/docs/MOBILE_RUNBOOK.md index 114497b4..417391d7 100644 --- a/docs/MOBILE_RUNBOOK.md +++ b/docs/MOBILE_RUNBOOK.md @@ -439,6 +439,14 @@ PID=$(lsof -ti tcp:8081 -sTCP:LISTEN); lsof -p $PID | grep cwd # cwd가 지 kill $PID && (cd apps/mobile && npx expo start --dev-client --port 8081) ``` +### 함정 12. 지도 조작 중 앱이 통째로 꺼진다 — 커스텀 뷰 마커 비트맵 recycle (라이브러리 버그) + +**증상** — 붉은 에러 화면 없이 앱이 그냥 사라진다. `adb logcat -d -b crash`에 `SIGABRT` + `Abort message: 'JNI DETECTED ERROR ... bitmap decoding: could not lock pixels'` + `from com.naver.maps.map.renderer.MapRenderer.nativeRender()`가 찍히고, 직전에 `W/Bitmap: Called getDensity() on a recycle()'d bitmap!`가 있다. + +**원인** — `@mj-studio/react-native-naver-map` 2.9.0 안드로이드의 `RNCNaverMapMarker.removeCustomView()`가 지도 아이콘이 아직 참조 중인 비트맵을 먼저 `recycle()`하고 아이콘을 교체한다. 커스텀 뷰 마커(경로 번호·클러스터·미션 이름표)가 언마운트되는 순간 GL 렌더 스레드가 그 비트맵을 잠그려다 네이티브 abort. 줌 변경으로 클러스터가 교체되거나 경로를 해제할 때 확률적으로 재현된다(2026-09-07 실측, 프로세스 uptime 182초). 에뮬레이터·Metro 문제가 아니다. + +**대응** — `patches/@mj-studio__react-native-naver-map@2.9.0.patch`에 recycle 제거가 들어 있다(GC에 맡김). Kotlin 변경이라 **dev client 재빌드**가 필요하다 — 옛 APK를 쓰면 패치 전 코드로 돌아간다. 같은 시그니처가 다시 보이면 APK가 패치 이후에 빌드된 것인지부터 확인한다. + ### 에뮬레이터 재현 성공 경로 (막혔을 때 통째로 다시 밟을 순서) 2026-08-20에 실제로 통한 경로다. 개별 대응이 안 먹으면 이 순서로 초기화한다. diff --git a/docs/STATUS.md b/docs/STATUS.md index 5aa56a59..22d895a9 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -149,6 +149,7 @@ - MSG-572: 업로드 미리보기 공개 범위 선택(웹 MSG-476 ① 이식) — `upload-flow-store`에 **영속 필드 `visibility: VideoVisibility`(초기 `"PUBLIC"`) + `setVisibility`**, `upload-flow-storage` 하위호환(필드 없음→PUBLIC, `VISIBILITY_OPTIONS` 밖 값→진행 없음 — `eventTarget` 패턴 미러), `confirm-input` 유니온 일반 분기만 `visibility` 필수 + `buildConfirmInput` 5번째 필수 인자, `upload-effects` 일반 body `visibility` 명시 전송(행사 body 무변경 — DTO 부재), `preview-screen` 위치 카드 아래 공개 범위 카드(ui-native `Selector type="radio"` + 행 `Pressable accessible role=radio` 44dp, 컨테이너 `radiogroup`, 행사 모드 미노출). 옵션 정본은 video-actions `VISIBILITY_OPTIONS` 재사용. 신규 파일 0·수정 9, `use-upload-mutations` 무수정(MSG-567 교집합 회피), 웹·packages 0줄 - MSG-578: 검색 실연동(웹 MSG-328·412 이식) — `features/search/model/zone-search.ts`(웹 복사 포팅 5함수 + 22케이스 + parity, `zoomForGridFocus` 미포팅), `api/` 옵션 팩토리+얇은 훅 3종(`use-place-search-query` 디바운스 300ms+`searchNow` · `use-trending-query` · `use-zones-query` Infinity 캐시 — 인증 게이트 없음), `ui/search-result-lists.tsx`(`TrendingList`·`GridResultList`·`PlaceResultList` 표시 전용), `search-screen.tsx` 재작성 — idle = **인기 검색어 → 전체 지역(`useExploreRegionsQuery` + `selectRegion` + `router.back()`, 홈이 그 지역 첫 격자 중심으로 `moveTo`)**, 입력 중 = 격자 섹션 → 장소 결과, placeholder "장소, 격자 검색". **최근 검색·최근 방문 섹션 제거**(사용자 결정, 웹 HomeSearchBox 미러) → `search-store`·`recent-history`·`entities/region/model/regions`·`match-region` 삭제. 홈 복귀는 `map-home/model/home-focus.ts`(`parseHomeFocus`·`homeFocusParams` — `/home?lat&lng&gridId&bounds&ts` 5키 항상 동봉, 우선순위 gridId > bounds > point) + GridMapRef `focusTo`(줌 <16이면 16 보장)·`fitBounds`(`animateCameraWithTwoCoords`) + 홈 `searchHighlight` state → `GridMap highlightCell`(마지막 1건 유지·교체). `shared/use-debounced-value.ts` 웹 시그니처(`{debounced, flush}`)로 교체. 모바일 테스트 1321 → **1349** - `ui-native` 비파괴 확장 이력: `Avatar.fallbackIcon`(MSG-426, 미지정 시 렌더 불변) — `index.ts` diff 0줄 합의는 계속 유효. · `VideoCard.accessibilityLabel`(MSG-446, 미지정 시 렌더·낭독 불변) · `VideoRow`의 `accessibilityRole`을 `onPress` 조건부로 교정(MSG-446 — 목적지 없이도 버튼으로 낭독되던 결함, `VideoCard`와 같은 규칙) +- MSG-582: 네이버 지도 RN 라이브러리(`@mj-studio/react-native-naver-map` 2.9.0) **pnpm 패치 2건째** — `RNCNaverMapMarker.removeCustomView()`의 `customViewBitmap.recycle()` 제거(참조만 끊고 GC에 맡김). 커스텀 뷰 마커(경로 번호·클러스터·미션 이름표) 언마운트 시 GL 렌더 스레드가 recycle된 비트맵을 잠그려다 네이티브 abort로 앱이 통째로 꺼지던 라이브러리 버그. 패치 파일은 `patches/@mj-studio__react-native-naver-map@2.9.0.patch` 하나(MSG-445 onLoad `topLoaded` 패치와 같은 파일). Kotlin 변경이라 dev client 재빌드 필요 — 런북 함정 12 ## 티켓 이력 (2026-08-13 이후 — 티켓당 한 줄 append) @@ -235,3 +236,4 @@ - MSG-580: [모바일] 경로추천 칩 목록 상태에서 지도에 코스 경로선·번호 마커가 안 그려짐 — 사용자 QA("지도에 경로가 표시가 안 된다")로 하네스가 티켓 생성. 앱은 MSG-427 AC 10대로 선택 코스만 라인을 그렸고 웹은 목록 전체(`buildCourseRoutes`)로 갔던 파리티 갭. `courseRoutesOf` 신설 + GridMap `routes` prop(단일 `route`와 합류 렌더). 코스별 이름표는 미이식(Q4 유지). 경량 실행, 실기 확인 - MSG-581: [모바일] 광역 줌(bbox > 0.5°)에서 테마 칩 미션 목록 0개 — 경로선 미표시·클러스터 16/시트 0 불일치(세 칩 공통) — 사용자 QA 3건으로 하네스가 티켓 생성. 원인은 미션 조회의 0.5° 초과 `enabled=false` + MSG-579가 비활성 placeholder를 비운 결과(예고된 후속). `viewportQueryArgs` 대신 `clampBoundsToSpan`으로 요청 bbox 클램프. 경량 실행, 두 번째 AVD + Metro 8083 실기 4장 - MSG-578: [모바일] 검색 실연동 — 인기 검색어·장소 검색·격자 검색(웹 MSG-328·412 이식). MSG-297 목 화면(구 목록 로컬 매칭)을 실 API 3종 + zones 로컬 역파싱으로 교체하고 결과 탭을 기존 `/home` params 복귀 경로 확장(격자 줌 보장+점선 하이라이트, 구역 fitBounds)으로 배선. 검증 중 사용자 결정으로 **최근 검색 섹션 제거**(웹 따라가기, 스토어 삭제). 검증 S10이 `navigate("/home")` 리마운트 버그(선택 지역 소실)를 잡아 `router.back()`으로 수정. 전체 지역 탭은 DTO에 좌표가 없어 시트용 격자 목록의 첫 격자 중심으로 지도 이동(A1 승인 → 사용자 번복 "지도도 옮겨야") +- MSG-582: [모바일] 지도 조작 중 앱 강제 종료 — 사용자 QA("갑자기 앱이 꺼지는데")로 하네스가 티켓 생성. `adb logcat -b crash`의 `could not lock pixels` + `recycle()'d bitmap` 시그니처로 `@mj-studio/react-native-naver-map` 안드로이드 `removeCustomView()`의 recycle 선행 버그 특정. 앱 코드 0줄, pnpm 패치 1줄 + 런북 함정 12. 경량 실행, 패치 APK 재빌드 후 실기 콜드 스타트 확인 diff --git a/docs/decisions/DECISIONS.md b/docs/decisions/DECISIONS.md index a73f9c88..61917a76 100644 --- a/docs/decisions/DECISIONS.md +++ b/docs/decisions/DECISIONS.md @@ -657,3 +657,4 @@ | 2026-09-07 | MSG-578 | 결정(codex 리뷰 P2 ①): 검색 결과 → 홈 복귀를 `router.navigate`에서 **`router.dismissTo`(POP_TO)** 로 — 기존 홈 인스턴스로 돌아가 params만 갱신 | 실기: 줌 18에서 격자 검색 후 복귀 시 줌이 16으로 리셋(홈 리마운트 → GridMap `initialZoom`). SDK `animateCameraTo`는 zoom 생략 시 `scrollTo`라 `focusTo`는 무죄. `dismissTo` 뒤 임시 마운트 로그로 리마운트 0회·줌 18 유지·하이라이트 정상 확인. 빌더의 "홈 마운트 유지" 전제가 틀렸고 params 병합 관찰만 맞았다 | | 2026-09-07 | MSG-578 | 결정(codex 리뷰 P2 ②): 홈이 `selectedRegion`(싱글턴)이 채워지면 테마·미션·격자·이벤트 선택을 비우는 effect 추가 — 검색의 전체 지역 탭이 칩 활성 중에도 지역 시트에 닿게 | `homePanelKind`는 격자 > 상세 > 칩 목록 > 기본 순이라 칩이 켜져 있으면 고른 지역이 헤더에 못 온다. 선택 3종은 홈 로컬 state라 검색 화면에서 못 지운다 → 홈 effect가 유일한 자리. `panel-branch` 우선순위 변경은 parity 파일이라 기각. 실기: 지역축제 칩 → 검색 → 서초2동 탭 → 칩 해제 + 헤더 서초2동 | | 2026-09-07 | MSG-578 | 결정(사용자, PR 리뷰 중 — A1 번복): 지역을 고르면(검색 전체 지역 탭·홈 "전체 보기") 홈이 **그 지역 격자 목록의 첫 격자(최신순) 중심으로 `moveTo`** 한다 — `selectedRegion` + `regionGrids.data`(regionCode 에코 일치) effect, 지역당 1회(`movedToRegionRef`) | 사용자: "아 지도도 옮겨야하는데". 지역 API에 좌표가 없어 스펙 A1은 "시트만 교체"였으나, 시트가 이미 받는 `GET /api/regions/{code}/grids` 카드에 `gridX·gridY`(5179)가 있어 요청 추가 없이 중심을 구할 수 있다. 이동이 `onViewportChange → clearSelectedRegion`을 부르지만 새 중심의 라이브 행정동이 곧 그 지역이라 헤더가 이어진다(실기: 서초2동 → 강남 I-6). 웹 `RegionListView`는 안 움직이는데 모바일만 움직이는 parity 차이는 사용자 결정으로 수용 | +| 2026-09-07 | MSG-582 | 결정: 라이브러리 버그를 **업그레이드가 아니라 기존 pnpm 패치 파일에 1줄 추가**로 해소 — `removeCustomView()`의 `recycle()` 제거(`customViewBitmap = null`) | 크래시 시그니처가 recycle 선행 → GL 스레드 lock 실패로 명확. Android O 이후 비트맵은 네이티브 힙을 GC가 회수하므로 명시 recycle 없이 안전. 대안 (a) 아이콘 교체 후 recycle로 순서만 바꾸기 — GL 스레드가 다음 프레임까지 옛 아이콘을 잡고 있을 수 있어 레이스가 남는다, 기각. (b) 라이브러리 업그레이드 — 기존 onLoad 패치 재작업 + 네이티브 의존성 재검증 비용, 상위 버전이 이 경로를 고쳤는지 미확인, 기각. 업스트림 제보는 후속 | diff --git a/docs/spec/MSG-582.md b/docs/spec/MSG-582.md new file mode 100644 index 00000000..a7e5a647 --- /dev/null +++ b/docs/spec/MSG-582.md @@ -0,0 +1,59 @@ +# MSG-582: [모바일] 지도 조작 중 앱 강제 종료 — 네이버 지도 RN 라이브러리 커스텀 뷰 마커 비트맵 recycle 크래시 패치 + +> 원문: https://soma17-msg.atlassian.net/browse/MSG-582 (하네스 생성, 2026-09-07 — 사용자 QA "갑자기 앱이 꺼지는데") +> **수정 범위: `patches/@mj-studio__react-native-naver-map@2.9.0.patch` 1줄 + `pnpm-lock.yaml` 패치 해시 + `docs/MOBILE_RUNBOOK.md` 함정 12.** 앱 코드(`apps/mobile/src`)·`apps/web`·`packages/**` 0줄. + +## 기획 요약 +지도 홈에서 조작 중 붉은 에러 화면 없이 앱이 통째로 사라진다. 에뮬레이터 재가동 직후 약 3분 뒤 재현. + +## 0. 착수 실측 (develop `43f425e`, 에뮬레이터 FillMap_Pixel8) + +| 사실 | 실측 | 함의 | +|---|---|---| +| 크래시 종류 | `adb logcat -b crash`: `signal 6 (SIGABRT)`, `Abort message: 'JNI DETECTED ERROR IN APPLICATION: ... bitmap decoding: could not lock pixels'`, `from com.naver.maps.map.renderer.MapRenderer.nativeRender()`, 스레드 `GLThread 139` | JS 에러가 아닌 네이티브 abort — RN 에러 화면이 뜰 수 없다 | +| 직전 경고 | `W/Bitmap: Called getDensity() on a recycle()'d bitmap!` (크래시 6ms 전) | 지도 SDK가 이미 recycle된 비트맵을 아이콘으로 그리려 했다 | +| 라이브러리 코드 | `RNCNaverMapMarker.kt:91 removeCustomView()`: `customViewBitmap.recycle()` → `setImageWithLastImage()` 순서 | 아이콘이 아직 그 비트맵을 참조하는 상태에서 recycle | +| 앱의 커스텀 뷰 마커 | `grid-map.tsx` 경로 번호 마커·클러스터 마커·미션 이름표 (`NaverMapMarkerOverlay` 자식 뷰) | 줌 변경·코스 진입/이탈·칩 해제마다 언마운트 = 트리거 | +| 기존 패치 | `pnpm-workspace.yaml` `patchedDependencies` → 같은 패치 파일에 onLoad(`topLoaded`) 패치 존재 | 새 패치를 같은 파일에 병합 | +| 메모리·GPU | AVD RAM 2G/가용 1.2G, GPU auto | 자원 부족 아님 | + +## 1. 결정 + +| # | 결정 | 근거 | +|---|---|---| +| D1 | `removeCustomView()`에서 `recycle()` 호출을 제거하고 `customViewBitmap = null`로 참조만 끊는다 | Android O 이후 비트맵 픽셀은 네이티브 힙을 GC가 회수 — 명시 recycle 불필요. 1줄 diff | +| D2 | 순서 교체(아이콘 교체 후 recycle)는 기각 | GL 스레드가 다음 프레임까지 옛 아이콘을 잡을 수 있어 레이스가 남는다 | +| D3 | 라이브러리 업그레이드 기각, 2.9.0 유지 | 기존 onLoad 패치 재작업 + 네이티브 의존성 재검증 비용. 업스트림 제보는 후속 | +| D4 | `pnpm patch --edit-dir` → 수정 → `pnpm patch-commit`으로 기존 패치 파일에 병합 | 패치 파일 1개 유지, lock 해시 자동 갱신 | + +## 2. 수용 기준 + +| # | 기준 | 유형 | 검증 | +|---|---|---|---| +| L1 | 패치 파일에 `recycle()` 제거 diff가 있고, 설치된 라이브러리 소스에 반영된다 | 로직 | `grep "customViewBitmap = null" patches/…` + `node_modules` 심링크 대상 소스 | +| G1 | dev client 재빌드 시 `:mj-studio_react-native-naver-map:compileDebugKotlin` 재실행, BUILD SUCCESSFUL | 게이트 | 빌드 로그 | +| S1 | 패치 APK 콜드 스타트 후 앱이 Metro에 붙어 정상 기동 | 화면 | 실기 | +| S2 | 줌 인/아웃 반복(클러스터 교체)·코스 진입/이탈에도 앱이 종료되지 않고 `adb logcat -b crash` 무출력 | 화면 | 실기 | + +## 3. 리스크 +- 확률적 재현이라 S2는 "재현 안 됨"이 곧 증명은 아니다. 시그니처가 다시 보이면 APK가 패치 이후 빌드인지부터 확인(런북 함정 12). + +--- + +## 작업 로그 (2026-09-07) + +**변경 파일** +- `patches/@mj-studio__react-native-naver-map@2.9.0.patch` — `RNCNaverMapMarker.kt` hunk 추가 (+15) +- `pnpm-lock.yaml` — 패치 해시 갱신 +- `docs/MOBILE_RUNBOOK.md` — 함정 12 신설 +- `docs/STATUS.md` · `docs/decisions/DECISIONS.md` · 본 파일 + +**실측** +- L1 ✅ 패치 파일 grep 1건, `apps/mobile/node_modules/@mj-studio/react-native-naver-map` 심링크가 새 해시(`b41f33e…`) 디렉토리로 교체, 그 소스에 `customViewBitmap = null` 반영 +- G1 ✅ `expo run:android --no-bundler --device FillMap_Pixel8` — `compileDebugKotlin` 재실행, BUILD SUCCESSFUL in 26s +- S1 ✅ force-stop 후 `localhost:8081` 딥링크 콜드 스타트, `Running "main"` 로그, 로그인 화면 표시 +- S2 ⏳ 사용자 실기 진행 중(세션의 원래 목적이 안드로이드 테스트) — 재현 조작 반복은 사용자 몫. 스크린샷 없음(크래시 부재는 화면으로 증명되지 않음) + +**검토한 대안** — 결정 D2·D3 참조. + +**후속** — 업스트림(mj-studio/react-native-naver-map) 이슈 제보. diff --git a/patches/@mj-studio__react-native-naver-map@2.9.0.patch b/patches/@mj-studio__react-native-naver-map@2.9.0.patch index 2478a917..b7a192df 100644 --- a/patches/@mj-studio__react-native-naver-map@2.9.0.patch +++ b/patches/@mj-studio__react-native-naver-map@2.9.0.patch @@ -77,6 +77,21 @@ index c92093f93ff92013db5574361eb0a21853f356cd..7dacb623e928c3bfedc973775f7b70ae registerDirectEvent(this, NaverMapOptionChangeEvent.EVENT_NAME) registerDirectEvent(this, NaverMapCameraChangeEvent.EVENT_NAME) registerDirectEvent(this, NaverMapCameraIdleEvent.EVENT_NAME) +diff --git a/android/src/main/java/com/mjstudio/reactnativenavermap/overlay/marker/RNCNaverMapMarker.kt b/android/src/main/java/com/mjstudio/reactnativenavermap/overlay/marker/RNCNaverMapMarker.kt +index 509e1ec715d475671f26797a70fc2872cd6fde9c..1d771624fe55010629419f231079f91ba0f6c6a8 100644 +--- a/android/src/main/java/com/mjstudio/reactnativenavermap/overlay/marker/RNCNaverMapMarker.kt ++++ b/android/src/main/java/com/mjstudio/reactnativenavermap/overlay/marker/RNCNaverMapMarker.kt +@@ -88,7 +88,9 @@ class RNCNaverMapMarker( + fun removeCustomView(index: Int) { + customView = null + ViewChangesTracker.getInstance().removeMarker(this) +- if (customViewBitmap != null && !customViewBitmap!!.isRecycled) customViewBitmap!!.recycle() ++ // FillMap 패치: overlay.icon이 아직 이 비트맵을 참조하는 상태에서 recycle하면 GL 렌더 스레드가 ++ // "could not lock pixels"로 네이티브 abort(앱 종료)한다. GC에 맡긴다 — 2026-09-07 실측. ++ customViewBitmap = null + isImageSetFromSubview = false + setImageWithLastImage() + super.removeView(children.elementAt(index)) diff --git a/lib/typescript/commonjs/src/component/NaverMapView.d.ts b/lib/typescript/commonjs/src/component/NaverMapView.d.ts index 83f41067be85014cf8c43f5d970c9b361e12cf12..f6e7e8041b27b25ea1100095dbe16e342b4a105d 100644 --- a/lib/typescript/commonjs/src/component/NaverMapView.d.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0b16f11..ab84997f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ catalogs: patchedDependencies: '@mj-studio/react-native-naver-map@2.9.0': - hash: 43772e87fd9f6c5873a79c59dab05568f4b56897bcea457c3c91beaf7334d759 + hash: b41f33e5e73729d884b5014c12c2275e0a1d21ae33d7a61bf9d1f4b8f67e92e3 path: patches/@mj-studio__react-native-naver-map@2.9.0.patch importers: @@ -51,7 +51,7 @@ importers: version: link:../../packages/ui-native '@mj-studio/react-native-naver-map': specifier: ^2.9.0 - version: 2.9.0(patch_hash=43772e87fd9f6c5873a79c59dab05568f4b56897bcea457c3c91beaf7334d759)(react-native@0.86.2(@babel/core@7.29.7)(@react-native/metro-config@0.86.2(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) + version: 2.9.0(patch_hash=b41f33e5e73729d884b5014c12c2275e0a1d21ae33d7a61bf9d1f4b8f67e92e3)(react-native@0.86.2(@babel/core@7.29.7)(@react-native/metro-config@0.86.2(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) '@react-native-async-storage/async-storage': specifier: 2.2.0 version: 2.2.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native/metro-config@0.86.2(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) @@ -9390,7 +9390,7 @@ snapshots: '@mj-studio/js-util@1.1.3': {} - '@mj-studio/react-native-naver-map@2.9.0(patch_hash=43772e87fd9f6c5873a79c59dab05568f4b56897bcea457c3c91beaf7334d759)(react-native@0.86.2(@babel/core@7.29.7)(@react-native/metro-config@0.86.2(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': + '@mj-studio/react-native-naver-map@2.9.0(patch_hash=b41f33e5e73729d884b5014c12c2275e0a1d21ae33d7a61bf9d1f4b8f67e92e3)(react-native@0.86.2(@babel/core@7.29.7)(@react-native/metro-config@0.86.2(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)': dependencies: object-hash: 3.0.0 react: 19.2.3 From 10f95e42e507b5d714294f8433524c2b2ec0faf6 Mon Sep 17 00:00:00 2001 From: KangJeongmin Date: Mon, 7 Sep 2026 16:32:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?MSG-582=20docs:=20PR=20#151=20Claude=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20=E2=80=94=20=EC=8A=A4?= =?UTF-8?q?=ED=8E=99=20=EB=A6=AC=EC=8A=A4=ED=81=AC=EC=97=90=20recycle=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=EC=9D=98=20GC=20=EC=A7=80=EC=97=B0=20?= =?UTF-8?q?=ED=8A=B8=EB=A0=88=EC=9D=B4=EB=93=9C=EC=98=A4=ED=94=84=20+=20?= =?UTF-8?q?=EC=A4=8C=2010=ED=9A=8C=20=EB=A9=94=EB=AA=A8=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=A4=EC=B8=A1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 실측: Java 힙 22.0→27.9→22.5MB(3분 후 복귀), 네이티브 407→416→412MB, 크래시 0 Claude-Session: https://claude.ai/code/session_012MtDBFuKP9N4wqmsuoXE7P --- docs/spec/MSG-582.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/spec/MSG-582.md b/docs/spec/MSG-582.md index a7e5a647..d3524dd1 100644 --- a/docs/spec/MSG-582.md +++ b/docs/spec/MSG-582.md @@ -36,6 +36,7 @@ | S2 | 줌 인/아웃 반복(클러스터 교체)·코스 진입/이탈에도 앱이 종료되지 않고 `adb logcat -b crash` 무출력 | 화면 | 실기 | ## 3. 리스크 +- recycle 제거로 비트맵 회수가 GC 타이밍에 맡겨진다(클로드 리뷰 🟡). 실측(에뮬레이터, 핀치 줌 인/아웃 10회 반복, `dumpsys meminfo`): Java 힙 22.0→27.9MB, 3분 뒤 22.5MB로 복귀 · 네이티브 힙 407→416→412MB(지도 타일 캐시 변동 범위). 마커 비트맵은 `ARGB_4444` 수십 px라 개당 수 KB이고 GC가 회수함을 확인 — 누적 OOM 리스크는 낮다. 크래시 없이 느려짐·OOM이 보이면 이 티켓과 연결 - 확률적 재현이라 S2는 "재현 안 됨"이 곧 증명은 아니다. 시그니처가 다시 보이면 APK가 패치 이후 빌드인지부터 확인(런북 함정 12). ---