diff --git a/.gitignore b/.gitignore
index aa724b77..1f613224 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,187 @@
+# Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin
+# Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio,kotlin
+
+### Android ###
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Log/OS Files
+*.log
+
+# Android Studio generated files and folders
+captures/
+.externalNativeBuild/
+.cxx/
+*.apk
+output.json
+
+# IntelliJ
*.iml
+.idea/
+misc.xml
+deploymentTargetDropDown.xml
+render.experimental.xml
+
+# Keystore files
+*.jks
+*.keystore
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Android Profiling
+*.hprof
+
+### Android Patch ###
+gen-external-apklibs
+
+# Replacement of .externalNativeBuild directories introduced
+# with Android Studio 3.5.
+
+### Kotlin ###
+# Compiled class file
+*.class
+
+# Log file
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### AndroidStudio ###
+# Covers files to be ignored for android development using Android Studio.
+
+# Built application files
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
-.DS_Store
-/build
-/captures
+
+# Signing files
+.signing/
+
+# Local configuration file (sdk path, etc)
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+
+# Android Studio
+/*/build/
+/*/local.properties
+/*/out
+/*/*/build
+/*/*/production
+.navigation/
+*.ipr
+*~
+*.swp
+
+# Keystore files
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Android Patch
+
+# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
-.cxx
-local.properties
+
+# NDK
+obj/
+
+# IntelliJ IDEA
+*.iws
+/out/
+
+# User-specific configurations
+.idea/caches/
+.idea/libraries/
+.idea/shelf/
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/.name
+.idea/compiler.xml
+.idea/copyright/profiles_settings.xml
+.idea/encodings.xml
+.idea/misc.xml
+.idea/modules.xml
+.idea/scopes/scope_settings.xml
+.idea/dictionaries
+.idea/vcs.xml
+.idea/jsLibraryMappings.xml
+.idea/datasources.xml
+.idea/dataSources.ids
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+.idea/assetWizardSettings.xml
+.idea/gradle.xml
+.idea/jarRepositories.xml
+.idea/navEditor.xml
+
+# Legacy Eclipse project files
+.classpath
+.project
+.cproject
+.settings/
+
+# Mobile Tools for Java (J2ME)
+
+# Package Files #
+
+# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
+
+## Plugin-specific files:
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Mongo Explorer plugin
+.idea/mongoSettings.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### AndroidStudio Patch ###
+
+!/gradle/wrapper/gradle-wrapper.jar
+
+# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index d843f340..94a25f7f 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,4 +1,6 @@
-
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d8be16d6..56b95fd3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,25 +1,26 @@
plugins {
- alias(libs.plugins.android.application)
+ alias(libs.plugins.spot.android.application)
alias(libs.plugins.kotlin.android)
- alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.spot.android.hilt)
}
android {
namespace = "com.umcspot.spot"
- compileSdk = 35
- defaultConfig {
- applicationId = "com.umcspot.spot"
- minSdk = 35
- targetSdk = 35
- versionCode = 1
- versionName = "1.0"
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ signingConfigs {
+ getByName("debug") {
+ keyAlias = "androiddebugkey"
+ keyPassword = "android"
+ storeFile = file("debug.keystore")
+ }
}
buildTypes {
+ debug {
+ signingConfig = signingConfigs.getByName("debug")
+ }
release {
+ signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
@@ -27,33 +28,19 @@ android {
)
}
}
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
- kotlinOptions {
- jvmTarget = "11"
- }
- buildFeatures {
- compose = true
- }
}
dependencies {
-
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.activity.compose)
- implementation(platform(libs.androidx.compose.bom))
- implementation(libs.androidx.ui)
- implementation(libs.androidx.ui.graphics)
- implementation(libs.androidx.ui.tooling.preview)
- implementation(libs.androidx.material3)
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.junit)
- androidTestImplementation(libs.androidx.espresso.core)
- androidTestImplementation(platform(libs.androidx.compose.bom))
- androidTestImplementation(libs.androidx.ui.test.junit4)
- debugImplementation(libs.androidx.ui.tooling)
- debugImplementation(libs.androidx.ui.test.manifest)
+ implementation(projects.feature.home)
+ implementation(projects.feature.main)
+ implementation(projects.feature.mypage)
+ implementation(projects.domain.home)
+ implementation(projects.core.ui)
+ implementation(projects.core.network)
+ implementation(projects.core.model)
+ implementation(projects.core.designsystem)
+ implementation(projects.core.common)
+ implementation(projects.core.buildconfig)
+ implementation(projects.core.navigation)
+ implementation(projects.data.home)
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d48b97fa..b4251374 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,7 +2,13 @@
+
+
+
+
+
+
+ android:theme="@style/Theme.Spot">
-
-
-
+
+
-
\ No newline at end of file
diff --git a/app/src/main/java/com/umcspot/spot/MainActivity.kt b/app/src/main/java/com/umcspot/spot/MainActivity.kt
deleted file mode 100644
index d920d79e..00000000
--- a/app/src/main/java/com/umcspot/spot/MainActivity.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.umcspot.spot
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import com.umcspot.spot.ui.theme.SPOTTheme
-
-class MainActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
- setContent {
- SPOTTheme {
- Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
- Greeting(
- name = "Android",
- modifier = Modifier.padding(innerPadding)
- )
- }
- }
- }
- }
-}
-
-@Composable
-fun Greeting(name: String, modifier: Modifier = Modifier) {
- Text(
- text = "Hello $name!",
- modifier = modifier
- )
-}
-
-@Preview(showBackground = true)
-@Composable
-fun GreetingPreview() {
- SPOTTheme {
- Greeting("Android")
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/umcspot/spot/SpotApplication.kt b/app/src/main/java/com/umcspot/spot/SpotApplication.kt
new file mode 100644
index 00000000..0b442c23
--- /dev/null
+++ b/app/src/main/java/com/umcspot/spot/SpotApplication.kt
@@ -0,0 +1,11 @@
+package com.umcspot.spot
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class SpotApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt
deleted file mode 100644
index 88be1ab2..00000000
--- a/app/src/main/java/com/umcspot/spot/ui/theme/Color.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.umcspot.spot.ui.theme
-
-import androidx.compose.ui.graphics.Color
-
-val Purple80 = Color(0xFFD0BCFF)
-val PurpleGrey80 = Color(0xFFCCC2DC)
-val Pink80 = Color(0xFFEFB8C8)
-
-val Purple40 = Color(0xFF6650a4)
-val PurpleGrey40 = Color(0xFF625b71)
-val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt
deleted file mode 100644
index 27398949..00000000
--- a/app/src/main/java/com/umcspot/spot/ui/theme/Theme.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.umcspot.spot.ui.theme
-
-import android.app.Activity
-import android.os.Build
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-
-private val DarkColorScheme = darkColorScheme(
- primary = Purple80,
- secondary = PurpleGrey80,
- tertiary = Pink80
-)
-
-private val LightColorScheme = lightColorScheme(
- primary = Purple40,
- secondary = PurpleGrey40,
- tertiary = Pink40
-
- /* Other default colors to override
- background = Color(0xFFFFFBFE),
- surface = Color(0xFFFFFBFE),
- onPrimary = Color.White,
- onSecondary = Color.White,
- onTertiary = Color.White,
- onBackground = Color(0xFF1C1B1F),
- onSurface = Color(0xFF1C1B1F),
- */
-)
-
-@Composable
-fun SPOTTheme(
- darkTheme: Boolean = isSystemInDarkTheme(),
- // Dynamic color is available on Android 12+
- dynamicColor: Boolean = true,
- content: @Composable () -> Unit
-) {
- val colorScheme = when {
- dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
- val context = LocalContext.current
- if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
- }
-
- darkTheme -> DarkColorScheme
- else -> LightColorScheme
- }
-
- MaterialTheme(
- colorScheme = colorScheme,
- typography = Typography,
- content = content
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt b/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt
deleted file mode 100644
index 50f65e35..00000000
--- a/app/src/main/java/com/umcspot/spot/ui/theme/Type.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.umcspot.spot.ui.theme
-
-import androidx.compose.material3.Typography
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
-
-// Set of Material typography styles to start with
-val Typography = Typography(
- bodyLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.5.sp
- )
- /* Other default text styles to override
- titleLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 22.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- labelSmall = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Medium,
- fontSize = 11.sp,
- lineHeight = 16.sp,
- letterSpacing = 0.5.sp
- )
- */
-)
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 9587e093..a7ea9ace 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,5 +1,18 @@
-
-
+
+
+
\ No newline at end of file
diff --git a/build-logic/convention/.gitignore b/build-logic/convention/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/build-logic/convention/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
new file mode 100644
index 00000000..e8ae96bc
--- /dev/null
+++ b/build-logic/convention/build.gradle.kts
@@ -0,0 +1,56 @@
+plugins {
+ `kotlin-dsl`
+}
+group = "com.umcspot.spot.convention"
+
+dependencies {
+ compileOnly(libs.android.gradle.plugin)
+ compileOnly(libs.kotlin.gradle.plugin)
+ compileOnly(libs.ksp.gradle.plugin)
+ compileOnly(libs.compose.compiler.extension)
+}
+
+gradlePlugin {
+ plugins {
+ register("androidApplication") {
+ id = "spot.android.application"
+ implementationClass = "AndroidApplicationPlugin"
+ }
+ register("buildConfig") {
+ id = "spot.android.build.config"
+ implementationClass = "BuildConfigPlugin"
+ }
+ register("androidLibrary") {
+ id = "spot.android.library"
+ implementationClass = "AndroidLibraryPlugin"
+ }
+ register("androidComposeLibrary") {
+ id = "spot.android.compose.library"
+ implementationClass = "AndroidComposeLibraryPlugin"
+ }
+ register("javaLibrary") {
+ id = "spot.android.java.library"
+ implementationClass = "JavaLibraryPlugin"
+ }
+ register("androidTest") {
+ id = "spot.android.test"
+ implementationClass = "AndroidTestPlugin"
+ }
+ register("unitTest") {
+ id = "spot.android.unittest"
+ implementationClass = "UnitTestPlugin"
+ }
+ register("androidHilt") {
+ id = "spot.android.hilt"
+ implementationClass = "HiltPlugin"
+ }
+ register("spotFeature") {
+ id = "spot.feature"
+ implementationClass = "SpotFeaturePlugin"
+ }
+ register("spotData") {
+ id = "spot.data"
+ implementationClass = "SpotDataPlugin"
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt
new file mode 100644
index 00000000..dcaa6ab5
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidApplicationPlugin.kt
@@ -0,0 +1,27 @@
+import com.android.build.api.dsl.ApplicationExtension
+import com.umcspot.spot.convention.configureAndroidCompose
+import com.umcspot.spot.convention.configureKotlinAndroid
+import com.umcspot.spot.convention.extension.getVersion
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+
+class AndroidApplicationPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.application")
+ }
+ extensions.configure {
+ configureKotlinAndroid(this)
+ configureAndroidCompose(this)
+ with(defaultConfig) {
+ targetSdk = libs.getVersion("targetSdk").requiredVersion.toInt()
+ versionCode = libs.getVersion("versionCode").requiredVersion.toInt()
+ versionName = libs.getVersion("versionName").requiredVersion
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt b/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt
new file mode 100644
index 00000000..5de04b09
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidComposeLibraryPlugin.kt
@@ -0,0 +1,19 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.umcspot.spot.convention.configureAndroidCompose
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+
+class AndroidComposeLibraryPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("spot.android.library")
+ }
+
+ extensions.configure {
+ configureAndroidCompose(this)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt
new file mode 100644
index 00000000..cb659bc3
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidLibraryPlugin.kt
@@ -0,0 +1,29 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.umcspot.spot.convention.configureKotlinAndroid
+import com.umcspot.spot.convention.configureKotlinCoroutine
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+
+class AndroidLibraryPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.library")
+ }
+
+ extensions.configure {
+ configureKotlinAndroid(this)
+ configureKotlinCoroutine(this)
+ }
+
+ dependencies {
+ implementation(libs.getLibrary("timber"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidTestPlugin.kt b/build-logic/convention/src/main/java/AndroidTestPlugin.kt
new file mode 100644
index 00000000..84866454
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidTestPlugin.kt
@@ -0,0 +1,29 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.umcspot.spot.convention.extension.androidTestImplementation
+import com.umcspot.spot.convention.extension.debugImplementation
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+
+class AndroidTestPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ dependencies {
+ androidTestImplementation(libs.getLibrary("androidx.test.runner"))
+ debugImplementation(libs.getLibrary("androidx.test.core"))
+ androidTestImplementation(libs.getLibrary("kotlinx.coroutines.test"))
+ implementation(libs.getLibrary("androidx.test.ext.junit"))
+ }
+
+ extensions.configure {
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/BuildConfigPlugin.kt b/build-logic/convention/src/main/java/BuildConfigPlugin.kt
new file mode 100644
index 00000000..6a779e52
--- /dev/null
+++ b/build-logic/convention/src/main/java/BuildConfigPlugin.kt
@@ -0,0 +1,15 @@
+import com.android.build.gradle.LibraryExtension
+import com.umcspot.spot.convention.configureBuildConfig
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+
+class BuildConfigPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ extensions.configure {
+ configureBuildConfig(this)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/HiltPlugin.kt b/build-logic/convention/src/main/java/HiltPlugin.kt
new file mode 100644
index 00000000..dc804355
--- /dev/null
+++ b/build-logic/convention/src/main/java/HiltPlugin.kt
@@ -0,0 +1,26 @@
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.ksp
+import com.umcspot.spot.convention.extension.kspTest
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+class HiltPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.google.devtools.ksp")
+ apply("dagger.hilt.android.plugin")
+ }
+
+ dependencies {
+ implementation(libs.getLibrary("hilt.android"))
+ ksp(libs.getLibrary("hilt.compiler"))
+ implementation(libs.getLibrary("hilt.testing"))
+ kspTest(libs.getLibrary("hilt.testing.compiler"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/JavaLibraryPlugin.kt b/build-logic/convention/src/main/java/JavaLibraryPlugin.kt
new file mode 100644
index 00000000..cea86265
--- /dev/null
+++ b/build-logic/convention/src/main/java/JavaLibraryPlugin.kt
@@ -0,0 +1,35 @@
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.getVersion
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.JavaVersion
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
+
+class JavaLibraryPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("java-library")
+ apply("org.jetbrains.kotlin.jvm")
+ }
+
+ extensions.configure {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ extensions.configure {
+ jvmToolchain(libs.getVersion("jdkVersion").requiredVersion.toInt())
+ }
+
+ dependencies {
+ implementation(libs.getLibrary("javax.inject"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/SpotDataPlugin.kt b/build-logic/convention/src/main/java/SpotDataPlugin.kt
new file mode 100644
index 00000000..23deb8d9
--- /dev/null
+++ b/build-logic/convention/src/main/java/SpotDataPlugin.kt
@@ -0,0 +1,28 @@
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+class SpotDataPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("spot.android.library")
+ apply("spot.android.hilt")
+ apply("org.jetbrains.kotlin.plugin.serialization")
+ }
+
+ dependencies {
+ implementation(project(":core:network"))
+ implementation(project(":core:common"))
+ implementation(project(":core:model"))
+
+ implementation(libs.getLibrary("kotlinx.serialization.json"))
+ implementation(libs.getLibrary("retrofit.core"))
+ implementation(libs.getLibrary("retrofit.kotlin.serialization"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/SpotFeaturePlugin.kt b/build-logic/convention/src/main/java/SpotFeaturePlugin.kt
new file mode 100644
index 00000000..9ee9db5c
--- /dev/null
+++ b/build-logic/convention/src/main/java/SpotFeaturePlugin.kt
@@ -0,0 +1,28 @@
+import com.umcspot.spot.convention.extension.getBundle
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+class SpotFeaturePlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("spot.android.compose.library")
+ apply("spot.android.hilt")
+ apply("org.jetbrains.kotlin.plugin.serialization")
+ }
+
+ dependencies {
+ implementation(libs.getLibrary("kotlinx.serialization.json"))
+ implementation(project(":core:ui"))
+ implementation(project(":core:designsystem"))
+ implementation(project(":core:model"))
+ implementation(project(":core:navigation"))
+ implementation(libs.getBundle("compose"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/UnitTestPlugin.kt b/build-logic/convention/src/main/java/UnitTestPlugin.kt
new file mode 100644
index 00000000..e5d55268
--- /dev/null
+++ b/build-logic/convention/src/main/java/UnitTestPlugin.kt
@@ -0,0 +1,29 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.libs
+import com.umcspot.spot.convention.extension.testImplementation
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+
+class UnitTestPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ dependencies {
+ testImplementation(libs.getLibrary("kotlinx.coroutines.test"))
+ testImplementation(libs.getLibrary("mockito"))
+ testImplementation(libs.getLibrary("junit"))
+ testImplementation(libs.getLibrary("robolectric"))
+ }
+
+ extensions.configure {
+ testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt
new file mode 100644
index 00000000..83450583
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/AndroidCompose.kt
@@ -0,0 +1,34 @@
+package com.umcspot.spot.convention
+
+import com.android.build.api.dsl.CommonExtension
+import com.umcspot.spot.convention.extension.debugImplementation
+import com.umcspot.spot.convention.extension.getBundle
+import com.umcspot.spot.convention.extension.getLibrary
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
+import org.gradle.kotlin.dsl.dependencies
+
+internal fun Project.configureAndroidCompose(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
+
+ commonExtension.apply {
+ buildFeatures {
+ compose = true
+ }
+ extensions.getByType().apply {
+ enableStrongSkippingMode.set(true)
+ includeSourceInformation.set(true)
+ }
+ dependencies {
+ implementation(platform(libs.getLibrary("compose.bom")))
+ implementation(libs.getBundle("compose"))
+ debugImplementation(libs.getBundle("compose.debug"))
+ implementation(libs.getLibrary("kotlinx.collections"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt
new file mode 100644
index 00000000..94a5dd54
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/BuildConfig.kt
@@ -0,0 +1,30 @@
+package com.umcspot.spot.convention
+
+import com.android.build.api.dsl.CommonExtension
+import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
+import org.gradle.api.Project
+
+internal fun Project.configureBuildConfig(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ defaultConfig {
+ val properties = gradleLocalProperties(rootDir, providers)
+
+ buildConfigField(
+ "String",
+ "BASE_URL",
+ "\"${properties.getProperty("base.url") ?: "https://default-url.com/"}\""
+ )
+ buildConfigField(
+ "String",
+ "KAKAO_NATIVE_KEY",
+ "\"${properties.getProperty("kakao.native.key") ?: ""}\""
+ )
+ }
+
+ buildFeatures {
+ buildConfig = true
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt
new file mode 100644
index 00000000..afbafd68
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinAndroid.kt
@@ -0,0 +1,56 @@
+package com.umcspot.spot.convention
+
+import com.android.build.api.dsl.CommonExtension
+import com.umcspot.spot.convention.extension.getVersion
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
+
+internal fun Project.configureKotlinAndroid(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ pluginManager.apply("org.jetbrains.kotlin.android")
+
+ commonExtension.apply {
+ compileSdk = libs.getVersion("compileSdk").requiredVersion.toInt()
+
+ defaultConfig {
+ minSdk = libs.getVersion("minSdk").requiredVersion.toInt()
+
+ vectorDrawables.useSupportLibrary = true
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ extensions.getByType().configureCompilerOptions()
+
+ buildTypes {
+ getByName("debug") {
+ proguardFiles(
+ getDefaultProguardFile("proguard-android.txt"),
+ "proguard-debug.pro",
+ )
+ }
+
+ getByName("release") {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+ }
+}
+
+fun KotlinAndroidProjectExtension.configureCompilerOptions() {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_17)
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt
new file mode 100644
index 00000000..678c6baa
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/KotlinCoroutine.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.convention
+
+import com.android.build.api.dsl.CommonExtension
+import com.umcspot.spot.convention.extension.getBundle
+import com.umcspot.spot.convention.extension.implementation
+import com.umcspot.spot.convention.extension.libs
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+internal fun Project.configureKotlinCoroutine(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ dependencies {
+ implementation(libs.getBundle("coroutine"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt
new file mode 100644
index 00000000..128262c1
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/DependencyHandlerScope.kt
@@ -0,0 +1,55 @@
+package com.umcspot.spot.convention.extension
+
+import org.gradle.api.Project
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.ConfigurableFileTree
+import org.gradle.api.provider.Provider
+import org.gradle.kotlin.dsl.DependencyHandlerScope
+
+fun DependencyHandlerScope.implementation(project: Project) {
+ "implementation"(project)
+}
+
+fun DependencyHandlerScope.implementation(provider: Provider<*>) {
+ "implementation"(provider)
+}
+
+fun DependencyHandlerScope.implementation(fileTree: ConfigurableFileTree) {
+ "implementation"(fileTree)
+}
+
+fun DependencyHandlerScope.implementation(fileCollection: ConfigurableFileCollection) {
+ "implementation"(fileCollection)
+}
+
+fun DependencyHandlerScope.debugImplementation(provider: Provider<*>) {
+ "debugImplementation"(provider)
+}
+
+fun DependencyHandlerScope.releaseImplementation(provider: Provider<*>) {
+ "releaseImplementation"(provider)
+}
+
+fun DependencyHandlerScope.ksp(provider: Provider<*>) {
+ "ksp"(provider)
+}
+
+fun DependencyHandlerScope.kspTest(provider: Provider<*>) {
+ "kspTest"(provider)
+}
+
+fun DependencyHandlerScope.coreLibraryDesugaring(provider: Provider<*>) {
+ "coreLibraryDesugaring"(provider)
+}
+
+fun DependencyHandlerScope.androidTestImplementation(provider: Provider<*>) {
+ "androidTestImplementation"(provider)
+}
+
+fun DependencyHandlerScope.testImplementation(provider: Provider<*>) {
+ "testImplementation"(provider)
+}
+
+fun DependencyHandlerScope.compileOnly(provider: Provider<*>) {
+ "compileOnly"(provider)
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt
new file mode 100644
index 00000000..78ab6fe5
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/ProjectExt.kt
@@ -0,0 +1,9 @@
+package com.umcspot.spot.convention.extension
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.getByType
+
+val Project.libs: VersionCatalog
+ get() = extensions.getByType().named("libs")
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt
new file mode 100644
index 00000000..168abe55
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/umcspot/spot/convention/extension/VersionCatalogExt.kt
@@ -0,0 +1,25 @@
+package com.umcspot.spot.convention.extension
+
+import org.gradle.api.artifacts.ExternalModuleDependencyBundle
+import org.gradle.api.artifacts.MinimalExternalModuleDependency
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionConstraint
+import org.gradle.api.provider.Provider
+
+fun VersionCatalog.getBundle(bundleName: String): Provider {
+ return findBundle(bundleName).orElseThrow {
+ NoSuchElementException("Bundle with name $bundleName not found in the catalog")
+ }
+}
+
+fun VersionCatalog.getLibrary(libraryName: String): Provider {
+ return findLibrary(libraryName).orElseThrow {
+ NoSuchElementException("Library with name $libraryName not found in the catalog")
+ }
+}
+
+fun VersionCatalog.getVersion(versionName: String): VersionConstraint {
+ return findVersion(versionName).orElseThrow {
+ NoSuchElementException("Version with name $versionName not found in the catalog")
+ }
+}
\ No newline at end of file
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
new file mode 100644
index 00000000..62257853
--- /dev/null
+++ b/build-logic/settings.gradle.kts
@@ -0,0 +1,14 @@
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs") {
+ from(files("../gradle/libs.versions.toml"))
+ }
+ }
+}
+
+rootProject.name = "build-logic"
+include(":convention")
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 952b9306..a73ae685 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,5 +2,11 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
- alias(libs.plugins.kotlin.compose) apply false
+ alias(libs.plugins.compose.compiler) apply false
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.kotlin.serialization) apply false
+ alias(libs.plugins.ksp) apply false
+ alias(libs.plugins.hilt) apply false
+ alias(libs.plugins.ktlint)
+ alias(libs.plugins.kotlin.jvm) apply false
}
\ No newline at end of file
diff --git a/core/buildconfig/.gitignore b/core/buildconfig/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/buildconfig/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/buildconfig/build.gradle.kts b/core/buildconfig/build.gradle.kts
new file mode 100644
index 00000000..0417476c
--- /dev/null
+++ b/core/buildconfig/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+ alias(libs.plugins.spot.android.library)
+ alias(libs.plugins.spot.android.build.config)
+ alias(libs.plugins.spot.android.hilt)
+ alias(libs.plugins.spot.android.test)
+}
+
+android {
+ namespace = "com.umcspot.spot.buildconfig"
+}
+dependencies {
+ implementation(projects.core.common)
+}
\ No newline at end of file
diff --git a/core/buildconfig/consumer-rules.pro b/core/buildconfig/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/buildconfig/proguard-rules.pro b/core/buildconfig/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/buildconfig/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/buildconfig/src/main/AndroidManifest.xml b/core/buildconfig/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/buildconfig/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt
new file mode 100644
index 00000000..1dffeb3b
--- /dev/null
+++ b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/di/BuildConfigModule.kt
@@ -0,0 +1,19 @@
+package com.umcspot.spot.buildconfig.di
+
+import com.umcspot.spot.buildconfig.impl.BuildConfigFieldsProviderImpl
+import com.umcspot.spot.common.BuildConfigFieldProvider
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@InstallIn(SingletonComponent::class)
+@Module
+object BuildConfigModule {
+ @Provides
+ @Singleton
+ fun provideBuildConfigFieldsProvider(
+ buildConfigFieldProvider: BuildConfigFieldsProviderImpl
+ ): BuildConfigFieldProvider = buildConfigFieldProvider
+}
\ No newline at end of file
diff --git a/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt
new file mode 100644
index 00000000..2f089249
--- /dev/null
+++ b/core/buildconfig/src/main/java/com/umcspot/spot/buildconfig/impl/BuildConfigFieldsProviderImpl.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.buildconfig.impl
+
+import com.umcspot.spot.buildconfig.BuildConfig.BASE_URL
+import com.umcspot.spot.buildconfig.BuildConfig.KAKAO_NATIVE_KEY
+import com.umcspot.spot.common.BuildConfigFieldProvider
+import com.umcspot.spot.common.BuildConfigFields
+
+
+import javax.inject.Inject
+
+class BuildConfigFieldsProviderImpl @Inject constructor() : BuildConfigFieldProvider {
+ override fun get(): BuildConfigFields =
+ BuildConfigFields(
+ baseUrl = BASE_URL,
+ kakaoNativeKey = KAKAO_NATIVE_KEY,
+ isDebug = true
+ )
+}
\ No newline at end of file
diff --git a/core/common/.gitignore b/core/common/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/common/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
new file mode 100644
index 00000000..c374ceb2
--- /dev/null
+++ b/core/common/build.gradle.kts
@@ -0,0 +1,9 @@
+plugins {
+ alias(libs.plugins.spot.android.library)
+ alias(libs.plugins.spot.android.hilt)
+ alias(libs.plugins.spot.android.test)
+}
+
+android {
+ namespace = "com.umcspot.spot.common"
+}
\ No newline at end of file
diff --git a/core/common/consumer-rules.pro b/core/common/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/common/proguard-rules.pro b/core/common/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/common/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/common/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt
new file mode 100644
index 00000000..3af0effc
--- /dev/null
+++ b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigFields.kt
@@ -0,0 +1,7 @@
+package com.umcspot.spot.common
+
+data class BuildConfigFields(
+ val baseUrl: String,
+ val kakaoNativeKey: String,
+ val isDebug: Boolean
+)
\ No newline at end of file
diff --git a/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt
new file mode 100644
index 00000000..66dbecc2
--- /dev/null
+++ b/core/common/src/main/java/com/umcspot/spot/common/BuildConfigProvider.kt
@@ -0,0 +1,5 @@
+package com.umcspot.spot.common
+
+interface BuildConfigFieldProvider {
+ fun get(): BuildConfigFields
+}
\ No newline at end of file
diff --git a/core/datastore/.gitignore b/core/datastore/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/datastore/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts
new file mode 100644
index 00000000..87efe8c7
--- /dev/null
+++ b/core/datastore/build.gradle.kts
@@ -0,0 +1,16 @@
+plugins {
+ alias(libs.plugins.spot.android.library)
+ alias(libs.plugins.spot.android.hilt)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.spot.android.test)
+}
+
+android {
+ namespace = "com.umcspot.spot.datastore"
+}
+
+dependencies {
+ implementation(projects.core.common)
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.bundles.datastore)
+}
\ No newline at end of file
diff --git a/core/datastore/consumer-rules.pro b/core/datastore/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/datastore/proguard-rules.pro b/core/datastore/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/datastore/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/datastore/src/main/AndroidManifest.xml b/core/datastore/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/datastore/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt
new file mode 100644
index 00000000..d8a58837
--- /dev/null
+++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotSecureDataStoreSerializer.kt
@@ -0,0 +1,100 @@
+package com.umcspot.spot.datastore
+
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import androidx.datastore.core.Serializer
+import kotlinx.serialization.json.Json
+import java.io.InputStream
+import java.io.OutputStream
+import java.security.KeyStore
+import javax.crypto.Cipher
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+import javax.crypto.spec.GCMParameterSpec
+import javax.inject.Inject
+
+class SpotSecureDataStoreSerializer @Inject constructor() : Serializer {
+ companion object {
+ private const val KEYSTORE_PROVIDER = "AndroidKeyStore"
+ private const val KEY_ALIAS = "data-store-key"
+ private const val TRANSFORMATION = "AES/GCM/NoPadding"
+ private const val IV_SIZE = 12
+ }
+
+ private val keyStore: KeyStore by lazy {
+ KeyStore.getInstance(KEYSTORE_PROVIDER).apply {
+ load(null)
+ if (!containsAlias(KEY_ALIAS)) {
+ createKey()
+ }
+ }
+ }
+
+ private fun createKey() {
+ val keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES,
+ KEYSTORE_PROVIDER
+ )
+ val keyGenParameterSpec = KeyGenParameterSpec.Builder(
+ KEY_ALIAS,
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+ )
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setKeySize(256)
+ .build()
+
+ keyGenerator.init(keyGenParameterSpec)
+ keyGenerator.generateKey()
+ }
+
+ override val defaultValue: SpotTokenData
+ get() = SpotTokenData()
+
+ override suspend fun readFrom(input: InputStream): SpotTokenData {
+ return try {
+ val encryptedDataWithIv = input.readBytes()
+ if (encryptedDataWithIv.size < IV_SIZE) {
+ return defaultValue
+ }
+
+ val iv = encryptedDataWithIv.copyOfRange(0, IV_SIZE)
+ val encryptedData = encryptedDataWithIv.copyOfRange(IV_SIZE, encryptedDataWithIv.size)
+
+ val cipher = Cipher.getInstance(TRANSFORMATION)
+ val key = keyStore.getKey(KEY_ALIAS, null) as SecretKey
+ val spec = GCMParameterSpec(128, iv)
+ cipher.init(Cipher.DECRYPT_MODE, key, spec)
+
+ val decryptedBytes = cipher.doFinal(encryptedData)
+ Json.decodeFromString(
+ deserializer = SpotTokenData.serializer(),
+ string = decryptedBytes.decodeToString()
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ defaultValue
+ }
+ }
+
+ override suspend fun writeTo(t: SpotTokenData, output: OutputStream) {
+ try {
+ val serializedData = Json.encodeToString(
+ serializer = SpotTokenData.serializer(),
+ value = t
+ )
+
+ val cipher = Cipher.getInstance(TRANSFORMATION)
+ val key = keyStore.getKey(KEY_ALIAS, null) as SecretKey
+ cipher.init(Cipher.ENCRYPT_MODE, key)
+
+ val iv = cipher.iv
+ val encryptedData = cipher.doFinal(serializedData.toByteArray())
+
+ output.write(iv + encryptedData)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ throw e
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt
new file mode 100644
index 00000000..8fb51620
--- /dev/null
+++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/SpotTokenData.kt
@@ -0,0 +1,10 @@
+package com.umcspot.spot.datastore
+
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SpotTokenData(
+ val accessToken: String = "",
+ val refreshToken: String = "",
+)
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt
new file mode 100644
index 00000000..2f4b76f4
--- /dev/null
+++ b/core/datastore/src/main/java/com/umcspot/spot/datastore/di/DataStoreModule.kt
@@ -0,0 +1,32 @@
+package com.umcspot.spot.datastore.di
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.dataStoreFile
+import com.umcspot.spot.datastore.SpotSecureDataStoreSerializer
+import com.umcspot.spot.datastore.SpotTokenData
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object DataStoreModule {
+ @Provides
+ @Singleton
+ fun providesDataStore(
+ @ApplicationContext context: Context,
+ spotSecureDataStoreSerializer: SpotSecureDataStoreSerializer
+ ): DataStore =
+ DataStoreFactory.create(
+ serializer = spotSecureDataStoreSerializer
+ ) {
+ context.dataStoreFile(DATASTORE_PREFERENCES)
+ }
+
+ private const val DATASTORE_PREFERENCES = "com.umcspot.spot.datastore"
+}
\ No newline at end of file
diff --git a/core/designsystem/.gitignore b/core/designsystem/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/designsystem/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts
new file mode 100644
index 00000000..31077f7e
--- /dev/null
+++ b/core/designsystem/build.gradle.kts
@@ -0,0 +1,11 @@
+plugins {
+ alias(libs.plugins.spot.android.compose.library)
+}
+
+android {
+ namespace = "com.umcspot.spot.designsystem"
+}
+dependencies {
+ implementation(projects.core.ui)
+ implementation(libs.flexible.bottomsheet)
+}
\ No newline at end of file
diff --git a/core/designsystem/consumer-rules.pro b/core/designsystem/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/designsystem/proguard-rules.pro b/core/designsystem/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/designsystem/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/designsystem/src/main/AndroidManifest.xml b/core/designsystem/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/designsystem/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt
new file mode 100644
index 00000000..54bd2415
--- /dev/null
+++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/component/button/SpotButton.kt
@@ -0,0 +1,51 @@
+package com.umcspot.spot.designsystem.component.button
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.umcspot.spot.ui.extension.noRippleClickable
+import com.umcspot.spot.ui.extension.screenHeightDp
+
+@Composable
+fun SpotButton(
+ onClick: () -> Unit,
+ buttonText: String,
+ buttonStyle: TextStyle,
+ buttonTextColor: Color,
+ modifier: Modifier = Modifier,
+ buttonBackgroundColor: Color = Color.Unspecified,
+ buttonStrokeColor: Color = Color.Unspecified,
+ textAlign: TextAlign = TextAlign.Center
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(12.dp))
+ .background(color = buttonBackgroundColor)
+ .border(width = 1.dp, color = buttonStrokeColor, shape = RoundedCornerShape(12.dp))
+ .noRippleClickable(onClick = onClick)
+ .padding(vertical = screenHeightDp(16.dp)),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = buttonText,
+ style = buttonStyle,
+ color = buttonTextColor,
+ textAlign = textAlign
+ )
+ }
+}
diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt
new file mode 100644
index 00000000..8021782f
--- /dev/null
+++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Color.kt
@@ -0,0 +1,446 @@
+package com.umcspot.spot.designsystem.theme
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.graphics.Color
+
+// primary
+val primary50 = Color(0xFFF5F2FC)
+val primary100 = Color(0xFFF0EBFA)
+val primary200 = Color(0xFFE0D5F5)
+val primary300 = Color(0xFFAA7CFF)
+val primary400 = Color(0xFF8C6DC8)
+val primary500 = Color(0xFF7D61B2)
+val primary600 = Color(0xFF755BA7)
+val primary700 = Color(0xFF5E4985)
+val primary800 = Color(0xFF463664)
+val primary900 = Color(0xFF372A4E)
+
+// secondary
+val secondary50 = Color(0xFFFFFAEE)
+val secondary100 = Color(0xFFFFF8E5)
+val secondary300 = Color(0xFFFFCD54)
+
+// olive_green
+val oliveGreen300 = Color(0xFF96CE5A)
+val oliveGreen700 = Color(0xFF5A7C37)
+
+// soft_blue
+val softBlue200 = Color(0xFFD4EEFA)
+val softBlue300 = Color(0xFF75C8EE)
+val softBlue600 = Color(0xFF5896B3)
+
+// sub
+val sand100 = Color(0xFFFFF8E5)
+
+val orange300 = Color(0xFFFDC6B0)
+
+val warning50 = Color(0xFFFFF5E6)
+val warning300 = Color(0xFFFFA01A)
+
+// state
+val success50 = Color(0xFFE8F4FC)
+val success300 = Color(0xFF2C9CE3)
+
+val error50 = Color(0xFFFCEAEA)
+val error300 = Color(0xFFF15655)
+
+// grayscale
+val gray50 = Color(0xFFFEFEFE)
+val gray100 = Color(0xFFF4F4F4)
+val gray200 = Color(0xFFE2E2E2)
+val gray300 = Color(0xFFC4C4C4)
+val gray400 = Color(0xFFA6A6A6)
+val gray500 = Color(0xFF878787)
+val gray600 = Color(0xFF6E6E6E)
+val gray700 = Color(0xFF404040)
+val gray800 = Color(0xFF262626)
+val gray900 = Color(0xFF161616)
+
+// black_and_white
+val black = Color(0xFF000000)
+val white = Color(0xFFFFFFFF)
+
+// opacity
+val blackAlpha80 = black.copy(alpha = 0.8f)
+val blackAlpha50 = black.copy(alpha = 0.5f)
+
+val whiteAlpha50 = white.copy(alpha = 0.5f)
+val whiteAlpha10 = white.copy(alpha = 0.1f)
+
+val primary300Alpha10 = primary300.copy(alpha = 0.1f)
+val primary300Alpha20 = primary300.copy(alpha = 0.2f)
+val primary50Alpha50 = primary50.copy(alpha = 0.5f)
+
+val secondary300Alpha30 = secondary300.copy(alpha = 0.3f)
+val secondary300Alpha10 = secondary300.copy(alpha = 0.1f)
+
+val gray900Alpha80 = gray900.copy(alpha = 0.8f)
+
+// kakao
+val kakaoYellow = Color(0xFFFEE500)
+
+@Stable
+class SpotColors(
+ primary50: Color,
+ primary100: Color,
+ primary200: Color,
+ primary300: Color,
+ primary400: Color,
+ primary500: Color,
+ primary600: Color,
+ primary700: Color,
+ primary800: Color,
+ primary900: Color,
+ secondary50: Color,
+ secondary100: Color,
+ secondary300: Color,
+ oliveGreen300: Color,
+ oliveGreen700: Color,
+ softBlue200: Color,
+ softBlue300: Color,
+ softBlue600: Color,
+ sand100: Color,
+ orange300: Color,
+ warning50: Color,
+ warning300: Color,
+ success50: Color,
+ success300: Color,
+ error50: Color,
+ error300: Color,
+ gray50: Color,
+ gray100: Color,
+ gray200: Color,
+ gray300: Color,
+ gray400: Color,
+ gray500: Color,
+ gray600: Color,
+ gray700: Color,
+ gray800: Color,
+ gray900: Color,
+ black: Color,
+ white: Color,
+ blackAlpha80: Color,
+ blackAlpha50: Color,
+ whiteAlpha50: Color,
+ whiteAlpha10: Color,
+ primary300Alpha10: Color,
+ primary300Alpha20: Color,
+ primary50Alpha50: Color,
+ secondary300Alpha30: Color,
+ secondary300Alpha10: Color,
+ gray900Alpha80: Color,
+ kakaoYellow: Color,
+ isLight: Boolean
+) {
+ var primary50 by mutableStateOf(primary50)
+ private set
+ var primary100 by mutableStateOf(primary100)
+ private set
+ var primary200 by mutableStateOf(primary200)
+ private set
+ var primary300 by mutableStateOf(primary300)
+ private set
+ var primary400 by mutableStateOf(primary400)
+ private set
+ var primary500 by mutableStateOf(primary500)
+ private set
+ var primary600 by mutableStateOf(primary600)
+ private set
+ var primary700 by mutableStateOf(primary700)
+ private set
+ var primary800 by mutableStateOf(primary800)
+ private set
+ var primary900 by mutableStateOf(primary900)
+ private set
+ var secondary50 by mutableStateOf(secondary50)
+ private set
+ var secondary100 by mutableStateOf(secondary100)
+ private set
+ var secondary300 by mutableStateOf(secondary300)
+ private set
+ var oliveGreen300 by mutableStateOf(oliveGreen300)
+ private set
+ var oliveGreen700 by mutableStateOf(oliveGreen700)
+ private set
+ var softBlue200 by mutableStateOf(softBlue200)
+ private set
+ var softBlue300 by mutableStateOf(softBlue300)
+ private set
+ var softBlue600 by mutableStateOf(softBlue600)
+ private set
+ var sand100 by mutableStateOf(sand100)
+ private set
+ var orange300 by mutableStateOf(orange300)
+ private set
+ var warning50 by mutableStateOf(warning50)
+ private set
+ var warning300 by mutableStateOf(warning300)
+ private set
+ var success50 by mutableStateOf(success50)
+ private set
+ var success300 by mutableStateOf(success300)
+ private set
+ var error50 by mutableStateOf(error50)
+ private set
+ var error300 by mutableStateOf(error300)
+ private set
+ var gray50 by mutableStateOf(gray50)
+ private set
+ var gray100 by mutableStateOf(gray100)
+ private set
+ var gray200 by mutableStateOf(gray200)
+ private set
+ var gray300 by mutableStateOf(gray300)
+ private set
+ var gray400 by mutableStateOf(gray400)
+ private set
+ var gray500 by mutableStateOf(gray500)
+ private set
+ var gray600 by mutableStateOf(gray600)
+ private set
+ var gray700 by mutableStateOf(gray700)
+ private set
+ var gray800 by mutableStateOf(gray800)
+ private set
+ var gray900 by mutableStateOf(gray900)
+ private set
+ var black by mutableStateOf(black)
+ private set
+ var white by mutableStateOf(white)
+ private set
+ var blackAlpha80 by mutableStateOf(blackAlpha80)
+ private set
+ var blackAlpha50 by mutableStateOf(blackAlpha50)
+ private set
+ var whiteAlpha50 by mutableStateOf(whiteAlpha50)
+ private set
+ var whiteAlpha10 by mutableStateOf(whiteAlpha10)
+ private set
+ var primary300Alpha10 by mutableStateOf(primary300Alpha10)
+ private set
+ var primary300Alpha20 by mutableStateOf(primary300Alpha20)
+ private set
+ var primary50Alpha50 by mutableStateOf(primary50Alpha50)
+ private set
+ var secondary300Alpha30 by mutableStateOf(secondary300Alpha30)
+ private set
+ var secondary300Alpha10 by mutableStateOf(secondary300Alpha10)
+ private set
+ var gray900Alpha80 by mutableStateOf(gray900Alpha80)
+ private set
+ var kakaoYellow by mutableStateOf(kakaoYellow)
+ private set
+ var isLight by mutableStateOf(isLight)
+
+ fun copy(): SpotColors = SpotColors(
+ primary50,
+ primary100,
+ primary200,
+ primary300,
+ primary400,
+ primary500,
+ primary600,
+ primary700,
+ primary800,
+ primary900,
+ secondary50,
+ secondary100,
+ secondary300,
+ oliveGreen300,
+ oliveGreen700,
+ softBlue200,
+ softBlue300,
+ softBlue600,
+ sand100,
+ orange300,
+ warning50,
+ warning300,
+ success50,
+ success300,
+ error50,
+ error300,
+ gray50,
+ gray100,
+ gray200,
+ gray300,
+ gray400,
+ gray500,
+ gray600,
+ gray700,
+ gray800,
+ gray900,
+ black,
+ white,
+ blackAlpha80,
+ blackAlpha50,
+ whiteAlpha50,
+ whiteAlpha10,
+ primary300Alpha10,
+ primary300Alpha20,
+ primary50Alpha50,
+ secondary300Alpha30,
+ secondary300Alpha10,
+ gray900Alpha80,
+ kakaoYellow,
+ isLight
+ )
+
+ fun update(colors: SpotColors) {
+ primary50 = colors.primary50
+ primary100 = colors.primary100
+ primary200 = colors.primary200
+ primary300 = colors.primary300
+ primary400 = colors.primary400
+ primary500 = colors.primary500
+ primary600 = colors.primary600
+ primary700 = colors.primary700
+ primary800 = colors.primary800
+ primary900 = colors.primary900
+ secondary50 = colors.secondary50
+ secondary100 = colors.secondary100
+ secondary300 = colors.secondary300
+ oliveGreen300 = colors.oliveGreen300
+ oliveGreen700 = colors.oliveGreen700
+ softBlue200 = colors.softBlue200
+ softBlue300 = colors.softBlue300
+ softBlue600 = colors.softBlue600
+ sand100 = colors.sand100
+ orange300 = colors.orange300
+ warning50 = colors.warning50
+ warning300 = colors.warning300
+ success50 = colors.success50
+ success300 = colors.success300
+ error50 = colors.error50
+ error300 = colors.error300
+ gray50 = colors.gray50
+ gray100 = colors.gray100
+ gray200 = colors.gray200
+ gray300 = colors.gray300
+ gray400 = colors.gray400
+ gray500 = colors.gray500
+ gray600 = colors.gray600
+ gray700 = colors.gray700
+ gray800 = colors.gray800
+ gray900 = colors.gray900
+ black = colors.black
+ white = colors.white
+ blackAlpha80 = colors.blackAlpha80
+ blackAlpha50 = colors.blackAlpha50
+ whiteAlpha50 = colors.whiteAlpha50
+ whiteAlpha10 = colors.whiteAlpha10
+ primary300Alpha10 = colors.primary300Alpha10
+ primary300Alpha20 = colors.primary300Alpha20
+ primary50Alpha50 = colors.primary50Alpha50
+ secondary300Alpha30 = colors.secondary300Alpha30
+ secondary300Alpha10 = colors.secondary300Alpha10
+ gray900Alpha80 = colors.gray900Alpha80
+ kakaoYellow = colors.kakaoYellow
+ isLight = colors.isLight
+ }
+}
+
+fun SpotDayColors(
+ Primary50: Color = primary50,
+ Primary100: Color = primary100,
+ Primary200: Color = primary200,
+ Primary300: Color = primary300,
+ Primary400: Color = primary400,
+ Primary500: Color = primary500,
+ Primary600: Color = primary600,
+ Primary700: Color = primary700,
+ Primary800: Color = primary800,
+ Primary900: Color = primary900,
+ Secondary50: Color = secondary50,
+ Secondary100: Color = secondary100,
+ Secondary300: Color = secondary300,
+ OliveGreen300: Color = oliveGreen300,
+ OliveGreen700: Color = oliveGreen700,
+ SoftBlue200: Color = softBlue200,
+ SoftBlue300: Color = softBlue300,
+ SoftBlue600: Color = softBlue600,
+ Sand100: Color = sand100,
+ Orange300: Color = orange300,
+ Warning50: Color = warning50,
+ Warning300: Color = warning300,
+ Success50: Color = success50,
+ Success300: Color = success300,
+ Error50: Color = error50,
+ Error300: Color = error300,
+ Gray50: Color = gray50,
+ Gray100: Color = gray100,
+ Gray200: Color = gray200,
+ Gray300: Color = gray300,
+ Gray400: Color = gray400,
+ Gray500: Color = gray500,
+ Gray600: Color = gray600,
+ Gray700: Color = gray700,
+ Gray800: Color = gray800,
+ Gray900: Color = gray900,
+ Black: Color = black,
+ White: Color = white,
+ BlackAlpha80: Color = blackAlpha80,
+ BlackAlpha50: Color = blackAlpha50,
+ WhiteAlpha50: Color = whiteAlpha50,
+ WhiteAlpha10: Color = whiteAlpha10,
+ Primary300Alpha10: Color = primary300Alpha10,
+ Primary300Alpha20: Color = primary300Alpha20,
+ Primary50Alpha50: Color = primary50Alpha50,
+ Secondary300Alpha30: Color = secondary300Alpha30,
+ Secondary300Alpha10: Color = secondary300Alpha10,
+ Gray900Alpha80: Color = gray900Alpha80,
+ KakaoYellow: Color = kakaoYellow
+) = SpotColors(
+ Primary50,
+ Primary100,
+ Primary200,
+ Primary300,
+ Primary400,
+ Primary500,
+ Primary600,
+ Primary700,
+ Primary800,
+ Primary900,
+ Secondary50,
+ Secondary100,
+ Secondary300,
+ OliveGreen300,
+ OliveGreen700,
+ SoftBlue200,
+ SoftBlue300,
+ SoftBlue600,
+ Sand100,
+ Orange300,
+ Warning50,
+ Warning300,
+ Success50,
+ Success300,
+ Error50,
+ Error300,
+ Gray50,
+ Gray100,
+ Gray200,
+ Gray300,
+ Gray400,
+ Gray500,
+ Gray600,
+ Gray700,
+ Gray800,
+ Gray900,
+ Black,
+ White,
+ BlackAlpha80,
+ BlackAlpha50,
+ WhiteAlpha50,
+ WhiteAlpha10,
+ Primary300Alpha10,
+ Primary300Alpha20,
+ Primary50Alpha50,
+ Secondary300Alpha30,
+ Secondary300Alpha10,
+ Gray900Alpha80,
+ KakaoYellow,
+ isLight = true
+)
diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt
new file mode 100644
index 00000000..86ea9259
--- /dev/null
+++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/Theme.kt
@@ -0,0 +1,71 @@
+package com.umcspot.spot.designsystem.theme
+
+
+import android.app.Activity
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val LocalSpotColors =
+ staticCompositionLocalOf { error("No colors provided") }
+private val LocalSpotTypography =
+ staticCompositionLocalOf { error("No typography provided") }
+
+object SpotTheme {
+ val colors: SpotColors
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalSpotColors.current
+
+ val typography: SpotTypography
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalSpotTypography.current
+}
+
+@Composable
+fun ProvideSpotColorsAndTypography(
+ colors: SpotColors,
+ typography: SpotTypography,
+ content: @Composable () -> Unit
+) {
+ val provideColors = remember { colors.copy() }
+ provideColors.update(colors)
+ val provideTypography = remember { typography.copy() }
+ provideTypography.update(typography)
+
+ CompositionLocalProvider(
+ LocalSpotColors provides provideColors,
+ LocalSpotTypography provides provideTypography,
+ content = content
+ )
+}
+
+@Composable
+fun SpotTheme(
+ darkTheme: Boolean = false,
+ content: @Composable () -> Unit
+) {
+ val colors = SpotDayColors()
+ val typography = SpotTypography()
+
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true
+ WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = true
+ }
+ }
+ ProvideSpotColorsAndTypography(colors, typography) {
+ MaterialTheme(
+ content = content
+ )
+ }
+}
diff --git a/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt
new file mode 100644
index 00000000..1735bd70
--- /dev/null
+++ b/core/designsystem/src/main/java/com/umcspot/spot/designsystem/theme/TypoGraphy.kt
@@ -0,0 +1,207 @@
+package com.umcspot.spot.designsystem.theme
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.LineHeightStyle
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import com.umcspot.spot.designsystem.R
+
+val pretendardSemiBold = FontFamily(Font(R.font.pretendard_semibold, FontWeight.SemiBold))
+val pretendardMedium = FontFamily(Font(R.font.pretendard_medium, FontWeight.Medium))
+val pretendardRegular = FontFamily(Font(R.font.pretendard_regular, FontWeight.Normal))
+
+private fun SpotTextStyle(
+ fontFamily: FontFamily,
+ fontSize: TextUnit,
+ lineHeight: TextUnit,
+ letterSpacing: TextUnit
+): TextStyle = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize,
+ lineHeight = lineHeight,
+ letterSpacing = letterSpacing,
+ lineHeightStyle = LineHeightStyle(
+ alignment = LineHeightStyle.Alignment.Center,
+ trim = LineHeightStyle.Trim.None
+ )
+)
+
+@Stable
+class SpotTypography internal constructor(
+ head1: TextStyle,
+ head2: TextStyle,
+ sub1: TextStyle,
+ sub2: TextStyle,
+ sub3: TextStyle,
+ sub4: TextStyle,
+ body1: TextStyle,
+ body2: TextStyle,
+ body3: TextStyle,
+ body4: TextStyle,
+ body5: TextStyle,
+ body6: TextStyle,
+ cap1: TextStyle,
+ cap2: TextStyle
+) {
+ var head1 by mutableStateOf(head1)
+ private set
+ var head2 by mutableStateOf(head2)
+ private set
+ var sub1 by mutableStateOf(sub1)
+ private set
+ var sub2 by mutableStateOf(sub2)
+ private set
+ var sub3 by mutableStateOf(sub3)
+ private set
+ var sub4 by mutableStateOf(sub4)
+ private set
+ var body1 by mutableStateOf(body1)
+ private set
+ var body2 by mutableStateOf(body2)
+ private set
+ var body3 by mutableStateOf(body3)
+ private set
+ var body4 by mutableStateOf(body4)
+ private set
+ var body5 by mutableStateOf(body5)
+ private set
+ var body6 by mutableStateOf(body6)
+ private set
+ var cap1 by mutableStateOf(cap1)
+ private set
+ var cap2 by mutableStateOf(cap2)
+ private set
+
+ fun copy(): SpotTypography = SpotTypography(
+ head1 = head1,
+ head2 = head2,
+ sub1 = sub1,
+ sub2 = sub2,
+ sub3 = sub3,
+ sub4 = sub4,
+ body1 = body1,
+ body2 = body2,
+ body3 = body3,
+ body4 = body4,
+ body5 = body5,
+ body6 = body6,
+ cap1 = cap1,
+ cap2 = cap2
+ )
+
+ fun update(typography: SpotTypography) {
+ head1 = typography.head1
+ head2 = typography.head2
+ sub1 = typography.sub1
+ sub2 = typography.sub2
+ sub3 = typography.sub3
+ sub4 = typography.sub4
+ body1 = typography.body1
+ body2 = typography.body2
+ body3 = typography.body3
+ body4 = typography.body4
+ body5 = typography.body5
+ body6 = typography.body6
+ cap1 = typography.cap1
+ cap2 = typography.cap2
+ }
+}
+
+@Composable
+fun SpotTypography(): SpotTypography {
+ return SpotTypography(
+ head1 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 24.sp,
+ lineHeight = 31.sp,
+ letterSpacing = 0.em
+ ),
+ head2 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 22.sp,
+ lineHeight = 26.sp,
+ letterSpacing = 0.em
+ ),
+ sub1 = SpotTextStyle(
+ fontFamily = pretendardSemiBold,
+ fontSize = 20.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.em
+ ),
+ sub2 = SpotTextStyle(
+ fontFamily = pretendardSemiBold,
+ fontSize = 18.sp,
+ lineHeight = 22.sp,
+ letterSpacing = 0.em
+ ),
+ sub3 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 18.sp,
+ lineHeight = 22.sp,
+ letterSpacing = 0.em
+ ),
+ sub4 = SpotTextStyle(
+ fontFamily = pretendardRegular,
+ fontSize = 18.sp,
+ lineHeight = 22.sp,
+ letterSpacing = 0.em
+ ),
+ body1 = SpotTextStyle(
+ fontFamily = pretendardSemiBold,
+ fontSize = 16.sp,
+ lineHeight = 21.sp,
+ letterSpacing = 0.em
+ ),
+ body2 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 16.sp,
+ lineHeight = 21.sp,
+ letterSpacing = 0.em
+ ),
+ body3 = SpotTextStyle(
+ fontFamily = pretendardRegular,
+ fontSize = 16.sp,
+ lineHeight = 21.sp,
+ letterSpacing = 0.em
+ ),
+ body4 = SpotTextStyle(
+ fontFamily = pretendardSemiBold,
+ fontSize = 14.sp,
+ lineHeight = 18.sp,
+ letterSpacing = 0.em
+ ),
+ body5 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 14.sp,
+ lineHeight = 18.sp,
+ letterSpacing = 0.em
+ ),
+ body6 = SpotTextStyle(
+ fontFamily = pretendardRegular,
+ fontSize = 14.sp,
+ lineHeight = 18.sp,
+ letterSpacing = 0.em
+ ),
+ cap1 = SpotTextStyle(
+ fontFamily = pretendardMedium,
+ fontSize = 12.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.em
+ ),
+ cap2 = SpotTextStyle(
+ fontFamily = pretendardRegular,
+ fontSize = 12.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.em
+ )
+ )
+}
diff --git a/core/designsystem/src/main/res/drawable/ic_home.xml b/core/designsystem/src/main/res/drawable/ic_home.xml
new file mode 100644
index 00000000..cde30daf
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_home.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_user.xml b/core/designsystem/src/main/res/drawable/ic_user.xml
new file mode 100644
index 00000000..43fa8103
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_user.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/core/designsystem/src/main/res/font/pretendard_medium.otf b/core/designsystem/src/main/res/font/pretendard_medium.otf
new file mode 100644
index 00000000..05750698
Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_medium.otf differ
diff --git a/core/designsystem/src/main/res/font/pretendard_regular.otf b/core/designsystem/src/main/res/font/pretendard_regular.otf
new file mode 100644
index 00000000..08bf4cfc
Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_regular.otf differ
diff --git a/core/designsystem/src/main/res/font/pretendard_semibold.otf b/core/designsystem/src/main/res/font/pretendard_semibold.otf
new file mode 100644
index 00000000..e7e36abc
Binary files /dev/null and b/core/designsystem/src/main/res/font/pretendard_semibold.otf differ
diff --git a/core/model/.gitignore b/core/model/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/model/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts
new file mode 100644
index 00000000..0b14d571
--- /dev/null
+++ b/core/model/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+ alias(libs.plugins.spot.android.java.library)
+ alias(libs.plugins.kotlin.serialization)
+}
+dependencies {
+ implementation(libs.kotlinx.serialization.json)
+}
\ No newline at end of file
diff --git a/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt b/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt
new file mode 100644
index 00000000..72d866fe
--- /dev/null
+++ b/core/model/src/main/java/com/umcspot/spot/model/CoreDummy.kt
@@ -0,0 +1,5 @@
+package com.umcspot.spot.model
+
+enum class CoreDummy(
+ val coreDummy: String
+)
\ No newline at end of file
diff --git a/core/navigation/.gitignore b/core/navigation/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/navigation/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts
new file mode 100644
index 00000000..213673a6
--- /dev/null
+++ b/core/navigation/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+ alias(libs.plugins.spot.android.library)
+}
+
+android {
+ namespace = "com.umcspot.spot.navigation"
+}
\ No newline at end of file
diff --git a/core/navigation/consumer-rules.pro b/core/navigation/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/navigation/proguard-rules.pro b/core/navigation/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/navigation/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/navigation/src/main/AndroidManifest.xml b/core/navigation/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/navigation/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt b/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt
new file mode 100644
index 00000000..9ae0d96a
--- /dev/null
+++ b/core/navigation/src/main/java/com/umcspot/spot/navigation/MainTabRoute.kt
@@ -0,0 +1,3 @@
+package com.umcspot.spot.navigation
+
+interface MainTabRoute : Route
\ No newline at end of file
diff --git a/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt b/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt
new file mode 100644
index 00000000..db143b3d
--- /dev/null
+++ b/core/navigation/src/main/java/com/umcspot/spot/navigation/Route.kt
@@ -0,0 +1,3 @@
+package com.umcspot.spot.navigation
+
+interface Route
\ No newline at end of file
diff --git a/core/network/.gitignore b/core/network/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/network/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
new file mode 100644
index 00000000..4e05fdbb
--- /dev/null
+++ b/core/network/build.gradle.kts
@@ -0,0 +1,20 @@
+plugins {
+ alias(libs.plugins.spot.android.library)
+ alias(libs.plugins.spot.android.hilt)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.spot.android.test)
+}
+
+android {
+ namespace = "com.umcspot.spot.network"
+}
+dependencies {
+ implementation(libs.bundles.datastore)
+ implementation(projects.core.common)
+ implementation(projects.core.model)
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.retrofit.core)
+ implementation(libs.retrofit.kotlin.serialization)
+ implementation(libs.okhttp.logging)
+ implementation(libs.process.phoenix)
+}
\ No newline at end of file
diff --git a/core/network/consumer-rules.pro b/core/network/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/network/proguard-rules.pro b/core/network/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/network/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/network/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt b/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt
new file mode 100644
index 00000000..730a7247
--- /dev/null
+++ b/core/network/src/main/java/com/umcspot/spot/network/di/NetworkModule.kt
@@ -0,0 +1,54 @@
+package com.umcspot.spot.network.di
+
+import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
+import com.umcspot.spot.common.BuildConfigFieldProvider
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.Json
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Converter
+import retrofit2.Retrofit
+
+@Module
+@InstallIn(SingletonComponent::class)
+object NetworkModule {
+ @Provides
+ @Singleton
+ fun providesLoggingInterceptor() =
+ HttpLoggingInterceptor().apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }
+
+ @Provides
+ @Singleton
+ fun providesOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
+ OkHttpClient.Builder()
+ .addInterceptor(loggingInterceptor)
+ .build()
+
+ @OptIn(ExperimentalSerializationApi::class)
+ @Provides
+ @Singleton
+ fun providesConverterFactory(): Converter.Factory = Json.asConverterFactory(
+ "application/json".toMediaType()
+ )
+
+ @Provides
+ @Singleton
+ fun providesRetrofit(
+ client: OkHttpClient,
+ converterFactory: Converter.Factory,
+ buildConfigProvider: BuildConfigFieldProvider
+ ): Retrofit =
+ Retrofit.Builder()
+ .baseUrl(buildConfigProvider.get().baseUrl)
+ .client(client)
+ .addConverterFactory(converterFactory)
+ .build()
+}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt b/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt
new file mode 100644
index 00000000..a13dd6d7
--- /dev/null
+++ b/core/network/src/main/java/com/umcspot/spot/network/model/BaseResponse.kt
@@ -0,0 +1,14 @@
+package network.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class BaseResponse(
+ @SerialName("success")
+ val success: Boolean,
+ @SerialName("message")
+ val message: String,
+ @SerialName("data")
+ val data: T
+)
\ No newline at end of file
diff --git a/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt b/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt
new file mode 100644
index 00000000..6c0421ed
--- /dev/null
+++ b/core/network/src/main/java/com/umcspot/spot/network/service/TokenRefreshService.kt
@@ -0,0 +1,4 @@
+package network.service
+
+class TokenRefreshService {
+}
\ No newline at end of file
diff --git a/core/ui/.gitignore b/core/ui/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/ui/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
new file mode 100644
index 00000000..83674fcc
--- /dev/null
+++ b/core/ui/build.gradle.kts
@@ -0,0 +1,11 @@
+plugins {
+ alias(libs.plugins.spot.android.compose.library)
+}
+
+android {
+ namespace = "com.umcspot.spot.ui"
+}
+
+dependencies {
+ androidTestImplementation(libs.bundles.coil)
+}
\ No newline at end of file
diff --git a/core/ui/consumer-rules.pro b/core/ui/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/ui/proguard-rules.pro b/core/ui/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/ui/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/core/ui/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt b/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt
new file mode 100644
index 00000000..8e938c85
--- /dev/null
+++ b/core/ui/src/main/java/com/umcspot/spot/ui/extension/DimensionExt.kt
@@ -0,0 +1,23 @@
+package com.umcspot.spot.ui.extension
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+private val figmaScreenWidth = 360.dp
+private val figmaScreenHeight = 800.dp
+
+@Composable
+fun screenHeightDp(height: Dp): Dp {
+ val screenHeight = LocalConfiguration.current.screenHeightDp.dp
+ val ratio = screenHeight / figmaScreenHeight
+ return height * ratio
+}
+
+@Composable
+fun screenWidthDp(width: Dp): Dp {
+ val screenWidth = LocalConfiguration.current.screenWidthDp.dp
+ val ratio = screenWidth / figmaScreenWidth
+ return width * ratio
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt b/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt
new file mode 100644
index 00000000..3b757fa8
--- /dev/null
+++ b/core/ui/src/main/java/com/umcspot/spot/ui/extension/ModifierExt.kt
@@ -0,0 +1,32 @@
+package com.umcspot.spot.ui.extension
+
+import android.annotation.SuppressLint
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+
+@SuppressLint("ModifierFactoryUnreferencedReceiver")
+inline fun androidx.compose.ui.Modifier.noRippleClickable(crossinline onClick: () -> Unit = {}): Modifier =
+ composed {
+ this.clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ onClick()
+ }
+ }
+
+inline fun Modifier.noRippleCombineClickable(
+ crossinline onClick: () -> Unit = {},
+ crossinline onLongClick: () -> Unit
+): Modifier = composed {
+ combinedClickable(
+ interactionSource = remember { MutableInteractionSource() },
+ indication = null,
+ onClick = { onClick() },
+ onLongClick = { onLongClick() }
+ )
+}
diff --git a/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt b/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt
new file mode 100644
index 00000000..ff1eebce
--- /dev/null
+++ b/core/ui/src/main/java/com/umcspot/spot/ui/state/UiState.kt
@@ -0,0 +1,15 @@
+package com.umcspot.spot.ui.state
+
+sealed interface UiState {
+ data object Empty : UiState
+
+ data object Loading : UiState
+
+ data class Success(
+ val data: T
+ ) : UiState
+
+ data class Failure(
+ val msg: String
+ ) : UiState
+}
\ No newline at end of file
diff --git a/data/home/.gitignore b/data/home/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/data/home/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/data/home/build.gradle.kts b/data/home/build.gradle.kts
new file mode 100644
index 00000000..05197446
--- /dev/null
+++ b/data/home/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ alias(libs.plugins.spot.data)
+}
+
+android {
+ namespace = "com.umcspot.spot.home"
+}
+dependencies {
+ implementation(projects.domain.home)
+}
\ No newline at end of file
diff --git a/data/home/consumer-rules.pro b/data/home/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/data/home/proguard-rules.pro b/data/home/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/data/home/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/data/home/src/main/AndroidManifest.xml b/data/home/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/data/home/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt b/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt
new file mode 100644
index 00000000..d06b2254
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/datasource/HomeDataSource.kt
@@ -0,0 +1,9 @@
+package com.umcspot.spot.home.datasource
+
+import com.umcspot.spot.home.dto.request.HomeRequestDto
+import com.umcspot.spot.home.dto.response.HomeResponseDto
+import network.model.BaseResponse
+
+interface HomeDataSource {
+ suspend fun getDummies(request: HomeRequestDto): BaseResponse
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt b/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt
new file mode 100644
index 00000000..a087b289
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/datasourceimpl/HomeDataSourceImpl.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.home.datasourceimpl
+
+import com.umcspot.spot.home.datasource.HomeDataSource
+import com.umcspot.spot.home.dto.request.HomeRequestDto
+import com.umcspot.spot.home.dto.response.HomeResponseDto
+import com.umcspot.spot.home.service.HomeService
+import network.model.BaseResponse
+import javax.inject.Inject
+
+
+class HomeDataSourceImpl @Inject constructor(
+ private val homeService: HomeService
+) : HomeDataSource {
+ override suspend fun getDummies(
+ request: HomeRequestDto
+ ): BaseResponse =
+ homeService.getDummies(request)
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt
new file mode 100644
index 00000000..61a51d89
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeDataModule.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.home.di
+
+import com.umcspot.spot.home.datasource.HomeDataSource
+import com.umcspot.spot.home.datasourceimpl.HomeDataSourceImpl
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class HomeDataModule {
+ @Binds
+ @Singleton
+ abstract fun bindDummyRemoteDataSource(impl: HomeDataSourceImpl): HomeDataSource
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt
new file mode 100644
index 00000000..bba43bfe
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeRepositoryModule.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.home.di
+
+
+import com.umcspot.spot.home.repository.HomeRepository
+import com.umcspot.spot.home.repositoryimpl.HomeRepositoryImpl
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class HomeRepositoryModule {
+ @Binds
+ @Singleton
+ abstract fun bindsDummyRepository(dummyRepositoryImpl: HomeRepositoryImpl): HomeRepository
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt b/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt
new file mode 100644
index 00000000..71a60c30
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/di/HomeServiceModule.kt
@@ -0,0 +1,19 @@
+package com.umcspot.spot.home.di
+
+import com.umcspot.spot.home.service.HomeService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import retrofit2.Retrofit
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object HomeServiceModule {
+ @Provides
+ @Singleton
+ fun providesDummyService(retrofit: Retrofit): HomeService = retrofit.create(
+ HomeService::class.java
+ )
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt b/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt
new file mode 100644
index 00000000..a996e217
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/dto/request/HomeRequestDto.kt
@@ -0,0 +1,12 @@
+package com.umcspot.spot.home.dto.request
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class HomeRequestDto(
+ @SerialName("id")
+ val id: Int,
+ @SerialName("email")
+ val email: String
+)
diff --git a/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt b/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt
new file mode 100644
index 00000000..6ff3d08c
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/dto/response/HomeResponseDto.kt
@@ -0,0 +1,10 @@
+package com.umcspot.spot.home.dto.response
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class HomeResponseDto(
+ @SerialName("info")
+ val info: List
+)
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt b/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt
new file mode 100644
index 00000000..769bb7de
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/mapper/HomeMapper.kt
@@ -0,0 +1,18 @@
+package com.umcspot.spot.home.mapper
+
+
+import com.umcspot.spot.home.dto.request.HomeRequestDto
+import com.umcspot.spot.home.dto.response.HomeResponseDto
+import com.umcspot.spot.home.model.Dummy
+import com.umcspot.spot.home.model.DummyResult
+
+fun Dummy.toData(): HomeRequestDto =
+ HomeRequestDto(
+ id = this.id,
+ email = this.email
+ )
+
+fun HomeResponseDto.toDomain(): DummyResult =
+ DummyResult(
+ info = info
+ )
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt b/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt
new file mode 100644
index 00000000..0f1b05fe
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/repositoryimpl/HomeRepositoryImpl.kt
@@ -0,0 +1,20 @@
+package com.umcspot.spot.home.repositoryimpl
+
+import com.umcspot.spot.home.mapper.toData
+import com.umcspot.spot.home.mapper.toDomain
+import com.umcspot.spot.home.model.Dummy
+import com.umcspot.spot.home.model.DummyResult
+import com.umcspot.spot.home.repository.HomeRepository
+import com.umcspot.spot.home.service.HomeService
+import javax.inject.Inject
+
+
+class HomeRepositoryImpl @Inject constructor(
+ private val homeService: HomeService
+) : HomeRepository {
+ override suspend fun getDummies(request: Dummy): Result =
+ runCatching {
+ val response = homeService.getDummies(request = request.toData())
+ response.data.toDomain()
+ }
+}
\ No newline at end of file
diff --git a/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt b/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt
new file mode 100644
index 00000000..f0f5595f
--- /dev/null
+++ b/data/home/src/main/java/com/umcspot/spot/home/service/HomeService.kt
@@ -0,0 +1,16 @@
+package com.umcspot.spot.home.service
+
+
+import com.umcspot.spot.home.dto.request.HomeRequestDto
+import com.umcspot.spot.home.dto.response.HomeResponseDto
+import network.model.BaseResponse
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+
+interface HomeService {
+ @POST("/api/v1/service")
+ suspend fun getDummies(
+ @Body request: HomeRequestDto
+ ): BaseResponse
+}
\ No newline at end of file
diff --git a/domain/home/.gitignore b/domain/home/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/domain/home/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/domain/home/build.gradle.kts b/domain/home/build.gradle.kts
new file mode 100644
index 00000000..14f8d7b6
--- /dev/null
+++ b/domain/home/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+ alias(libs.plugins.spot.android.java.library)
+}
+dependencies {
+ implementation(projects.core.model)
+ implementation(libs.bundles.coroutine)
+}
\ No newline at end of file
diff --git a/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt b/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt
new file mode 100644
index 00000000..03995228
--- /dev/null
+++ b/domain/home/src/main/java/com/umcspot/spot/home/model/Dummy.kt
@@ -0,0 +1,6 @@
+package com.umcspot.spot.home.model
+
+data class Dummy(
+ val id: Int,
+ val email: String
+)
\ No newline at end of file
diff --git a/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt b/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt
new file mode 100644
index 00000000..cc79504c
--- /dev/null
+++ b/domain/home/src/main/java/com/umcspot/spot/home/model/DummyResult.kt
@@ -0,0 +1,5 @@
+package com.umcspot.spot.home.model
+
+data class DummyResult(
+ val info: List
+)
\ No newline at end of file
diff --git a/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt b/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt
new file mode 100644
index 00000000..55e99740
--- /dev/null
+++ b/domain/home/src/main/java/com/umcspot/spot/home/repository/HomeRepository.kt
@@ -0,0 +1,8 @@
+package com.umcspot.spot.home.repository
+
+import com.umcspot.spot.home.model.Dummy
+import com.umcspot.spot.home.model.DummyResult
+
+interface HomeRepository {
+ suspend fun getDummies(request: Dummy): Result
+}
\ No newline at end of file
diff --git a/feature/home/.gitignore b/feature/home/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/feature/home/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts
new file mode 100644
index 00000000..24510945
--- /dev/null
+++ b/feature/home/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ alias(libs.plugins.spot.feature)
+}
+android {
+ namespace = "com.umcspot.spot.home"
+}
+
+dependencies {
+ implementation(projects.domain.home)
+}
\ No newline at end of file
diff --git a/feature/home/consumer-rules.pro b/feature/home/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/feature/home/proguard-rules.pro b/feature/home/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/feature/home/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/home/src/main/AndroidManifest.xml b/feature/home/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/feature/home/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt
new file mode 100644
index 00000000..31b2dae5
--- /dev/null
+++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeScreen.kt
@@ -0,0 +1,48 @@
+package com.umcspot.spot.home
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.umcspot.spot.ui.state.UiState
+
+@Composable
+fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.White),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ when (val state = uiState.user) {
+ is UiState.Loading -> {
+ Text(text = "로딩 중...", color = Color.Gray)
+ }
+
+ is UiState.Success -> {
+ state.data.info.forEachIndexed { index, item ->
+ Text(text = "Info[$index]: $item")
+ }
+ }
+
+ is UiState.Failure -> {
+ Text(text = "에러: ${state.msg}", color = Color.Red)
+ }
+
+ UiState.Empty -> {
+ Text(text = "데이터가 없습니다.")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt
new file mode 100644
index 00000000..941fb4f4
--- /dev/null
+++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeState.kt
@@ -0,0 +1,8 @@
+package com.umcspot.spot.home
+
+import com.umcspot.spot.home.model.DummyResult
+import com.umcspot.spot.ui.state.UiState
+
+data class HomeState(
+ var user: UiState = UiState.Loading
+)
\ No newline at end of file
diff --git a/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt b/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt
new file mode 100644
index 00000000..f37a6178
--- /dev/null
+++ b/feature/home/src/main/java/com/umcspot/spot/home/HomeViewModel.kt
@@ -0,0 +1,45 @@
+package com.umcspot.spot.home
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.umcspot.spot.home.model.Dummy
+import com.umcspot.spot.home.repository.HomeRepository
+import com.umcspot.spot.ui.state.UiState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+@HiltViewModel
+class HomeViewModel
+@Inject
+constructor(
+ private val dummyRepository: HomeRepository
+) : ViewModel() {
+ var uiState = MutableStateFlow(HomeState())
+ private set
+
+ fun getDummies(
+ id: Int,
+ email: String
+ ) {
+ viewModelScope.launch {
+ dummyRepository.getDummies(
+ request = Dummy(id = id, email = email)
+ )
+ .onSuccess { response ->
+ uiState.update {
+ it.copy(
+ user = UiState.Success(response)
+ )
+ }
+ }
+ .onFailure { e ->
+ uiState.update {
+ it.copy(user = UiState.Failure(e.message ?: "오류 발생"))
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt b/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt
new file mode 100644
index 00000000..37d09563
--- /dev/null
+++ b/feature/home/src/main/java/com/umcspot/spot/home/navigation/HomeNavigation.kt
@@ -0,0 +1,23 @@
+package com.umcspot.spot.home.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.umcspot.spot.home.HomeScreen
+import com.umcspot.spot.navigation.MainTabRoute
+import kotlinx.serialization.Serializable
+
+
+fun NavController.navigateToHome(navOptions: NavOptions? = null) {
+ navigate(Home, navOptions)
+}
+
+fun NavGraphBuilder.homeGraph() {
+ composable {
+ HomeScreen()
+ }
+}
+
+@Serializable
+data object Home : MainTabRoute
\ No newline at end of file
diff --git a/feature/main/.gitignore b/feature/main/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/feature/main/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts
new file mode 100644
index 00000000..ad2ad365
--- /dev/null
+++ b/feature/main/build.gradle.kts
@@ -0,0 +1,17 @@
+plugins {
+ alias(libs.plugins.spot.feature)
+}
+android {
+ namespace = "com.umcspot.spot.main"
+}
+
+dependencies {
+ implementation(projects.feature.home)
+ implementation(projects.feature.mypage)
+ implementation(libs.androidx.splashscreen)
+ implementation(libs.lottie)
+ implementation(libs.lottie.compose)
+ implementation(libs.material3.compose)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.google.material)
+}
\ No newline at end of file
diff --git a/feature/main/consumer-rules.pro b/feature/main/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/feature/main/proguard-rules.pro b/feature/main/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/feature/main/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/main/src/main/AndroidManifest.xml b/feature/main/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/feature/main/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt
new file mode 100644
index 00000000..b198799b
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/MainActivity.kt
@@ -0,0 +1,26 @@
+package com.umcspot.spot.main
+
+import android.graphics.Color
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.SystemBarStyle
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import com.umcspot.spot.designsystem.theme.SpotTheme
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge(
+ statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT),
+ navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT)
+ )
+ setContent {
+ SpotTheme {
+ MainScreen()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt
new file mode 100644
index 00000000..e6f66e24
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt
@@ -0,0 +1,27 @@
+package com.umcspot.spot.main
+
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.compose.NavHost
+import com.umcspot.spot.home.navigation.homeGraph
+import com.umcspot.spot.mypage.navigation.mypageGraph
+
+@Composable
+fun MainNavHost(
+ navigator: MainNavigator,
+ modifier: Modifier = Modifier
+) {
+ NavHost(
+ enterTransition = { EnterTransition.None },
+ exitTransition = { ExitTransition.None },
+ popEnterTransition = { EnterTransition.None },
+ popExitTransition = { ExitTransition.None },
+ navController = navigator.navController,
+ startDestination = navigator.startDestination
+ ) {
+ homeGraph()
+ mypageGraph()
+ }
+}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt
new file mode 100644
index 00000000..608280e0
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavTab.kt
@@ -0,0 +1,42 @@
+package com.umcspot.spot.main
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.compose.runtime.Composable
+import com.umcspot.spot.designsystem.R.drawable.ic_user
+import com.umcspot.spot.designsystem.R.drawable.ic_home
+import com.umcspot.spot.home.navigation.Home
+import com.umcspot.spot.mypage.navigation.Mypage
+import com.umcspot.spot.main.R.string.ic_home_desc
+import com.umcspot.spot.main.R.string.ic_mypage_desc
+import com.umcspot.spot.navigation.MainTabRoute
+import com.umcspot.spot.navigation.Route
+
+enum class MainNavTab(
+ @DrawableRes val icon: Int,
+ @StringRes val contentDescription: Int,
+ val route: MainTabRoute
+) {
+ HOME(
+ icon = ic_home,
+ contentDescription = ic_home_desc,
+ route = Home
+ ),
+ MYPAGE(
+ icon = ic_user,
+ contentDescription = ic_mypage_desc,
+ route = Mypage
+ );
+
+ companion object {
+ @Composable
+ fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainNavTab? {
+ return entries.find { predicate(it.route) }
+ }
+
+ @Composable
+ fun contains(predicate: @Composable (Route) -> Boolean): Boolean {
+ return entries.map { it.route }.any { predicate(it) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt
new file mode 100644
index 00000000..a8b3b25c
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/MainNavigator.kt
@@ -0,0 +1,65 @@
+package com.umcspot.spot.main
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.navigation.NavDestination
+import androidx.navigation.NavDestination.Companion.hasRoute
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navOptions
+import com.umcspot.spot.home.navigation.Home
+import com.umcspot.spot.home.navigation.navigateToHome
+import com.umcspot.spot.mypage.navigation.navigateToMypage
+
+class MainNavigator(
+ val navController: NavHostController
+) {
+ private val currentDestination: NavDestination?
+ @Composable get() =
+ navController
+ .currentBackStackEntryAsState().value?.destination
+
+ val startDestination = Home
+
+ val currentTab: MainNavTab?
+ @Composable get() =
+ MainNavTab.find { tab ->
+ currentDestination?.hasRoute(tab::class) == true
+ }
+
+ fun navigate(tab: MainNavTab) {
+ val navOptions =
+ navOptions {
+ navController.currentDestination?.route?.let {
+ popUpTo(it) {
+ inclusive = true
+ saveState = true
+ }
+ }
+ launchSingleTop = true
+ restoreState = true
+ }
+ when (tab) {
+ MainNavTab.HOME -> navController.navigateToHome(navOptions)
+ MainNavTab.MYPAGE -> navController.navigateToMypage(navOptions)
+ }
+ }
+
+ @Composable
+ fun showBottomBar() =
+ MainNavTab.contains {
+ currentDestination?.hasRoute(it::class) == true
+ }
+
+ fun navigateUp() {
+ navController.navigateUp()
+ }
+}
+
+@Composable
+fun rememberMainNavigator(
+ navController: NavHostController = rememberNavController()
+): MainNavigator = remember(navController) {
+ MainNavigator(navController)
+}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt b/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt
new file mode 100644
index 00000000..d2752ac0
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/MainScreen.kt
@@ -0,0 +1,36 @@
+package com.umcspot.spot.main
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.umcspot.spot.main.component.MainBottomBar
+import kotlinx.collections.immutable.toImmutableList
+
+@Composable
+fun MainScreen(navigator: MainNavigator = rememberMainNavigator()) {
+ Scaffold(
+ bottomBar = {
+ MainBottomBar(
+ visible = navigator.showBottomBar(),
+ tabs = MainNavTab.entries.toImmutableList(),
+ currentTab = navigator.currentTab,
+ onTabSelected = navigator::navigate
+ )
+ },
+ modifier =
+ Modifier
+ .background(Color.White)
+ .systemBarsPadding()
+ .fillMaxSize()
+ ) { innerPadding ->
+ MainNavHost(
+ navigator = navigator,
+ modifier = Modifier.padding(innerPadding)
+ )
+ }
+}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt b/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt
new file mode 100644
index 00000000..80cf7c99
--- /dev/null
+++ b/feature/main/src/main/java/com/umcspot/spot/main/component/MainBottomBar.kt
@@ -0,0 +1,105 @@
+package com.umcspot.spot.main.component
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.umcspot.spot.designsystem.theme.SpotTheme
+import com.umcspot.spot.designsystem.theme.black
+import com.umcspot.spot.designsystem.theme.success300
+import com.umcspot.spot.main.MainNavTab
+import com.umcspot.spot.ui.extension.noRippleClickable
+import com.umcspot.spot.ui.extension.screenHeightDp
+import kotlinx.collections.immutable.ImmutableList
+
+@Composable
+fun MainBottomBar(
+ visible: Boolean,
+ tabs: ImmutableList,
+ currentTab: MainNavTab?,
+ onTabSelected: (MainNavTab) -> Unit
+) {
+ AnimatedVisibility(
+ visible = visible,
+ enter = EnterTransition.None,
+ exit = ExitTransition.None
+ ) {
+ Column(
+ modifier = Modifier.background(SpotTheme.colors.white)
+ ) {
+ HorizontalDivider(
+ thickness = 1.dp,
+ color = SpotTheme.colors.gray300
+ )
+ Row(
+ modifier = Modifier
+ .navigationBarsPadding()
+ .fillMaxWidth()
+ .padding(vertical = screenHeightDp(10.dp)),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ tabs.forEach { tab ->
+ key(tab.route) {
+ val selected = currentTab == tab
+ MainBottomBarItem(
+ tab = tab,
+ selected = selected,
+ onClick = { onTabSelected(tab) }
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun RowScope.MainBottomBarItem(
+ modifier: Modifier = Modifier,
+ tab: MainNavTab,
+ selected: Boolean,
+ onClick: () -> Unit
+) {
+ val bottomItemColor = if (selected) success300 else black
+ val bottomTextStyle = if (selected) SpotTheme.typography.body4 else SpotTheme.typography.body6
+
+ Column(
+ modifier = modifier
+ .noRippleClickable(onClick = onClick)
+ .weight(1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(1.dp)
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(tab.icon),
+ contentDescription = stringResource(tab.contentDescription),
+ tint = bottomItemColor
+ )
+ Text(
+ text = stringResource(tab.contentDescription),
+ fontSize = 14.sp,
+ style = bottomTextStyle,
+ color = bottomItemColor
+ )
+ }
+}
diff --git a/feature/main/src/main/res/values/strings.xml b/feature/main/src/main/res/values/strings.xml
new file mode 100644
index 00000000..92f79e6e
--- /dev/null
+++ b/feature/main/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ 홈
+ 마이페이지
+
\ No newline at end of file
diff --git a/feature/mypage/.gitignore b/feature/mypage/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/feature/mypage/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
new file mode 100644
index 00000000..cde42ceb
--- /dev/null
+++ b/feature/mypage/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+ alias(libs.plugins.spot.feature)
+}
+
+android {
+ namespace = "com.umcspot.spot.mypage"
+}
\ No newline at end of file
diff --git a/feature/mypage/consumer-rules.pro b/feature/mypage/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/feature/mypage/proguard-rules.pro b/feature/mypage/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/feature/mypage/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/mypage/src/main/AndroidManifest.xml b/feature/mypage/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/feature/mypage/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt
new file mode 100644
index 00000000..8fdb8b9a
--- /dev/null
+++ b/feature/mypage/src/main/java/com/umcspot/spot/mypage/MypageScreen.kt
@@ -0,0 +1,27 @@
+package com.umcspot.spot.mypage
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+
+@Composable
+fun MypageScreen() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.White),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = "mypage Screen",
+ color = Color.Black
+ )
+ }
+}
\ No newline at end of file
diff --git a/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt b/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt
new file mode 100644
index 00000000..976361a4
--- /dev/null
+++ b/feature/mypage/src/main/java/com/umcspot/spot/mypage/navigation/MypageNavigation.kt
@@ -0,0 +1,22 @@
+package com.umcspot.spot.mypage.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.umcspot.spot.mypage.MypageScreen
+import com.umcspot.spot.navigation.MainTabRoute
+import kotlinx.serialization.Serializable
+
+fun NavController.navigateToMypage(navOptions: NavOptions? = null) {
+ navigate(Mypage, navOptions)
+}
+
+fun NavGraphBuilder.mypageGraph() {
+ composable {
+ MypageScreen()
+ }
+}
+
+@Serializable
+data object Mypage : MainTabRoute
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index fbf747f8..8e33a70e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,32 +1,271 @@
[versions]
-agp = "8.9.1"
-kotlin = "2.0.21"
-coreKtx = "1.17.0"
+# SDK Versions
+accompanistInsets = "0.24.13-rc"
+accompanistPermissions = "0.34.0"
+awsAndroidSdkMobileClient = "2.13.5"
+compileSdk = "35"
+lightcompressor = "1.3.2"
+minSdk = "28"
+targetSdk = "35"
+jdkVersion = "17"
+
+# App Versioning
+versionName = "1.0.0"
+versionCode = "1"
+
+# Android Plugin Versions
+android-gradle-plugin = "8.6.0"
+ksp = "2.0.0-1.0.22"
+
+# Formatting Plugin Versions
+ktlint = "11.6.1"
+detekt = "1.23.6"
+
+# Kotlin Versions
+kotlin = "2.0.0"
+kotlinx-coroutines = "1.8.1"
+kotlinx-serialization-json = "1.8.0"
+kotlinx-datetime = "0.6.0"
+kotlinx-collections = "0.3.7"
+
+# Test Libraries Versions
junit = "4.13.2"
-junitVersion = "1.3.0"
-espressoCore = "3.7.0"
-lifecycleRuntimeKtx = "2.9.2"
-activityCompose = "1.10.1"
-composeBom = "2024.09.00"
+mockito = "5.12.0"
+robolectric = "4.12.2"
+androidx-test-ext-junit = "1.2.1"
+androidx-test-runner = "1.6.1"
+androidx-test = "1.6.1"
+espresso-core = "3.6.1"
-[libraries]
-androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
-junit = { group = "junit", name = "junit", version.ref = "junit" }
-androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
-androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
-androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
-androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
-androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
-androidx-ui = { group = "androidx.compose.ui", name = "ui" }
-androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
-androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
-androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
-androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
-androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
-androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+# AndroidX Libraries Versions
+androidx-app-compat = "1.7.0"
+androidx-core = "1.13.1"
+androidx-lifecycle = "2.8.3"
+androidx-navigation = "2.8.2"
+androidx-constraint-layout = "2.1.4"
+androidx-activity-ktx = "1.9.0"
+androidx-fragment-ktx = "1.8.1"
+androidx-recycler-view = "1.3.2"
+androidx-splash-screen = "1.0.1"
+androidx-datastore = "1.1.1"
+androidx-paging = "3.3.0"
+androidx-paging-compose = "3.3.0"
+androidx-media3 = "1.3.1"
+androidx-splashscreen = "1.0.1"
+androix-room = "2.6.1"
+androidx-hilt-navigation-compose = "1.2.0"
+androidx-workmanager = "2.9.0"
+
+# Google Libraries Versions
+google-service = "4.4.2"
+material = "1.12.0"
+firebase-bom = "33.1.1"
+crashlytics = "3.0.2"
+google-location = "21.3.0"
+
+# Naver Maps Versions
+naverMapCompose = "1.8.2"
+naverMapLocation = "21.0.2"
+naverMapSdk = "3.22.0"
+
+# Compose Versions
+compose-compiler = "1.5.1"
+compose-bom = "2025.06.01"
+compose-material3 = "1.2.0-alpha07"
+activity-compose = "1.8.2"
+compose-stable-marker = "1.0.3"
+
+# Hilt Versions
+hilt = "2.51.1"
+hilt-androidx = "1.2.0"
+
+# Other Libraries Versions
+javax = "1"
+retrofit = "2.11.0"
+retrofit-kotlinx-serialization-json = "1.0.0"
+okhttp = "4.12.0"
+orbit = "8.0.0"
+timber = "5.0.1"
+coil = "2.7.0"
+lottie = "6.4.1"
+jsoup = "1.17.2"
+kakao-login = "2.19.0"
+process-pheonix = "3.0.0"
+preference = "1.2.1"
+collapsing-toolbar = "2.3.5"
+runtimeAndroid = "1.7.8"
+flexibleBottomSheet = "0.1.5"
[plugins]
-android-application = { id = "com.android.application", version.ref = "agp" }
+# Gradle Plugins
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
+android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
+android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
+google-service = { id = "com.google.gms.google-services", version.ref = "google-service" }
+navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "androidx-navigation" }
+android-test = { id = "com.android.test", version.ref = "android-gradle-plugin" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+
+# Custom Plugins
+spot-android-application = { id = "spot.android.application", version = "unspecified" }
+spot-android-build-config = { id = "spot.android.build.config", version = "unspecified" }
+spot-android-library = { id = "spot.android.library", version = "unspecified" }
+spot-android-compose-library = { id = "spot.android.compose.library", version = "unspecified" }
+spot-android-java-library = { id = "spot.android.java.library", version = "unspecified" }
+spot-android-test = { id = "spot.android.test", version = "unspecified" }
+spot-android-unitTest = { id = "spot.android.unittest", version = "unspecified" }
+spot-android-hilt = { id = "spot.android.hilt", version = "unspecified" }
+spot-feature = { id = "spot.feature", version = "unspecified" }
+spot-data = { id = "spot.data", version = "unspecified"}
+
+[libraries]
+# Android Gradle Plugin
+android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android-gradle-plugin" }
+kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
+ksp-gradle-plugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
+
+# Kotlin Extensions and Libraries
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
+kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
+kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
+kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
+kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" }
+kotlinx-collections = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections" }
+
+# Testing Libraries
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+lightcompressor = { module = "com.github.AbedElazizShe:LightCompressor", version.ref = "lightcompressor" }
+mockito = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
+robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
+androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
+androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidx-test-runner" }
+androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidx-test" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
+
+# AndroidX Libraries
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-app-compat" }
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraint-layout" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
+androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
+androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "androidx-activity-ktx" }
+androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment-ktx" }
+androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recycler-view" }
+androidx-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidx-splash-screen" }
+androidx-paging = { group = "androidx.paging", name = "paging-runtime", version.ref = "androidx-paging" }
+androidx-paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "androidx-paging" }
+androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging-compose" }
+androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "androidx-media3" }
+androidx-media3-exoplayer-dash = { group = "androidx.media3", name = "media3-exoplayer-dash", version.ref = "androidx-media3" }
+androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "androidx-media3" }
+navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "androidx-navigation" }
+navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "androidx-navigation" }
+androidx-datastore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidx-datastore" }
+androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidx-datastore" }
+androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androix-room" }
+androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androix-room" }
+androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androix-room" }
+androidx-workmanager = { group = "androidx.work", name = "work-runtime", version.ref = "androidx-workmanager" }
+
+# Compose Libraries
+compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
+ui = { group = "androidx.compose.ui", name = "ui" }
+ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+ui-foundation = { group = "androidx.compose.foundation", name = "foundation" }
+material3-compose = { group = "androidx.compose.material3", name = "material3" }
+coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
+coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
+coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" }
+activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-ktx" }
+lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
+navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" }
+hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidx-hilt-navigation-compose" }
+compose-compiler-extension = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
+
+# Hilt
+hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
+hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
+hilt-core = { group = "com.google.dagger", name = "hilt-core", version.ref = "hilt" }
+hilt-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" }
+hilt-testing-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
+hilt_androidx_common = { module = "androidx.hilt:hilt-common", version.ref = "hilt_androidx" }
+hilt_androidx_work = { module = "androidx.hilt:hilt-work", version.ref = "hilt_androidx" }
+hilt_androidx_compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt_androidx" }
+javax-inject = { group = "javax.inject", name = "javax.inject", version.ref = "javax" }
+
+# Orbit
+orbit-core = { group = "org.orbit-mvi", name = "orbit-core", version.ref = "orbit" }
+orbit-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = "orbit" }
+orbit-viewmodel = { group = "org.orbit-mvi", name = "orbit-viewmodel", version.ref = "orbit" }
+
+# Retrofit
+retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofit-kotlinx-serialization-json" }
+okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
+
+# Google Libraries
+google-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+crashlytics-plugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version.ref = "crashlytics" }
+firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
+firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" }
+firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" }
+firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" }
+firebase-remoteConfig = { group = "com.google.firebase", name = "firebase-config-ktx" }
+firebase-database = { group = "com.google.firebase", name = "firebase-database-ktx" }
+accompanist-insets = { module = "com.google.accompanist:accompanist-insets", version.ref = "accompanistInsets" }
+accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
+accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistPermissions" }
+google-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "google-location" }
+
+# Naver Libraries
+naver-map-compose = { group = "io.github.fornewid", name = "naver-map-compose", version.ref = "naverMapCompose" }
+naver-map-location = { group = "io.github.fornewid", name = "naver-map-location", version.ref = "naverMapLocation" }
+naver-map-sdk = { group = "com.naver.maps", name = "map-sdk", version.ref = "naverMapSdk" }
+
+# AWS
+aws-android-sdk-cognito = { module = "com.amazonaws:aws-android-sdk-cognito", version.ref = "awsAndroidSdkMobileClient" }
+aws-android-sdk-mobile-client = { module = "com.amazonaws:aws-android-sdk-mobile-client", version.ref = "awsAndroidSdkMobileClient" }
+aws-android-sdk-s3 = { module = "com.amazonaws:aws-android-sdk-s3", version.ref = "awsAndroidSdkMobileClient" }
+
+# Timber
+timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
+
+# formatting
+detekt-plugin-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
+itlab-detekt-plugin-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-plugin-formatting", version.ref = "detekt" }
+
+# Miscellaneous Libraries
+coil = { group = "io.coil-kt", name = "coil", version.ref = "coil" }
+lottie = { group = "com.airbnb.android", name = "lottie", version.ref = "lottie" }
+lottie-compose = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" }
+jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" }
+kakao-login = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-login" }
+process-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "process-pheonix" }
+collapsing-toolbar = { group = "me.onebone", name = "toolbar-compose", version.ref = "collapsing-toolbar" }
+androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
+flexible-bottomsheet = { group = "com.github.skydoves", name = "flexible-bottomsheet-material3", version.ref = "flexibleBottomSheet" }
+[bundles]
+coil = ["coil", "coil-svg", "coil-gif"]
+firebase = ["firebase-analytics", "firebase-database", "firebase-messaging", "firebase-remoteConfig"]
+androidx-lifecycle = ["androidx-lifecycle-runtime-ktx", "androidx-lifecycle-viewmodel-ktx"]
+androidx-navigation = ["navigation-fragment-ktx", "navigation-ui-ktx"]
+coroutine = ["kotlinx-coroutines-android", "kotlinx-coroutines-core"]
+datastore = ["androidx-datastore-core", "androidx-datastore-preferences"]
+compose = ["ui", "ui-graphics", "ui-tooling-preview", "material3-compose", "coil-compose", "ui-foundation", "activity-compose", "lifecycle-compose", "navigation-compose", "hilt-navigation-compose"]
+compose-debug = ["ui-tooling", "ui-test-manifest"]
+media3 = ["androidx-media3-exoplayer", "androidx-media3-exoplayer-dash", "androidx-media3-ui"]
+accompanist = ["accompanist-insets", "accompanist-permissions", "accompanist-systemuicontroller"]
+aws = ["aws-android-sdk-s3", "aws-android-sdk-cognito", "aws-android-sdk-mobile-client"]
+orbit = ["orbit-core", "orbit-viewmodel", "orbit-compose"]
+naver-maps = ["naver-map-compose", "naver-map-location", "google-location", ]
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index ad2c20cd..cd8a755e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,4 +1,6 @@
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
+ includeBuild("build-logic")
repositories {
google {
content {
@@ -21,4 +23,16 @@ dependencyResolutionManagement {
rootProject.name = "SPOT"
include(":app")
-
\ No newline at end of file
+include(":core:designsystem")
+include(":core:buildconfig")
+include(":core:common")
+include(":core:model")
+include(":core:navigation")
+include(":core:network")
+include(":data:home")
+include(":domain:home")
+include(":feature:home")
+include(":core:ui")
+include(":feature:main")
+include(":feature:mypage")
+include(":core:datastore")