본문 바로가기
Front-End/React + Native

[React Native] CodePush 설정 및 유의사항

by MS_developer 2023. 9. 24.

CodePush란?

CodePush는 React Native를 통해 개발한 모바일 앱을 추가적인 심사 과정 없이 기존 버전에서 사용자의 디바이스에 직접 업데이트할 수 있도록 하는 잠수함 패치 App Center 클라우드 서비스이다. 무려 마이크로 소프트에서 지원하는 서비스로, 단순 CodePush 서비스만 사용한다면 무료다. 

 

모바일 앱 개발이 익숙지 않은 사람이라면 CodePush의 편의성이 크게 와닿지 않을 수 있으니 예를 들어보겠다.

 

 

잠시 식당을 운영하는 자영업자가 되어보자.

 

자영업자는 메뉴를 선정해 재료를 조달받고 음식을 만들어 손님들에게 판매한다.

 

하지만 계절, 경제적 상황, 새로운 시도 등 다양한 요인에 의해 메뉴를 바꿀 때도 있다.

 

최근 신메뉴를 개발해 손님들에게 호평받고 있는데, 오늘 조달받는 재료에 깜빡하고 신메뉴에 필요한 중요한 재료를 주문하지 않은 상황을 떠올려 보자.

 

급하게 재료를 조달받는 도매상에게 전화해 보았지만, 이미 재료는 출발했다고 한다.

 

도매상이 해당 재료를 급하게 다시 보내준다고 해도, 해당 재료를 받기 위한 추가적인 비용 발생에 더불어 재료가 식당에 늦게 도착해 메뉴를 제시간에 선보일 수도 없다. 최근 손님들이 신메뉴를 찾고 있어 식당은 하루 매출에 상당한 타격을 받을 수 있다.

 

이때, 한 회사에서 연락이 왔다.

 

지금 배송 중인 차량을 찾아가서 같이 담아서 보내드릴게요

 

꽤 솔깃한 이야기가 아닌가?

 

진짜로 이러시면 안됩니다

 

마치 영화의 한 장면 같다.

 

완벽하게 적절한 예시는 아니지만, CodePush는 심사 단계를 거쳐야 하는 앱 배포의 복잡한 과정을 줄일 수 있는 방법이다. 하지만 고속도로(배포 과정)에서 이런 일을 반복한다면 문제가 생길 수 있기 때문에 중간 과정을 건너뛰는 방법은 남발하지 않는 것이 좋다고 생각한다.

 

아무튼 이토록 유용한 CodePush의 배포 방법에 대해 알아보자.


1. App Center CLI 설치 및 로그인

 

앞서 언급한 대로 CodePush는 App Center 클라우드 서비스이므로, App Center를 이용해야 한다.

 

 

이후 설정 과정에서 App Center 로그인이 필요하므로 먼저 회원가입을 해 놓도록 하자. 

 

npm install -g appcenter-cli

 

터미널에서 App Center에 접근하기 위한 CLI 툴을 npm 명령어를 통해 설치한다. (npm으로만 설치가 가능)

 

appcenter login

 

 

이후 appcenter login 명령어를 입력하면 브라우저 창에 appcenter 홈페이지가 열린다. 

 

 

로그인을 성공적으로 했다면 다음과 같이 토큰 코드가 발행될 텐데, 해당 코드를 복사해 기존 터미널의 CLI 창에 붙여넣기 해 로그인을 완료할 수 있다.

 

다음과 같은 화면이 보인다면 성공!

 


2. APP 생성, SDK 설치, Prdouction & Staging 키 등록

 

먼저 준비된 앱을 연동하기 위해 App Center에서 App을 생성해야 한다.

 

통상의 모바일 앱은 Android와 IOS를 모두 지원하기 때문에 총 2개의 앱을 만들어 관리하는 편이 편하다. 만약 앱을 분할해서 관리한다면 각각의 Release Type, OS 체제, 개발 플랫폼을 모두 선택한 뒤 생성하면 된다.

 

 

앱 생성을 완료했다면 좌측 탭에서 Distribute - CodePush를 선택한 뒤 보이는 가이드를 따르면 된다.

 

 

1번 과정인 CLI 설치는 이미 완료했으니, 이후 과정들을 진행하면 된다.

 

그전에!!

 

appcenter codepush deployment list -a <ownerName>/<appName> -k

