[ 앱 개발자 도전기 : 크로스플랫폼_Flutter ] Flutter 개발자들이 간과할만한 네이티브 : Android 빌드 시스템.

1

App_Dev : Flutter

목록 보기
7/9
post-thumbnail

[ 앱 개발자 도전기 : 크로스플랫폼_Flutter ] Flutter 개발자들이 간과할만한 네이티브 : Android 빌드 시스템.

▽ [ 앱 개발자 도전기 : 크로스플랫폼_Flutter ] Flutter 개발자들이 간과할만한 네이티브 : Android 빌드 시스템.

목  차

1. 빌드 시스템의 개요.

2. Gradle이 무엇이고 왜 중요한가?

3. Android 애플리케이션의 Gradle 빌드 구조

4. AOSP 플랫폼 빌드 : Soong + Kati + Ninja

5. Gradle 최적화 및 고급 기능

6. 왜 Flutter 개발자도 Android 빌드 시스템을 알아야 할까??

7. Flutter 프로젝트 안의 Android 빌드 시스템 구조

8. Gradle과 AGP(안드로이드 Gradle Plugin)

8. Flutter 빌드 -> Gradle 빌드 -> AOSP 빌드의 연결 고리

10. Flutter 개발자가 자주 만나는 빌드 문제들.

11. 빌드 Variants와 Product Flavors

12. NDK 빌드와 Flutter

13. 빌드 속도 최적화

14. CI/CD 파이프라인

15. Google Play의 API 레벨 요구사항은 어떻게 대응해야 할까??

16. Build Variants와 Product Flavors는 어떻게 활용하는가??

17. Package Name은 어떻게 설정하고 변경할까?

18. Multidex 에러는 어떻게 해결해야할까??

19. Desugaring은 무엇이고 언제 필요할까

20. Android 권한은 어떻게 관리해야할까

21. Keystore 관리와 앱 서명은 어떻게 해야할까

22. Google Play App Signing과 SHA-1 문제는 어떻게 해겨랗까

23. AndroidManifest.xml 충돌은 어떻게 해결할까

24. Firebase 설정은?

1. 빌드 시스템의 개요.


1-1. 빌드 시스템이란?

  • 소스 코드를 "실행 가능한 앱/APK/AAB" 로 변환하는 시스템

  • 여러 도구(컴파일러, 로거처리기, 리소스 처리기 등)를 연쇄적으로 조정하는 역할 !

1-2. Android의 빌드 시스템 종류.

  • 앱 단위 : Gradle + Android Gradle Plugin(AGP)

  • 플랫폼(AOSP) 단위 : Soong, Kati, Ninja 기반

  • 배경 및 역사 : GNU Make -> Soong + Ninja +Kati

2. Gradle이 무엇이고 왜 중요한가?


Gradle은 Android의 빌드 도구.

  • Gradle은 소스 코드를 APK나 AAB 파일로 변환하는 빌드 자동화 도구입니다.

  • Flutter Android 앱도 결국 Android 앱이기 때문에 Gradle을 사용합니다.

  • Flutter 프로젝트의 Android 부분 구조.
android/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── AndroidManifest.xml
│   │   │   ├── kotlin/.../MainActivity.kt
│   │   │   └── res/
│   │   ├── debug/
│   │   └── profile/
│   └── build.gradle  # 앱 레벨 Gradle 설정
├── gradle/
│   └── wrapper/
│       └── gradle-wrapper.properties  # Gradle 버전 정의
├── build.gradle  # 프로젝트 레벨 Gradle 설정
├── settings.gradle
└── local.properties

Gradle 버전과 AGP(Android Gradle Plugin) 버전의 관계

  • Gradle : 빌드 도구 자체의 버전
  • AGP ( Android Gradle Plugin) : Android 앱을 빌드하기 위한 플러그인 버전.

이 둘은 호환성이 있어야 합니다.

  • gradle-wrapper.properties에서 Gradle 버전 확인.
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
  • android/build.gradle에서 AGP 버전 확인.
buildscript {
    dependencies {
        classpath 'com.android.tools.build:gradle:8.2.0'  // AGP 버전
    }
}

