Android (안드로이드)

[Android] Build Variants (1) - 빌드 변형 구성으로 앱 파생하기 / ProductFlavors / BuildTypes

Oscar:) 2025. 2. 7. 20:30
728x90

 

 

 

빌드 변형(Build Variants)을 구성하여 단일 프로젝트에서 다양한 버전의 앱을 빌드하는 방법을 알아본다.

 

 

 

 


 

 

 

✅ 빌드 변형을 구성하는 이유?

 

 

앱의 전체적인 구조와 기능들이 동일한 상황에서,

아주 약간의 기능만 변경 · 추가된 앱을 만들어야 하는 상황에 놓였다.

 

 

기존 프로젝트와 99% 이상 겹치는 신규 프로젝트를 1개 더 제작할 것인가?

보일러 플레이트 코드 한 줄도 기겁하는 개발자들에게는 끔찍한 일이 아닐 수 없다.

 

 

위와 같은 상황은 빌드 변형 구성을 통해 손쉽게 해결할 수 있다.

 

빌드 변형을 구성하면 단일 프로젝트에서 여러 버전의 고유한 앱을 빌드할 수 있다.

즉, 기존 앱을 재사용, 파생하여 고유한 앱으로 찍어낼 수 있는 것이다.

 

 

 

 

🎈 고유한 앱?

 

안드로이드에서 고유한 앱의 기준은 '패키지명'이 동일한 앱이다.

즉, 같은 기기에 동일한 패키지명을 가진 여러 앱이 설치될 수 없다.

 

release 관련 옛 포스팅에서 사용했던 예제를 보며 간단히 설명하자면

defaultConfig {
  applicationId = "com.example.testapp"
}

buildTypes {
  release {
    ...
  }
  debug {
    applicationIdSuffix = ".debug"
    ...
  }
}

 

defaultConfig에 작성된 applicationId가 우리에게 흔히 익숙한 '패키지명'이다.

그리고 buildTypes - debug에 작성된 applicationIdSuffix가 debug 빌드용 접미사다.

 

그렇기에 각 앱을 실행하면 다음과 같은 패키지명으로 빌드된다.

● release → com.example.testapp

● debug → com.example.testapp.debug

 

위 처럼 release/debug 앱의 패키지명이 다르기 때문에 하나의 기기에 2개의 앱으로 설치할 수 있는 원리다.

 

 

 

 

 

✅ BuildTypes와 ProductFlavors

 

빌드 변형을 구성하는 요소는 크게 위 2가지로 나뉜다.

 

BuildTypes는 release/debug와 같이 빌드 설정을 분류할 때 사용된다.

(이외에도 내부 배포용, QA용 등 더욱 세밀하게 분류하기도 한다)

 

ProductFlavors앱의 종류를 분류하며 공식 문서에서는 '제품 버전'이라고 부른다.

 

 

빌드 변형은 위 2가지 요소를 조합한 이름으로 생성된다.

 

예시)

BuildTypes : release / debug

ProductFlavors : oscar / sam

 

 

위 처럼 BuildTypes * ProductFlavors 개수만큼 생성되는 원리이며

BuildTypes 이름이 가장 마지막에 붙는다.

 

패키지명은 다음과 같이 생성된다.

- oscarDebug → com.example.testapp.oscar.debug

- oscarRelease → com.example.testapp.oscar

- samDebug → com.example.testapp.sam.debug

- samRelease → com.example.testapp.sam

 

결론적으로, 하나의 기기에 4개의 앱을 설치할 수 있는 것이다.

플레이 스토어에도 2개의 앱으로 등록할 수 있다. (Debug 제외)

 

 

 

 

 

✅ 예제

 

이제 직접 빌드 변형을 구성해보자.

 

build.gradle.kts (:app)

android {
  buildTypes {
    release {
      ...
    }
    debug {
      applicationIdSuffix = ".debug"
      ...
    }
  }
  
  flavorDimensions += "version"
  productFlavors {
    create("oscar") {
      dimension = "version"
      applicationIdSuffix = ".oscar"
    }
    create("sam") {
      dimension = "version"
      applicationIdSuffix = ".sam"
    }
  }
}

 

buildTypes는 대부분 생략했다. release / debug 분류만 인지하고 넘어가자.

주요 초점은 productFlavors다.

 

"oscar"와 "sam" 버전을 생성해줬고, 패키지명의 접미사까지 작성해주었다.

하지만 flavorDimension과 dimension 개념이 생소할 수 있다.

 

 

 

 

