Logo
Published on
·20 min read

Flutter 문서 번역 - Android 앱 빌드 및 출시


일반적인 개발 주기 동안, command line에서 flutter run을 사용하거나 IDE에서 Run and Debug 옵션을 사용하여 앱을 테스트합니다. 기본적으로 Flutter는 앱의 디버그(debug) 버전을 빌드합니다.

예를 들어 Google Play Store에 게시하기 위한 앱의 출시(release) 버전을 준비할 때가 되면 이 페이지가 도움이 될 수 있습니다. 게시하기 전에 앱을 마무리하는 것이 좋습니다. 이 페이지는 다음 주제를 다룹니다:

참고: 이 페이지에서 [project]는 당신의 앱이 있는 디렉터리를 나타냅니다. 이 지침을 따르는 동안 [project]를 당신의 앱 디렉터리로 바꾸세요.

런처 아이콘 추가

새 Flutter 앱이 생성되면, 기본 런처 아이콘이 표시됩니다. 이 아이콘을 변경하려면 flutter_launcher_icons 패키지를 확인하세요.

또는, 다음 단계를 사용하여 수동으로 진행할 수 있습니다.

  1. 아이콘 디자인에 관한 Material Design product icons 지침을 검토하세요.

  2. [project]/android/app/src/main/res/ 디렉터리에서, 구성 한정자(configuration qualifiers)를 사용하여 이름이 지정된 폴더에 아이콘 파일을 넣습니다. 기본 mipmap- 폴더는 올바른 명명 규칙을 보여줍니다.

  3. AndroidManifest.xml에서, 이전 단계의 아이콘을 참조하도록 application 태그의 android:icon 속성을 업데이트합니다(예: <application android:icon="@mipmap/ic_launcher" ...).

  4. 아이콘이 대체되었는지 확인하려면, 앱을 실행하고 런처에서 앱 아이콘을 검사하세요.

Material Components 활성화

앱이 Platform Views를 사용하는 경우, Getting Started guide for Android에 설명된 단계에 따라 Material Components를 활성화할 수 있습니다.

예를 들어:

  1. <my-app>/android/app/build.gradle에 Android의 Material에 대한 종속성을 추가합니다.
dependencies {
    // ...
    implementation 'com.google.android.material:material:<version>'
    // ...
}

최신 버전을 확인하려면, Google Maven을 방문하세요.

  1. <my-app>/android/app/src/main/res/values/styles.xml에서 light them를 설정하세요.
-<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+<style name="NormalTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
  1. <my-app>/android/app/src/main/res/values-night/styles.xml에서 dark theme를 설정하세요.
-<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+<style name="NormalTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">

앱 서명

Play Store에 앱을 게시하려면, 디지털 인증서로 앱을 서명해야 합니다.

Android는 두 가지 서명 키를 사용합니다: upload 와 app signing.

  • 개발자는 upload key로 서명된 .aab 또는 .apk 파일을 Play Store에 업로드합니다.
  • 사용자는 app signing key로 서명된 .apk 파일을 다운로드합니다.

앱 서명 키를 생성하려면, 공식 Play Store 문서에 설명된 Play App Signing을 사용하세요.

앱을 서명하려면, 다음 지침을 사용하세요.

upload keystore 생성

keystore가 이미 있는 경우, 다음 단계로 넘어가세요. 그렇지 않다면, 다음 방법 중 하나를 사용하여 생성하세요.

  1. Android Studio 키 생성 단계를 따르세요.

  2. 다음 명령어를 command line에서 실행하세요:

    macOS 또는 Linux에서는, 다음 명령어를 사용하세요:

keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
        -keysize 2048 -validity 10000 -alias upload

Windows에서는, PowerShell에서 다음 명령어를 사용하세요:

keytool -genkey -v -keystore %userprofile%\upload-keystore.jks ^
        -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 ^
        -alias upload

이 명령어는 upload-keystore.jks 파일을 홈 디렉터리에 저장합니다. 다른 곳에 저장하려면 -keystore 매개변수에 전달하는 인수를 변경하세요. 하지만 keystore 파일은 비공개(private)를 유지하세요; 공개 소스 제어에 올리지 마세요!

참고

  • keytool 명령어가 경로에 없을 수도 있습니다—이것은 Android Studio에서 설치되는 Java의 일부입니다. 구체적인 경로의 경우 flutter doctor -v를 실행하고 ‘Java binary at:’ 에 적힌 경로를 찾으세요. 그런 다음 해당 경로의 끝부분에 있는 javakeytool로 바꾸어 사용하세요. 경로에 Program Files와 같이 공백으로 구분된 이름이 포함된 경우, 이름에 대해 플랫폼에 적합한 표기법을 사용하십시오. 예를 들어 Mac/Linux에서는 Program\ Files를 사용하고 Windows에서는 "Program Files"를 사용합니다.

  • -storetype JKS 태그는 Java 9 이상에만 필요합니다. Java 9 릴리스부터 keystore type의 기본값은 PKS12입니다.