호환성 매트릭스(2025년 기준)

  • Flutter가 자동으로 생성하는 버전이 항상 최신은 아닙니다.
  • 특히 오래된 프로젝트를 업데이트할 때 주의가 필요합니다.

Gradle Wrapper를 사용하는 이유

  • Gradles(Gradle Wrapper)가 뭐길래 꼭 써야 하나요?

    • Gradle Wrapper의 장점
      1. 버전 일관성 보장
        • 모든 팀원이 동일한 Gradle 버전 사용
        • '내 컴퓨터에선 되는데' 문제 방지
      2. Gradle 설치 불필요
        • 프로젝트에 필요한 Gradle 버전 사용
        • CI/CD 환경에서도 별도 설치 없이 빌드 가능
      3. 버전 관리가 쉬움
        • gradle-wrapper.properties 파일만 수정하면 끝
  • 사용 방법

# Mac/Linux
./gradlew clean
./gradlew build

# Windows
gradlew.bat clean
gradlew.bat build

# 잘못된 방법 (시스템 gradle 사용)
gradle clean  # 이렇게 하지 마세요!
  • Gradle 버전 업그레이드
# Wrapper를 통해 Gradle 버전 업그레이드
# 중요: 이 명령어를 2번 실행해야 합니다!
./gradlew wrapper --gradle-version=8.2
./gradlew wrapper --gradle-version=8.2  # 두 번째 실행

# 또는 gradle-wrapper.properties 직접 수정
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
  • 왜 2번 실행하는가???
    • Android 개발자 공식 문서에 따르면, Gradle과 Gradle Wrapper 자체를 모두 업그레이드하려면
      wrapper 명령어를 2번 실행해야 합니다.
    • 첫 번째 실행은 Wrapper 자체를 업데이트하고,
      두 번째 실행은 새로운 Wrapper로 Gradle을 업데이트합니다.

3. Android 애플리케이션의 Gradle 빌드 구조


3-1. Android 프로젝트에서 Gradle이 하는 일.

  • Gradle의 역할은 한 마디로,
    "코드를 앱(프로덕트)으로 만들어주는 모든 작업을 관리하는 빌드 툴"이라고 할 수 있습니다.

    • 소스 코드 -> Bytecode 컴파일(javac, kotlinc)
    • 리소스 처리(AAPT2)
    • Manifext merge
    • DEX 변환 (D8, R8 )
    • APK/AAB 패키징
    • 빌드 캐시 관리
    • 의존성 관리
    • 빌드 환경별 분기(Debug/Release, Flavors)

3-2. Android Gradle Plugin (AGP).

  • Gradle이 Android를 빌드할 수 있게 도와주는 것이 바로 "Android Gradle Plugin (AGP) 입니다.

    • Android Studio -> Gradle -> AGP -> 빌드 도구들 호출
    • 주요 역할
      • build.gradle DSL 제공
      • 빌드 타입, flavors 설정
      • Proguard/R8 tlfgod
      • AAPT2로 리소스 컴파일.

3-3. Android 프로젝트의 Gradle 구성 파일들.

  • Android 프로젝트를 만들면 Gradle 관련 파일들이 아래와 같이 생성됩니다.
MyApp/
├── build.gradle            # 프로젝트 수준
├── settings.gradle         # 프로젝트 세팅
├── gradle.properties       # Gradle 속성
├── gradle/                 # Wrapper
│    └── wrapper/
│         └── gradle-wrapper.properties
└── app/
     ├── build.gradle       # 모듈 수준
     └── src/
3-3-1. 프로젝트 수준 : build.gradle
  • 위치 : MyApp/build.gradle

  • 주요 역할

    • 전체 프로젝트 공통 설정
    • 모든 모듈에 적용할 플러그인/빌드스크립트
    • 빌드 스크립트 저장소 선언
  • ex

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:8.1.2")
    }
}
3-3-2. settings.gradle.
  • 위치 : MyApp/settings.gradle 또는 settings.gradle.kts

  • 주요 역할

    • 모듈 포함 여부 정의.
    • Gradle Version Catalogs (dependencies.toml) 지원
  • 예시 :

include ':app'
3-3-3. gradle.properties.
  • 위치 : MyApp/gradle.properties

  • 주요 역할

    • Gradle 빌드에 사용할 속성 값 설정.
    • 빌드 캐시 옵션, JVM 옵션 등.
  • 예시 :

