Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import java.util.Properties

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
kotlin("plugin.serialization")
alias(libs.plugins.kotlin.serialization)
}

val properties = Properties().apply {
load(project.rootProject.file("local.properties").inputStream())
}


android {
namespace = "com.sopt.dive"
compileSdk = 36
Expand All @@ -17,6 +24,8 @@ android {
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

buildConfigField("String", "BASE_URL", "\"${properties["base.url"]}\"")
}

buildTypes {
Expand All @@ -37,6 +46,7 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
}

Expand All @@ -53,5 +63,8 @@ dependencies {
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.core)
implementation(libs.retrofit.kotlin.serialization)
implementation(libs.okhttp.logging)
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Dive"
tools:targetApi="31">
tools:targetApi="31"
android:usesCleartextTraffic="true">
<activity
android:name=".feature.main.MainActivity"
android:exported="true"
Expand Down
50 changes: 50 additions & 0 deletions app/src/main/java/com/sopt/dive/core/common/di/ServiceLocator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.sopt.dive.core.common.di

import android.content.Context
import com.sopt.dive.core.data.datasource.FakeAuthDataSource
import com.sopt.dive.core.data.repository.AuthRepositoryImpl
import com.sopt.dive.core.domain.repository.AuthRepository
import com.sopt.dive.core.domain.usecase.LoginUseCase

/**
* ServiceLocator - 수동 의존성 주입 (Hilt 대체)
*
* Service Locator 패턴이란?
* - 객체 생성과 의존성 주입을 중앙에서 관리
* - 싱글톤 객체를 제공
* - Dagger/Hilt 없이 간단한 DI 구현
*/
object ServiceLocator {

private var context: Context? = null

private val fakeAuthDataSource: FakeAuthDataSource by lazy {
FakeAuthDataSource() // ✅ Fake 사용 중
}

private val authRepository: AuthRepository by lazy {
AuthRepositoryImpl(dataSource = fakeAuthDataSource)
}

private val loginUseCase: LoginUseCase by lazy {
LoginUseCase(authRepository = authRepository)
}

fun init(appContext: Context) {
this.context = appContext.applicationContext
}

fun provideContext(): Context {
return context ?: throw IllegalStateException("ServiceLocator not initialized")
}

fun provideAuthRepository(): AuthRepository = authRepository

fun provideLoginUseCase(): LoginUseCase = loginUseCase

fun provideFakeAuthDataSource(): FakeAuthDataSource = fakeAuthDataSource

fun reset() {
fakeAuthDataSource.reset()
}
}
77 changes: 77 additions & 0 deletions app/src/main/java/com/sopt/dive/core/data/AuthPreferences.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.sopt.dive.core.data

import android.content.Context
import android.content.SharedPreferences

/**
* 로그인 상태만 관리하는 간단한 클래스
* 사용자 정보는 모두 API에서 가져옴
*/
class AuthPreferences private constructor(context: Context) {

private val prefs: SharedPreferences = context.getSharedPreferences(
"auth_prefs",
Context.MODE_PRIVATE
)

companion object {
private const val KEY_IS_LOGGED_IN = "is_logged_in"
private const val KEY_USER_ID = "user_id"
private const val KEY_USERNAME = "username"
private const val KEY_PASSWORD = "password"

@Volatile
private var instance: AuthPreferences? = null

fun getInstance(context: Context): AuthPreferences {
return instance ?: synchronized(this) {
instance ?: AuthPreferences(context.applicationContext).also {
instance = it
}
}
}
}

/**
* 로그인 성공 시 필요한 정보만 저장
*/
fun saveLoginInfo(userId: Int, username: String, password: String) {
prefs.edit()
.putBoolean(KEY_IS_LOGGED_IN, true)
.putInt(KEY_USER_ID, userId)
.putString(KEY_USERNAME, username)
.putString(KEY_PASSWORD, password)
.apply()
}

/**
* 로그인 상태 확인
*/
fun isLoggedIn(): Boolean = prefs.getBoolean(KEY_IS_LOGGED_IN, false)

/**
* 자동 로그인용 정보 가져오기
*/
fun getLoginInfo(): LoginInfo? {
return if (isLoggedIn()) {
LoginInfo(
userId = prefs.getInt(KEY_USER_ID, 0),
username = prefs.getString(KEY_USERNAME, "") ?: "",
password = prefs.getString(KEY_PASSWORD, "") ?: ""
)
} else null
}

/**
* 로그아웃
*/
fun logout() {
prefs.edit().clear().apply()
}
}

data class LoginInfo(
val userId: Int,
val username: String,
val password: String
)
143 changes: 0 additions & 143 deletions app/src/main/java/com/sopt/dive/core/data/UserPreferences.kt

This file was deleted.

38 changes: 38 additions & 0 deletions app/src/main/java/com/sopt/dive/core/data/datasource/ApiFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.sopt.dive.core.data.datasource

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.sopt.dive.BuildConfig
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit

object ApiFactory {
private const val BASE_URL: String = BuildConfig.BASE_URL
// gradle 설정후, Clean & Build 하면 빨간줄 풀림

private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

private val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()

val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
}

inline fun <reified T> create(): T = retrofit.create(T::class.java)
}

object ServicePool {
val authService: AuthService by lazy {
ApiFactory.create<AuthService>()
}
}
Loading