React Native 에선 pnpm-lock 파일을 함부로 제거하면 안되는 이유

eeennsu·2026년 2월 24일

React Native

목록 보기
13/96

웹 개발 할 때는 node_modules랑 package-lock.json 싹 지우고 다시 깔면(Re-install) 만병통치약처럼 해결되곤 했다.
하지만 React Native(RN)에서 락 파일(lock file)을 지우는 건 매우 민감하다.

package 관련 파일의 핵심 역할

  • package.json은 개발자가 원하는 패키지 + 업데이트 허용 범위 정의파일이다.
  • 만약 "react": "^18.2.0" 이면 ^(Caret)범위 내 최신 버전이 설치 가능한 것이며 ^이 없으면 항상 고정
  • package-lock.json은 정확한 버전과 의존성 트리 전체를 고정하는 역할을 함

즉, package-lock.json이 있으면 모두 똑같은 node_modules가 만들어진다. 만약 lock 파일이 없다면? 다음의 상황을 예로 들어보자.

  1. 오늘 react-native-reanimated 개발자가 버그가 포함된 3.3.1 버전을 배포했다고 치자.
    • Lock 파일이 있을 때 : pnpm-lock.yaml에 3.3.0이라고 적혀있으면, pnpm은 3.3.1이 나왔든 말든 무시하고 무조건 3.3.0을 설치한다. 안전하다.
    • Lock 파일을 삭제했을 때 : pnpm은 package.json만 보고 설치를 진행한다. ^3.3.0 규칙에 따라 최신 버전인 3.3.1을 설치해 버린다.
  2. 내가 라이브러리 A를 설치해서 쓰고 있다.
  3. 근데 라이브러리 A가 내부적으로 라이브러리 B를 사용한다.
  4. 라이브러리 A의 package.json에는 라이브러리B가 ^2.0.0 이라고 되어있다.

이때 lock 파일을 삭제하고 pnpm install 을 한다면? 내가 라이브러리 A를 유지했더라도, 라이브러리B가 몰래 2.1.0으로 설치된다. 내 눈에는 안보이지만 node_modules 구조가 바뀌어버리는 것이다. RN은 js 라이브러리들이 서로 얽혀있어서, 이 하위 의존성 하나가 바뀌면 전체 빌드가 깨지는 경우가 매우 흔하다.



React Native에서 특히 위험한 이유?

1. 네이티브 모듈과 버전 호환성 문제

React Native 라이브러리는 Java / Kotlin (Android) 코드와 Objective-C/Swift (iOS) 코드를 포함한다. node_modules에 설치된 버전에 맞춰서 Gradle이나 CocoaPods가 네이티브 코드를 컴파일한다. 이때 일부 라이브러리 버전이 미세하게 바뀌면?

javascript 코드는 v1.0의 기능을 호출하는데, 네이티브 코드는 v1.1로 설치되면서 메서드 이름이 바뀌었거나 파일 위치가 달라질 수 있다. 결국 빌드 에러가 나거나, 앱이 켜지자 마자 크래시가 발생한다. 특히 애니메이션이나 제스처처럼 사용자 인터렉션과 관련된 라이브러리들이 매우 민감하다.

2. peerDependencies 충돌이 매우 빈번

