Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Modularization Recipe

Sungyong An
December 10, 2023

Android Modularization Recipe

2023년 12월 10일 (일) Devfest Songdo 2023 행사에서의 발표자료입니다.
https://festa.io/events/4246

Sungyong An

December 10, 2023
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. য়ט ߊ಴ח, ৢ೧ ׮ܲ ѐߊ ೯ࢎীࢲ੄ ߊ಴ղਊҗ োҙػ ղਊੑפ׮. 


    Dagger Hiltী ؀೧ࢲח ࢸݺೞ૑ ঋणפ׮. I/O Extended 2023 Seoul DroidKnights 2023
  2. A app data domain যڃ ഋక۽ জਸ ѐߊೞҊ ੓աਃ? B

    data app domain or app app common C domain app app data app app feature app app app or
  3. ҳӖীࢲ ӂ੢ೞח জ ইఃఫ୊ о੉٘ (ۨ੉য ܻ࠙, ੄ઓࢿ ઱ੑ, UDF

    ١) “ۨ੉যܳ যڌѱ ܻ࠙ೡ ࣻ ੓ਸө?” → interface, class ܻ࠙ 
 (੄ઓࢿ ઱ੑ) “ܻ࠙ܳ ъઁೞח ߑߨ?” → ݽٕച App Architecture Link: h tt ps://developer.android.com/topic/architecture#recommended-app-arch
  4. App Architecture — Modularization Modern App Architecture: • UI Layer

    • Domain Layer (Optional) • Data Layer → ࢲ۽ 1:1 ݒடغ૑ ঋח׮. Types of modules: • App Modules • Feature Modules • Data Modules • Common Modules • Test Modules
  5. → ੿׹ਸ тҳೞח ѐߊ੗ “੉ѱ ݏա…?” Layer Domain Data UI

    Feature Domain Data UI ੷݃׮ ׮ܲ ӝળਵ۽ ݽٕਸ ա׀׮ Layer + Feature Domain Data UI
  6. → ݽٕചী ױ ೞա੄ ੿׹਷ হ׮. “੸׼ೠ ӏݽ੄ জী ੸೤ೠ

    ೞա੄ ߑߨ” → ೞ૑݅ ؊ ੘਷/ழ׮ۆ ࢲ࠺झীࢲח যڌѱ…? Now in Android Link: h tt ps://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md
  7. Recipe “a set of instructions that describes how to prepare

    or make something.” 
 (ޖ঱оܳ ળ࠺ೞѢա ݅٘ח ߑߨਸ ࢸݺೞח ੌ۲੄ ૑ஜ) → উ٘۽੉٘ জਸ ױ҅੸ਵ۽ ݽٕചೞח ೞա੄ ߑߨਸ ࢸݺ೧ࠄ׮. Link: h tt ps://en.wikipedia.org/wiki/Recipe
  8. ݽٕച੄ ݾ੸ = “ѐߊ ࢤ࢑ࢿ” “যڃ ҃਋ী ݽٕചܳ Ҋ޹ೞѱ غחо?”

    • ѐߊ੗ ࣻо טযաݶࢲ, ࢲ۽ Ҁ஖ח ࠗ࠙੉ ࢤӟ׮. • ౱݃׮ ׮ܲ ߑधਵ۽, ੌࠗ࠙݅ ࢜۽਍ ӝࣿਸ ࢎਊೞৈ ѐߊೞҊ र׮. • জ੄ ӝמ੉ טযաݶࢲ, ࢲ۽ ࢚ҙ হח ࠗ࠙ীب औѱ ৔ೱਸ ઱Ҋ ߉ח׮. • ௏٘о ݆ইઉࢲ рױೠ ࣻ੿ীب ࠽٘दр੉ য়ې Ѧܽ׮. • ׮নೠ ಬ ಂఠܳ ૑ਗೞח ١ জ ݽٕ੉ ৈ۞ ѐо ػ׮. 
 → ݽٕച ૓೯ җ੿ ࣘীࢲب ࢤ࢑ࢿਸ ਬ૑ೞח Ѫਸ ୶ୌೠ׮.
  9. (1) Version Catalog ੸ਊೞӝ زੌೠ ۄ੉࠳۞ܻܳ ৈ۞ ݽٕীࢲ ࢎਊೞѱ ػ׮.

    (ex. Kotlin, Hilt) Version Catalogܳ ੉ਊೞৈ, ۄ੉࠳۞ܻ ߡ੹ਸ ೠ Ҕীࢲ ҙܻೡ ࣻ ੓׮. → ٘۽੉٘ա੉எ 2023 ߊ಴৔࢚ ୶ୌ “Gradle Version Catalog 적 용 하 기 ” Link: h tt ps://docs.gradle.org/current/userguide/pla tf orms.html
  10. // build.gradle dependencies { implementation 'androidx.core:core-ktx:1.9.0' } // gradle/libs.versions.toml [versions]

    androidx-core-ktx = "1.9.0" [libraries] androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" } // build.gradle dependencies { implementation libs.androidx.core.ktx } Link: h tt ps://developer.android.com/build/migrate-to-catalogs
  11. (2) Data ݽٕ ܻ࠙ೞӝ о੢ ݢ੷ ࢲߡ, ӝӝ ١ ৻ࠗ৬

    ాनೞח ࠗ࠙ਸ ܻ࠙ೞ੗. • data: API, DB, SharedPreferences ١ ؘ੉ఠܳ ୊ܻೞח ݽٕ ◦ Repository, DataSource, DTO, Entity ١ • model: ؘ੉ఠ ݽٕীࢲ ৻ࠗী ֢୹ೞח ݽ؛ ௿ېझܳ ઁҕೞח ݽٕ ◦ ݽ؛ ௿ېझী ࠛ೙ਃೠ ௏٘о ୶оغח Ѫਸ ߑ૑ೞ۰ח ݾ੸ (ex. DTO, Enity annotation) ◦ ࣽࣻೠ Java, Kotlin ݽٕب оמ :data :model :app Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#data-modules
  12. // :data module interface ExampleRepository { suspend fun getExampleList(): List<Example>

    } internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } internal interface ExampleRemoteDataSource { ... } 
 internal class ExampleRemoteDataSourceImpl(...) : ExampleRemoteDataSource { ... } 
 internal class ExampleResponse(val something: String) 
 internal interface ExampleLocalDataSource { ... } 
 internal class ExampleLocalDataSourceImpl(...) : ExampleLocalDataSource { ... } 
 internal class ExampleEntity(val something: String) 
 // :model module 
 class Example(val something: String) Repository৬ Model ௿ېझ ৻ীח, internel ఃਕ٘ܳ ੉ਊೞৈ ੽Ӕਸ ઁೠೠ׮.
  13. (2) Data ݽٕ ܻ࠙ೞӝ - Legacy DB, SharedPreferences ୊ۢ ӝӝ

    ۽ஸীࢲ ҙܻ೧ঠ ೞח ࠗ࠙਷ ೠߣী ੉زೞӝ য۵׮. interfaceܳ ੉ਊೞৈ पઁ۽ח app ݽٕ੄ ҳഅ୓۽ োѾ೧فҊ, 
 ੉റী оמೠ ࠗ࠙ࠗఠ ੼૓੸ਵ۽ data ݽٕ۽ ৤ӝח ߑߨب оמೞ׮. Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#data-modules :app :data AppPreferences LegacyRemoteDataSource LegacyPreferences ExampleViewModel ExampleRepository ExampleApi ExamplePreferences ExamplePreferencesImpl ExampleRepositoryImpl internal LegacyLocalDataSource ExampleDatabase
  14. // :data module interface LegacyPreferences { var something: String }

    interface ExamplePreferences : LegacyPreferences { ... } internal class ExamplePreferencesImpl( context: Context, private val legacyPrefs: LegacyPreferences, ) : ExamplePreferences, LegacyPreferences by legacyPrefs { ... } // :app module object AppPreferences : LegacyPreferences { override var something: String = ... } inte rf aceܳ ੉ਊೞৈ, app ݽٕ੄ Singletonਸ data ݽٕী ઁҕೡ ࣻ ੓׮.
  15. // :app module @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun

    providesLegacyPreferences() : LegacyPreferences { return AppPreferences } } Hiltܳ ੉ਊೞৈ, ੄ઓࢿਸ ࢜۽਍ inte rf ace۽ ઁҕೡ ࣻ ੓׮.
  16. // How to use - val something = AppPreferences.something +

    @Inject + lateinit var prefs: ExamplePreferences + + val something = prefs.something ੉ઁ ࢎਊೞ؍ ࠗ࠙ਸ ೞաঀ ੹ജೡ ࣻ ੓׮.
  17. (3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽੉٘ জ੄ ࠽٘ ੿ࠁܳ ನೣೞח ௿ېझ.

    • BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom)
  18. // :app module android { defaultConfig { buildConfigField "String", "VERSION_NAME",

    “String.valueOf(\"1.0.0\")" } flavorDimensions "default" productFlavors { dev { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://dev...\")" } stage { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://stage...\")" } real { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://real...\")" } } } ࠁా fl avorী ٮۄ ч੉ ׳ۄ૑ח ࢎਊ੗ ੿੄ ೙٘ܳ ୶оೞৈ ࢎਊೠ׮. Link: h tt ps://developer.android.com/build/gradle-tips#share-custom- fi elds-and-resource-values-with-your-app-code
  19. (3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽੉٘ জ੄ ࠽٘ ੿ࠁܳ ನೣೞח ௿ېझ.

    • BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom) data ݽٕীࢲب BuildConfigܳ ࢎਊ೧ঠ ೠ׮ݶ: (1) app ݽٕ ௏٘ܳ Ӓ؀۽ ࠂࠢೠ׮.
  20. (3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽੉٘ জ੄ ࠽٘ ੿ࠁܳ ನೣೞח ௿ېझ.

    • BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom) data ݽٕীࢲب BuildConfigܳ ࢎਊ೧ঠ ೠ׮ݶ: (1) app ݽٕ ௏٘ܳ Ӓ؀۽ ࠂࠢೠ׮. (2) BuildConfigܳ ݽٕ۽ ܻ࠙ೠ׮.
  21. // :app module dependencies { implementation project(':buildconfig') } // :data

    module dependencies { implementation project(':buildconfig') } ೧஖ਛա…?
  22. // :buildconfig module android { namespace 'soup.movie.buildconfig' defaultConfig { buildConfigField

    "String", "VERSION_NAME", “String.valueOf(\"1.0.0\")" } buildFeatures { buildConfig true } flavorDimensions "default" productFlavors { dev { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://dev...\")" } stage { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://stage...\")" } real { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://real...\")" } } } ੄ઓࢿਸ ыח ݽٚ ݽٕী fl avorо ੹౵غח ޙઁ੼.
  23. // :data module android { buildFeatures { buildConfig false }

    flavorDimensions "default" productFlavors { dev {} dev2 {} stage {} real {} } } ೧Ѿೡ ࣻ ੓ח ߑߨ੉ ੓ਸө…? // :app module android { buildFeatures { buildConfig false } flavorDimensions "default" productFlavors { dev {} dev2 {} stage {} real {} } }
  24. // :buildconfig-stub module android { namespace 'soup.movie.buildconfig' defaultConfig { buildConfigField

    "String", "VERSION_NAME", "String.valueOf(\"0\")" buildConfigField "String", "API_BASE_URL", "String.valueOf(\"\")" } buildFeatures { buildConfig true } // productFlavors X } (1) fl avorо হח, stub ݽٕਸ ୶оೠ׮.
  25. // :app module dependencies { implementation project(':buildconfig') } // :data

    module dependencies { compileOnly project(':buildconfig-stub') } (2) fl avorо ੓ח app ݽٕ ৻ীח stubਸ compileOnly۽ ࢶ঱ೠ׮.
  26. (3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ compileOnlyח ஹ౵ੌ द੼ী݅ ࢎਊغҊ, ࠽٘ Ѿҗޛীח

    ನೣغ૑ ঋח׮. ૊, ࠽٘ Ѿҗޛীח buildconfig-stub੉ ইצ, buildconfig۽ Ү୓غয ನೣػ׮. ੉ܳ ੉ਊೞৈ, ׮ܲ ݽٕী flavorо ੹౵غח Ѫਸ ݄ਸ ࣻ ੓׮. :data :app :buildcon fi g :buildcon fi g-stub compileOnly implementation = :others … Link: h tt ps://developer.android.com/build/dependencies#dependency_con fi gurations
  27. Milestone 1. :app :data :buildcon fi g :buildcon fi g-stub

    :model compileOnly ੉ߣীח app ݽٕীࢲ feature ݽٕਸ աׇࠁ੗.
  28. Feature Core ݽٕ ܻ࠙ೞӝ (?) feature ݽٕ݃׮ ҕా੸ਵ۽ ೙ਃೠ Ѫਸ

    ઁҕೞח ݽٕ. • ܻࣗझ: color, dimen, drawable, string, style, … • Base ௿ېझ: Activity, Fragment, Dialog, … • Widget, ഛ੢ೣࣻ, ੉޷૑۽٬, ۽Ӓੋ • …١١… → গݒೞݶ ކٶ core۽ ٜযр׮. 
 ژ ׮ܲ गಌ জ ݽٕਸ ݅٘ח ߑध੉ۄ ୶ୌೞ૑ ঋח׮. ӒܻҊ data ݽٕীࢲب ೙ਃೞ׮ݶ? :app :feature-core :data :feature:search :model api ?
  29. ৈ۞ ചݶীࢲ ੗઱ ࢎਊೞח ௏٘ܳ, ҙबࢎী ٮۄ ੸੺൤ ܻ࠙ೠ׮. •

    kotlin: Kotlin, Coroutines ҕా ௏٘ • logger: Log • designsystem: ٣੗ੋ दझమ • resources: ܻࣗझ • … (4) Common ݽٕ ܻ࠙ೞӝ Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#common-modules :common :data :feature:search :kotlin :logger :resources :designsystem api …
  30. ӝઓীח ੄ઓೞח ݽٚ ݽٕ੄ ܻࣗझ IDо R ௿ېझী ನೣػ׮. non-transitive

    R ௿ېझܳ ࢎਊೞݶ, R ௿ېझ੄ ௼ӝо ઴যٚ׮. (࠽٘ࣘب ߂ জ ௼ӝ ѐࢶ) 
 ױ, ܻࣗझо ನೣغয ੓ח ੿ഛೠ R ௿ېझܳ import ೧ঠ ೠ׮. (4) Common ݽٕ ܻ࠙ೞӝ - resources Link: h tt ps://developer.android.com/build/optimize-your-build#use-non-transitive-r-classes com.myapp.library2.R welcome_text = Hi, Songdo com.myapp.library1.R welcome_text = Hi, Songdo goodbye_text = Bye :library2 module welcome_text = Hi, Songdo :library1 module goodbye_text = Bye com.myapp.library.R welcome_text = Hi, Songdo com.myapp.library1.R goodbye_text = Bye non-transitive R classes
  31. // gradle.properties android.nonTransitiveRClass=true // :library2 module import com.myapp.library2.R val bar

    = R.string.welcome_text // :library1 module import com.myapp.library1.R - val foo = R.string.welcome_text + val foo = com.myapp.library2.R.string.welcome_text val bar = R.string.goodbye drawable, string, color ١ ܻࣗझܳ ೞա੄ ݽٕী فݶ…? Link: h tt ps://medium.com/androiddevelopers/5-ways-to-prepare-your-app-build-for-android-studio- fl amingo-release-da34616bb946
  32. // gradle.properties android.nonTransitiveRClass=true // :library2 module - import com.myapp.library2.R +

    import com.myapp.resources.R val bar = R.string.welcome_text // :library1 module - import com.myapp.library1.R + import com.myapp.resources.R val foo = R.string.welcome_text val bar = R.string.goodbye impo r ܳ ҙܻೞӝ ಞೞ׮. Link: h tt ps://medium.com/androiddevelopers/5-ways-to-prepare-your-app-build-for-android-studio- fl amingo-release-da34616bb946
  33. ܻࣗझܳ ݽٕ۽ ܻ࠙ೡ ٸ, ܻࣗझ ੉ܴ੉ زੌೞݶ overrideغח ޙઁо ੓ਸ

    ࣻ ੓׮. যڃ ࣽࢲ۽ override غযঠ ೞח૑ জীࢲ Ѿ੿ೡ ࣻ হ׮. (4) Common ݽٕ ܻ࠙ೞӝ - resources Link: h tt ps://developer.android.com/studio/projects/android-library#Considerations com.myapp.library2.R welcome_text = Hi, Incheon com.myapp.library1.R welcome_text = Hi, Incheon :library2 module welcome_text = Hi, Songdo :library1 module welcome_text = Hi, Incheon
  34. // :library2 module android { resourcePrefix 'myapp_lib2_' } <string name="welcome_text">Hi,

    Songdo</string> // :library1 module android { resourcePrefix 'myapp_lib1_' } <string name="welcome_text">Hi, Incheon</string> // :app module val foo = R.string.myapp_lib2_welcome_text // Hi, Songdo val bar = R.string.myapp_lib1_welcome_text // Hi, Incheon (1) resourcePre fi xܳ ੉ਊೞח ߑߨ. ೞ૑݅ ৻ࠗ ۄ੉࠳۞ܻীࢲ Ҁ஘׮ݶ…? Link: h tt ps://developer.android.com/reference/tools/gradle-api/8.2/com/android/build/api/dsl/CommonExtension#resourcePre fi x()
  35. // 3rd party library <string name="app_name">3rd Party Library</string> // :common:resources

    module <string name="app_name">MyApp</string> // :app module android { sourceSets { main.res.srcDirs += '../common/resources/src/main/res' } } val foo = R.string.app_name // MyApp (2) জ ݽٕ੄ sourceSetsী ܻࣗझ ݽٕ੄ ಫ؊ܳ ୶оೞח ߑߨ. Link: h tt ps://developer.android.com/build/build-variants#con fi gure-sourcesets
  36. (5) Convention Plugins ࢎਊೞӝ ݽٕ рী ࠽٘ ۽૒ਸ ҕਬೞח ߑߨ.

    ݽٕਸ ੼੼ ؊ ݆੉ ٜ݅ѱ غחؘ, ݽٕਸ ࢤࢿೞח ࠗ׸ + पࣻܳ ઴ੌ ࣻ ੓׮. → ٘۽੉٘ա੉எ 2023 ߊ಴৔࢚ ୶ୌ “Gradle Kotlin ஶ߮࣌ ೒۞Ӓੋਵ۽ ബਯ੸ਵ۽ ݣ౭ ݽٕ ҙܻೞӝ” Link: h tt ps://docs.gradle.org/current/samples/sample_convention_plugins.html
  37. plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' } android { compileSdk

    34 defaultConfig { minSdk 21 targetSdk 34 } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = '11' } } plugins { id 'example.android.library' } ߈ࠂغח ௏٘ܳ ઴ੌ ࣻ ੓׮.
  38. plugins { id 'com.google.dagger.hilt.android' id 'org.jetbrains.kotlin.kapt' } dependencies { implementation

    libs.hilt.android kapt libs.hilt.compiler } plugins { id 'example.android.hilt' } ݽٕ݃׮ pluginਸ ٜ݅ӝࠁ׮ח, ੤ഝਊೡ ࣻ ੓ח ױਤ۽ ա־ח Ѫ੉ જ׮.
  39. // :feature:search module plugins { id 'example.android.library' id 'example.android.hilt' }

    dependencies { implementation project(':common:kotlin') implementation project(':common:logger') implementation project(':common:designsystem') implementation project(':common:resources') implementation project(':data') implementation project(':model') } // :feature:search module plugins { id 'example.android.feature' } ੸੺൤ ա־ݶ ݻ ઴݅ਵ۽ ୡӝചೡ ࣻ ੓Ҋ, ޘযࢲ ࢎਊೡ ࣻب ੓׮.
  40. ੌ߈੸ਵ۽ ౠ੿ दաܻয়, ӝמী োҙػ ചݶਸ ׸׼ೞח ݽٕ. • home:

    ക • detail: ࢚ࣁ • search: Ѩ࢝ • settings: ࢸ੿ • navigator: ചݶ ੉ز • … (6) Feature ݽٕ ܻ࠙ೞӝ Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :app :home :search :se tt ings :data :navigator :detail
  41. Feature ݽٕਸ ܻ࠙ೡ ٸ, ചݶ р ੉زೡ ࣻ ੓ח ߑߨ੉

    ೙ਃೞ׮. (6) Feature ݽٕ ܻ࠙ೞӝ - navigator Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :app :home :detail .DetailActivity .HomeActivity
  42. // :app module <manifest> <application> <activity android:name=".DetailActivity"/> </application> </manifest> //

    :others module startActivity(Intent(context, DetailActivity::class.java) э਷ ݽٕীࢲח Activityܳ ૒੽ ଵઑೡ ࣻ ੓૑݅, ׮ܲ ݽٕ੉ۄݶ…?
  43. // :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="true"> <intent-filter> ...

    <data android:scheme="..." android:host="detail" /> </intent-filter> </activity> </application> </manifest> // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail")) ٩݂௼ب оמೞ׮. ೞ૑݅ ৻ࠗীب Activityо ֢୹ػ׮.
  44. // :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="true"> <intent-filter> ...

    <data android:scheme="..." android:host="detail" /> </intent-filter> </activity> </application> </manifest> // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail?id=...")) ӒܻҊ ID э਷ чਸ ੹׳೧ঠ ೠ׮ݶ…?
  45. :app Feature ݽٕਸ ܻ࠙ೡ ٸ, ചݶ р ੉زೡ ࣻ ੓ח

    ߑߨ੉ ೙ਃೞ׮. (6) Feature ݽٕ ܻ࠙ೞӝ - navigator Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :home :detail .DetailActivity .HomeActivity :navigator Navigator NavigatorImpl
  46. // :feature:navigator module sealed interface Destination class Detail(val id: String)

    : Destination interface Navigator { fun createIntent(destination: Destination): Intent } // :app module class NavigatorImpl(private val context: Context) : Navigator { override fun createIntent(destination: Destination) { return when (destination) { is Detail -> Intent(context, DetailActivity::class.java) .putExtra(DetailActivity.EXTRA_ID, destination.id) ... } } } Activityী ੽Ӕೡ ࣻ ੓ח Ҕী ҳഅ୓ܳ ݅ٚ׮.
  47. // :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="false"/> </application> </manifest>

    // :feature:home module startActivity(navigator.createIntent(Detail(id = ...))) ৻ࠗী Activityܳ ֢୹ೞ૑ ঋҊ, ݽٕ р ചݶਸ ੉زೡ ࣻ ੓׮.
  48. Activity৬ ׳ܻ, Fragmentח ࢤࢿೡ ࣻ ੓ח ߑߨ੉ ೙ਃೞ׮. :app (6)

    Feature ݽٕ ܻ࠙ೞӝ - navigator :feature :home :detail .DetailFragment .HomeFragment :navigator FragmentFactory FragmentFactoryImpl Link: h tt ps://developer.android.com/guide/navigation/navigation-multi-module “Jetpack Navigation਷ 
 ೞױ੄ ݂௼ܳ ଵҊೞࣁਃ.”
  49. // :feature:navigator module sealed interface FragmentDestination class Detail(val id: String)

    : FragmentDestination interface FragmentFactory { fun createFragment(destination: FragmentDestination): Fragment } // :app module class FragmentFactoryImpl : FragmentFactory { override fun createFragment(destination: FragmentDestination): Fragment { return when (destination) { is Detail -> DetailFragment.newInstance(id = destination.id) ... } } } Fragmentী ੽Ӕೡ ࣻ ੓ח Ҕী ҳഅ୓ܳ ݅ٚ׮.
  50. // :feature:detail module class DetailFragment : Fragment() { ... companion

    object { fun newInstance(id: String): DetailFragment { ... } } } // :feature:home module fragmentManager.commit { val fragment = navigator.createFragment(Detail(id = ...)) add(R.id.fragment_view_container, fragment) } ݽٕ рী Fragmentܳ ҕਬೞৈ ࢎਊೡ ࣻ ੓׮.
  51. Milestone 2. :common :app :feature:navigator :data :common :designsystem :feature:search :logger

    :buildcon fi g :buildcon fi g-stub :resources :model compileOnly …
  52. ݽٕച ݾ੸ • ੄ઓࢿ Ѻܻ • ࠽٘दр ѐࢶ → ੜ

    غҊ ੓ਸө? Link: h tt ps://developer.android.com/topic/modularization :app :feature:se tt ings :feature:search :feature:navigator :data
  53. ੄ઓࢿ Ѻܻ ݽٕ ৻ࠗীࢲ ੽Ӕೡ ࣻ হب۾ 
 internal ఃਕ٘ܳ

    ୶оೞח ߑߨ਷ 
 → ѐߊ੗о ֬஖ӝ औ׮. (पࣻ ਬߊ) :feature:search :data interface SearchRepository internal class SearchRepositoryImpl( ... ) : SearchRepository { ... } @Module @InstallIn(SingletonComponent::class) internal interface DataModule { @Provids fun provideSearchRepository(...) : SearchRepository { ... } } @HiltViewModel class SearchViewModel @Inject constructor( private val repository: SearchRepository, ) : ViewModel() { ... } Link: h tt ps://kotlinlang.org/docs/visibility-modi fi ers.html#modules
  54. ੄ઓࢿ Ѻܻ Ӓېࢲ Domain Layerী ؀਽ೞח ݽٕਸ ୶оೞৈ, Data ݽٕ੄

    ੄ઓࢿਸ ܻ࠙ೡ ࣻب ੓׮. → ױࣽೠ ೣࣻ ഐ୹ীب UseCaseܳ ୶о೧ঠ ೠ׮. (࠺ബਯࢿ) UI Layer Domain Layer Data Layer Link: h tt ps://developer.android.com/topic/architecture/domain-layer#data-access-restriction
  55. // :ui module class ExampleViewModel @Inject constructor( private val usecase:

    ExampleUseCase, private val repository: ExampleRepository // X ) { ... } // :domain module class ExampleUseCase( private val repository: ExampleRepository ) { ... } // :data module interface ExampleRepository { ... } internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } :ui :domain :data
  56. ੄ઓࢿ Ѻܻ Data ݽٕ੉ Domain ݽٕਸ ߄ۄࠁب۾ ߸҃ೞח ߑߨ਷? (DIP)

    → app ݽٕীࢲח ৈ੹൤ ੽Ӕೡ ࣻ ੓׮. (गಌ জ ݽٕ੉ۄݶ…) 
 ѾҴ impl ௿ېझܳ ऀӝ۰ݶ, internal ఃਕ٘ܳ ࢎਊ೧ঠ ೠ׮. Link: h tt ps://developer.android.com/topic/architecture/domain-layer#data-access-restriction UI Layer Domain Layer Data Layer App
  57. // :ui module class ExampleViewModel @Inject constructor( private val usecase:

    ExampleUseCase, private val repository: ExampleRepository // O ) { ... } // :domain module class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... } interface ExampleRepository { ... } // :data module internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } Domain ݽٕী Dataо ೣԋ ੓ח Ѫ੉ ݏਸө…? :ui :domain :data
  58. ੄ઓࢿ Ѻܻ ݽٕਸ -api, -impl۽ ܻ࠙ೞݶ, internal ఃਕ٘ হ੉ب ੽Ӕਸ

    ઁೠೡ ࣻ ੓׮. runtimeOnlyਸ ੉ਊೞৈ, ஹ౵ੌ द੼ীח -impl ݽٕী ੽Ӕೡ ࣻ হ૑݅ ࠽٘ Ѿҗޛীח ನೣغب۾ ೡ ࣻ ੓׮. :feature:search :data-api :data-impl runtimeOnly Link: h tt ps://developer.android.com/build/dependencies#dependency_con fi gurations
  59. // :feature:search module dependencies { implementation project(':data-api') runtimeOnly project(':data-impl') }

    // :data-impl module dependencies { implementation project(':data-api') } // :data-api module dependencies { } :feature:search :data-impl :data-api runtimeOnly
  60. ࣽജ ଵઑ ؊ աইо ݽٕਸ ܻ࠙ೞ׮ ࠁݶ, ݽٕ р ࣽജ

    ଵઑо ߊࢤೞח ҃਋о ੓׮. :feature :se tt ings :feature-core ࣽജ ଵઑ Link: h tt ps://en.wikipedia.org/wiki/Circular_dependency
  61. // :feature:core module abstract class BaseActivity : AppCompatActivity { @Inject

    lateinit var getCurrentLanguage: GetCurrentLanguageUseCase } // :feature:settings module interface GetCurrentLanguageUseCase { operator fun invoke(): Language } class LanguageSettingActivity : BaseActivity() { ... } :feature ৘ܳ ٜয, Feature ݽٕী UI৬ Domain ۨ੉যо ҕઓೞח ҃਋. :feature-core :se tt ings
  62. ࣽജ ଵઑ ੉ ҃਋ח Domain ݽٕਸ ݅٘ח Ѫب ೞա੄ ߑߨ੉׮.

    → ׮ܲ Feature ݽٕী UIܳ ઁҕೞҊ र׮ݶ? :feature :se tt ings :feature-core :domain
  63. ࣽജ ଵઑ ࢜۽਍ ݽٕਸ ୶оೞח ߑߨ…? → ੉ܴ ૙ӝо о੢

    য۵׮… 
 ഑दۄب ӝઓ ݽٕ ݺடਸ ߄Բݶ diffо ழ૓׮. (Conflict ઱੄!) :new-ui :feature :se tt ings :feature-core :domain
  64. ࣽജ ଵઑ ݽٕਸ -api, -impl۽ ܻ࠙ೞݶ, ࣽജ ଵઑܳ ࣚऔѱ ݄ਸ

    ࣻ ੓׮. Link: h tt ps://en.wikipedia.org/wiki/Circular_dependency :feature-core :feature :se tt ings-api :app :se tt ings-impl runtimeOnly
  65. // :feature-common module abstract class BaseActivity : AppCompatActivity { @Inject

    lateinit var getCurrentLanguage: GetCurrentLanguageUseCase } // :feature:settings-api module interface GetCurrentLanguageUseCase { operator fun invoke(): Language } // :feature:settings-impl module class GetCurrentLanguageUseCaseImpl : GetCurrentLanguageUseCase { ... } class LanguageSettingActivity : BaseActivity() { ... } :feature-core :feature :se tt ings-api :se tt ings-impl impl ݽٕ਷ api ݽٕਸ ଵઑೞҊ, ৉ਵ۽ח ೡ ࣻ হ׮.
  66. ࠽٘दр ѐࢶ ݽٕਸ ա־ݶ ࠽٘दр੉ ઴যٚ׮…? → ୊਺ ݽٕചೡ ٸ,

    ݃઱ೞӝ ए਍ ೣ੿. 
 ߽۳۽ ࠽٘ೞ૑ ঋਵݶ ઴যٜ૑ ঋח׮. :app :feature-core :data
  67. ࠽٘दр ѐࢶ ݽٕ р ੄ઓࢿ੉ হ׮ݶ, ߽۳۽ ࠽٘ೡ ࣻ ੓׮.

    ࣻ੿ࢎ೦੉ ੓ਸ ٸীب ߸҃ػ ݽٕਸ ଵઑೞח ݽٕٜ݅ ׮द ࠽٘ೠ׮. :app :feature:search :feature-core :feature:navigator :data
  68. ࠽٘दр ѐࢶ → അप੸ਵ۽ח ੄ઓࢿਸ ыӝ औ׮. ׼ো൤ ࠽٘दр਷ ઴যٜ૑

    ঋח׮. Link: h tt ps://github.com/android/nowinandroid/issues/707 :app :feature:search :feature-core :feature:navigator :data
  69. ࠽٘दр ѐࢶ ݽٕਸ -api, -impl۽ ܻ࠙ೞҊ -impl਷ app ݽٕীࢲ݅ ଵઑೞب۾

    ೞݶ, -impl ݽٕ р੄ ੄ઓࢿਸ Րযյ ࣻ ੓׮. :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly
  70. ࠽٘दр ѐࢶ → ৈӝࢲ data ݽٕী ߸҃ࢎ೦੉ ੓ਵݶ…? ކٶ ׮द

    ࠽٘೧ঠ ೠ׮. :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly
  71. ࠽٘दр ѐࢶ data ݽٕب -api, -impl۽ ܻ࠙ೞݶ ػ׮. → ୭Ӕ

    উ٘۽੉٘ ҕध ޙࢲীب ݽٕച о੉٘о সؘ੉౟غ঻׮. Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#how_to_implement :data-impl :app :feature:search-impl :feature-core :feature:navigator-impl :data-api :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly
  72. (7) Feature ݽٕ ܻ࠙ೞӝ ׮ܲ ݽٕীࢲ ೙ਃೠ Ѫਸ ୭ࣗೠ݅ ֢୹ೞח

    ഋక۽ ܻ࠙ೠ׮. -api: • UseCase interface • UI (Widget, ViewHolder ١) -impl: • UseCase class • UI (Activity, Fragment, ViewModel ١) :feature :app :search-impl :se tt ings-impl :se tt ings-api UseCase :search-api
  73. runtimeOnly۽ ੋ೧, জ ݽٕীࢲח ؊੉࢚ Activityী ੽Ӕೡ ࣻ হ׮. (7)

    Feature ݽٕ ܻ࠙ೞӝ - navigator :app :feature :home-impl :detail-impl .DetailActivity .HomeActivity :navigator Navigator NavigatorImpl
  74. Hiltܳ ੉ਊೞৈ, Activityо ੓ח пп੄ -impl ݽٕীࢲ -NavigatorImplਸ ઁҕೞݶ ػ׮.

    :feature :navigator-impl :home-impl :detail-impl DetailNavigatorImpl HomeNavigatorImpl :navigator-api Navigator NavigatorImpl (7) Feature ݽٕ ܻ࠙ೞӝ - navigator HomeNavigator DetailNavigator
  75. // :feature:navigator module sealed interface Destination class Detail(val id: String)

    : Destination // :app module class NavigatorImpl(private val context: Context) : Navigator { override fun createIntent(destination: Destination) { return when (destination) { is Detail -> Intent(context, DetailActivity::class.java) // X ... } } } জ ݽٕীࢲח -impl ݽٕ੄ Activityী ੽Ӕೡ ࣻ হ׮.
  76. // :feature:navigator-api module interface DetailNavigator { fun createIntent(destination: Detail): Intent

    } // :feature:detail-impl module class DetailNavigatorImpl(private val context: Context) : DetailNavigator { override fun createIntent(destination: Detail): Intent { return Intent(context, DetailActivity::class.java) // O } } // :feature:navigator-impl module class NavigatorImpl(private val detailNavigator: DetailNavigator) : Navigator { override fun createIntent(destination: Destination) { ... } } Activityо ੓ח -impl ݽٕীࢲ -NavigatorImplਸ ҳഅೞݶ ػ׮.
  77. (8) Data ݽٕ ࣁ࠙ചೞӝ ৈ۞ ࢎۈ੉ زदী ೞա੄ ݽٕਸ ࣻ੿ೠ׮.

    → ߽ݾ, Conflict оמࢿ ߽۳۽ ѐߊೞחؘ ߑ೧غח ࠗ࠙੉ ੓׮ݶ ઴ৈաо੗. :data :home-impl :detail-impl :search-impl :database-impl :network-impl model … :home-api :detail-api :search-api :network-api :database-api
  78. Milestone 3. :data :feature :navigator-impl :navigator-api :common :app :common :designsystem

    :logger :buildcon fi g :buildcon fi g-stub :resources compileOnly … :search-impl :search-api :detail-impl :detail-api :search-api :search-impl :detail-api :detail-impl …
  79. زदী ࣻ੿ೞݶ Git Merge Conflictо ߊࢤೠ׮. ౱ ӏݽо ழ૕ࣻ۾ ੉۠

    ٜࠗ࠙ਸ ୭ࣗചೞח Ѫ੉ જ׮. ৘ܳ ٜݶ, Applicationীࢲ ୡӝചೞח Ѫٜ. :app ߽ݾ ઴੉ӝ :feature :home HomeData MyApplication :search SearchData
  80. // :app module class MyApplication : Application() { override fun

    onCreate() { ... HomeData.preload() + SearchData.preload() ... } } ࢲ۽ ׮ܲ PRীࢲ ୡӝച ೣࣻܳ ୶оೞݶ, Con fl ict੉ ߊࢤೠ׮.
  81. Dagger Hiltܳ ੉ਊೞৈ app ݽٕীࢲ feature ݽٕী ૒੽ ੽Ӕೞח Ѫ੉

    ইצ, feature ݽٕ੉ app ݽٕী ઁҕೞب۾ ߸҃ೞݶ ػ׮. :app ߽ݾ ઴੉ӝ :feature :home-impl HomeInitializer :sta rt up-api Initializer Set<Initializer> :search-impl SearchInitializer
  82. // :feature:search-impl module @Module @InstallIn(SingletonComponent::class) interface SearchModule { @IntoSet @Binds

    fun bindsInitializer(impl: SearchInitializer): Initializer } class SearchInitializer @Inject constructor() : Initializer { override operator fun invoke() { SearchData.preload(...) } } // :app module @HiltAndroidApp class MyApplication : Application() { @Inject lateinit var initializerSet: Set<@JvmSuppressWildcards Initializer> } // :feature:startup-api module interface Initializer { operator fun invoke() }
  83. Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#app-modules জ੄ ੌࠗ ӝמ݅ਸ ನೣೞח

    জ ݽٕ. ౠ൤ ߈ࠂ੸ਵ۽ ࠽٘೧ঠ ೞח ѐߊ ױ҅ীࢲ ࣗਃغח दрਸ ױ୷ೡ ࣻ ੓׮. (9) ࢠ೒ জ ݽٕ ୶оೞӝ … … … …
  84. Milestone 4. :data :feature :navigator-impl :navigator-api :common :app :common :designsystem

    :logger :buildcon fi g :buildcon fi g-stub :resources compileOnly … :search-impl :search-api :detail-impl :detail-api :search-api :search-impl :detail-api :detail-impl … :search-sample :detail-sample :designsystem-sample
  85. ੿ܻ • Data, Common, Feature ࣽਵ۽ ױ҅੸ਵ۽ ݽٕച ೡ ࣻ

    ੓׮. • ௏٘ܳ ҕాചೞৈ, ݽٕ ୡӝച ࠺ਊਸ ઴੉ח Ѫ੉ જ׮. ◦ Version Catalog, Convention Plugins ١ • compileOnly + stubਸ ੉ਊೞৈ, flavor ੹౵ܳ ݄ਸ ࣻ ੓׮. (ex. BuildConfig) • -api, -impl ݽٕ۽ ܻ࠙ೞৈ, ৈ۞о૑ ੉੼ਸ оઉৢ ࣻ ੓׮. ◦ ੄ઓࢿ Ѻܻ, ࣽജଵઑ, ࠽٘दр ١ • runtimeOnlyܳ ੉ਊೞৈ, app ݽٕীࢲ ߄ۄࠁח ੄ઓࢿਸ ઴ੌ ࣻ ੓׮. • ݽٕਸ ܻ࠙ೠ׮Ҋ ޖઑѤ ࢤ࢑ࢿ੉ ֫ই૑૑ ঋח׮. 
 ࢤ࢑ࢿਸ ֫ੌ ࣻ ੓ח ߑߨਸ Ҋ޹ೞݴ ݽٕച೧ঠ ೠ׮. ◦ ߽ݾ, Conflict ઴੉ӝ ١