// 앱을 분할해서 사용 중이라면 다음과 같은 방식으로 코드를 두 번 입력
appcenter codepush deployment list -a <ownerName>/<appName-android> -k
appcenter codepush deployment list -a <ownerName>/<appName-ios> -k

 

다음 코드를 통해 ios, android 환경 설정에 필수인 Staging 키와 Production 키를 가져오도록 하자. ownerName은 App Center 로그인에 사용했던 이메일을, appName에는 App을 생성 시 이름을 입력하면 된다. 

 

App Center

 

이전에 App Center에 정상적으로 로그인했다면, 다음과 같은 화면이 보일 것이다.

 

 

Staging Key개발 및 테스트 환경에서 사용되는 키로 주로 개발자나 QA(Quality Assurance) 팀이 애플리케이션을 테스트하고 디버깅하는 동안 사용한다.

 

Production Key는 실제 운영 환경에서 사용하는 키로 실제 사용자들에게 제공될 때 사용한다.

 

해당 키는 이후 환경 설정에 필요하므로 명령어를 따로 기록해 놓는 것을 추천한다.


3. IOS 환경 설정

 

0. pod install

 

cd ios
pod install
cd ..

 

먼저 ios 폴더로 이동해 pod install을 실행해 필수 CocoaPods 종속성(dependencies)을 설치한다.

 

 

1. AppDelegate.m 파일 설정

 

#import <CodePush/CodePush.h>

 

이후 ios 폴더 내부에 있는 AppDelegate.m 파일에 CodePush를 import 해온다. (경로는 ios > {프로젝트명} > AppDeletgate.m이다.) 이때, 해당 코드가 #ifdef FB_SONARKIT_ENABLED보다 코드보다 상단에 위치해 있어야 한다.

 

return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

 

같은 폴더에서 위 내용의 코드를 찾은 뒤, 다음과 같이 변경해 준다.

 

return [CodePush bundleURL];

 

위 변경을 통해 "항상" 모바일 앱의 가장 최신 버전의 JS bundle을 불러올 수 있도록 변경했다. CodePush를 사용할 시 이는 첫 설치 후 가장 최근 업데이트가 된 파일의 위치를 나타내는 설정이므로 꼭 바꿔주도록 하자.

 

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
  #else
    return [CodePush bundleURL];
  #endif
}

 

동일한 파일 내의 sourceURLForBridge가 위의 코드와 같이 보이면 된다.

 

 

2. info.plist 파일 설정

 

AppDelegate.m 파일이 있던 같은 폴더 내에 있는 Info.plist 파일을 열어 CodePushDeploymentKey라는 이름의 엔트리를 추가한다.

 

<key>CodePushDeploymentKey</key>
<string>${CODEPUSH_KEY}</string>


CODEPUSH_KEY에는 ${}없이 앞서 확인했던 키를 추가하면 된다. 이때, 현재 프로젝트의 성격에 맞는 Key(Staging 또는 Production)를 사용하면 된다.

 

 

3. info.plist에서 추가한 CODEPUSH_KEY를 전역변수로 연결

 

 

먼저 Xcode에서 프로젝트를 연 후, 최상위 폴더를 선택한 뒤 Info 탭에서 새로운 Configuration File을 추가한다.

 

 

이후 Build Settings 탭에서 새로운 User-Defined Setting을 추가(Add User-Defined Setting) 하고, 'MULTI_DEPLOYMENT_CONFIG'라는 이름으로 설정한다.

 

 

버튼 클릭 후 해당 창이 나오면 Release에 $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME), Staging에 $(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)을 입력한다.

 

위 값을 추가했다면 같은 방식으로 'CODEPUSH_KEY'라는 이름의 User-Defined Setting을 추가하고 Release에 Production deployment key를, Staging에 Staging deployment key를 추가한다. key값은 이전에 받아왔던 키와 동일하다.

 


4. Android 설정

 

Android 설정의 경우 프로젝트 내부 android 폴더에서 진행한다.

 

 

1. android/gradle.properties

 

먼저 gradle.properties에서 다음과 같은 코드를 추가한다.

 

CODEPUSH_DEPLOYMENT_KEY_DEBUG=
CODEPUSH_DEPLOYMENT_KEY_STAGING={staging deployment key}
CODEPUSH_DEPLOYMENT_KEY_PRODUCTION={production deployment key}

 

