React Native 0.71 업그레이드 하기 (ft. 업그레이드 방법)

하코·2023년 11월 26일
0

react native

목록 보기
1/2
post-thumbnail

들어가기 전에

안녕하세요. 며칠 전 1년만에 React Native 업그레이드 작업을 진행했습니다.지금 팀에서 업그레이드 작업이 벌써 두번째네요.

오늘은 다음 React Native 버전 업그레이드 작업할때 도움이 될만한 글을 적어보려고 합니다.

사실은 끙끙기에 가깝고…
더 정확히는 업그레이드 배경/ 사전준비 /진행과정을 공유하고 돌아보고자 해요.
(React Native 0.71 에서 업데이트 된 프레임워크 기능은 소개하지 않음)


React Native를 0.71로 업그레이드 한 배경?

팀 React Native 프로젝트 버전은 1년 전 업데이트 버전인 0.69.4에서 멈춰 있었어요.

RN은 결과물이 iOS, Android 플랫폼이기에 두 OS에 종속적입니다. iOS 특정 버전을 지원하려면 React Native의 버전도 올려야 하는 상황이 발생해요.

더 자세히 풀어볼게요!

첫번째 이유, 플랫폼사 스토어 앱 정책

Google Play Store는 ‘앱은 대상 API 수준 요구사항을 충족해야 한다’라는 정책이 있어요.

즉 안드로이드 앱을 스토어에 계속 서비스 하려면 1년 전 나온 Android 13를 타겟으로 올려야합니다.

Android 13(SDK 33)을 지원할 수 있는 React Native 최소 버전이 0.71 입니다. 0.69에서 0.71로 업그레이드 해야하는 주된 이유입니다.

버전 호환성은 어디서…?
버전 호환성은 React Native Upgrade Helper 사이트를 통해 사용 가능한 Android의 SDK 버전을 확인하고 있어요.
Upgrade React Native applications

Android SDK 호환은 React Native 버전에 종속되니 이런 업데이트 작업을 1년 주기마다 해야합니다.

팀 사정으로 대응 시점이 늦어지면 Play Store쪽에 기한 연장을 요청할 수 있어요. 업데이트 해야하는 시기는 구글에서 친절히 꽤 자주(!) 메일링을 통해 알려주니 놓친 적은 없네요.

iOS 경우엔 조금 다르다...
필수로 지원해야 하는 최소 iOS 버전만 Podfile에 명기할 수 있어요. 앱이 최소로 지원하는 OS 버전이 높다면 기기 OS 버전이 낮은 사용자들은 앱스토어에서 해당 앱을 다운받지 못하는 일이 발생해요.

두번째 이유, 앱 OS 업데이트된 기능 사용

업그레이드 배경에는 플랫폼사의 정책적인 부분도 있지만 유저에게 좋은 경험을 제공하기 위한 목적도 있습니다.

예를들어 Android13 이상부터 달라지는 기능이 있었습니다. 기존 안드로이드 유저는 앱을 새로 설치하면 늘 ‘알림 받을 수 있는 상태’ 였는데요. Android 13 부터는 기본적으로 ‘알림 받을 수 없는 상태’ 입니다. 즉 유저가 앱 알림을 받으려면 다이얼로그를 통해 유저에게 권한 허락을 받아야 합니다. iOS 처럼요.

최신 유저들에게 앱 알림의 존재를 알리고 허용할 수 있게끔 하려면 Android 13을 꼭 타겟으로 해야겠죠?

사전 준비

업그레이드 작업시 도움이 된 도구들 입니다. 업그레이드 작업을 처음 하는 분들이라면 React Native Upgrade Helper 가 좋은 가이드가 될거 같습니다.

  1. React Native 등 CLI
    1. npx @rnx-kit/align-deps --requirements react-native@0.71 --write : dependencies 버전들을 React Native 특정 버전에 맞는 버전으로 변경해줌
    2. brew doctor: cocoapods와 링크 연결 안된게 있는지 등 확인하거나 ruby 버전 호환성 이슈를 감지함
  2. React Native Upgrade Helper
  3. 라이브러리 공식문서의 Version Compatibility
  4. React Native Git 저장소 커뮤니티
  5. VsCode Extension GitLens: 특정 코드의 최근 커밋 히스토리를 시각적으로 보여줌
  6. android 실행 프로세스를 대략 알고있자 : initializing → Configuring → Executing
    • 어느 단계에서 실패했는지에 따라 추측할 수 있는 에러 범위와 수준이 다름
  7. 안드로이드의 빌드 캐시를 날리는 명령어
    • cd android && ./gradlew clean

작업 과정 요약

우선 프로젝트 전체 업데이트는 RN 업데이트를 먼저 진행 후, 각 OS 설정을 하나씩 진행했습니다.

  1. CLI를 이용해 React Native 프로젝트 0.71로 업데이트 하기
  2. iOS: 설정 파일 변경 → 로컬 빌드 → 디바이스 실행 테스트
  3. AOS: 설정 파일 변경 → 로컬 빌드 → 디바이스 실행 테스트
  4. JavaScript 파일 수정

