직접 구현하는 코드 변경된 코드가 PR diff에 보여서 리뷰할 수 있다 매년 targetSdk에 맞춰 라이브러리를 올린다 비즈니스와 방향이 일치하지 않을 수 있다 기획, 디자인, 운영, 사업 같은 요구 리소스를 확보해 제때 Android, Google Play, Firebase 등 요구 에 대응해야 한다 3
컴파일 통과. 화면도 확인했다 여기까지 보인다 emoji2 1.0.0 텍스트뷰가 EmojiCompat으로 이모지를 그린다 폰트를 받기 전엔 새로운 이모지가 ☐로 표시 startup-runtime 1.0.0 manifest에 InitializationProvider가 생겨, 앱 시작 때 초기화 코드를 자동 실행 검토해야 하지만, lifecycle-process 2.4.0 앱 시작 때부터 ProcessLifecycleOwner가 포그라운드, 백그라운드 전환을 추적 쉽다 6
한 줄 activity 1.2.4 fragment 1.3.6 중복은 하나로 1.1.0 1.2.0 이름순 정렬 core 1.7.0 라이브러리가 선언한 버전까지 포함해 https://github.com/dropbox/dependency-guard releaseRuntimeClasspath.txt androidx.activity:activity:1.2.4 androidx.appcompat:appcompat:1.4.0 androidx.core:core:1.7.0 androidx.fragment:fragment:1.3.6 현재 의존성 목록과 baseline 비교 이 baseline에 남는다 19
Task :app:dependencyGuard FAILED Dependencies Changed in :app for configuration releaseRuntimeClasspath - androidx.appcompat:appcompat:1.3.1 + androidx.appcompat:appcompat:1.4.0 + androidx.emoji2:emoji2:1.0.0 If this is intentional, re-baseline using ./gradlew :app:dependencyGuardBaseline 의도한 변경 baseline을 업데이트하여 PR에 커밋한다 모르는 변경 어떤 라이브러리가 올렸는지 찾아 검토한다 baseline을 함께 업데이트해야 한다 21
올리면 의존성 앱 동작이 달라질 수 있다 Manifest 권한 Play 심사, 설치할 수 있는 기기 exported 보안, 초기화 없이 열리는 진입점 PR txt libs.versions.toml 의존성 txt Merged Manifest가 변경되어도 인지할 수 없다 24
수 있다 ✦ merged AndroidManifest.xml <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" android:required="true" /> 또는 uses-feature가 필수로 추가되면, 설치할 수 있는 기기가 줄어들 수 있다 27
merged AndroidManifest.xml <uses-permission android:name="android.permission.INTERNET" /> <activity android:name="com.example.cinema.MainActivity" android:exported="true" /> <service android:name="com.example.cinema.SyncService" android:exported="false" /> 그래야 변경된 부분을 app/manifestShield/releaseAndroidManifest.txt uses-permission: android.permission.INTERNET activity: com.example.cinema.MainActivity (exported) 로 볼 수 있다 31
activity (exported) service receiver (권한 보호) releaseAndroidManifest.txt 요소마다 한 줄 권한 없는 exported만 종류별로 정렬 uses-permission: android.permission.INTERNET activity: com.example.cinema.MainActivity (exported) 다시 합쳐 baseline과 비교 권한, exported 등 검출하고 싶은 부분만 baseline에 남긴다 https://github.com/fornewid/manifest-shield 32
id("io.github.fornewid.manifest-shield") version "0.1.11" } ./gradlew manifestShieldBaseline manifestShield { configuration("release") } ./gradlew manifestShield baseline을 업데이트한다 다시 합쳐 baseline과 비교한다 필요한 baseline을 직접 만들 수 있다 https://github.com/fornewid/manifest-shield 34
uses-feature: + android.hardware.camera (required) uses-permission: + android.permission.MANAGE_EXTERNAL_STORAGE activity: + com.example.cinema.WebViewActivity (exported) 의도한 변경 baseline을 업데이트하여 PR에 커밋한다 ✅ 리뷰어는 모르는 변경 어떤 라이브러리가 넣었는지 찾는다 을 보고 검토할 수 있다 35
버전을 올리면 의존성 앱 동작이 달라질 수 있다 Manifest 권한 Play 심사, 설치할 수 있는 기기 exported 보안, 초기화 없이 열리는 진입점 R8 규칙 최적화를 막고, 앱 크기를 늘릴 수 있다 txt PR libs.versions.toml txt 의존성 txt Manifest txt R8 규칙이 변경되어도 인지할 수 없다 37
# 넓은 keep -keep class com.google.gson.** { *; } -keep class * extends android.app.Activity # 전역 옵션 -dontshrink -dontoptimize 라이브러리에 든 -dontoptimize 같은 전역 옵션은 AGP 9부터 무시된다 최적화를 막아 가 늘어난다 38
있다 ✦ 앱의 규칙 파일 규칙마다 한 줄 AGP 기본 규칙 라이브러리 AAR 규칙, 클래스 패키지 releaseOptimizationBlockingRules.txt 최적화를 막는 것만 -keep interface androidx.webkit.ProfileStore { *; } -keep public class com.google.protobuf.** { *; } 이름순 정렬 현재 규칙 목록과 baseline 비교 컴파일 없이 규칙 파일만 읽어 https://github.com/fornewid/proguard-shield 을 적는다 42
넣는다: 최적화를 막는 규칙 앱 규칙 파일의 전역 옵션 -dontoptimize 모든 클래스를 잡는 keep -keep class * { *; } 라이브러리가 외부 코드에 거는 keep ❌ 넣지 않는다 classpath에 없는 패키지를 가리키는 규칙 -keep class org.conscrypt.** { *; } 코드를 지우게만 하는 규칙 -assumenosideeffects class android.util.Log { *** d(...); } -keep public class com.google.protobuf.** { *; } 규칙만 넣는다 https://github.com/fornewid/proguard-shield 43
앱 동작이 달라질 수 있다 Manifest 권한 Play 심사, 설치할 수 있는 기기 exported 보안, 초기화 없이 열리는 진입점 txt R8 규칙 최적화를 막고, 앱 크기를 늘릴 수 있다 txt txt PR libs.versions.toml 의존성 txt Manifest txt R8 규칙 txt ✅ 3가지 변경사항이 모두 PR에서 보인다 46
AI 에이전트에게 한 줄로 요청 "androidx.appcompat을 최신 안정 버전으로 업데이트해줘" "AdMob SDK를 최신 버전으로 업데이트해줘" 버전만 올려도 변경된 의존성, Manifest, R8 규칙 가운데 AI 에이전트가 알려 준 비율 baseline 없음 baseline 있음 44% baseline이 있을 때는 AI 에이전트가 100% 알려 줬다 48
baseline을 업데이트하여 커밋한다 PR diff 커밋 app/dependencies/releaseRuntimeClasspath.txt -androidx.appcompat:appcompat:1.3.1 +androidx.appcompat:appcompat:1.4.0 +androidx.emoji2:emoji2:1.0.0 검사만 CI baseline을 자동으로 커밋하지 않는다 PR 리뷰에서 확인할 수 있다 50
수 있다 impl 모듈이 변경되면 그 모듈만 다시 컴파일하면 된다 (2) 하위 계층은 상위 계층 모듈을 참조할 수 없다 app > feature > data > core (3) api 모듈에는 Hilt 컴파일러를 넣을 수 없다 api 모듈은 가볍게 유지해야 한다 모두 을 늘리지 않으려고 정한 규칙이다 55
참조할 수 있다 ✦ APP :app FEATURE search api impl DATA search api impl ❌ module-rules.txt impl: :feature:search:impl -> :data:search:impl impl 모듈이 변경되면 그 모듈만 다시 컴파일하면 된다 58
컴파일러를 넣을 수 없다 ✦ APP :app Hilt FEATURE search api impl Hilt DATA search api Hilt impl Hilt module-rules.txt hilt: :data:search:api api 모듈에 의존하는 모듈이 Hilt 코드 생성을 기다리지 않아도 된다 60
:feature:community:api + layer 위반사항 DATA :data:community:api :data:community:impl Module rules changed in module-rules.txt: + layer: :data:community:impl -> :feature:community:api If this is intended, re-baseline with ./gradlew moduleRulesBaseline 검사가 실패하고, 이 표시된다 62
N개 이상만 적는다 :feature:search:impl (30) :data:search:impl (17) :core:designsystem (10) :core:common (6) Module sizes grew beyond the baseline: module-sizes.txt + :feature:search:impl (31) grew from 30 Split the module, or if it is meant to grow, re-baseline with: ./gradlew moduleSizesBaseline 규칙과 모듈 크기를 함께 검사해 이 늘어나는 변경을 막는다 66