org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true
android.enableJetifier=true

3-3-4. gradle-wrapper.properties.
  • 위치 : MyApp/gradle/wrapper/gradle-wrapper.properties

  • 주요 역할

    • Gradle 버전 고정.
    • CI/CD 환경에서 동일한 버전 유지.
  • 예시 :

distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
3-3-5. 모듈 수준 build.gradle.
  • 위치 : MyApp/app/build.gradle
  • 주요 역할
    • Android 모듈(앱 or 라이브러리) 빌드 정의.
    • compileSdk, minSdk, targetSdk
    • dependencies
    • buildTypes (Debug/Release)
    • flavors 설정
  • 예시 :
plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    namespace 'com.example.myapp'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.myapp"
        minSdk 24
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile(
                'proguard-android-optimize.txt'
            ), 'proguard-rules.pro'
        }
    }

    flavorDimensions "version"
    productFlavors {
        free {
            dimension "version"
            applicationIdSuffix ".free"
        }
        paid {
            dimension "version"
            applicationIdSuffix ".paid"
        }
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.10.1'
    implementation 'androidx.appcompat:appcompat:1.6.1'
}

3-4. Build Variants (빌드 변형).

3-5. 의존성 관리.

3-6. Proguard / R8.

3-7. Gradle 빌드 최적화.

4. AOSP 플랫폼 빌드 : Soong + Kati + Ninja


4-1. 🔰 AOSP 빌드란??

AOSP(Android Open Source Project)는 Android 플랫폼 자체를 빌드하기 위한 전체 소스 코드와 도구들의 모음.

  • 우리가 흔히 사용하는 Android 시스템(커널 제외) 전체를 제작 가능.

💡 이 빌드는 앱이 아닌 OS 자체를 빌드하는 것!

  • 이 빌드 시스템으로 생성되는 결과물
    • /system, /vendor, /boot, /recovery 파티션 이미지
    • system.img , boot.img, vendor.img, userdata.img ....
    • 다양한 시스템 서비스, 프레임워크 JAR, 네이티브 HAL 바이너리 등등
4-1-1. 🧱 AOSP 빌드의 기본 구성요소.
구성 요소역할
device/, vendor/, kernel/디바이스별 설정 및 드라이버
frameworks/, system/Android 플랫폼의 핵심 코드
packages/기본 앱들 (Settings, Launcher 등)
build/빌드 시스템 관련 스크립트
Android.mk, Android.bp모듈 빌드 스크립트
4-1-2. 🛠️ 어떤 툴이 AOSP를 빌드할까?
  • AOSP 빌드는 기본적으로 아래 3개의 핵심 빌드 툴을 사용해 구성.
이름역할설명
Soong프론트엔드Android.bp 기반의 빌드 시스템 (Go로 작성)
Kati중간 파서Android.mk 파일을 Ninja 포맷으로 변환 (Make 호환)
Ninja백엔드실제 빌드 수행 (컴파일, 링크 등 빠르게 병렬 처리)

★ 기존 GNU Make 기반 빌드에서 성능과 유지보수성을 개선한 구조

4-1-3. ⚙️ 빌드 명령어 흐름.
  • AOSP에서는 다음과 같은 순서로 빌드가 진행.
$ source build/envsetup.sh
$ lunch aosp_x86_64-eng
$ m

각 단계의 의미는

단계설명
envsetup.sh빌드 환경 변수 등록
lunch빌드 대상 디바이스와 variant 선택
m빌드 실행 (make wrapper → soong_ui → ninja 실행)
4-1-4. 🏗️ 빌드 대상(Target) 구조.
  • Android는 하나의 소스 트리로 여러 기기를 빌드할 수 있게 설계.

  • 이를 위한 Product와 Variant 개념이 존재.

    • Product: 기기별 구성 (aosp_x86, aosp_arm64, aosp_pixel)
    • Variant: 빌드 타입 (user, userdebug, eng)

ex)

lunch aosp_arm64-eng
  • aosp_arm64 = Product
  • eng = Variant(개발용 디버깅 전체 가능)