앱에서 keystore 참조

keystore에 대한 참조가 포함된 [project]/android/key.properties라는 파일을 만듭니다. 꺾쇠 괄호(< >)를 포함하지 마세요. 꺾쇠 괄호는 해당 텍스트가 여러분의 값(value)의 자리 표시자로 사용됨을 나타냅니다.

storePassword=<password-from-previous-step>
keyPassword=<password-from-previous-step>
keyAlias=upload
storeFile=<keystore-file-location>

storeFile은 macOS의 경우 /Users/<user name>/upload-keystore.jks, Windows의 경우 C:\\Users\\<user name>\\upload-keystore.jks에 있을 수 있습니다.

경고: key.properties 파일을 비공개로 유지하세요; 공개 소스 제어에 올리지 마세요!


Gradle에서 서명(signing) 구성

[project]/android/app/build.gradle 파일을 편집해서 release 모드에서 앱을 빌드할 때 upload key를 사용하도록 gradle을 구성하세요.

  1. android 블록 앞에 properties 파일의 keystore 정보를 추가하세요:

    def keystoreProperties = new Properties()
    def keystorePropertiesFile = rootProject.file('key.properties')
    if (keystorePropertiesFile.exists()) {
        keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    }
    
    android {
          ...
    }
    

    key.properties 파일을 keystoreProperties 객체에 로드합니다.

  2. buildTypes 블록을 찾으세요:

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build. (release build에 대한 자체 서명 구성을 추가)
            // Signing with the debug keys for now, (현재 debug key로 서명 중)
            // so `flutter run --release` works. (그래서 `flutter run --release`가 작동됨.)
            signingConfig signingConfigs.debug
        }
    }
    

    그리고 이를 다음 서명 구성 정보로 바꿉니다:

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
    

이제 앱의 release 빌드는 자동으로 서명됩니다.

참고: gradle 파일을 변경한 후에 flutter clean을 실행해야 할 수도 있습니다. 이렇게 하면 캐시된 빌드가 서명 프로세스에 영향을 미치는 것을 방지할 수 있습니다.

앱 서명에 대한 더 자세한 내용은 developer.android.com의 Sign your app를 확인하세요.

R8로 코드 축소

R8은 Google의 새로운 코드 축소기이며, release APK 또는 AAB를 빌드할 때 기본적으로 활성화됩니다. R8을 비활성화하려면 --no-shrink 플래그를 flutter build apk 또는 flutter build appbundle에 전달하세요.

참고: 난독화 및 축소로 Android 애플리케이션의 컴파일 시간이 상당히 늘어날 수 있습니다.


멀티덱스(multidex) 지원 활성화

대규모 앱을 작성하거나 대규모 플러그인을 작성할 때, 최소 API가 20 이하일 경우 Android의 dex 제한인 64k 메서드에 직면할 수 있습니다. 축소가 활성화되지 않은 flutter run을 사용하여 앱의 디버그 버전을 실행할 때도 이런 문제가 발생할 수 있습니다.

Flutter 도구는 멀티덱스(multidex)를 쉽게 활성화할 수 있습니다. 가장 간단한 방법은 prompt가 표시될 때 multidex support를 선택하는 것입니다. 도구는 멀티덱스 빌드 오류를 감지하고 Android 프로젝트에 변경을 가하기 전에 묻습니다. 선택하면 Flutter가 자동으로 androidx.multidex:multidex에 의존하고, 생성된 FlutterMultiDexApplication을 프로젝트 애플리케이션으로 사용합니다.

IDE에서 RunDebug 옵션으로 앱을 빌드하고 실행하려고 할 때, 다음 메시지와 함께 빌드가 실패할 수 있습니다:

screenshot of build failure because Multidex support is required

command line에서 multidex를 활성화하려면, flutter run --debug를 실행하고 Android device를 선택하세요:

screenshot of selecting an Android device

prompt가 표시되면 y를 입력하세요. Flutter 도구는 multidex support를 활성화하고 빌드를 다시 시도합니다:

screenshot of a successful build after adding multidex

참고: Android SDK 21 이상을 타겟팅하면 멀티덱스 지원이 기본적으로 포함됩니다. 그러나 순전히 멀티덱스 문제를 해결하기 위해 API 21+을 타겟팅하는 것은 권장하지 않습니다. 이렇게 하면 오래된 기기를 사용하는 사용자가 의도하지 않게 제외될 수 있습니다.

