Slide 1

Slide 1 text

Android Modularization Recipe Songdo 🍳

Slide 2

Slide 2 text

Sungyong An NAVER WEBTOON Android GDE @fornewid

Slide 3

Slide 3 text

য়ט ߊ಴ח, ৢ೧ ׮ܲ ѐߊ ೯ࢎীࢲ੄ ߊ಴ղਊҗ োҙػ ղਊੑפ׮. 
 Dagger Hiltী ؀೧ࢲח ࢸݺೞ૑ ঋणפ׮. I/O Extended 2023 Seoul DroidKnights 2023

Slide 4

Slide 4 text

A app data domain যڃ ഋక۽ জਸ ѐߊೞҊ ੓աਃ? B data app domain or app app common C domain app app data app app feature app app app or

Slide 5

Slide 5 text

ҳӖীࢲ ӂ੢ೞח জ ইఃఫ୊ о੉٘ (ۨ੉য ܻ࠙, ੄ઓࢿ ઱ੑ, UDF ١) “ۨ੉যܳ যڌѱ ܻ࠙ೡ ࣻ ੓ਸө?” → interface, class ܻ࠙ 
 (੄ઓࢿ ઱ੑ) “ܻ࠙ܳ ъઁೞח ߑߨ?” → ݽٕച App Architecture Link: h tt ps://developer.android.com/topic/architecture#recommended-app-arch

Slide 6

Slide 6 text

ҳӖীࢲ ઁҕೞח ੌ߈੸ੋ ݽٕച ಁఢ (app, feature, data, common) Modularization Link: h tt ps://developer.android.com/topic/modularization

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

→ ੿׹ਸ тҳೞח ѐߊ੗ “੉ѱ ݏա…?” Layer Domain Data UI Feature Domain Data UI ੷݃׮ ׮ܲ ӝળਵ۽ ݽٕਸ ա׀׮ Layer + Feature Domain Data UI

Slide 9

Slide 9 text

→ ݽٕചী ױ ೞա੄ ੿׹਷ হ׮. “੸׼ೠ ӏݽ੄ জী ੸೤ೠ ೞա੄ ߑߨ” → ೞ૑݅ ؊ ੘਷/ழ׮ۆ ࢲ࠺झীࢲח যڌѱ…? Now in Android Link: h tt ps://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md

Slide 10

Slide 10 text

Recipe “a set of instructions that describes how to prepare or make something.” 
 (ޖ঱оܳ ળ࠺ೞѢա ݅٘ח ߑߨਸ ࢸݺೞח ੌ۲੄ ૑ஜ) → উ٘۽੉٘ জਸ ױ҅੸ਵ۽ ݽٕചೞח ೞա੄ ߑߨਸ ࢸݺ೧ࠄ׮. Link: h tt ps://en.wikipedia.org/wiki/Recipe

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

ױ҅੸ਵ۽ ݽٕചೞӝ. Songdo

Slide 13

Slide 13 text