4-1-5. 📁 AOSP 소스 트리 구조.
AOSP/
├── build/               # 빌드 시스템 관련 (core, soong, etc)
├── device/              # 디바이스 정의
├── vendor/              # 벤더 드라이버, HAL
├── kernel/              # 리눅스 커널 소스
├── system/              # core 시스템 컴포넌트
├── frameworks/          # Android Framework (Java)
├── packages/            # 기본 앱
├── prebuilts/           # 미리 빌드된 바이너리
├── external/            # 외부 오픈소스
├── Android.bp / mk      # 각 디렉토리의 빌드 정의
4-1-6. 🧩 빌드 아티팩트란?

빌드 후 생성되는 주요 이미지 파일.

| 이미지            | 설명                                           |
| -------------- | -------------------------------------------- |
| `system.img`   | `/system` 파티션 이미지 (framework, system apps 등) |
| `vendor.img`   | `/vendor` 파티션 (HAL, 드라이버 등)                  |
| `boot.img`     | 커널 + initramfs                               |
| `recovery.img` | 복구 파티션                                       |
| `userdata.img` | 초기 사용자 데이터 파티션                               |
4-1-7. 🔗 Android.mk vs Android.bp
항목Android.mkAndroid.bp
문법MakefileJSON-like DSL
처리 도구Kati → MakeSoong
유지보수복잡, 오래됨단순화, 현대화
사용 위치legacy 모듈신규 Soong 기반 모듈

-> 점점 Android.mk는 사라지고 Android.bp로 전환 중.

4-1-8. 📌 실무에서 자주 마주하는 빌드 관련 명령어.
명령어역할
m / mm전체 / 현재 디렉토리만 빌드
mmm path특정 경로 모듈만 빌드
m MODULE_NAME특정 모듈만 빌드
m installclean빌드 캐시 초기화
m clean전체 클린 빌드
4-1-9. 📝 요약 정리.
항목내용
목적Android 플랫폼(시스템) 전체 빌드
결과물system.img, boot.img 등
핵심 툴Soong + Kati + Ninja
빌드 정의Android.bp (신규), Android.mk (레거시)
빌드 흐름soong_ui.ninja 생성 → ninja 실행
기기 정의device/, vendor/ 디렉토리에서 구성
빌드 대상lunch 명령으로 Product + Variant 설정

4-2. 🧠 AOSP 빌드 시스템의 핵심 구성요소: Soong + Kati + Ninja

4-2-0. 0️⃣ 먼저: 왜 일반적인 Make나 Gradle로는 부족했을까?
  • 🔸 기존 시스템의 문제.
도구문제점
Make (Android.mk)빌드 의존성 복잡도 증가, 성능 저하, 유지보수 어려움
Gradle앱 수준 빌드에 최적화되어 있으며, 플랫폼 전체를 다루기엔 부적합
  • 🔸 AOSP 빌드는 규모가 다름.
    • 수십만 가지의 소스 파일
    • 수천 개의 모듈
    • 크로스 컴파일
    • 다중 플랫폼/ 디바이스 지원

-> 빌드 속도, 병렬성, 명확한 의존성 관리가 반드시 필요해짐.

4-2-1. 1️⃣ 빌드 시스템 구성요소 개요
구성요소역할언어특징
Soong빌드 시스템의 프런트엔드GoAndroid.bp 파서 및 의존성 관리
Kati레거시 Android.mk 호환C++Makefile → Ninja로 변환
Ninja빌드 백엔드 실행기C++빠르고 단순한 빌드 수행
4-2-2. 2️⃣ Soong: Android의 빌드 시스템 새 시대.
✨ Soong이란?

Soong은 Android에서 기존 Make 시스템을 대체하기 위해 도입된 현대적인 빌드 시스템.

  • Android 8.0(Oreo)부터 공식 도입.
  • Google이 직접 설계
  • Go 언어 기반
  • Android.bp라는 새로운 DSL(Domain-Specifit Language) 사용
🔧 Soong의 역할.
  1. Android.bp 파일 파싱

  2. 모듈 간 의존성 분석

  3. 빌드 규칙을 생성

  4. Ninja 빌드 파일 (build.ninja) 생성

