목록으로
인프라 · 의존성

모노레포 React 중복으로 인한 "Invalid hook call"

코드 변경 없이 앱이 깨진 원인이 번들에 두 벌 들어간 React임을 찾아, 모든 import가 루트 React 하나만 보게 막아 해결.

01

상황

모바일 코드를 한 줄도 바꾸지 않았는데, 앱을 부팅하면 'Invalid hook call'이 나며 화이트 스크린에서 멈췄습니다. 웹은 멀쩡했습니다.

02

원인 분석

admin 앱이 모노레포에 추가되면서 pnpm이 의존성을 다시 배치했습니다. pnpm은 더 많은 앱이 요구하는 버전 기준으로 공유 패키지를 설치하는데, 새 앱이 들어오며 다수 쪽 React 버전이 바뀌자 공유 패키지 안에 다른 버전의 React 사본이 새로 생겼고, 모바일 번들에 루트 React와 이 사본 두 벌이 함께 들어갔습니다. 웹(Next.js)은 번들러가 중복 버전을 하나로 합쳐줘(dedup) 같은 충돌이 있어도 가려졌지만, 합치기 기능이 없는 모바일 번들러(Metro)에서는 그대로 장애로 드러났습니다.

03

시도 · 트레이드오프

처음엔 모바일 코드 문제로 의심했지만 변경 이력이 없었고, 웹은 정상이라는 점에서 번들러 차이로 방향을 좁혔습니다. 의존성 트리를 따라가 공유 패키지 안에 중첩된 React 사본을 찾았습니다.

04

해결

Metro 설정의 blockList에 중첩된 react/react-dom 경로를 추가해, 어느 패키지가 사본을 품고 있어도 모든 React가 루트 하나로 모이게 했습니다(반영 커밋은 메인 앱 개발자에게 요청). 근본 해결은 React 버전 통일이지만 React Native는 React와 버전이 묶여 있어 RN·Expo SDK까지 함께 올려야 하는 큰 작업이라, 업그레이드 일정에 맞추기로 하고 우선 차단으로 막았습니다. 적용하려면 Metro 캐시를 비워야 해서 팀 공유용 안내(prebuild --clean)까지 정리했습니다.

05

결과 · 배운 점

화이트 스크린이 사라지고 앱이 정상 부팅됐습니다. 코드를 한 줄도 안 바꿔도 옆에 앱이 하나 추가되는 것만으로 다른 앱이 깨질 수 있다는 게 모노레포의 특성이라, 증상이 난 자리가 아니라 의존성 구조에서 원인을 찾아야 했던 문제였습니다.