본문 바로가기
  • Survival Plan
개발 이야기/React-Native

2. 리액트 네이티브 - 코드 사이닝 (code signing - 앱서명)

by IT/머신러닝 엔지니어의 독서/경제/육아 공부 리치윈드 - windFlex 2020. 4. 25.

[ 리액트 네이티브(React-Native)로 안드로이드 앱 출시 하기 ]
   1.편) 리액트 네이티브 - 안드로이드 릴리즈 빌드 하기 (https://richwind.co.kr/99)
   2.편 ) 리액트 네이티브 - 코드 사이닝 (앱서명) : 

 


1) 안드로이드 서명 인증

일단 서명인증 - 코드사이닝 (code signing) - 할 키스토어(keystore) 부터 만들어 주어야 합니다. 

keytool -genkey -v -keystore [key-name].keystore -alias [key alias] -keyalg RSA -keysize 2048 -validity 10000
$ cd android/app
$ keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 

입력완료 후, android/app 에 my-release-key.keystore (key-name) 으로 저장되어 있는 것을 확인 할 수 있다. 

https://facebook.github.io/react-native/docs/signed-apk-android

audit01@sagobunseog-ui-MacBook-Pro:~/my_dev/react_native/my2048/android/app$ ls
_BUCK                   build_defs.bzl proguard-rules.pro
app.iml                 debug.keystore src
build                   google-services.json
build.gradle            my-release-key.keystore

2)서명키 설정

 

서명키 (Signing key)가 생성 되었다면, apk를 빌드할 때 적용을 해야 하기 때문에 , gradle에서 이 Key를 설정해 주어야 한다. Key의 설정값을 저장하는 field는 android/gradle.properties 파일에 변수값을 추가하면 된다. 

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****

 

각 변수 입력 사이에 공백이 있으면 안된다. 공백없이 입력한다. 

실제 설정은 android/app/build.gradle 파일에 존재하면, android/gradle.properties의 변수 내용을 사용하여 아래와 같은 내용 추가를 통해 설정한다. 내용을 보면, release 버전 빌드 시, key 파일을 정의하고, 패스워드와 별칭(Alias) 등의 정보를 포함하여 빌드를 진행 한다는 것을 설정한 것이다. 

...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
...


android/app/build.gradle 내용을 조금 자세히 살펴 보면,
일반적으로 signingConfigs 항목에 debug만 존재하고 있다. release를 추가해 주면 된다. 

signingConfigs {
      debug {
          storeFile file('debug.keystore')
          storePassword 'android'
          keyAlias 'androiddebugkey'
          keyPassword 'android'
      }
       release {
          if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
              storeFile file(MYAPP_RELEASE_STORE_FILE)
              storePassword MYAPP_RELEASE_STORE_PASSWORD
              keyAlias MYAPP_RELEASE_KEY_ALIAS
              keyPassword MYAPP_RELEASE_KEY_PASSWORD
          }

        }
  }

buildTypes {
      debug {
          signingConfig signingConfigs.debug
      }
      release {
          // Caution! In production, you need to generate your own keystore file.
          // see https://facebook.github.io/react-native/docs/signed-apk-android.
          signingConfig signingConfigs.debug
          minifyEnabled enableProguardInReleaseBuilds
          proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
          signingConfig signingConfigs.release
      }

 

다음 명령어를 통하여 코드 사이닝이 제대로 수행 되었는지 확인 할 수 있다.  

$ ./gradlew signingReport


코드 사이닝의 패스워드 등이 잘 동작하지 않을 경우가 있으므로, 미리 미리 확인 해두도록 한다. 만약 패스워드가 잘 동작하지 않을 경우, gradle.properties 변수 대신에, android/app/build.gradle에 패스워드를 직접 기입해 보면 확인할 수 있다.
 


3) 다시 한번 빌드 (Build) - 코드서명 반영하여 마무리 하기