즉, Soong은 "어떻게 빌드할 것인가"를 정의해서 Ninja가 실행할 수 있도록 만들어주는 도구 !

📄 Android.bp 문법 예시.
cc_library {
    name: "libhello",
    srcs: ["hello.cpp"],
    shared_libs: ["liblog"],
}

위 코드처럼 선언하면 Soong은

  • 이 모듈은 이름, 소스 파일
  • 의존하는 다른 모듈(liblog)
  • 빌드 방식 (cc_library = c++ 라이브러리 )

이것들을 분석해서 build.ninja 파일에 변환.

4-2-3. 3️⃣ Kati: Android.mk를 포기하지 못할 때
🔸 Kati란?

Kati는 Makefile(=Android.mk)을 Ninja 형식으로 변환하는 GNU Make 호환 빌드 파서

  • C++로 작성
  • 기존 Make 구조를 완전히 탈피하지 못한 모듈들을 위해 존재
  • Android.mk -> " .ninja " 파일로 변환
  • 내부적으로 " ninja " 를 실행하는 방식으로 빌드 연결.
🧭 언제 사용되나?.
  • 레거시 모듈(3rd-party 라이브러리, 커널 드라이버 등)
  • 아직 Soong(Android.bp)으로 전환되지 않는 모듈을 다룰 때

Google은 점진적으로 Android.mk → Android.bp 전환 중이지만 완전한 전환은 아직 진행형

4-2-4. 4️⃣ Ninja: 궁극의 빌드 실행기
🔥 Ninja란?

Ninja는 Soong과 Kati가 만든 .ninja 파일을 실제로 실행하는 초고속 빌드 툴

  • Google이 개발
  • Make보다 10배 빠른 속도
  • 병렬 빌드에 최적화
  • 내부 문법/기능 최소화(빠름에 집중)
🧱 특징.
| 항목     | 설명                          |
| ------ | --------------------------- |
| 의존성 관리 | `.ninja` 파일 기반으로 처리됨        |
| 속도     | Make 대비 매우 빠름 (수천 파일 병렬 처리) |
| 추상화    | 없음. 순수 실행만 담당               |
| 실행 위치  | `out/` 디렉토리 하위에서 실행됨        |
4-2-5. 5️⃣ 전체 빌드 흐름 정리.
        Android.bp         Android.mk
            │                  │
         [Soong]           [Kati]
            │                  │
            └─────┬──────┬─────┘
                  ▼
             build.ninja
                  │
              [Ninja]
                  ▼
        바이너리 / 이미지 생성
🔁 실제 명령 실행 흐름
$ source build/envsetup.sh
$ lunch aosp_x86_64-eng
$ m

→ m 은 결국 soong_ui를 실행하고
→ soong_ui는 Soong → Kati → Ninja를 호출
→ Ninja가 모든 작업을 병렬로 실행해 빌드 완료

4-2-6. 6️⃣ out 디렉토리 구조 (빌드 결과물).
out/
├── build.ninja        ← Ninja 실행용 빌드 스크립트
├── target/
│   └── product/
│       └── aosp_x86/
│           ├── system.img
│           ├── boot.img
│           └── ... (파티션 이미지들)
├── soong/
│   └── .intermediates/ ← 각 모듈별 중간 결과물
4-2-7. 7️⃣ 요약: 각 툴의 역할 비교.
| 도구    | 위치                    | 입력          | 출력          | 핵심 기능       |
| ----- | --------------------- | ----------- | ----------- | ----------- |
| Soong | `build/soong`         | Android.bp  | build.ninja | 신규 모듈 정의 파싱 |
| Kati  | `prebuilts/.../kati`  | Android.mk  | ninja 파일    | 레거시 모듈 변환   |
| Ninja | `prebuilts/.../ninja` | build.ninja | 실제 실행       | 병렬 빌드       |

5. Gradle 최적화 및 고급 기능


6. 왜 Flutter 개발자도 Android 빌드 시스템을 알아야 할까??


7. Flutter 프로젝트 안의 Android 빌드 시스템 구조


8. Gradle과 AGP(안드로이드 Gradle Plugin)


8. Flutter 빌드 -> Gradle 빌드 -> AOSP 빌드의 연결 고리


10. Flutter 개발자가 자주 만나는 빌드 문제들.