DEBUG의 경우 비워 놓은 채로 추가하면 되고, Staging과  Production에는 이전에 ios 세팅과 같은 방식의 키를 추가하되, Android와 ios 개발 환경을 앱으로 분리해 놓았다면 key 값 자체는 다를 수 있다.

 

2. android/settings.gradle

다음으로 settings.gradle에 아래와 같은 코드를 추가해 준다.

 

include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3. android/MainApplication.java

 

IOS 환경에서 AppDelegate.m파일의 설정을 수정한 것과 같은 과정이다. 우선 MainApplication.java파일의 페이지 상단에 다음과 같이 CodePush 패키지를 import 한다.

 

import com.microsoft.codepush.react.CodePush;

 

다음으로 같은 파일 public class 하단의 private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this)라는 변수의 내부에 다음과 같은 코드를 추가해 준다. 해당 과정을 통해 Code Push가 앱 구동 시 가져올 JS bundle의 위치를 지정할 수 있다.

 

@Override
protected String getJSBundleFile() {
	return CodePush.getJSBundleFile();
}

 

 

 

 

 

 

4. android/app/build.gradle

 

진행하기 전에 build.gradle의 파일 위치를 꼭 확인하자. app 폴더 외부에도 build.gradle 파일이 있을 수 있다.

 

먼저, 파일의 최하단에 다음 코드를 추가해 준다.

 

apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"

 

이어서 해당 파일에서 BuildTypes에 대한 지정을 하는 코드에 다음과 같은 방식으로 코드를 추가해 준다.

buildTypes {
   debug {
      ...
      resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_DEBUG
   }
   release {
      ...
      resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_PRODUCTION
   }
   releaseStaging { // 없다면 새로 추가
      initWith release
      resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_STAGING
      matchingFallbacks = ['release']
   }
}

 

한 가지 주의점이 있다면, CODEPUSH_DEPLOYMENT_KEY 관련된 부분은 기존 키 값을 넣는 것이 아니라 문자 그대로 입력해야 한다.

 

이어서 같은 파일 내의 android > defaultConfig에 대한 설정에도 다음과 같은 코드를 추가해 준다.

 

android {
	...
	defaultConfig {
		...
		resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
	}
	...
}

 

추가로 dependencies 설정에 if (enableHermes) 조건문이 있다면 다음과 같은 코드를 추가해 준다. (이미 있다면 그대로 두면 된다.)

 

releaseStagingImplementation files(hermesPath + "hermes-release.aar")

 


5. React Native 내부 설정

 

마지막으로 현재 프로젝트의 최상단 영역인 App.tsx(TypeScript가 아니라면 App.jsx) 파일로 이동한다.

 

먼저 파일 상단에 CodePush를 import 한다.

 

import CodePush from 'react-native-code-push';



이어서 파일 최하단에 CodePush 모듈과 CodePush 사용 시 적용할 옵션을 설정해 준다. 이때, 이전에 사용 중이던 export default App; 코드도 하단 코드와 같은 방식으로 변경해서 적용해 준다.

 

const codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_START,
  updateDialog: { 
    title: '업데이트 제목', 
    optionalUpdateMessage: '업데이트 시 메세지', 
    optionalInstallButtonLabel: '업데이트', 
    optionalIgnoreButtonLabel: '아니요.' 
  },
  installMode: CodePush.InstallMode.IMMEDIATE 
}

export default CodePush(codePushOptions)(App);

 

CodePush 옵션의 경우 새 버전이 업데이트될 때마다 사용자에게 보이는 모달 창이기 때문에 현재 제작 중인 앱의 성격에 맞게 변경해 주면 된다.

 

내가 참여한 프로젝트의 경우, 사용자가 직접적으로 메시지를 보지 않더라도 앱을 자동 설치하기 때문에 UX 개선을 위해 updateDialog 옵션을 삭제했고 checkFrequency도 사용자가 앱을 끄지 않고 잠시 비활성화시킨 상태에서 CodePush가 될 상황을 고려해. ON_APP_RESUME으로 변경했다.

 

const codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
  installMode: CodePush.InstallMode.IMMEDIATE,
};

export default CodePush(codePushOptions)(App);

 

CodePushOptions에 대해 더 자세히 알아보고 싶다면 공식 가이드를 참고해서 적용하도록 하자.

 

CodePush API 참조를 사용하여 SDK React Native - Visual Studio App Center

CodePush API를 사용하여 React Native SDK에 사용하는 방법