🎈 flavorDimension ?

 

각 Flavors를 그룹화하고 관리하는데 사용되는 개념이며 공식문서에서는 버전 차원이라고 정의한다.

 

BuildTypes는 1차원적 분류를 진행했다면,

ProductFlavors는 flavorDimension을 통해 다차원적 빌드 분류를 진행할 수 있다.

 

 

현재 "version" 이라는 차원으로 'oscar', 'sam' 2개를 정의했다.

 

하지만 여기서 3가지 업체에 납품할 앱을 추가로 만든다는 상황이 놓였다고 가정했을 때,

'oscarA' 'samA' 'oscarB' 'samB' 'oscarC' ... 이런식으로 flavor를 나열하는 것은 비효율적이다.

 

이러한 상황을 'company' 라는 새로운 차원을 생성해 해결할 수 있는 것이다.

flavorDimensions += listOf("version", "company")
productFlavors {
  create("oscar") {
    dimension = "version"
    applicationIdSuffix = ".oscar"
  }
  create("sam") {
    dimension = "version"
    applicationIdSuffix = ".sam"
  }
  create("a") {
    dimension = "company"
    applicationIdSuffix = ".a"
  }
  create("b") {
    dimension = "company"
    applicationIdSuffix = ".b"
  }
  create("c") {
    dimension = "company"
    applicationIdSuffix = ".c"
  }
}

 

위 처럼 "company" 라는 차원을 지정해주고 3가지 flavor를 추가해주면

 

 

총 12개의 빌드 변형을 확인할 수 있다.

BuildTypes : 2

ProductFlavors : 2, 3

2 x 2 x 3 = 12

 

 

 

ProductFlavors를 다차원으로 사용하지 않는다는 이유로

flavorDimensions를 정의하지 않고 빌드하면 다음과 같은 에러를 만나볼 수 있다.

 

Error: All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.

 

그렇기에 내가 다차원 빌드를 이용하지 않더라도 최소 1개는 꼭 작성해줘야 한다.

 

 

 

 

 

✅ SigningConfigs 작성하기 - 독립적인 서명키 사용

 

위 과정까지만 진행해도 Build Variants 구성은 끝난 것이나 마찬가지다.

하지만 앱을 release로 빌드하기 위해 진행하는 서명 구성까지 고려해봐야 한다.

 

 

 

[Android] release 모드 빌드하기 / Build Variants / Signing Configs

당신의 앱에 별다른 설정을 하지 않았다면 항상 debug 모드로 빌드되고 있을 것이다.앱을 release 모드로 빌드하는 방법을 알아본다.       ✅ APK 빌드하기 APK를 빌드하는 최종 목적은 Signing Conf

oscarstory.tistory.com

 

↑ release 관련 옛 포스팅이다.

 

위 포스팅에서는 BuildTypes에 포함된 release {} 영역에서 서명 구성을 지정했다.

android {
  signingConfigs {
    create("release") {
      ...
    }
  }

  buildTypes {
    release {
      ...
      signingConfig = signingConfigs.getByName("release")
    }
  }
}

 

하지만 빌드 변형을 진행한 뒤 위 방법대로 빌드하게되면,

모든 "release" 앱은 동일한 서명키로 서명이 된다는 문제가 발생한다.

 

물론 패키지명만 다르다면 서명키는 동일해도 지장이 없을 수 있지만,

앱을 완전히 독립적으로 사용하려면 각자 다른 서명키를 사용하는 것이 더 안전한 방법이다.

 

 

 

SigningConfig를 각 Flavor마다 개별적으로 생성해주고,

각 Flavor에서 해당 SigningConfig를 지정해주면 된다.

android {
  signingConfigs {
    create("oscar") {
      ...
    }
    create("sam") {
      ...
    }
  }
  
  productFlavors {
    create("oscar") {
      ...
      signingConfig = signingConfigs.getByName("oscar")
    }
    create("sam") {
      ...
      signingConfig = signingConfigs.getByName("sam")
    }
  }
}

 

 

SigningConfig 생성 방법은 release 관련 포스팅을 참고하기 바란다.

 

 

 

 

 

 


 

 

 

 

빌드 변형을 구성하게 되면서 자연스레 Gradle 파일 내에서 이루어지는 빌드 구성을 제대로 이해하게 되었다.

 

 

다음 포스팅에서는 각 빌드 변형마다 별개의 코드와 리소스를 적용시켜보자.

 

 

 

728x90