11. 빌드 Variants와 Product Flavors


12. NDK 빌드와 Flutter


13. 빌드 속도 최적화


14. CI/CD 파이프라인


15. Google Play의 API 레벨 요구사항은 어떻게 대응해야 할까??


2025년 8월 31일 , 큰거 온다

Google Play는 매년 타겟 API 레벨을 올리도록 요구합니다.

2025년 8월 31일부터는 모든 신규 앱과 업데이트가 Android 15(API 레벨 35) 이상을 타겟해야합니다.

  • android/app/build/gradle에서 설정.
android {
    compileSdkVersion 35  // 컴파일 시 사용할 SDK
    
    defaultConfig {
        applicationId "com.example.myapp"
        minSdkVersion 21      // 최소 지원 Android 버전
        targetSdkVersion 35   // 타겟 Android 버전 (2025년 8월 31일 필수)
        versionCode 1
        versionName "1.0.0"
    }
}

각 SDK 버전의 의미

  • minSdkVersion (최소 SDK 버전)

    • 앱이 실행될 수 있는 최소 Android 버전
    • Flutter 기본값은 21(Android 5.0)
    • 낮출수록 더 많은 기기 지원, 하지만 사용할 수 있는 API 제한.
  • targetSdkVersion (타겟 SDK 버전)

    • 앱이 테스트되고 최적화된 Android 버전
    • 새로운 Android의 보안 및 성능 기능 적용
    • Google Play 정책에 따라 주기적으로 업데이트 필요
  • compileSdkVersion (컴파일 SDK 버전)
    • 앱을 컴파일할 때 사용하는 Android SDK 버전
    • 보통 targetSdkVersion과 같거나 높게 설정
    • 최신 API를 사용하려면 높여야 함.

Flutter의 자동 SDK 버전 설정 활용하기

  • Flutter 3.16 이상의 새로운 기능
    • Flutter는 이제 권장 Android SDK 버전을 자동으로 설정해줍니다!(이득)
    • android/app/build.gradle에서는 이렇게 사용.
android {
    // 하드코딩 대신 Flutter 권장 버전 사용
    compileSdkVersion flutter.compileSdkVersion
    
    defaultConfig {
        applicationId "com.example.myapp"
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode 1
        versionName "1.0.0"
    }
}
  • 장점.
    • Flutter 업그레이드 시 자동으로 최신 권장 버전 적용
    • Google Play 정책 변경에 자동 대응
    • 수동으로 버전 관리할 필요가 없음.!
  • 예외 : 수동 설정이 필요한 경우.
android {
    // 특정 버전이 필요하면 직접 지정
    compileSdkVersion 35
    
    defaultConfig {
        // 더 낮은 최소 버전이 필요한 경우
        minSdkVersion 19  // Flutter 기본값보다 낮게
        
        // Google Play 요구사항에 맞춰 직접 설정
        targetSdkVersion 35
    }
}
  • 현재 Flutter 버전의 기본값 확인.
# Flutter SDK의 기본 설정값 확인
cat $FLUTTER_ROOT/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy | grep -E "(compileSdk|minSdk|targetSdk)"

API 35로 업데이트 시 주의사항.

    1. 새로운 권한 모델 확인
      • 특히 알림, 위치, 사진 기능과 관련된 권한 변경사항 체크
    1. 백그라운드 작업 제한
      • 배터리 최적화로 인한 제한 강화
    1. 테스트 필수
      • 실제 Android 15 기기나 에뮬레이터에서 테스트.

16. Build Variants와 Product Flavors는 어떻게 활용하는가??


개발/스테이징/운영 환경을 분리하고 싶다면??

많은 개발자들이 개발 서버와 운영 서버를 사용하는데,
매번 코드를 수정하며 빌드하는 것은 코드 안전성 측면에서 매우 위험.

  • android/app/build.gradle에 flavor 추가
