Skip to content

Latest commit

ย 

History

536 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

MODY

์นœ๊ตฌ๋“ค๊ณผ ์‹์‚ฌ ๋ฐ ์šด๋™์„ ์นผ๋กœ๋ฆฌ ์ œ์•ฝ ์—†์ด ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ธฐ๋กํ•˜๊ณ  ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. โ€™์†Œ์…œ๋„›์ง€โ€™ ๊ฐœ๋…์„ ํ™œ์šฉํ•œ ์†Œํ†ต๊ณผ ์ฑŒ๋ฆฐ์ง€๋ฅผ ํ†ตํ•ด ๋‹ค์ด์–ดํŠธ ์Šต๊ด€์„ ์ง€์†ํ•˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค.

Tech Stack

๋ถ„๋ฅ˜ ์‚ฌ์šฉ ๊ธฐ์ˆ 
Language Kotlin
UI Jetpack Compose, Coil, Lottie
Architecture Clean Architecture + MVI, ๋ฉ€ํ‹ฐ๋ชจ๋“ˆ
DI Hilt
Async Coroutine / Flow
Network Retrofit, OkHttp
Local DataStore, EncryptedSharedPreferences(ํ† ํฐ)
Navigation Type-safe Navigation + NavigationHelper
Auth Kakao SDK, Google Sign-In
Camera CameraX (์ดฌ์˜ยทํฌ๋กญยทEXIF ์ •๊ทœํ™”)
Health Health Connect (๊ฑธ์Œ ์ˆ˜ ์ฝ๊ธฐ)
Firebase Cloud Messaging, Crashlytics, Analytics, Remote Config
Test JUnit (์ˆœ์ˆ˜ ๋กœ์ง ๋‹จ์œ„ ํ…Œ์ŠคํŠธ), Konsist (์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ๊ฒ€์ฆ)

Architecture

MODY Android๋Š” ๋ฉ€ํ‹ฐ๋ชจ๋“ˆ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ, Feature์™€ Core Layer๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

ํ™”๋ฉด์€ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ํ๋ฆ…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์•ก์…˜์€ Intent ๋กœ๋งŒ ViewModel ์— ๋“ค์–ด๊ฐ€๊ณ , ์ƒํƒœ ๋ณ€๊ฒฝ์€ setState { copy(...) } ํ•œ ๊ณณ์œผ๋กœ ๋ชจ์ž…๋‹ˆ๋‹ค. Screen ์€ ๋ Œ๋”๋ง๋งŒ ๋งก์•„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์งยทํ™”๋ฉด ์ด๋™์„ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Screen โ”€โ”€onIntent(Intent)โ”€โ”€> ViewModel โ”€โ”€> Repository(:core:domain ์ธํ„ฐํŽ˜์ด์Šค)
   ^                             โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€ StateFlow<State> โ”€โ”€โ”€โ”€โ”€โ”˜

Project Structure


mody

โ”œโ”€โ”€ app                 # Application, Hilt EntryPoint

โ”œโ”€โ”€ presentation        # MainActivity, AppNavHost, ์•ฑ ์ง„์ž… ๋ฐ ๋ผ์šฐํŒ…

โ”œโ”€โ”€ core

โ”‚   โ”œโ”€โ”€ common-ui       # MVI Base(BaseViewModel, UiState, UiIntent)

โ”‚   โ”œโ”€โ”€ designsystem    # Theme, Typography, Components

โ”‚   โ”œโ”€โ”€ navigation      # Route, NavigationHelper

โ”‚   โ”œโ”€โ”€ domain          # Model, Repository, UseCase

โ”‚   โ”œโ”€โ”€ data            # Repository ๊ตฌํ˜„, DataStore

โ”‚   โ”œโ”€โ”€ network         # Retrofit, DTO, Interceptor, Authenticator

โ”‚   โ””โ”€โ”€ camera          # CameraX ์ดฌ์˜/ํฌ๋กญ ์˜ค๋ฒ„๋ ˆ์ด, EXIF ์ •๊ทœํ™”

