#Frontend

pnpm 모노레포에서 React 19 마이그레이션하기: 숨겨진 호이스팅 레이어가 만든 타입 충돌 트러블슈팅

pnpm 모노레포에서 React 19 마이그레이션하기: 숨겨진 호이스팅 레이어가 만든 타입 충돌 트러블슈팅
01

Summary

React 19로 가기 전 반드시 체크할 pnpm 모노레포 '타입 오염' 탈출기

98개의 타입 에러를 뚫고 찾아낸 pnpm 3계층 node_modules의 비밀과 의존성 격리 전략

pnpm 모노레포 환경에서 React 19 마이그레이션 중 마주치는 복잡한 타입 충돌 문제를 심층 분석합니다. pnpm의 독특한 호이스팅 메커니즘과 TypeScript의 동작 원리를 결합하여 단순한 버그 수정을 넘어선 근본적인 의존성 설계 최적화 과정을 다룹니다.

  • 01pnpm Catalogs를 활용한 모노레포 내 다중 리액트 버전 관리 전략
  • 02pnpm node_modules의 3가지 계층(Strict, Virtual, Hoisting) 구조 분석
  • 03TypeScript가 물리적 실제 경로를 기준으로 모듈을 해석하며 발생하는 트러블슈팅
  • 04packageExtensions를 이용한 서드파티 라이브러리 의존성 강제 주입 기법
  • 05tsconfig paths나 독립 락파일 방식의 한계점과 유지보수 관점의 대안 비교

RECOMMENDATION

pnpm 모노레포를 운영하면서 여러 버전의 React를 혼용해야 하거나, 원인 모를 @types 충돌로 고생 중인 시니어 개발자에게 강력히 추천합니다. 특히 React 19 마이그레이션을 앞두고 점진적 배포 전략을 고민 중인 팀에게 실무적인 가이드를 제공합니다.

The Problem

pnpm 기반 모노레포에서 React 19로의 단계적 마이그레이션을 위해 앱별 버전을 분리했으나, pnpm의 호이스팅 레이어 특성으로 인해 React 18 앱이 React 19 타입을 참조하는 타입 오염 현상이 발생했습니다. 이로 인해 98개의 타입 에러가 발생했으며, 이는 동일 패키지의 여러 버전 중 하나만 호이스팅되는 pnpm의 내부 구조와 TypeScript의 모듈 해석 순서가 얽히며 발생한 문제였습니다.

The Solution

먼저 .npmrc 설정을 통해 @types/react의 호이스팅을 원천 차단하여 타입 혼선을 막았으며, 이후 특정 라이브러리에서 발생하는 타입 누락 문제를 해결하기 위해 pnpm의 packageExtensions 기능을 도입했습니다. 이를 통해 서드파티 라이브러리의 package.json에 누락된 peerDependencies를 가상으로 주입하여 각 앱이 자신의 환경에 맞는 정확한 타입 경로를 참조하도록 구성했습니다.

The Result

모노레포의 장점인 단일 락파일과 공유 저장소 구조를 유지하면서도, React 18과 19 버전이 공존하는 환경에서 모든 타입 충돌 이슈를 완벽히 해결하고 빌드에 성공했습니다. 결과적으로 앱 간의 의존성 격리를 강화하고 향후 대규모 마이그레이션을 안전하게 수행할 수 있는 구조적 기반을 마련했습니다.

Trade-off

packageExtensions 방식은 누락된 의존성을 가진 라이브러리를 발견할 때마다 수동으로 설정을 추가해야 하는 유지보수 리스크가 있습니다. 본문에 명시되었듯 pnpm v11에서 관련 플러그인 이슈가 해결될 때까지는 수동 관리가 불가피하며, 이는 자동화된 도구 대비 운영 공수가 추가되는 측면이 있습니다.

03

Key Concepts

Concept · 01

pnpm Catalogs

모노레포 워크스페이스 내에서 공통으로 사용할 패키지 버전을 한 곳(pnpm-workspace.yaml)에 정의하고 각 앱에서 참조하게 하는 기능입니다.

  • 여러 package.json의 버전 파편화를 방지하고 중앙에서 통제 가능
  • React 18용 v1과 React 19용 v2 카탈로그를 분리하여 단계적 마이그레이션에 활용
Concept · 02

Internal Hoisting Layer

pnpm의 가상 스토어 상단에 위치하며, 유령 의존성에 의존하는 패키지들을 위해 최소한의 패키지를 노출하는 안전망 계층입니다.

  • 동일한 패키지의 여러 버전 중 오직 하나만 이 계층에 올라갈 수 있음
  • 비결정적인 호이스팅 결과로 인해 의도치 않은 버전의 타입이 참조되는 원인이 됨
Concept · 03

pnpm packageExtensions

서드파티 패키지의 코드를 직접 수정하지 않고, 의존성 해석 단계에서 가상으로 package.json 설정을 패치하는 기능입니다.

  • 누락된 peerDependencies를 명시적으로 주입하여 타입 추론 오류 해결
  • 라이브러리 내부에서 React 타입을 찾지 못해 any로 추론되는 문제를 해결하는 핵심 도구로 사용