android {
    flavorDimensions "environment"
    
    productFlavors {
        dev {
            dimension "environment"
            applicationIdSuffix ".dev"
            versionNameSuffix "-dev"
            
            // 개발 서버 URL 등을 buildConfigField로 정의
            buildConfigField "String", "API_URL", '"https://dev-api.example.com"'
        }
        
        staging {
            dimension "environment"
            applicationIdSuffix ".staging"
            versionNameSuffix "-staging"
            
            buildConfigField "String", "API_URL", '"https://staging-api.example.com"'
        }
        
        prod {
            dimension "environment"
            
            buildConfigField "String", "API_URL", '"https://api.example.com"'
        }
    }
}

Flavor별 폴더 구조와 AndroidManifest.xml 활용.

  • 각 Flavor마다 별도의 폴더를 만들어 리소스를 다르게 설정할 수 있습니다.
android/app/src/
├── main/
│   ├── AndroidManifest.xml  # 기본 Manifest
│   ├── res/
│   │   ├── mipmap-hdpi/
│   │   │   └── ic_launcher.png
│   │   └── values/
│   │       └── strings.xml
│   └── kotlin/
├── dev/
│   ├── AndroidManifest.xml  # dev용 Manifest (선택적)
│   └── res/
│       ├── mipmap-hdpi/
│       │   └── ic_launcher.png  # dev용 아이콘
│       └── values/
│           └── strings.xml  # dev용 앱 이름
├── staging/
│   └── res/
└── prod/
    └── res/

Flavor별 앱 이름 변경

  • main/res/values/strings.xml (기본)
<resources>
    <string name="app_name">My App</string>
</resources>
  • dev/res/values/strings.xml (개발용)
<resources>
    <string name="app_name">My App (Dev)</string>
</resources>
  • staging/res/values/strings.xml (스테이징용)
<resources>
    <string name="app_name">My App (Staging)</string>
</resources>

Flavor별 아이콘 변경

  • 각 flavor 폴더의 res/mipmap-* 디렉토리에 다른 아이콘을 넣으면 자동으로 적용됩니다.

    • dev/res/mipmap-hdpi/ic_launcher.png -> 개발용 아이콘
    • staging/res/mipmap-hdpi/ic_launcher.png -> 스테이징용 아이콘

Flavor별 AndroidManifest.xml 활용.

  • dev/AndroidManifest.xml 로 특정 권한이나 설정 추가.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- dev 환경에서만 필요한 디버깅 권한 -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    
    <application>
        <!-- 개발 환경에서만 HTTP 허용 -->
        <meta-data
            android:name="android.network-security-config"
            android:resource="@xml/network_security_config_dev" />
    </application>
</manifest>

병합 규칙 이해하기.

AndroidManifest.xml은 다음 순서로 병합
    1. main/AndroidManifest.xml (기본)
    1. flavor/AndroidManifest.xml (flavor별)
    1. 빌드 타입별 Manifest(debug/release)

충돌입 라생 시, tools:replace를 사용해 덮어쓰기 가능.

Flutter에서 실행.

# 개발 환경으로 실행 (dev 아이콘과 이름 사용)
flutter run --flavor dev

# 운영 환경으로 빌드 (prod 설정 사용)
flutter build apk --flavor prod

Dart 코드에서 접근하려면 별도 설정이 필요합니다.

보통 flutter_flavor 패키지를 사용합니다.

17. Package Name은 어떻게 설정하고 변경할까?


Package Name은 앱의 고유 식별자입니다.

  • Package Name(Application ID)은 Google Play Store에서 앱을 식별하는 고유한 ID입니다.
    • "한 번 출시하면, 절대 변경할 수 없으니" 신중하게 결정해야 합니다.
  • 기본 Package Name 확인.
// android/app/build.gradle
defaultConfig {
    applicationId "com.example.myapp"  // 이것이 Package Name
}

Package Name 변경 방법.

    1. build.gradle 수정.
defaultConfig {
    applicationId "com.mycompany.awesomeapp"  // 원하는 이름으로 변경
}
    1. MainActivity 패키지 경로 변경
android/app/src/main/kotlin/com/example/myapp/  
→ android/app/src/main/kotlin/com/mycompany/awesomeapp/
    1. MainActivity.kt 수정.
package com.mycompany.awesomeapp  // 패키지명 변경

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}
    1. AndroidManifest.xml은 수정하지 않아도 됩니다
      • .MainActivity 로 되어 있으면 자동으로 applicationid를 따라갑니다.