โ””โ”€โ”€ feature

    โ”œโ”€โ”€ auth

    โ”œโ”€โ”€ onboarding

    โ”œโ”€โ”€ group

    โ”œโ”€โ”€ feed

    โ”œโ”€โ”€ challenge

    โ”œโ”€โ”€ record

    โ”œโ”€โ”€ notification

    โ””โ”€โ”€ mypage

Dependency Graph

graph TD
    app[":app<br/>Application ยท DI ์กฐ๋ฆฝ"]
    pres[":presentation<br/>MainActivity ยท AppNavHost"]
    feat[":feature:*<br/>auth ยท onboarding ยท group ยท feed<br/>challenge ยท record ยท notification ยท mypage"]

    cui[":core:common-ui"]
    ds[":core:designsystem"]
    nav[":core:navigation"]
    cam[":core:camera"]
    dom[":core:domain<br/>์˜์กด์„ฑ ์—†์Œ"]
    data[":core:data"]
    net[":core:network"]

    app --> pres
    app --> data
    app --> cui
    app --> nav
    app --> dom
    pres --> feat
    pres --> cui
    pres --> ds
    pres --> nav
    pres --> dom

    feat --> cui
    feat --> ds
    feat --> nav
    feat --> dom
    feat -. "challenge ยท record ๋งŒ" .-> cam

    cam --> ds
    cam -- "api" --> dom

    cui --> dom

    data --> dom
    data --> net
    net --> dom

    classDef domain fill:#2d5a3d,stroke:#4caf50,color:#fff
    classDef impl fill:#4a3a5a,stroke:#9575cd,color:#fff
    class dom domain
    class data,net impl
Loading

ํ™”์‚ดํ‘œ๋Š” ๊ธฐ๋ณธ์ด implementation ์ž…๋‹ˆ๋‹ค. api ๋กœ ํ‘œ์‹œํ•œ ๊ณณ์€ ๊ณต๊ฐœ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๊ทธ ๋ชจ๋“ˆ์˜ ํƒ€์ž…์ด ๋“œ๋Ÿฌ๋‚˜ ์†Œ๋น„์ž์—๊ฒŒ ์ „์ด๋˜๋Š” ์˜์กด์„ฑ์ž…๋‹ˆ๋‹ค.

์˜์กด์„ฑ ๊ทœ์น™

์˜์กด์„ฑ ๋ฐฉํ–ฅ์„ ๋ฌธ์„œ๋‚˜ ์ฝ”๋“œ๋ฆฌ๋ทฐ๊ฐ€ ์•„๋‹ˆ๋ผ Gradle ๋ชจ๋“ˆ ๊ฒฝ๊ณ„๋กœ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.

๊ทœ์น™ ๊ฐ•์ œ ๋ฐฉ์‹
:core:domain ์€ ์•„๋ฌด๊ฒƒ๋„ ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค ์ˆœ์ˆ˜ Kotlin ๊ณ„์•ฝ ๊ณ„์ธต (Android SDKยทRetrofit ๋ฏธ์ฐธ์กฐ)
:feature:* ๋Š” :core:data / :core:network ๋ฅผ ๋ชจ๋ฅธ๋‹ค ์˜์กด์„ฑ์— ์„ ์–ธํ•˜์ง€ ์•Š์Œ โ†’ DTOยทRetrofit API ์ฐธ์กฐ ์‹œ ์ปดํŒŒ์ผ ์‹คํŒจ
:feature:* ๋ผ๋ฆฌ ์„œ๋กœ ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค ํ™”๋ฉด ์ด๋™์€ :core:navigation ์˜ Route + NavigationHelper ๊ฒฝ์œ 
๊ตฌํ˜„์ฒด ์ฃผ์ž…์€ :app ํ•œ ๊ณณ์—์„œ๋งŒ :core:data ๋ฅผ ์˜์กดํ•˜๋Š” ์œ ์ผํ•œ ๋ชจ๋“ˆ
๊ณต๊ฐœ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋“œ๋Ÿฌ๋‚˜๋Š” ์˜์กด์„ฑ๋งŒ api ๋‚˜๋จธ์ง€๋Š” implementation ์œผ๋กœ ์ „์ด ์ฐจ๋‹จ โ†’ ์†Œ๋น„์ž๊ฐ€ ๋‚จ์˜ ๋‚ด๋ถ€ ํƒ€์ž…์— ๊ธฐ๋Œ€์ง€ ๋ชปํ•จ

