Q&A
코틀린 안드로이드앱 개발할때 베스트 프렉티스가 머에요?
관리자
7일 전
1701
리액트 네이티브로 하는경우도 있고 많던데 플레이스토어에서만 한다는 기준으로 어떻게 기술스택 정하고 배포까지 생각했을 때의 최적의 베스트 프렉티스 2025 08월 기준
댓글 1개
관
관리자7일 전
1️⃣ 프로젝트 초기 설정 – 최신 도구와 Gradle 플러그인 사용
- Android Studio Flamingo (2024.2.1) 이상 → IDE 자체에서 최신 Lint, Compose 템플릿, 자동 코드 리팩터를 제공한다.
- Gradle DSL은
settings.gradle.kts
·build.gradle.kts
와 같이 Kotlin DSL로 통일한다. - Gradle 플러그인 버전은
androidGradlePlugin
8.2.x,kotlin
1.9.22 이상을 사용하고,compileSdk
와targetSdk
는 현재 API 35(Android 15)로 맞춘다.
// build.gradle.kts (app module)
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
}
android {
compileSdk = 35
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
// Jetpack Compose 활성화
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.6.0"
}
}
2️⃣ 아키텍처 – Clean Architecture + MVVM + Jetpack Compose
- 도메인 레이어 :
usecase
,entity
(비즈니스 로직, 순수 Kotlin). - 데이터 레이어 :
repository
,datasource
(Room, Retrofit, DataStore). - 프레젠테이션 레이어 :
ViewModel
+StateFlow
/LiveData
, UI는 Compose 기반.
// domain/usecase/GetUserUseCase.kt
class GetUserUseCase @Inject constructor(
private val repository: UserRepository
) {
operator fun invoke(userId: String): Flow<User> =
repository.getUser(userId)
}
// presentation/viewmodel/UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
private val getUserUseCase: GetUserUseCase
) : ViewModel() {
private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
fun loadUser(id: String) = viewModelScope.launch {
getUserUseCase(id)
.catch { _uiState.value = UserUiState.Error(it) }
.collect { _uiState.value = UserUiState.Success(it) }
}
}
3️⃣ 핵심 라이브러리 스택 (2025‑08 기준)
추천 라이브러리 | ||
---|---|---|
DI | Hilt (Dagger) | 안드로이드 전용 바인딩, Gradle 플러그인으로 설정 간편 |
네트워킹 | Retrofit 2.11, OkHttp 5, Ktor client(옵션) | 최신 HTTP/2, HTTP/3 지원, 자동 코루틴 어댑터 |
JSON 파싱 | Moshi (코틀린 코드제너레이터 kotlinx-moshi ) | @JsonClass(generateAdapter = true) 로 컴파일 타임 안전 |
로컬 DB | Room 2.6, SQLDelight(선택) | Flow 기반 관찰, 자동 마이그레이션 지원 |
비동기 | Kotlin Coroutines 1.8, Flow | 구조적 동시성, 메모리 누수 방지 |
UI | Jetpack Compose 1.6, Accompanist(이미지, 시스템 UI) | 선언형 UI, UI 테스트 용이 |
테스트 | JUnit5, MockK, Turbine(Flow 테스트), Compose Testing | 모듈 별 단위·통합·UI 테스트 지원 |
로그/크래시 | Timber, Firebase Crashlytics | 가벼운 로그 + 실시간 크래시 리포트 |
배포 | Google Play Billing, In‑App Updates API | 구글 스토어 내 결제·업데이트 관리 |
- Static analysis:
detekt
,ktlint
, Android Lint를 Gradle에 연결하고 PR마다 자동 실행. - Unit Test:
testImplementation
에junit-jupiter
,mockk
를 추가하고,coverage
는 JaCoCo 0.8.12 이상. - UI Test:
androidTestImplementation
에androidx.test.espresso
,compose-test
활용.
// build.gradle.kts (app)
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
id("io.gitlab.arturbosch.detekt") version "1.23.6"
}
detekt {
toolVersion = "1.23.6"
config = files("$rootDir/detekt.yml")
parallel = true
}
- GitHub Actions 예시 (빌드·테스트·배포):
name: Android CI
on:
push:
branches: [ main ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
- name: Build & Test
run: ./gradlew clean assembleDebug testDebugUnitTest
- name: Upload Play Store AAB
if: github.ref == 'refs/heads/main'
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
packageName: com.example.myapp
releaseFile: app/build/outputs/bundle/release/app-release.aab
track: internal
5️⃣ 배포·버전 관리 전략
버전 코드 | versionCode = 1 + (year * 10000 + month * 100 + day) 형태로 자동 증가. |
버전명 | major.minor.patch (SemVer) → git tag 와 연동해 git describe --tags 로 자동 생성. |
베타 테스트 | Google Play Internal Test Track → CI에서 자동 업로드, Firebase App Distribution 보조. |
점진적 롤아웃 | Play Console 의 Managed rollout(예: 10% → 100%) 사용. |
앱 서명 | Google Play App Signing을 기본 사용하고, 로컬 keystore는 CI에 저장하지 않는다. |
업데이트 전략 | In‑App Updates API (Flexible/Immediate) 로 강제 업데이트와 백그라운드 다운로드를 구분. |
// InAppUpdateManager.kt (간단 구현)
class InAppUpdateManager @Inject constructor(
private val activity: ComponentActivity,
private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(activity)
) {
fun checkForUpdate() {
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
appUpdateManager.startUpdateFlowForResult(
info,
AppUpdateType.FLEXIBLE,
activity,
UPDATE_REQUEST_CODE
)
}
}
}
}
6️⃣ 보안·프라이버시 체크리스트
- 네트워크: TLS 1.3 이상,
CertificatePinner
(OkHttp) 적용. - 저장소: 민감 데이터는 EncryptedSharedPreferences 혹은 Jetpack Security 사용.
- 권한: 최소 권한 원칙,
android.permission.FOREGROUND_SERVICE
등은 런타임 요청 후 사용. - ProGuard/R8:
release
빌드에minifyEnabled true
와rules.pro
를 통해 API 키와 모델 클래스를 난독화. - GDPR/CCPA: 데이터 수집 시 반드시
User Consent
UI를 제공하고,Google Play Console
의 데이터 정책을 최신화.
7️⃣ 마무리 – 유지보수와 팀 협업 팁
- 모듈화:
:core
,:feature:login
,:feature:home
등 기능별 Gradle 모듈을 나누면 빌드 속도와 책임 구분이 명확해진다. - 디자인 시스템: Compose‑ 기반 Material3 테마와
@Composable
재사용 컴포넌트를ui-kit
모듈에 모아두면 UI 일관성 유지가 쉬워진다. - 문서화:
KDoc
+Dokka
로 자동 API 문서를 생성하고,README.md
에 각 모듈의 아키텍처 다이어그램을 삽입한다. - 피드백 루프: Crashlytics + Firebase Remote Config 로 실시간 A/B 테스트와 버그 추적을 자동화한다.
핵심 요약위 가이드라인을 기반으로 프로젝트를 시작하면 2025 년 현재 안드로이드 생태계에서 안정적·효율적·확장 가능한 앱을 빠르게 출시하고 지속적으로 운영할 수 있습니다. 🚀
- Kotlin DSL + 최신 Gradle → 빌드 안정성 확보
- Clean Architecture + MVVM + Compose → 테스트 가능·확장성 높은 코드베이스
- Hilt, Retrofit, Room, Coroutines 등 검증된 라이브러리 스택 채택
- CI(Gradle + GitHub Actions) + 자동 배포 로 릴리즈 주기 단축
- 보안·프라이버시와 버전 관리를 정책화해 Play Store 요구사항에 맞추기