세부 진행 과정

1. React Native 0.71 업그레이드 하기

npx @rnx-kit/align-deps --requirements react-native@0.71 --write

업그레이드 할때 가장 먼저 실행하는 명령이에요. 업그레이드 하려는 버전을 0.71로 입력하면 package.json dependencies의 버전이 0.71과 호환성이 부합하는 버전으로 바뀝니다.

dependencies 버전이 바뀌었으니 yarn install로 node_modules를 재설치해요.

참고로 이 단계에서 node 버전과 모듈의 호환성 문제가 감지됩니다.
프로젝트 상황에 따라 node, yarn, watchman 같은 도구의 버전을 업데이트 해줍니다.

이제 npx react-native start 로 메트로가 실행되면 이 단계는 끝입니다.

2. iOS

이제 iOS 설정을 바꿔봅시다.

React Native 프로젝트의 이전 버전에서 0.71로 올라가며 바뀐 설정을 반영해주어야 합니다.

수동으로 진행한다면 어떤 파일을 수정해야 하는지는 React Native Upgrade Helper 사이트에서 확인할 수 있습니다. 자동으로 할 경우 npx react-native upgrade VERSION 명령어를 통해 템플릿을 프로젝트에 반영할 수 있습니다. (공식문서)

iOS 설정 업데이트 반영

OS를 하나씩 변경하고 확인하기 위해 수동으로 진행했습니다. Helper에서 Android 설정은 제외하고 iOS에 해당되는 범위만 확인합니다.

물론 전부 반영하지 않아도 된다…
위 캡쳐처럼 changes 목록 중 파일 전체를 꼭 동일하게 변경할 필요는 없습니다. 팀 프로젝트에선 Gemfile, .ruby-version 파일을 따로 두진 않아서 제외했어요. 사용 환경에 맞게 결정하면 될거 같아요.


pod 재설치

파일 설정이 끝났으면 npx pod-install 명령어로 pod 파일을 재설치 합니다. 이때 빌드파일 꼬임 현상을 방지하기 위해 재설치 전에 pods, podlock를 삭제하고 설치를 진행했습니다. (틈틈히...)


시뮬레이터 실행

이제 시뮬레터 실행 테스트를 위해 npx react-native run-ios 를 실행했습니다. iOS 빌드에 실패했습니다… 이유는 라이브러리 호환성 이슈였어요. 아무래도 가장 처음에 했던 align-deps가 라이브러리 호환 버전을 전부 찾아주진 않는거 같습니다.

그래도 어떤 라이브러리에서 문제가 발생하는지 메트로가 알려주니 하나씩 해결할 수 있습니다.


이따금 확인했던 것
참고로 이 단계에서 cocoapods 또는 Ruby 호환 문제가 감지되기도 하니 brew doctor 같은 명령어로 의심해보고 두드리며 진행했습니다.

그렇게 pod 설치가 끝나고 로컬 빌드를 거쳐
시뮬레이터 실행까지 성공적으로 끝나면
iOS 작업은 마무리 입니다. 🎉


추가로 스키마 변경해 실행

우리 팀은 iOS 프로젝트를 빌드시 Xcode에서 스키마를 바꿔가며 빌드 타입을 스위치하고 있습니다. 또한 android에서 build config 사용 방식이 바뀌었기 때문에 이 부분도 공식문서에 따라 변경작업을 거쳤습니다.

React Native를 시작한 이후론 예전보다 더 불안한 부분을 직접 눈으로 확인해야 속이 편한거 같아요… 이런 변경사항을 어떻게 더 잘 파악할 수 있을지🤔


3. Android

이번엔 안드로이드 업그레이드를 진행해 봅시다. iOS와 동일한 단계로 진행하면 됩니다.
설정 변경해보고, 빌드 시도하고.


android 설정 업데이트 반영

React Native upgrade helper 웹사이트를 통해 바뀐 설정들을 하나씩 반영합니다. 프로젝트 업그레이드 목적이었던 targetSdkVersion 버전도 까먹지 않고 33으로 바꿔줍니다. (android/build.gradle)


수동으로 파일 변경시 라이브러리 플러그인을 없애지 않도록 주의하자

파일 수동 업데이트 할때 각별히 주의해야 할 점이 있습니다. 내장된 플러그인 이외 별도로 추가한 라이브러리 플러그인을 없애지 않도록 해야해요. 수동으로 진행하면 이런 부분에서 휴먼 에러가 발생하기 쉬운거 같아요.

코드에 따로 주석처리를 미리 해두는 방법도 있고요.
주석이 없는 경우에는 코드 히스토리를 파악해서 판단에 도움을 받았습니다.


코드 히스토리를 파악하는 도구
GitLens (VScode Extension) 는 활성화 하면 특정 코드 라인을 누가, 언제, 어떤 commit에서 수정했는지 시각적으로 보여주는 도구에요.