๊ณต์œ ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ˆœ๊ฐ„ core ๋กœ ์Šน๊ฒฉํ•ฉ๋‹ˆ๋‹ค. :core:camera ๋Š” :feature:challenge ์™€ :feature:record ๊ฐ€ ํ•จ๊ป˜ ์“ฐ๊ฒŒ ๋˜๋ฉด์„œ, feature ๊ฐ„ ์˜์กด์„ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๋ถ„๋ฆฌํ•œ ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

์Šน๊ฒฉํ•œ ๋ชจ๋“ˆ์€ ๊ณต๊ฐœ ํ‘œ๋ฉด์„ ์ตœ์†Œ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. :core:camera ๊ฐ€ ๋ฐ–์œผ๋กœ ์—ฌ๋Š” ๊ฒƒ์€ ์˜ค๋ฒ„๋ ˆ์ด ์ง„์ž…์  ModyCameraOverlay ์™€ ํ”„๋ ˆ์ž„ ๋น„์œจ ์ƒ์ˆ˜๋ฟ์ด๊ณ , ์ดฌ์˜ยท๋ณด์ • ๋‹จ๊ณ„์™€ ํŒŒ์ผ ์ •๋ฆฌ ํ•จ์ˆ˜๋Š” internal ์ž…๋‹ˆ๋‹ค. ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋›ด ๋ฐ˜์ชฝ ํ˜ธ์ถœ์ด๋‚˜ ์™ธ๋ถ€์—์„œ์˜ ์บ์‹œ ์‚ญ์ œ๋ฅผ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ๋ง‰์Šต๋‹ˆ๋‹ค.

๋ชจ๋“ˆ ๊ฒฝ๊ณ„๊ฐ€ ๋ชป ์žก๋Š” ๊ฒƒ์€ ํ…Œ์ŠคํŠธ๋กœ ์žก์Šต๋‹ˆ๋‹ค

Gradle ์€ ๋ชจ๋“ˆ ์‚ฌ์ด๋งŒ ๋ง‰์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ชจ๋“ˆ ์•ˆ์—์„œ State ์— var ๋ฅผ ๋„ฃ๊ฑฐ๋‚˜ Screen ์ด NavController ๋ฅผ ์ง์ ‘ ๋งŒ์ง€๋Š” ๊ฒƒ์€ ์ปดํŒŒ์ผ์ด ํ†ต๊ณผํ•˜๋ฏ€๋กœ, Konsist ๋กœ ๊ฒ€์‚ฌํ•ด CI ์—์„œ ์‹คํŒจ์‹œํ‚ต๋‹ˆ๋‹ค (app/src/test/.../ArchitectureRuleTest.kt, 10๊ฐœ ๊ทœ์น™).

@Test
fun `State ์— var ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—†๋‹ค`() { /* ... */ }

@Test
fun `Screen ์€ NavController ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค`() { /* ... */ }
๊ฒ€์‚ฌ ๋Œ€์ƒ ๊ทœ์น™
State / Intent State ๋Š” data class : UiState, Intent ๋Š” sealed : UiIntent, State ์— var ๊ธˆ์ง€
ViewModel BaseViewModel ์ƒ์†, @HiltViewModel ์ฃผ์ž…
Screen NavController ์ง์ ‘ ์กฐ์ž‘ ๊ธˆ์ง€, Repository ์ง์ ‘ ์ฐธ์กฐ ๊ธˆ์ง€
๋ ˆ์ด์–ด featureยทpresentation ์ด data/network ๊ตฌํ˜„์ฒด ๋ฏธ์ฐธ์กฐ, domain ์ด Android ํ”„๋ ˆ์ž„์›Œํฌ ๋ฏธ์˜์กด
๋„ค์ด๋ฐ Repository ๊ตฌํ˜„์ฒด๋Š” Impl ๋กœ ๋๋‚˜๊ณ  @Singleton
./gradlew :app:testDebugUnitTest   # ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ๊ฒ€์‚ฌ ํฌํ•จ