Android 가이드에 따라 프로젝트의 Android 디렉터리 구성을 수정하여 수동으로 multidex를 지원하도록 선택할 수도 있습니다. 다음을 포함하도록 multidex keep file을 지정해야 합니다.

io/flutter/embedding/engine/loader/FlutterLoader.class
io/flutter/util/PathUtils.class

또한, 앱 시작에 사용되는 다른 클래스도 포함하세요. 멀티덱스 지원을 수동으로 추가하는 더 자세한 방법은 공식 Android 문서를 확인하세요.

앱 매니페스트 검토

기본 앱 매니페스트 파일인 AndroidManifest.xml을 검토하세요.이 파일은 [project]/android/app/src/main에 있습니다. 다음 값을 확인하세요:

application

앱의 최종 이름을 반영하도록 application 태그의 android:label을 편집하세요.

uses-permission

애플리케이션 코드에 인터넷 액세스가 필요한 경우, android.permission.INTERNET 권한을 추가하세요. 표준 템플릿에는 이 태그가 포함되어 있지 않지만, 개발 중에 인터넷 액세스를 허용하여 Flutter 도구와 실행 중인 앱 간의 통신을 가능하게 합니다.

Gradle 빌드 구성 검토

기본 Gradle build file([project]/android/app에 있는 build.gradle)을 검토하여 값이 올바른지 확인하세요.

Under the defaultConfig block

applicationId

최종의 고유한 application ID를 지정합니다.

minSdkVersion

앱을 실행하도록 설계한 minimum API level을 지정합니다. 기본값은 flutter.minSdkVersion입니다.

targetSdkVersion

앱을 실행하도록 설계한 target API level을 지정합니다. 기본값은 flutter.targetSdkVersion입니다.

versionCode

내부 버전 번호로 사용되는 양의 정수입니다. 이 숫자는 한 버전이 다른 버전보다 더 최신인지 확인하는 데에만 사용되며, 숫자가 높을수록 최신 버전을 나타냅니다. 이 버전은 사용자에게 표시되지 않습니다.

versionName

사용자에게 표시되는 버전 번호로 사용되는 문자열입니다. 이 설정은 원시 문자열 또는 문자열 리소스에 대한 참조로 지정할 수 있습니다.

buildToolsVersion

Gradle 플러그인은 프로젝트에서 사용하는 빌드 도구의 기본 버전을 지정합니다. 이 옵션을 사용하여 다른 버전의 빌드 도구를 지정할 수 있습니다.

Under the android block

compileSdkVersion

Gradle이 앱을 컴파일하는 데 사용할 API level을 지정합니다. 기본값은 flutter.compileSdkVersion입니다.

출시용(release) 앱 빌드

Play Store에 게시할 때 두 가지 출시 형식이 있습니다.

  • App bundle (권장)
  • APK

참고: Google Play Store는 app bundle 형식을 선호합니다. 자세한 내용은 Android App Bundles에서 확인하세요.


Build an app bundle

이 섹션에서는 release app bundle을 빌드하는 방법을 설명합니다. 서명 단계를 완료했다면, 앱 번들은 서명됩니다. 이 지점에서, reverse engineer를 더 어렵게 만들기 위한 Dart 코드를 난독화를 고려할 수 있습니다. 코드를 난독화하는 것은 빌드 명령에 몇 가지 플래그를 추가하고, 스택 추적을 해독하기 위한 추가 파일을 유지해야 합니다.

command line에서:

  1. cd [project]를 입력하세요.
  2. flutter build appbundle을 실행하세요.
    (flutter build를 실행하면 기본적으로 release 빌드로 설정됩니다.)

앱의 release bundle은 [project]/build/app/outputs/bundle/release/app.aab에 생성됩니다.

기본적으로, 앱 번들에는 armeabi-v7a (ARM 32-bit), arm64-v8a (ARM 64-bit), x86-64 (x86 64-bit)용으로 컴파일된 Dart 코드와 Flutter 런타임이 포함됩니다.

Test the app bundle

앱 번들은 여러 방법으로 테스트할 수 있습니다. 이 섹션에서는 두 가지 방법을 설명합니다.

bundle tool을 사용하여 오프라인으로

  1. GitHub 저장소에서 bundletool을 다운로드하세요.
  2. 앱 번들에서 APK 세트를 생성하세요.
  3. 연결된 장치에 APK를 배포하세요.

Google Play을 사용하여 온라인으로

  1. 테스트하려면 번들을 Google Play에 업로드하세요. 내부 테스트 트랙이나 알파 또는 베타 채널을 사용하여 번들을 프로덕션에 출시하기 전에 테스트할 수 있습니다.
  2. Play 스토어에 번들을 업로드하려면 다음 단계를 따르세요.

Build an APK