learn.microsoft.com

 


6.  CodePush 사용방법

 

모든 준비를 마쳤다면 코드가 변경된 상태로 프로젝트 폴더를 기준으로 터미널에 다음과 같이 입력해 보자.

 

appcenter codepush release-react -a {userName}/{AppName} -d {type(Production or Staging)}

// i.e user name = R2D2, AppName = sampleApp-android
appcenter codepush release-react -a R2D2/sampleApp-android -d Production

 

위 코드를 입력하고 정상적으로 완료되었다면 App Center로 들어가 보자. 다음과 같은 화면이 보일 것이다. 

 

최초 배포라면 v1으로 하나의 항목이 있을 것이다.

해당 목록에서 가장 최신 버전을 클릭하자.

 

 

이곳에서 Off로 설정되어 있던 설정을 모두 ON으로 변경 후, 마지막으로 Done 버튼을 클릭하면, 곧 해당 변경 사항이 현재 사용 중인 앱의 최신 버전에 반영된 것을 확인할 수 있다.

 

단, CodePush는 기존에 심사를 통해 등록된 파일들의 내부 내용을 별도의 심사 없이 바꾸는 과정이기 때문에 프로젝트에 새 파일이 추가되었다면 새롭게 심사를 통해 업데이트해야 한다.

 

해당 업데이트는 앱 심사를 거치지 않고 앱의 내용을 바꾸기 때문에 당연히 조심해서 업데이트하는 것이 좋다. 또한, 현재 로컬 파일들을 기반으로 CodePush가 되기 때문에 Hotfix를 진행하다 dev 모드 반영을 되돌리지 않는 등의 불상사를 초래할 수 있으므로 반드시 파일들을 확인하고 명령어를 입력하자. (Git Push도 그러하듯...)

 

마지막으로 CodePush를 통한 업데이트를 되돌리고 싶다면 다음과 같은 방법을 참고해서 진행하자.

// 가장 최근 내용을 롤백하려면
appcenter codepush rollback -a {userName}/{appName} {type}

// 선택적 내용을 롤백하고자 하면
appcenter codepush rollback -a {userName}/{appName} {type} --target-release {release id}

// i.e. v1으로 rollback하려는 경우
// appcenter codepush rollback -a R2D2/sampleApp-android Production --target-release V1

 


번외)  Android 오류 시 주의사항

 

안드로이드 환경의 경우, 설정하면서 오류가 있던 부분들이 있어서 혹시 CodePush 진행 중 오류가 발생했다면 다음을 참고하자.

 

1. gradle.properties 키 값 입력 확인

 

이전 과정에서 gradle.properties 파일에 다음과 같이 코드가 작성되어 있지 않은지 확인해 보자.

 

CODEPUSH_DEPLOYMENT_KEY_DEBUG=
CODEPUSH_DEPLOYMENT_KEY_STAGING={staging deployment key}
CODEPUSH_DEPLOYMENT_KEY_PRODUCTION={production deployment key}

 

만약 위와 같다면 staging deployment keyproduction deployment key에 알맞은 값을 입력해주어야 한다. 여기서 알맞은 값이란 이전에 App Center 로그인 후 터미널을 통해 요청했던 키를 말하고 있다.

 

키는 다음과 같이 문자열 방식으로 입력했다. (문자열 방식이 아닌 Plain Text 값도 괜찮은 것으로 보인다.) 혹시 키 값이 { } 안에 포함되어 있지는 않은지 확인해 볼 것!

 

2. strings.xml에서 Deployment Key 삭제

 

 

다음과 같은 식의 ':app:mergeReleaseResources' 류의 에러가 발생했다면 android 폴더 내부의 strings.xml 파일을 확인해봐야 한다.

 

 

공식 가이드에서는 strings.xml에 Deployment Key(배포 키)를 추가하라는 내용이 있었는데, 이 경우 오히려 에러가 발생했다.

 

따라서 해당 코드를 삭제하고 진행했을 때 오히려 정상적으로 CodePush가 반영될 수도 있다.

 

현재 진행 중인 세팅 과정에서 gradle.properties에 키 값을 추가해서 충돌이 일어나는 건지, 아니면 공식 가이드가 부정확한 것인지는 모르겠지만, codepush 에러를 살펴보면 키 값이 중복되어 있다는 것을 알 수 있다.

 

 


참조

댓글