๊ณ„์‚ฐ์ด ๋“ค์–ด๊ฐ„ ๋กœ์ง์€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋กœ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค

UIยท๋„คํŠธ์›Œํฌ์— ๋ฌถ์ด์ง€ ์•Š๋Š” ๊ณ„์‚ฐ์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋นผ์„œ JVM ํ…Œ์ŠคํŠธ๋กœ ๋ฎ์Šต๋‹ˆ๋‹ค. ์ขŒํ‘œยท์‹œ๊ฐ„ยท๊ฒฝ๊ณ„๊ฐ’์€ ๊ธฐ๊ธฐ์—์„œ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•˜๊ธฐ ์–ด๋ ค์šด ๋Œ€์‹  ํ…Œ์ŠคํŠธ๋กœ๋Š” ์‹ธ๊ฒŒ ์žกํž™๋‹ˆ๋‹ค (36๊ฐœ).

๋Œ€์ƒ ๋ฌด์—‡์„ ๊ณ ์ •ํ•˜๋‚˜
:core:camera CropGeometryTest ํฌ๋กญ ํ”„๋ ˆ์ž„ ์œ„์น˜, ๋“œ๋ž˜๊ทธ ๊ฒฝ๊ณ„ ํด๋žจํ”„, ์„ธ๋กœ ์Šฌ๋ผ์ด์Šค ํฌ๋กญ ์˜์—ญ
:core:domain StepSyncWindowTest ๊ฑธ์Œ ์ˆ˜ ๋™๊ธฐํ™” ๊ตฌ๊ฐ„ ๋ถ„ํ•  โ€” ๋‚ ์งœ ๊ฒฝ๊ณ„, ์ž์ • ์ฒ˜๋ฆฌ, ์ฝ๊ธฐ ์ฐฝ ์ดˆ๊ณผ
:core:domain StepChallengeStatusTest ์ฑŒ๋ฆฐ์ง€ ์ƒํƒœ ๋งคํ•‘, ๋‹ฌ์„ฑ๋ฅ  ๋‚ด๋ฆผยท์ƒํ•œยท0 ๋‚˜๋ˆ—์…ˆ
:feature:challenge StepChallengeTitleTest ์ฑŒ๋ฆฐ์ง€ ์ œ๋ชฉ ํŒŒ์‹ฑ์˜ ๋น„์ •์ƒ ์ž…๋ ฅ ์ฒ˜๋ฆฌ

ํ˜„์žฌ Feature ๋ชจ๋“ˆ์„ ์ง€์†์ ์œผ๋กœ ๋ถ„๋ฆฌ ๋ฐ ํ™•์žฅํ•˜๋ฉฐ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Build & Run

./gradlew assembleDebug     # debug ๋นŒ๋“œ
./gradlew installDebug      # ๊ธฐ๊ธฐ์— ์„ค์น˜
./gradlew test              # unit test (์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ๊ฒ€์‚ฌ ํฌํ•จ)
./gradlew lint              # lint
buildType applicationId ์šฉ๋„
debug com.makeus.mody.dev ๊ฐœ๋ฐœ โ€” release ์™€ ๋™์‹œ ์„ค์น˜ ๊ฐ€๋Šฅ
release com.makeus.mody ๋ฐฐํฌ

์นด์นด์˜ค ๋„ค์ดํ‹ฐ๋ธŒ ํ‚ค๋Š” local.properties ์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค(๋ฏธ์„ค์ • ์‹œ ๋นˆ ๊ฐ’์œผ๋กœ ๋นŒ๋“œ๋˜๋ฉฐ ์นด์นด์˜ค ๋กœ๊ทธ์ธยท๊ณต์œ ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

KAKAO_NATIVE_KEY_DEV=...
KAKAO_NATIVE_KEY_PROD=...

About

[๐Ÿฅ‡ CMC 19th ๋Œ€์ƒ ] ์นœ๊ตฌ๋“ค๊ณผ ํ•จ๊ป˜ํ•˜๋Š” ๋‹ค์ด์–ดํŠธ ์Šต๊ด€, ๋ชจ๋”” mody

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages