📌 참고
하기 포스트를 참고하여 작성하였음을 알립니다.
React Native에서 CodePush 사용하기!!!
현재 기준 모바일 크로스 플랫폼 시장에서 React Native 와 Flutter 가 서로 엎치닥 뒤치락 하며 시장 점유율을 가져가고 있다.
두 스택 모두 장단점이 뚜렷하다. 예를 들면 Third pary의 의존성이 크냐 안크냐가 제일 나뉠 것이다.
그 중 뚜렷한 차이점 중 하나는 바로 'CodePush' 를 지원하냐 안하냐의 차이이다.
이 포스트의 타이틀에서 보면 유추할 수 있듯이, React Native의 가장 큰 장점 중 하나는 바로 CodePush 기능을 지원한다는 것이다.
그렇다면 CodePush 는 무엇일까??
CodePush 는 Microsoft의 Visual Studio App Center에서 제공하는 서비스 중 하나로 클라우드 기반의 App 원격 업데이트 서비스이다.
원리는 git
과 유사하다. git
은 코드를 수정하고 우리가 터미널을 통해 수정한 코드를 커밋하고 우리의 git 서버에 push하면 다른 개발자가 pull을 통해 내가 수정한 코드를 내려받을 수 있는 구조다.
CodePush 도 우리가 터미널에서 배포 명령을 실행하면 앱을 bundle
파일로 변환하고 이를 CodePush 클라우드 서버에 저장한다. 그럼 사용자가 기존에 설치한 앱을 실행하면 CodePush 서버에 업데이트 여부를 확인하고 수정된 내용을 병합하는 것이다.
다음과 같이 몇 가지 특징을 지니고 있다.
심사절차가 필요없다.
방금 언급했듯이 CodePush는 클라우드를 기반으로 원격으로 App을 업데이트하기 때문에 별도의 스토어 심사절차가 필요하지 않다.
하지만, JavaScript 단에서 수정한 내용만 반영할 수 있다.
즉, Third-party의 의존성이 높은 RN은 패키지 수정 사항은 CodePush로 반영하지 못하고 재심사를 거쳐야 한다.
또한, JavaScript 파일이 중간에 새로 추가되거나 삭제된 경우 역시 CodePush로 올라갈 수 없으며 스토어의 심사를 통해 업데이트 해야 한다.
설치를 위해 하기 커맨드를 실행한다.
$ yarn global add appcenter-cli
App center에 로그인 하기 위해 설치한 appcenter-cli
를 이용하여 다음과 같이 로그인 한다.
$ appcenter login
실행하면 다음과 같이 App center 홈페이지가 열린다.
이 후 로그인을 완료하면 아래 이미지와 같이 토큰값이 가운데 표시된다.
이 토큰을 복사하여 커맨드 창에 나타나는 Enter your token from the browser:
부분에 복붙한다.
(버전에 따라 다르므로 하기 이미지와 일부 상이할 수 있다. 중요한 것은 토큰 값을 붙여넣는 것이다.)
아래 명령어를 따라 입력하여 App center 콘솔에 App을 등록한다.
📌 참고) iOS, Android 따로 등록해야 한다.
$ appcenter apps create -d {앱 이름} -o {os} -p {플랫폼 타입}
예시를 들면 다음과 같다.
# for iOS
$ appcenter apps create -d myapp_ios -o iOS -p React-Native
# for Android
$ appcenter apps create -d myapp_android -o Android -p React-Native
Key는 Staging, Production 각각 발급 및 등록한다.
📌 Staging? Production?
Staging : 배포하기 전 단계로 보통 QA(품질 보증) 및 내부 테스터들이 테스트를 해볼 때 사용하는 모드를 뜻한다. Debug 모드라고 봐도 무방하다.
Production : 실제 App을 배포하는 환경을 뜻한다.
보통 Staging 모드에서 Beta 테스트를 진행하고 테스트에 이상이 없으면 Production 모드로 승격시켜 모든 사용자에게 수정된 앱을 배포하는 과정을 지닌다.
등록을 위한 명령어는 다음과 같다.
$ appcenter codepush deployment add -a {user name}/{앱 이름} {Staging or Production)
예시를 들어보자.
$ appcenter codepush deployment add -a developer/myapp_ios Staging
$ appcenter codepush deployment add -a developer/myapp_ios Production
$ appcenter codepush deployment add -a developer/myapp_android Staging
$ appcenter codepush deployment add -a developer/myapp_android Production
참고로 위 4개의 명령어를 '모두 실행해야 한다!!'
명령어를 하나씩 입력할 때마다 각 앱과 모드에 맞는 키가 반환된다.
이 키를 잘 저장해두자 😀
만약 키 값을 잊어버렸다면, 아래 명령어를 실행하자.
$ appcenter codepush deployment list -a {user name}/{앱 이름} -k
프로젝트 root 경로에서 다음 명령어를 실행한다.
$ yarn add react-native-code-push
먼저 pod
를 설치해준다.
$ cd ios
$ pod install
다음, info.plist
파일을 열고 아래 코드를 그대로 추가해준다.
<key>CodePushDeploymentKey</key>
<string>$(CODEPUSH_KEY)</string>
그 다음, Xcode로 프로젝트를 실행하여 AppDelegate.m
파일을 열고 최상단에 다음 코드를 추가한다.
#import <CodePush/CodePush.h>
중간에 이러한 코드가 있을 것이다.
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
이 코드를 다음과 같이 통째로 수정한다.
return [CodePush bundleURL];
다음은 info.plist
에 추가한 CODEPUSH_KEY를 전역 변수로 연결하는 과정이다.
먼저, 아래 이미지와 같이 경로를 따라 간 후 Duplicate "Release" Configuration
을 클릭하여 Staging 이라고 수정 후 저장한다.
(필자는 이미 구현하여 Staging이 만들어진 모습이다.)
다음 그대로 Project 상태에서 상단에 Build Settings 클릭 후 '+' 버튼을 클릭하여 Add User-Defined Setting
을 클릭한다.
새로 추가된 항목의 이름을 Multi_Deployment_Config
라고 짓는다.
그러면 TARGETS/본인 Project 의 Build Settings에 아래 이미지와 같이 Multi_Deployment_Config
가 생성이 되는데, 각 Release와 Staging에 다음과 같이 작성하면 된다.
# Release
$ $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
# Staging
$ $(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)
Release
로 하드코딩 되어있는데, 이는 다음과 같은 이슈 때문이다. 이렇게 안하면 Staging 환경에서 동작하지 않는다!!다시 위에서 했던 작업을 반복하여 '+' 버튼을 눌러 Add User-Defined Settings
를 선택하고 이번엔 이름을 CODEPUSH_KEY
라고 선언한다.
동일하게 각 Release 와 Staging에 본인이 앞서 발급 받은 Key값을 알맞게 입력하면 된다.
📌 참고) SDK 싱크 문제로 인해 되도록 Android Studio 안에서 진행하도록 하자.
먼저, gradle.properties
파일 하단에 다음과 같은 코드를 추가하자.
CODEPUSH_DEPLOYMENT_KEY_DEBUG=
CODEPUSH_DEPLOYMENT_KEY_STAGING={발급 받은 staging deployment key}
CODEPUSH_DEPLOYMENT_KEY_PRODUCTION={발급 받은 production deployment key}
그 다음, 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')
그 다음, MainApplication.java
파일을 열고 상단에 패키지를 import 해준다.
import com.microsoft.codepush.react.CodePush;
그리고 하단에 new ReactNativeHost(this) 내부 스코프 맨 하단에 다음 코드를 추가해준다.
private final ReactNativeHost mReactNativeHost =
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
...
// 👇 이 부분!
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
// 👆 이 부분!
});
그 다음, build.gradle(app)
으로 들어가서 하단에 다음 코드를 추가해준다.
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
같은 파일에서 buildTypes 내부 스코프에 다음과 같이 코드를 추가해준다.
(releaseStaging
은 그대로 복붙하여 새로 만들어줘야 한다!)
buildTypes {
debug {
...
resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_DEBUG //here
}
release {
...
resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_PRODUCTION // here
}
releaseStaging { // releaseStaging 자체를 추가해줘야 함!
initWith release
resValue "string", "CodePushDeploymentKey", CODEPUSH_DEPLOYMENT_KEY_STAGING
matchingFallbacks = ['release']
}
}
역시 같은 파일에서 defaultConfig 내부 스코프에 다음 코드를 추가해준다.
android {
...
defaultConfig {
...
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis()) // here
}
...
}
마지막으로 if(enableHermes) {...}
로 시작하는 블럭이 있다면, 그 안에 다음 코드도 추가해준다.
releaseStagingImplementation files(hermesPath + "hermes-release.aar") // 이미 있는 경우도 있다.
최종적으로 Android Studio 우측 상단에 Sync now 를 클릭하여 마무리 해준다.
CodePush 는 프로젝트의 최상단 영역인 App.tsx
에서 설정되어야 한다.
다음과 같이 코드를 반영해보자.
import React from 'react';
import CodePush from 'react-native-code-push';
const App = ({}) => {
return (
...
);
}
const codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_START,
updateDialog: {
title: '새로운 업데이트',
optionalUpdateMessage: '업데이트가 존재합니다. 진행할까요?',
optionalInstallButtonLabel: '예',
optionalIgnoreButtonLabel: '아니요.'
},
installMode: CodePush.InstallMode.IMMEDIATE
}
export default CodePush(codePushOptions)(App);
CodePush.CheckFrequency.ON_APP_START
: 앱을 첫 실행할 때CodePush.CheckFrequency.ON_APP_RESUME
: 앱을 잠시 나갔다가 다시 들어올 때CodePush.CheckFrequency.MANUAL
: 수동 지정CodePush.InstallMode.IMMEDIATE
: 즉시 업데이트하고 다시 실행CodePush.InstallMode.ON_NEXT_RESTART
: 업데이트를 설치하려 하지만 강재로 앱을 재실행하진 않음. 앱이 자연스럽게 재실행되면 자동으로 업데이트를 실행CodePush.InstallMode.ON_NEXT_RESUME
: 업데이트를 실행하지만 앱 종료 후 사용자가 앱을 재실행할 때까지 앱이 자동으로 재실행되지 않는다.title
: dialog 제목optionUpdateMessage
: dialog에 띄울 메시지optionalInstallButtonLabel
: 업데이틀 실행 버튼 (ex. 예)optionalIgnoreButtonLabel
: 업데이트 미실행 버튼 (ex. 아니오)더 자세한 사항은 여기 공식 문서를 눌러 확인할 수 있다.
기본적으로 App center에 접속하여 로그인을 한다.
원하는 App을 선택하고 들어가면 왼쪽 메뉴에 distribute > CodePush로 들어간다.
그러면 현재 bundle이 배포되어 있는 버전들이 존재한다. (처음이라면 당연히 없을 것이다.)
아래 커맨드를 사용하여 실제 배포를 해보자.
명령어는 다음과 같다.
$ appcenter codepush release-react -a {user name}/{앱 이름} -d {Production or Staging}
이 명령어 끝에 -m
을 붙이면 사용자의 의사와 관계없이 강제 업데이트를 실행하게 된다.
혹여나 Rollback이 필요한 상황이 있을 것이다.
이 때 다음과 같은 명령어를 사용하면 된다.
# 가장 최근 내용을 롤백
$ appcenter codepush rollback -a {user name}/{앱 이름} {Production or Staging}
# 선택적 내용을 롤백
$ appccenter codepush rollback -a {user name}/{앱 이름} {Production or Staging} --target-release {release id}
여기서 release id
는 위 이미지에 보이는 것과 같이 v1
, v2
등을 뜻한다.
안녕하세요. 참고하신 원본 포스트 작성자입니다.
이 포스트를 작성하신거 관련해서... 감사하다는 말씀을 드리고 싶어서 댓글을 남깁니다!!!
당시에 제가 포스트를 작성할 때는 CodePush를 연결하는 과정에서 구글에서 찾은 연결 절차들이 너무 제각각이였어서, 나중에 다른 프로젝트에 연결할 때 저도 보고 이를 필요로 하는 많은 분들이 혼란을 겪지 않고 안정적으로 연결하는데 도움이 되고자 하는 목적에서 작성했던 것이었습니다. 그런데 거기에서 그치지 않고 제 포스트를 참고해서 다른 포스트가 작성됬다는 건 작성자님을 포함해서 이미 많은 사람들한테 도움이 됬다는 것이고 작성자님의 포스트를 통해 제가 고민했던 내용들이 더 많은 사람들에게 알려지게 됬다는 거니까요.
그런데 많은 사람들에게 도움이 됬던 CodePush가 이제 내년 3월을 기점으로 서비스가 종료된다고 생각하니까 좀 씁쓸하긴 하네요... 왠만한 대기업이 아닌이상 토스처럼 자체적으로 CodePush 환경을 구축할 수도 없는 노릇인 만큼... 많이 안타까울 뿐입니다. 만약 작성자님의 말씀대로 이와 비슷한 서비스가 출시된다면(장담할 순 없지만) 저도 제가 포스트를 작성했을 때의 마인드를 가지고 다시한번 포스트를 작성하여 공유하도록 하겠습니다.
다시한번 제가 고민했던 내용들을 널리 알려주셔서 감사합니다!! ^^
p.s)
처음 이 글을 읽고 나서 깜짝 놀랐어요!! 터미널에서 토큰 입력하는 사진 땜에 ㅋㅋㅋ(배경화면이 제가 노르웨이 여행갔을 때 찍은 사진이거든요 ㅋㅋㅋㅋㅋㅋㅋ)
안녕하세요. 혹시 지금도 RN으로 개발하고 계신가요?
Codepush는 25년 3월에 섭종한다고 해서 혹시 어떻게 대응하고 계신지 궁금해서요. EAS나 직접구축 이외에 찾지 못했는데 ㅠ 혹시 알고 계신게 있으실까요?