: Unable to resolve module XX from ..

pnpm은 strict node_modules구조를 가진다.
다시말해 pnpm 워크스페이스에서 의존성은 각 패키지에서 symlink로 관리하며, 각 패키지의 의존성은 루트 node_modules의 의존성을 함께 사용한다.
Expo의 Metro 번들러가 symlink를 기본적으로 follow하지 않는다.
Metro 번들러는 파일을 resolve할 때 실제 파일 시스템 경로 기반으로 탐색한다. → metro가 react를 찾으러가면 symlink를 만나는데, symlink의 실제 경로(.pnpm/..)를 따라가면 그 위치를 기준으로 다시 의존성을 찾게된다. 그러면 .pnpm내부의 격리된 구조 안에서 다른 의존성을 찾지 못하는 연쇄 문제가 발생한다.
npm/yarn classic은 flat한 node_modules구조이기에 metro가 문제없이 탐색한다.
pnpm을 계속 사용한다면 .npmrc에 node-linker=hoisted를 설정해 flat하게 설치하도록 한다.
.npmrc에 public-hoist-pattern로 문제가 되는 각 패키지를 추가한다.
// .npmrc
public-hoist-pattern[]=*expo*
public-hoist-pattern[]=@expo/*
public-hoist-pattern[]=*react-native*
public-hoist-pattern[]=*react*
public-hoist-pattern: 매칭되는 패키지만 루트 node_modules로 호이스팅된다.metro.config.js에서 워크스페이스 루트를 인식시키도록 한다.
const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');
const projectRoot = __dirname;
const monorepoRoot = path.resolve(projectRoot, '../..');
const config = getDefaultConfig(projectRoot);
// 모노레포 루트의 node_modules도 탐색하도록 설정
config.watchFolders = [monorepoRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(monorepoRoot, 'node_modules'),
];
// 모노레포 내 다른 패키지에서 별도 react / react-native 인스턴스를 쓰지 않도록
config.resolver.disableHierarchicalLookup = true;
module.exports = config;
.npmrc > node-linker=hoisting 추가
이유

react-lucide를 사용하는 각종 패키지에서 타입 오류 발생
node-linker=hoisting을 설정하지 않을 경우(pnpm의 default상태인 strict node_modules 구조): 각 패키지는 루트 node_modules/.pnpm을 symlink로 가진다.

모든 패키지는 symlink로 루트 node_modules/.pnpm에 있는 패키지를 가리킨다.
예외 : react,react-dom. 루트에는 react@18이 설치되어있고, 이 app패키지에서는 react@19에 의존하기 때문.