자, 코드 사이닝이 완료된 것을 확인하였다. 이제 이전 포스팅에서 설명한 릴리즈 빌드(https://richwind.co.kr/5)를 다시 한다. 코드가 바뀌었으니 다시 빌드해 주는 것이다. 이번 빌드가 완료되면 결과파일(번들파일)로 Play Store에 출시 할 수 있다.

android/ 폴더로 이동하여, ./gradlew 를 이용하여 빌드를 수행한다. 

$ cd android && ./gradlew bundleRelease

빌드가 완료되면 ouputs (android/app/build/output/bundle/release/app-release.aab) 파일이 생성된다. plat store 출시 할 때 해당 파일을 드래그드롭 하여 업로드 한다. 


4) 이미지 아이콘 생성

이제 Play Store에 앱 출시가 임박하였다. 출시 프로세스를 거치기 전, 필수적인 요소는 아니지만, App 아이콘이 없으면 어색할 것이다.  이미지 아이콘을 생성하도록 한다.  이미지 아이콘 생성은 "bam.tech/react-native-make" 모듈을 사용하여 생성할 수 있다. 

"bam.tech/react-native-make"을 사용하기에 앞서 아이콘 이미지를 준비하도록 하자. 아이콘 이미지 다양한 해상도 버젼을 자동으로 생성하기 때문에, 1024x1024 해상도를 준비해야 두는 것이 좋다. 

react-native-make 모듈을 설치한다. 로컬 명령어 라인에서만 사용하기 때문에, dev 버전으로 설치하면 되겠다. 또한 소스코드에서 사용하지 않기 때문에, yarn 을 통한 자동 링크는 하지 않아도 된다. 

$ npm install --save-dev @bam.tech/react-native-make
$ yarn add @bam.tech/react-native-make

 

설치가 완료되었으며, 아이콘을 적용하도록 한다.

react-native set-icon --path ./my2048.png --platform android --background "#FFFFFF"

ios의 경우 platform 이름만 변경해 주면 된다. 

$ react-native set-icon --path ./my2048.png --platform ios --background "#FFFFFF"

 

아이콘은 간략하게 Web Image Editor를 사용해서 그릴 수 있다. 포토샵 등을 이미지 프로세스 프로그램을 설치하기가 번거로우면 web version을 사용하자. 필자는 간략히 사용할 때는 pixlr를 사용한다. https://pixlr.com/x/

Pixlr 사이트를 이용한 간단한 이미지 및 아이콘 생성

 

이 과정은 Splash 이미지를 적용한 것과 동일한 과정을 거치므로, splash 포스팅을 참조하도록 하자. https://richwind.co.kr/79

 

리액트 네이티브(React Native) 앱에 Splash Image 적용하기

Splash Image 적용하기 1. Splash 화면용 이미지 준비 어플의 스플래쉬 화면에 사용할 이미지를 준비 합니다. 저는 귀찮은게 싫으므로 이전 포스팅에서 소개 해 드렸던, 웹이미지 에디터에서 간략히 그렸습니다. h..

richwind.co.kr

 참조 : https://dev-yakuza.github.io/ko/react-native/react-native-make/
https://github.com/bamlab/react-native-make/blob/master/docs/set-icon.md


5) 구글 앱 등록

이제 마지막 단계이다. Play Store Console에 접속한다.  이미 알고있겠지만, play store 에 앱을 개시하기 위해서는 google 개발자로 등록되어 있어야 한다. 

https://play.google.com/

Google Play store console
google Playstore : 애플리케이션 만들기

 

 

자체서명하는 것도 좋으나, 키관리가 어려울 수 있으므로, 구글에서 권장하는 사이닝을 사용하도록 하자.



이미 자체 key를 통하여 서명한 APK 파일을 업로드 하면, 구글이 signing 처리를 하지 못한다. 따라서, signing을 거치지 않은 pure한 리소스인 번들파일 (app.aab)를 업로드 하도록 한다. (*.aab는 ./android/app/build/outputs/bundle/release/ 에 저장되어 있다.)

Facebook의 React Native 공식 홈페이지에는 다음과 같이 가이드 하고 있다. 

 Gradle's bundleRelease will bundle all the JavaScript needed to run your app into the AAB (Android App Bundle). If you need to change the way the JavaScript bundle and/or drawable resources are bundled (e.g. if you changed the default file/folder names or the general structure of the project), have a look at android/app/build.gradle to see how you can update it to reflect these changes.
The generated AAB can be found under android/app/build/outputs/bundle/release/app.aab, and is ready to be uploaded to Google Play.

 필자도 아래와 같은 app.aab를 업로드 하였다.