(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

Slide 14

Slide 14 text

// 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

Slide 15

Slide 15 text

(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

Slide 16

Slide 16 text

// :data module interface ExampleRepository { suspend fun getExampleList(): List } 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 ఃਕ٘ܳ ੉ਊೞৈ ੽Ӕਸ ઁೠೠ׮.

Slide 17

Slide 17 text

(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

Slide 18

Slide 18 text

// :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 ݽٕী ઁҕೡ ࣻ ੓׮.

Slide 19

Slide 19 text

// :app module @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun providesLegacyPreferences() : LegacyPreferences { return AppPreferences } } Hiltܳ ੉ਊೞৈ, ੄ઓࢿਸ ࢜۽਍ inte rf ace۽ ઁҕೡ ࣻ ੓׮.

Slide 20

Slide 20 text

// How to use - val something = AppPreferences.something + @Inject + lateinit var prefs: ExamplePreferences + + val something = prefs.something ੉ઁ ࢎਊೞ؍ ࠗ࠙ਸ ೞաঀ ੹ജೡ ࣻ ੓׮.

Slide 21

Slide 21 text

(3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽੉٘ জ੄ ࠽٘ ੿ࠁܳ ನೣೞח ௿ېझ. ● BuildConfig.DEBUG ● BuildConfig.VERSION_NAME ● BuildConfig.API_BASE_URL (custom)

Slide 22

Slide 22 text

// :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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

// :app module import soup.movie.BuildConfig // :data module import soup.movie.data.BuildConfig ... ࠛ೙ਃೞѱ BuildCon fi g ௿ېझ ࣻо טযդ׮.

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

// :app module dependencies { implementation project(':buildconfig') } // :data module dependencies { implementation project(':buildconfig') } ೧஖ਛա…?

Slide 27

Slide 27 text

// :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о ੹౵غח ޙઁ੼.

Slide 28

Slide 28 text

// :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 {} } }

Slide 29

Slide 29 text

// :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 ݽٕਸ ୶оೠ׮.

Slide 30

Slide 30 text

// :app module dependencies { implementation project(':buildconfig') } // :data module dependencies { compileOnly project(':buildconfig-stub') } (2) fl avorо ੓ח app ݽٕ ৻ীח stubਸ compileOnly۽ ࢶ঱ೠ׮.

Slide 31

Slide 31 text

(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

Slide 32

Slide 32 text

Milestone 1. :app :data :buildcon fi g :buildcon fi g-stub :model compileOnly ੉ߣীח app ݽٕীࢲ feature ݽٕਸ աׇࠁ੗.

Slide 33

Slide 33 text

Feature Core ݽٕ ܻ࠙ೞӝ (?) feature ݽٕ݃׮ ҕా੸ਵ۽ ೙ਃೠ Ѫਸ ઁҕೞח ݽٕ. ● ܻࣗझ: color, dimen, drawable, string, style, … ● Base ௿ېझ: Activity, Fragment, Dialog, … ● Widget, ഛ੢ೣࣻ, ੉޷૑۽٬, ۽Ӓੋ ● …١١… → গݒೞݶ ކٶ core۽ ٜযр׮. 
 ژ ׮ܲ गಌ জ ݽٕਸ ݅٘ח ߑध੉ۄ ୶ୌೞ૑ ঋח׮. ӒܻҊ data ݽٕীࢲب ೙ਃೞ׮ݶ? :app :feature-core :data :feature:search :model api ?

Slide 34

Slide 34 text

ৈ۞ ചݶীࢲ ੗઱ ࢎਊೞח ௏٘ܳ, ҙबࢎী ٮۄ ੸੺൤ ܻ࠙ೠ׮. ● 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 …

Slide 35

Slide 35 text

ӝઓীח ੄ઓೞח ݽٚ ݽٕ੄ ܻࣗझ 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

Slide 36

Slide 36 text

// 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

Slide 37

Slide 37 text

// 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

Slide 38

Slide 38 text

ܻࣗझܳ ݽٕ۽ ܻ࠙ೡ ٸ, ܻࣗझ ੉ܴ੉ زੌೞݶ 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

Slide 39

Slide 39 text

// :library2 module android { resourcePrefix 'myapp_lib2_' } Hi, Songdo // :library1 module android { resourcePrefix 'myapp_lib1_' } Hi, Incheon // :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()

Slide 40

Slide 40 text

// 3rd party library 3rd Party Library // :common:resources module MyApp // :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

Slide 41

Slide 41 text

(5) Convention Plugins ࢎਊೞӝ ݽٕ рী ࠽٘ ۽૒ਸ ҕਬೞח ߑߨ. ݽٕਸ ੼੼ ؊ ݆੉ ٜ݅ѱ غחؘ, ݽٕਸ ࢤࢿೞח ࠗ׸ + पࣻܳ ઴ੌ ࣻ ੓׮. → ٘۽੉٘ա੉எ 2023 ߊ಴৔࢚ ୶ୌ “Gradle Kotlin ஶ߮࣌ ೒۞Ӓੋਵ۽ ബਯ੸ਵ۽ ݣ౭ ݽٕ ҙܻೞӝ” Link: h tt ps://docs.gradle.org/current/samples/sample_convention_plugins.html

Slide 42

Slide 42 text

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' } ߈ࠂغח ௏٘ܳ ઴ੌ ࣻ ੓׮.

Slide 43

Slide 43 text

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ਸ ٜ݅ӝࠁ׮ח, ੤ഝਊೡ ࣻ ੓ח ױਤ۽ ա־ח Ѫ੉ જ׮.

Slide 44

Slide 44 text

// :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' } ੸੺൤ ա־ݶ ݻ ઴݅ਵ۽ ୡӝചೡ ࣻ ੓Ҋ, ޘযࢲ ࢎਊೡ ࣻب ੓׮.

Slide 45

Slide 45 text

ੌ߈੸ਵ۽ ౠ੿ दաܻয়, ӝמী োҙػ ചݶਸ ׸׼ೞח ݽٕ. ● 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

Slide 46

Slide 46 text

Feature ݽٕਸ ܻ࠙ೡ ٸ, ചݶ р ੉زೡ ࣻ ੓ח ߑߨ੉ ೙ਃೞ׮. (6) Feature ݽٕ ܻ࠙ೞӝ - navigator Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :app :home :detail .DetailActivity .HomeActivity

Slide 47

Slide 47 text

// :app module // :others module startActivity(Intent(context, DetailActivity::class.java) э਷ ݽٕীࢲח Activityܳ ૒੽ ଵઑೡ ࣻ ੓૑݅, ׮ܲ ݽٕ੉ۄݶ…?

Slide 48

Slide 48 text

// :feature:detail module ... // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail")) ٩݂௼ب оמೞ׮. ೞ૑݅ ৻ࠗীب Activityо ֢୹ػ׮.

Slide 49

Slide 49 text

// :feature:detail module ... // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail?id=...")) ӒܻҊ ID э਷ чਸ ੹׳೧ঠ ೠ׮ݶ…?

Slide 50

Slide 50 text

: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

Slide 51

Slide 51 text

// :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ী ੽Ӕೡ ࣻ ੓ח Ҕী ҳഅ୓ܳ ݅ٚ׮.

Slide 52

Slide 52 text

// :feature:detail module // :feature:home module startActivity(navigator.createIntent(Detail(id = ...))) ৻ࠗী Activityܳ ֢୹ೞ૑ ঋҊ, ݽٕ р ചݶਸ ੉زೡ ࣻ ੓׮.

Slide 53

Slide 53 text

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਷ 
 ೞױ੄ ݂௼ܳ ଵҊೞࣁਃ.”

Slide 54

Slide 54 text

// :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ী ੽Ӕೡ ࣻ ੓ח Ҕী ҳഅ୓ܳ ݅ٚ׮.

Slide 55

Slide 55 text

// :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ܳ ҕਬೞৈ ࢎਊೡ ࣻ ੓׮.

Slide 56

Slide 56 text

Milestone 2. :common :app :feature:navigator :data :common :designsystem :feature:search :logger :buildcon fi g :buildcon fi g-stub :resources :model compileOnly …

Slide 57

Slide 57 text

ݽٕച ݾ੸ਸ ׮द ࢤп೧ࠁӝ. Songdo

Slide 58

Slide 58 text

ݽٕച ݾ੸ ● ੄ઓࢿ Ѻܻ ● ࠽٘दр ѐࢶ → ੜ غҊ ੓ਸө? Link: h tt ps://developer.android.com/topic/modularization :app :feature:se tt ings :feature:search :feature:navigator :data

Slide 59

Slide 59 text

੄ઓࢿ Ѻܻ ݽٕ ৻ࠗীࢲ ੽Ӕೡ ࣻ হب۾ 
 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

Slide 60

Slide 60 text

੄ઓࢿ Ѻܻ Ӓېࢲ Domain Layerী ؀਽ೞח ݽٕਸ ୶оೞৈ, Data ݽٕ੄ ੄ઓࢿਸ ܻ࠙ೡ ࣻب ੓׮. → ױࣽೠ ೣࣻ ഐ୹ীب UseCaseܳ ୶о೧ঠ ೠ׮. (࠺ബਯࢿ) UI Layer Domain Layer Data Layer Link: h tt ps://developer.android.com/topic/architecture/domain-layer#data-access-restriction

Slide 61

Slide 61 text

// :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

Slide 62

Slide 62 text

੄ઓࢿ Ѻܻ 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

Slide 63

Slide 63 text

// :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

Slide 64

Slide 64 text

੄ઓࢿ Ѻܻ ݽٕਸ -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

Slide 65

Slide 65 text

// :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

Slide 66

Slide 66 text

ࣽജ ଵઑ ؊ աইо ݽٕਸ ܻ࠙ೞ׮ ࠁݶ, ݽٕ р ࣽജ ଵઑо ߊࢤೞח ҃਋о ੓׮. :feature :se tt ings :feature-core ࣽജ ଵઑ Link: h tt ps://en.wikipedia.org/wiki/Circular_dependency

Slide 67

Slide 67 text

// :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

Slide 68

Slide 68 text

ࣽജ ଵઑ ੉ ҃਋ח Domain ݽٕਸ ݅٘ח Ѫب ೞա੄ ߑߨ੉׮. → ׮ܲ Feature ݽٕী UIܳ ઁҕೞҊ र׮ݶ? :feature :se tt ings :feature-core :domain

Slide 69

Slide 69 text

ࣽജ ଵઑ ࢜۽਍ ݽٕਸ ୶оೞח ߑߨ…? → ੉ܴ ૙ӝо о੢ য۵׮… 
 ഑दۄب ӝઓ ݽٕ ݺடਸ ߄Բݶ diffо ழ૓׮. (Conflict ઱੄!) :new-ui :feature :se tt ings :feature-core :domain

Slide 70

Slide 70 text

ࣽജ ଵઑ ݽٕਸ -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

Slide 71

Slide 71 text

// :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 ݽٕਸ ଵઑೞҊ, ৉ਵ۽ח ೡ ࣻ হ׮.

Slide 72

Slide 72 text

࠽٘दр ѐࢶ ݽٕਸ ա־ݶ ࠽٘दр੉ ઴যٚ׮…? → ୊਺ ݽٕചೡ ٸ, ݃઱ೞӝ ए਍ ೣ੿. 
 ߽۳۽ ࠽٘ೞ૑ ঋਵݶ ઴যٜ૑ ঋח׮. :app :feature-core :data

Slide 73

Slide 73 text

࠽٘दр ѐࢶ ݽٕ р ੄ઓࢿ੉ হ׮ݶ, ߽۳۽ ࠽٘ೡ ࣻ ੓׮. ࣻ੿ࢎ೦੉ ੓ਸ ٸীب ߸҃ػ ݽٕਸ ଵઑೞח ݽٕٜ݅ ׮द ࠽٘ೠ׮. :app :feature:search :feature-core :feature:navigator :data

Slide 74

Slide 74 text

࠽٘दр ѐࢶ → അप੸ਵ۽ח ੄ઓࢿਸ ыӝ औ׮. ׼ো൤ ࠽٘दр਷ ઴যٜ૑ ঋח׮. Link: h tt ps://github.com/android/nowinandroid/issues/707 :app :feature:search :feature-core :feature:navigator :data

Slide 75

Slide 75 text

࠽٘दр ѐࢶ ݽٕਸ -api, -impl۽ ܻ࠙ೞҊ -impl਷ app ݽٕীࢲ݅ ଵઑೞب۾ ೞݶ, -impl ݽٕ р੄ ੄ઓࢿਸ Րযյ ࣻ ੓׮. :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly

Slide 76

Slide 76 text

࠽٘दр ѐࢶ → ৈӝࢲ data ݽٕী ߸҃ࢎ೦੉ ੓ਵݶ…? ކٶ ׮द ࠽٘೧ঠ ೠ׮. :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly

Slide 77

Slide 77 text

࠽٘दр ѐࢶ 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

Slide 78

Slide 78 text

abstraction - implementation module Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#how_to_implement

Slide 79

Slide 79 text

(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

Slide 80

Slide 80 text

runtimeOnly۽ ੋ೧, জ ݽٕীࢲח ؊੉࢚ Activityী ੽Ӕೡ ࣻ হ׮. (7) Feature ݽٕ ܻ࠙ೞӝ - navigator :app :feature :home-impl :detail-impl .DetailActivity .HomeActivity :navigator Navigator NavigatorImpl

Slide 81

Slide 81 text

Hiltܳ ੉ਊೞৈ, Activityо ੓ח пп੄ -impl ݽٕীࢲ -NavigatorImplਸ ઁҕೞݶ ػ׮. :feature :navigator-impl :home-impl :detail-impl DetailNavigatorImpl HomeNavigatorImpl :navigator-api Navigator NavigatorImpl (7) Feature ݽٕ ܻ࠙ೞӝ - navigator HomeNavigator DetailNavigator

Slide 82

Slide 82 text

// :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ী ੽Ӕೡ ࣻ হ׮.

Slide 83

Slide 83 text

// :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ਸ ҳഅೞݶ ػ׮.

Slide 84

Slide 84 text

(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

Slide 85

Slide 85 text

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 …

Slide 86

Slide 86 text

زदী ࣻ੿ೞݶ Git Merge Conflictо ߊࢤೠ׮. ౱ ӏݽо ழ૕ࣻ۾ ੉۠ ٜࠗ࠙ਸ ୭ࣗചೞח Ѫ੉ જ׮. ৘ܳ ٜݶ, Applicationীࢲ ୡӝചೞח Ѫٜ. :app ߽ݾ ઴੉ӝ :feature :home HomeData MyApplication :search SearchData

Slide 87

Slide 87 text

// :app module class MyApplication : Application() { override fun onCreate() { ... HomeData.preload() + SearchData.preload() ... } } ࢲ۽ ׮ܲ PRীࢲ ୡӝച ೣࣻܳ ୶оೞݶ, Con fl ict੉ ߊࢤೠ׮.

Slide 88

Slide 88 text

Dagger Hiltܳ ੉ਊೞৈ app ݽٕীࢲ feature ݽٕী ૒੽ ੽Ӕೞח Ѫ੉ ইצ, feature ݽٕ੉ app ݽٕী ઁҕೞب۾ ߸҃ೞݶ ػ׮. :app ߽ݾ ઴੉ӝ :feature :home-impl HomeInitializer :sta rt up-api Initializer Set :search-impl SearchInitializer

Slide 89

Slide 89 text

// :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() }

Slide 90

Slide 90 text

Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#app-modules জ੄ ੌࠗ ӝמ݅ਸ ನೣೞח জ ݽٕ. ౠ൤ ߈ࠂ੸ਵ۽ ࠽٘೧ঠ ೞח ѐߊ ױ҅ীࢲ ࣗਃغח दрਸ ױ୷ೡ ࣻ ੓׮. (9) ࢠ೒ জ ݽٕ ୶оೞӝ … … … …

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

Q & A Special thanks to @ganadist