이 도구로 anroid/build.gradle 에서 플러그인이 기본 설치된건지 외부 종속성으로 추가된건지 판단하는데 도움을 받았어요.

아래 이미지를 보시면, 실제 실행시 코드 우측에 최근 히스토리 메세지를 보여줍니다.

디바이스 실행

파일을 업데이트가 끝나면 설정이 잘 반영되었는지 확인해야 합니다. 로컬과 디바이스가 USB로 연결된 환경에서 npx react-native run-android 명령어를 실행했습니다.


에러하나 보고가시죠.
명령어 실행부터 에러가 발생했습니다. android 빌드 타입을 쓰고있어서 run-android를 실행할때 --mode 옵션을 붙였는데요. mode 명칭이 variant로 바뀌었나 봅니다.

"variant" flag is deprecated and will be removed in future release. Please switch to "mode" flag.

수정하고 다시 실행하니 Configuring는 통과됐지만 Executing 단계에서 실패했습니다. 실패한 위치는 특정 라이브러리의 디렉토리 였는데요. 공식문서에서 Compatibility Matrix를 확인하니 Android Gradle Plugin 버전이 달랐습니다. 이렇게 라이브러리에서 명령도구로 맞춰지지 않은 부분들을 맞춰줍시다.

이후 다시 쭉쭉 수정하다가 아래 메세지를 마주쳤습니다.

FAILURE: Build completed with 1 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:checkDevelopmentDebugDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
   > Duplicate class androidx.lifecycle.ViewModelLazy found in modules jetified-lifecycle-viewmodel-ktx-2.2.0-runtime (androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0) and lifecycle-viewmodel-2.5.0-runtime (androidx.lifecycle:lifecycle-viewmodel:2.5.0)
     
     Go to the documentation to learn how to <a href="d.android.com/r/tools/classpath-sync-errors">Fix dependency resolution errors</a>.

메세지를 읽었을땐 라이브러리의 종속성을 설치하는 과정에서 단일로 존재해야 하는 class가 중복으로 생긴거 같았어요.

lifecycle-viewmodel-ktx에서 ktx는 코틀린 종속성을 의미해요.

버전이 다른 문제를 해결하기 위해 높은 종속성 버전을 요구하는 쪽에 맞춰서 버전을 명시했습니다. app/build.gradle의 dependencies에 코드를 세 줄 추가합니다.

dependencies {
  // enforce version for dependency duplication
	def lifecycle_version = "2.5.0"
	implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
	implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
	...

참고로 안드로이드 gradle 파일에 변경사항이 생기면 cd android && ./gradlew clean 를 실행한 후 android를 실행했습니다.

이로서 안드로이드 실행 단계 initializing, configuring, Executing 를 모두 통과했습니다. 그럼 비로소 안드로이드 디바이스에서 앱이 정상 실행됩니다.

JavaScript 파일 수정하기

거의 다 왔습니다… 이 단계는 라이브러리 사용 코드를 API 버전에 맞게 바꿔주는 작업입니다.
앞서 진행한 iOS, AOS 작업 과정보다 훨씬 간단하게 느껴집니다.

이 단계는 개발자가 작성한 코드 수준에서 에러나 워닝이 발생해요. 원인은 다양할 수 있는데요. 보통 라이브러리 버전이 업데이트 되면서 함수명, 파라미터, 타입 등이 달라져 기존 코드와 맞지 않기 때문이에요. 린트를 실행해서 수정이 필요한 범위를 파악해 수정하면 됩니다.

마무리

이번 업데이트 과정은 다소 헤매는 과정이 있었습니다.

위에서 수동 업그레이드 배경을 언급하진 않았는데요. 명령어 업그레이드에 문제가 생겼고, 급한 상황에서 시간을 지체하지 않기위해 수동으로 진행했습니다.

돌이켜보면 자동 명령어로 했을때 시간이 꽤 단축되지
않았을까…합니다. 이유는 에러의 상당 부분이 (1) 있어야 할 코드가 누락되거나 (2) 없어야 할 코드가 존재해서 발생했기 때문이에요.

다음에는 해결에 시간이 조금 걸리더라도 명령어를 통한 업그레이드에 시간을 조금 투자할거 같아요.

이런 부분도..
다음에는 아래를 더 잘해보고 싶어요.

  • build 전에 외부 라이브러리 종속성의 호환 문제 발견하기
  • 외부 라이브러리가 새 React Native 버전을 지원하지 않는다면 어떤 방법을 취해야 할까?

배움이 많은 작업이다..
어쨌든… Swift, Kotlin 의 설정을 이해하는 과정은 아직 낯설지만 빌드 과정에 관여하는 native 설정을 접할 수 있는 좋은 기회였습니다. 또 프로젝트 엔진 동작을 구조적으로 바라볼 기회이기 때문에 흥미로운 면이 많아요.

업그레이드를 할때 더 매끄럽게 진행할 수 있는 방식이 있다면 댓글로 경험을 나누어주세요.

읽어주셔서 감사합니다.

profile
Frontend Enginner

0개의 댓글