Google Playstore console - Android App Bundle Upload 및 앱스토어 게시

 


참조 : 발생 가능한 에러들 (ERROR)

릴리즈 빌드하는 경우에 종종 발생할 수 있는 에러를 정리해 본다. 필자의 경우는 Build를 수행할 때 다음과 같은 에러가 발생 했다.

JVM Heap Space Error

아래 에러의 경우는, build Resource가 너무 크기 때문에 발생하는 경우로 알려 져 있다. 에러 문장을 잘 살펴보면, JVM Heap Space가 모두 소모 되었다고 한다. 

> Task :app:transformClassesAndResourcesWithR8ForRelease FAILED

FAILURE: Build failed with an exception.

* What went wrong:

Execution failed for task ':app:transformClassesAndResourcesWithR8ForRelease'.

> GC overhead limit exceeded

Expiring Daemon because JVM heap space is exhausted


이는 Android 개발 시, bundle/package 파일 1개당 포함될 수 있는 라이브러리와 용량이 제한적이므로 발생한다. 근본적인 해결 방법은 bundle의 파일 크기를 줄이면 된다. 그러나, assets 등 리소스가 많거나, 라이브러리의 수가 많은 경우 해결이 쉽지 않다. 

또 다른 방법으로, 번들을 multi/split 하면 문제가 해결 된다고 알려져 있으나, 이 또한 multi/split은 또다른 문제 발생이 예정되어 있어서 험난한 길이다. 따라서, split 하지 않고 resource 만을 최적화 하여 해결할 수 있도록, android/app/build.gradle에 빌드옵션을 설정하도록 한다. 

1) android/gradle.properties 에 다음과 같은 변수를 설정 ( JVM Heap Space 증가 설정)

org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2560m

2) android/app/build.gradle 파일에서  android 항목 하위에 dexOptions 항목을 추가한다.  (app 레벨 build.gradle임에 유의 한다.)

android {
  ...
  dexOptions {
      javaMaxHeapSize "3g"
  }
...
}

참조 : https://medium.com/@rishii.kumar.chawda/reduce-your-react-native-app-size-dramatically-5430d773c92f

 

password를  인식 오류/에러

> Task :app:packageRelease FAILED

FAILURE: Build failed with an exception.

* What went wrong:

Execution failed for task ':app:packageRelease'.

> 2 exceptions were raised by workers:

  java.lang.RuntimeException: java.lang.RuntimeException: com.android.ide.common.signing.KeytoolException: Failed to read key my-key-alias from store "/Users/audit01/my_dev/react_native/my2048/android/app/my-release-key.keystore": Keystore was tampered with, or password was incorrect

  java.lang.RuntimeException: java.lang.RuntimeException: com.android.ide.common.signing.KeytoolException: Failed to read key my-key-alias from store "/Users/audit01/my_dev/react_native/my2048/android/app/my-release-key.keystore": Keystore was tampered with, or password was incorrect

이 오류는 코드사이닝에 관련된 keystore file과 이와 관련된 password, alias 등을 정확하지 않기 때문에 발생한다. 종종 문자열을 복사하면서 다른 스트링으로 전환 되는 경우도 존재하므로, 정확히 확인을 한다. 특히 따옴표 등 특수 문자는 에디터(IDE) 특성에 따라서 동일해 보이지만 코드값이 다르게 입력되는 경우도 있다. 또한 White Space 등도 문제 될 수 있다.

필자는 gradle.properties 파일에 상수로 설정해 놓고 사용하는데, 이 경우 버그로 인하여 간혹 이런 경우가 발생하기도 한다. 코드 사이닝을 다시 한번 확인 하면 해결 할 수 있다.  

빌드 파일 사이즈 최적화

앞서 거론한 JVM Heap 사이즈 이슈와도 관련된 이야기이다. 빌드 파일 자체가 커서 좋을 것은 없다. 따라서 최적화 해줄 필요가 있는데, react-native의 경우는 다양한 최적화 플러그인들이 있다. 그러나 충분한 경험이 쌓이기 전에는 play store upload 시 진행해 주는 최적화 기능이 있으므로 이것을 적극 활용자.