App Bundle이 APK보다 선호되지만, 아직 App Bundle을 지원하지 않는 스토어가 있습니다. 이 경우, 각 target ABI(Application Binary Interface)에 대해 release APK를 빌드하세요.

서명 단계를 완료했다면, APK는 서명됩니다. 이 지점에서, reverse engineer를 더 어렵게 만들기 위한 Dart 코드를 난독화를 고려할 수 있습니다. 코드를 난독화하려면 빌드 명령에 몇 가지 플래그를 추가해야 합니다.

command line에서:

  1. cd [project]를 입력하세요.
  2. flutter build apk --split-per-abi를 실행하세요. (flutter build 명령은 기본적으로 --release로 설정됩니다.)

이 명령은 세 개의 APK 파일을 생성합니다:

  • [project]/build/app/outputs/apk/release/app-armeabi-v7a-release.apk
  • [project]/build/app/outputs/apk/release/app-arm64-v8a-release.apk
  • [project]/build/app/outputs/apk/release/app-x86_64-release.apk

--split-per-abi 플래그를 제거하면 모든 target ABI에 대해 컴파일된 코드가 포함된 fat APK가 생성됩니다. 이러한 APK는 분할된 것보다 크기가 더 크며, 사용자가 장치 아키텍처에 적합하지 않은 네이티브 바이너리를 다운로드하게 됩니다.

Install an APK on a device

연결된 Android 장치에 APK를 설치하려면 다음 단계를 따르세요.

command line에서:

  1. Android 장치를 USB 케이블로 컴퓨터에 연결하세요.
  2. cd [project]를 입력하세요.
  3. flutter install을 실행하세요.

Google Play Store에 게시

Google Play 스토어에 앱을 게시하는 방법에 대한 자세한 내용은, Google Play 출시 문서를 확인하세요.

앱 버전 번호 업데이트

앱의 기본 version number는 1.0.0입니다. 업데이트하려면, pubspec.yaml 파일로 이동하여 다음 줄을 업데이트하세요:

version: 1.0.0+1

version number는 위의 예에서 1.0.0과 같이 점으로 구분된 3개의 숫자이며, 위의 예에서 1과 같이 +로 구분된 선택적(optional) build number가 뒤에 옵니다.

--build-name--build-number를 각각 지정하여 Flutter 빌드에서 version과 build number를 모두 재정의할 수 있습니다.

Android에서 build-nameversionName으로, build-numberversionCode로 사용됩니다. 자세한 내용은 Android 문서의 Version your app를 확인하세요.

Android용 앱을 다시 빌드하면, pubspec 파일의 version number의 업데이트가 local.properties 파일의 versionNameversionCode를 업데이트합니다.

안드로이드 출시 FAQ

다음은 Android 앱 배포에 관해 자주 묻는 몇 가지 질문입니다.

언제 app bundle과 APK를 빌드해야 하나요?

Google Play Store는 사용자에게 앱을 더 효율적으로 전달하기 위해 app bundle을 배포하는 것을 권장합니다. 그러나 Play Store 이외의 수단으로 앱을 배포하는 경우, APK가 유일한 옵션일 수 있습니다.

fat APK가 뭔가요?

fat APK는 여러 ABI에 대한 바이너리가 포함된 단일 APK입니다.

단일 APK가 여러 아키텍처에서 실행되므로 더 넓은 호환성이 있다는 이점이 있지만, 파일 크기가 훨씬 커서 사용자가 애플리케이션을 설치할 때 더 많은 바이트를 다운로드하고 저장하게 된다는 단점이 있습니다. App Bundle 대신 APK로 빌드할 때, --split-per-abi 플래그를 사용하여 Build an APK에 설명된 대로 분할 APK 빌드를 강력히 권장합니다.

지원되는 target 아키텍처는 뭔가요?

release mode에서 애플리케이션을 빌드할 때, Flutter 앱은 armeabi-v7a (ARM 32-bit), arm64-v8a (ARM 64-bit), and x86-64 (x86 64-bit) 용으로 컴파일될 수 있습니다. Flutter는 ARM 에뮬레이션을 통해 x86 Android용 빌드를 지원합니다.

flutter build appbundle로 생성된 앱 번들을 어떻게 서명하나요?

앱 서명을 참조하세요.

Android Studio에서 릴리스 빌드를 어떻게 하나요?

Android Studio에서 앱 폴더 하위의 android/ 폴더를 엽니다. 그런 다음, 프로젝트 패널에서 build.gradle (Module: app) 을 선택하세요:

다음으로, 빌드 변형을 선택하세요. 메인 메뉴에서 Build > Select Build Variant를 클릭하세요. Build Variants 패널에서 variants 중 하나를 선택합니다(debug가 기본값):

결과로 생성된 app bundle 또는 APK 파일은 앱 폴더 내의 build/app/outputs에 있습니다.