react, react-native, @react-navigation/*, react-native-reanimated, react-native-gesture-handler 등 서로 정확한 버전을 요구하는 경우가 많다.
1. lock 파일 없으면 자동으로 최신 버전 끌어오면서 peer dependency 경고가 뜸.
2 런타임 크래시발생 가능성 높음

3. metro bundler 캐시 + hermes bytecode 문제

의존성 버전이 미세하게 바뀌면 metro 캐시가 깨져서 번들링 실패하거나 hermes bytecode와 호환 안 맞아서 앱이 죽을 수 있다.

4. 팀원과의 코드 호환성 문제, 보안 패치 등 자동 적용 위험

  • A 개발자는 B개발자와 서로 다른 마이너버전이 설치되어 서로 다른 버전 오류를 수정하려다가 오히려 꼬여버릴 수 있다.
  • 패치 버전이 올라가면서 보안 취약점이 고쳐졌다고 해도 의도치 않은 버그가 같이 들어올 수 있음.

RN 에서는 package-lock.json을 지울일은 거의 없다. 단일 시스템으로 작동되는 웹과 달리 크로스 플랫폼인 RN은 버전관리가 매우 어렵기 때문이다. 웹에서의 습관처럼 오류가 나면 package-lock.json을 지우기보단 node_modules 폴더를 지우거나 pnpm start --reset-cache를 시도해보자.




사용자 인터렉션 관련 라이브러리가 특히 민감한 이유?

React Native 생태계에서 react-native-reanimated, react-native-gesture-handler, react-native-screens 이 3대장이 가장 민감하고 에러가 자주 터지는 라이브러리들이다. 단순히 코드가 복잡해서가 아니다. React Native의 구조적 한계를 뚫으려고 네이티브 시스템 깊숙한 곳(C++)까지 건드리기 때문이다.

왜 유독 이 라이브러리들이 민감한지, 기술적인 이유 3가지를 정리해보자.

  1. "JS 스레드"를 탈출해야 하기 때문
  • 일반적인 RN 동작
    JS 스레드에서 로직 실행 → JSI/Fabric을 통해 네이티브 UI 스레드에 렌더링 명령한다. 대부분의 UI는 이것만으로도 충분히 빠름 (JSI 덕분)

  • 애니메이션/제스처의 문제
    사용자가 화면을 휙 스크롤 하거나 드래그할 때, 0.016초(60fps) 안에 반응해야 한다. 근데 JS 스레드가 바쁘면(데이터 가공 등) 버벅거림이 발생한다. 특히 복잡한 애니메이션은 매 프레임마다 계산이 필수적이다.

  • 해결책 (Reanimated 등)
    이 라이브러리들은 성능을 위해 JS 스레드를 우회하고 네이티브 UI 스레드에서 직접 코드를 실행시킨다.

    • 이걸 위해 JSI (JavaScript Interface)라는 기술을 써서 C++ 레벨에서 직접 통신한다.
    • 즉, RN의 가장 깊숙한 곳에 직통으로 연결되어 있는 셈이다. 그러니 버전이 조금만 안 맞아도 바로 크래시가 난다.

  1. Babel 플러그인 의존성
    react-native-reanimated를 설치할 때 babel.config.js에 플러그인 추가하라는 설정이 있다.
    plugins: ['react-native-reanimated/plugin'],
    내가 작성한 js 코드를 컴파일단계에서 가로채서 변형시키는 역할을 한다. 이 덕분에 JS 스레드가 바쁘거나 멈춰도 애니메이션은 부드럽게 계속 진행될 수 있다.

  1. 네이티브(Binary) 코드와의 강한 결합
    lodash나 moment 같은 순수 JS 라이브러리는 node_modules만 갈아끼우면 끝이다. 하지만 애니메이션/제스처 라이브러리는 다르다.

이들은 설치될 때 Android의 C++/Java 코드와 iOS의 C++/Objective-C 코드가 같이 컴파일되어 앱에 심어진다. 만약 lock 파일 삭제 시,

  • JS 쪽은 v3.5.0이 깔렸는데, 네이티브 빌드 캐시에는 v3.4.0의 C++ 코드가 남아있다면?
  • JS가 "A라는 C++ 함수 실행해!"라고 했는데, 네이티브에는 그 함수가 없어서 메모리 참조 오류(Segmentation Fault)가 나면서 앱이 즉시 꺼진다.

4. 왜 New Architecture에서도 여전히 민감한가? **New Architecture(RN 0.76+)의 변화:** - JSI가 표준 → 일반 네이티브 통신도 빨라짐 - Fabric(새 UI 레이어) → 렌더링 성능 향상 - Bridge 제거 → 통신 병목 해소

하지만 Reanimated는 여전히 특별함:
1. Worklet 시스템: UI 스레드에서 JS 코드 직접 실행 (다른 모듈은 안 함)
2. Shared Value: JS-네이티브 간 메모리 공유 (특수한 C++ 구조)
3. 동기적 상태 동기화: 일반 JSI보다 더 깊은 레벨의 통합

결국 New Architecture든 구 아키텍처든, Reanimated는 RN의 가장 깊숙한 곳을 건드리기 때문에 여전히 가장 민감한 라이브러리다.

profile
이력서 https://resume.eunsu.pro

0개의 댓글