그럼에도 불구하고, 아래 옵션 (프로가드 옵션)이 최적화 옵션과 연관되어 있다는 것은 인지하도록 한다. 

[android/app/build.gradle 파일]

...
defaultConfig {
    ...
    // ndk {
    //     abiFilters "armeabi-v7a", "x86"
    // }
}
...
def enableSeparateBuildPerCPUArchitecture = true
def enableProguardInReleaseBuilds = true
...

 

android/app/proguard-rules.pro 파일

# Note: the configuration keeps the entry point 'okio.AsyncTimeout { void scheduleTimeout(okio.AsyncTimeout,long,boolean); }', but not the descriptor class 'okio.AsyncTimeou
-dontwarn okio.**
# Note: the configuration keeps the entry point 'okhttp3.internal.ws.WebSocketWriter$FrameSink { void write(okio.Buffer,long); }', but not the descriptor class 'okio.Buffer'
-dontwarn okhttp3.**

 

Duplicate Resources - 리소스 충돌 (리소스 중복)

다음 에러의 종류는 동일한 리소스가 존재하기 때문에, 새롭게 빌드하는 리소스를 복사하거나 생성할 수 없는 경우이다. 근본적으로는 중복되는 리소스를 삭제해 주면 된다. 

> Task :app:mergeReleaseResources FAILED

[raw/app] /Users/audit01/my_dev/react_native/my2048/android/app/src/main/res/raw/app.json       [raw/app] /Users/audit01/my_dev/react_native/my2048/android/app/build/generated/res/react/release/raw/app.json: Error: Duplicate resources


FAILURE: Build failed with an exception.


* What went wrong:

Execution failed for task ':app:mergeReleaseResources'.

> [raw/app] /Users/audit01/my_dev/react_native/my2048/android/app/src/main/res/raw/app.json     [raw/app] /Users/audit01/my_dev/react_native/my2048/android/app/build/generated/res/react/release/raw/app.json: Error: Duplicate resources

대부분 이렇게 중복되는 리소스는, 아래 폴더에서 발생한다. 따라서 rm -rf를 사용하여 해당 리소스를 삭제한다. 

rm -rf ./android/app/src/main/res/drawable-mdpi/ 
rm -rf ./android/app/src/main/res/raw/ 

또한, 빌드 파일을 clean으로 정리해 주도록 하자.

$ cd android && ./gradlew clean && cd ..

Duplicated Resource 에러는 생각보다 자주 발생한다. 따라서, 이것을 통합하여 활용하면, package.json을 수정하여 간편하게 사용할 수 있다. package.json 파일에  release 스크립트를 별도로 만들어, build를 완료하고 나면, 사용된 리소스는 즉시 지우도록 설정할 수 있다.  (이 때 build를 수행 하면, rm -rf …. /res/… 등을 삭제한다. )

[ package.json ]

..
scripts: {
  "build": "react-native bundle --platform android
            --dev false
            --entry-file index.js
            --bundle-output android/app/src/main/assets/index.android.bundle
            --assets-dest android/app/src/main/res/
          && rm -rf ./android/app/src/main/res/drawable-mdpi/
          && rm -rf ./android/app/src/main/res/raw/",
  "release": "yarn build && cd ./android && ./gradlew bundleRelease"
}
...

 

 


[관련글]

2020/03/09 - [개발 이야기/React-Native] - 1. 리액트 네이티브 - 안드로이드 릴리즈 빌드 (Android Release Build)

2020/03/09 - [개발 이야기/React-Native] - React JS 입문 - 10만에 보는 리액트

2020/03/11 - [개발 이야기/React-Native] - 리액트 네이티브 (React-native) android Release 빌드 과정

2020/03/13 - [개발 이야기/React-Native] - 리액트 네이티브 보상형 광고 보여주기

2020/03/27 - [개발 이야기/React-Native] - 리액트 네이티브 - 앱 처음 실행 확인 하기

2020/03/27 - [개발 이야기/React-Native] - 리액트 네이티브(React Native) 앱에 Splash Image 적용하기

2020/03/09 - [개발 이야기/React-Native] - [ React Native ] 리액트 네이티브 10분만에 개발 - 구름 IDE 환경에서 리액트 네이티브 개발

댓글2