Package Name 작명 규칙.

  • 역 도메인 형식 권장 : com.회사명.앱이름
  • 소문자만 사용
  • 특수문자 사용 불가(언더스코어는 가능)
  • 예약어 사용 불가 (java, android 등)

18. Multidex 에러는 어떻게 해결해야할까??


✅ Multidex 에러란?

: Multidex 에러는 흔히 이런 식의 에러 메시지로 나타남.

com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

혹은

Cannot fit requested classes in a single dex file

이 오류 메시지의 의미는??

  • Android 앱은 classes.dex라는 파일에 자바 바이트코드를 담아서 빌드합니다.

  • Dalvik/ART의 single DEX limit -> 하나의 DEX 파일에 최대 65536 메서드 참조 가능.

  • 앱이 너무 커져서, 이 참조 가능 한도를 초과하면 에러가 발생하게 됩니다.

  • Flutter 프로젝트로 네이티브 라이브러리, 여러 Plugin, Firebase 등을 많이 쓰면
    금방 이 한도를 넘어서게 됩니다.

🔧 Multidex 해결법.

1. minSdkVersion 확인.

minSdkVersion ≥ 21 이라면 기본적으로 multidex 지원됨 (ART가 native로 멀티덱스 로딩 가능)

  • Flutter 권장
defaultConfig {
    minSdkVersion 21
}

-만약 20 이하라면, Legacy multidex 설치 필요

2. legacy multidex 설정 (minSdkVersion < 21)

(1) Gradle 의존성 추가

  • android/app/build.gradle -> dependencies 블록에 추가:
dependencies {
    implementation 'androidx.multidex:multidex:2.0.1'
}

(2) multiDexEnabled 설정

android {
    defaultConfig {
        ...
        multiDexEnabled true
    }
}

(3) Application 클래스 수정.

Applicaton 클래스를 Flutter에선 보통 생성해줘야 합니다.

  • android/app/src/main/java/.../MainApplication.java 생성

ex)

package com.example.myapp;

import io.flutter.app.FlutterApplication;
import androidx.multidex.MultiDex;
import android.content.Context;

public class MainApplication extends FlutterApplication {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}

그리고서 AndroidManifext.xml에 아래처럼 Application 이름을 등록해줍니다.

<application
    android:name=".MainApplication"
    ...>
3. Proguard/R8 최적화.
  • 앱 크기가 너무 커서 멀티덱스가 터진다면, R8(혹은 Proguard)로 불필요한 코드 줄이기.
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

그리고서 build,gradle:

release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
4. 의존성 정리.
  • 중복된 라이브러리 버전 제거
  • implementaion -> api 변경 여부 점검
  • 불필요한 plugin 제거
  • transitive dependency 확인 (→ ./gradlew app:dependencies)
5. Flutter-specific 주의점.
  • flutter build apk -> 결국 Gradle 빌드 호출
  • flavors 여러 개 쓰면 flavor마다 multidex 설정 확인
  • build.gradle 파일 여러 군데 있는지 확인(루트 vs app 모듈)

✅ 최종 예제 (Flutter)

build.gradle(app 모듈)
android {
    defaultConfig {
        minSdkVersion 21
        multiDexEnabled true
    }
}

dependencies {
    implementation 'androidx.multidex:multidex:2.0.1'
}
MainApplication.java.

minSdkVersion ≥ 21 이면 필요 없음
20 이하라면 반드시 필요

import io.flutter.app.FlutterApplication;
import androidx.multidex.MultiDex;
import android.content.Context;

public class MainApplication extends FlutterApplication {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}

✅ 요약.

상황조치
minSdkVersion ≥ 21multiDexEnabled true 설정만으로 OK
minSdkVersion ≤ 20- multidex 라이브러리 추가
- MainApplication 생성
앱 사이즈 줄이고 싶음R8(Proguard) 활성화

19. Desugaring은 무엇이고 언제 필요할까


20. Android 권한은 어떻게 관리해야할까


21. Keystore 관리와 앱 서명은 어떻게 해야할까


22. Google Play App Signing과 SHA-1 문제는 어떻게 해겨랗까


23. AndroidManifest.xml 충돌은 어떻게 해결할까


24. Firebase 설정은?


0개의 댓글