Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT/YAF-000] 사용자 등록/조회 연결을 진행합니다. #112

Merged
merged 16 commits into from
Feb 12, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.yapp.common.navigation.destination.HomeDestination
import com.yapp.common.navigation.destination.OnboardingDestination
import com.yapp.common.navigation.destination.TopLevelDestination

class OrbitNavigator(
val navController: NavHostController,
) {
val startDestination = HomeDestination.Route.route
val startDestination = OnboardingDestination.Route.route

private val currentDestination: NavDestination?
@Composable get() = navController
Expand Down
46 changes: 46 additions & 0 deletions core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.yapp.datastore

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.longPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class UserPreferences @Inject constructor(
private val dataStore: DataStore<Preferences>,
) {
private object Keys {
val USER_ID = longPreferencesKey("user_id")
val ONBOARDING_COMPLETED = booleanPreferencesKey("onboarding_completed")
}

val userIdFlow: Flow<Long?> = dataStore.data
.map { preferences -> preferences[Keys.USER_ID] }

val onboardingCompletedFlow: Flow<Boolean> = dataStore.data
.map { preferences -> preferences[Keys.ONBOARDING_COMPLETED] ?: false }

suspend fun saveUserId(userId: Long) {
dataStore.edit { preferences ->
preferences[Keys.USER_ID] = userId
}
}

suspend fun setOnboardingCompleted() {
dataStore.edit { preferences ->
preferences[Keys.ONBOARDING_COMPLETED] = true
}
}

suspend fun clearUserData() {
dataStore.edit { preferences ->
preferences.remove(Keys.USER_ID)
preferences.remove(Keys.ONBOARDING_COMPLETED)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import com.yapp.datastore.token.AuthToken
import com.yapp.datastore.token.TokenDataSerializer
import dagger.Module
Expand All @@ -27,4 +29,13 @@ object DataStoreModule {
) {
context.dataStoreFile("token.json")
}

@Provides
@Singleton
fun providesPreferencesDataStore(
@ApplicationContext context: Context,
): DataStore<Preferences> =
PreferenceDataStoreFactory.create {
context.dataStoreFile("user_prefs.preferences_pb")
}
}
16 changes: 14 additions & 2 deletions core/network/src/main/java/com/yapp/network/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ object NetworkModule {
level = if (buildConfigFieldProvider.get().isDebug) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}

@Provides
@Singleton
fun provideJson(): Json = Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}

@Provides
@Singleton
@Auth
Expand Down Expand Up @@ -74,9 +82,13 @@ object NetworkModule {
@Provides
@Singleton
@NoneAuth
fun provideNoneAuthRetrofit(@NoneAuth okHttpClient: OkHttpClient, buildConfigFieldProvider: BuildConfigFieldProvider): Retrofit =
fun provideNoneAuthRetrofit(
@NoneAuth okHttpClient: OkHttpClient,
buildConfigFieldProvider: BuildConfigFieldProvider,
json: Json,
): Retrofit =
Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.baseUrl(buildConfigFieldProvider.get().baseUrl)
.client(okHttpClient)
.build()
Expand Down
1 change: 1 addition & 0 deletions data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ dependencies {
implementation(libs.retrofit.core)
implementation(libs.retrofit.kotlin.serialization)
implementation(libs.okhttp.logging)
implementation(libs.androidx.datastore)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yapp.data.remote.datasource

import com.yapp.data.remote.dto.request.SignUpRequest

interface SignUpDataSource {
suspend fun postSignUp(request: SignUpRequest): Result<Long>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.yapp.data.remote.datasource

import android.util.Log
import com.yapp.data.remote.dto.request.SignUpRequest
import com.yapp.data.remote.service.ApiService
import com.yapp.data.remote.utils.ApiError
import com.yapp.data.remote.utils.safeApiCall
import javax.inject.Inject

class SignUpDataSourceImpl @Inject constructor(
private val apiService: ApiService,
) : SignUpDataSource {

override suspend fun postSignUp(request: SignUpRequest): Result<Long> {
return safeApiCall {
val response = apiService.postSignUp(request)

if (response.isSuccessful) {
val rawResponse = response.body()?.string()?.trim() ?: ""
Log.d("SignUpDataSource", "서버 응답: $rawResponse")

if (rawResponse.isNotEmpty() && rawResponse.all { it.isDigit() }) {
rawResponse.toLong()
} else {
throw ApiError("예상치 못한 서버 응답 형식: $rawResponse")
}
} else {
throw ApiError("서버 오류: ${response.code()} - ${response.errorBody()?.string()}")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yapp.data.remote.datasource

import com.yapp.data.remote.dto.response.UserResponse

interface UserInfoDataSource {
suspend fun getUserInfo(userId: Long): Result<UserResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yapp.data.remote.datasource

import com.yapp.data.remote.dto.response.UserResponse
import com.yapp.data.remote.service.ApiService
import com.yapp.data.remote.utils.safeApiCall
import javax.inject.Inject

class UserInfoDataSourceImpl @Inject constructor(
private val apiService: ApiService,
) : UserInfoDataSource {
override suspend fun getUserInfo(userId: Long): Result<UserResponse> {
return safeApiCall { apiService.getUserInfo(userId) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p3]
이 친구는 예외처리 안한 이유가??

Copy link
Member Author

@MoonsuKang MoonsuKang Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 좋아하는 명언이 있습니다.
"예외처리는 성능때문에 뺐다" 라는 말인데요. 그래서 저도 뺐습니다. 라고하면 안되겠죠~? ㅋㅋ
safeApiCall 내부에서 예외가 발생하면 Result.failure(exception)이 반환되는 구조라 예외처리를 안했는데. 생각해보니까
성공해도 body가 null로 넘어올때의 예외처리를 해야될 것 같네용 아닌가? null이 오는 경우가 없나요? 허,,

}
}
16 changes: 16 additions & 0 deletions data/src/main/java/com/yapp/data/remote/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package com.yapp.data.remote.di

import com.yapp.data.remote.datasource.DummyDataSource
import com.yapp.data.remote.datasource.DummyDataSourceImpl
import com.yapp.data.remote.datasource.SignUpDataSource
import com.yapp.data.remote.datasource.SignUpDataSourceImpl
import com.yapp.data.remote.datasource.UserInfoDataSource
import com.yapp.data.remote.datasource.UserInfoDataSourceImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -16,4 +20,16 @@ abstract class DataSourceModule {
abstract fun bindsDummyDataSource(
dummyDataSource: DummyDataSourceImpl,
): DummyDataSource

@Binds
@Singleton
abstract fun bindsSignUpDataSource(
signUpDataSource: SignUpDataSourceImpl,
): SignUpDataSource

@Binds
@Singleton
abstract fun bindsUserInfoDataSource(
userInfoDataSource: UserInfoDataSourceImpl,
): UserInfoDataSource
}

This file was deleted.

35 changes: 35 additions & 0 deletions data/src/main/java/com/yapp/data/remote/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.yapp.data.remote.di

import com.yapp.data.remote.repositoryimpl.DummyRepositoryImpl
import com.yapp.data.remote.repositoryimpl.SignUpRepositoryImpl
import com.yapp.data.remote.repositoryimpl.UserInfoRepositoryImpl
import com.yapp.domain.repository.DummyRepository
import com.yapp.domain.repository.SignUpRepository
import com.yapp.domain.repository.UserInfoRepository
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 RepositoryModule {
@Binds
@Singleton
abstract fun bindsDummyRepository(
dummyRepository: DummyRepositoryImpl,
): DummyRepository

@Binds
@Singleton
abstract fun bindsSignUpRepository(
signUpRepository: SignUpRepositoryImpl,
): SignUpRepository

@Binds
@Singleton
abstract fun bindsUserInfoRepository(
userInfoRepository: UserInfoRepositoryImpl,
): UserInfoRepository
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.yapp.data.remote.di

import com.yapp.data.remote.service.ApiService
import com.yapp.data.remote.service.DummyService
import com.yapp.network.di.NoneAuth
import dagger.Module
Expand All @@ -11,9 +12,14 @@ import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DummyServiceModule {
object ServiceModule {
@Provides
@Singleton
fun providesDummyService(@NoneAuth retrofit: Retrofit): DummyService =
retrofit.create(DummyService::class.java)

@Provides
@Singleton
fun providesSignUpService(@NoneAuth retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.yapp.data.remote.dto.request

import kotlinx.serialization.Serializable

@Serializable
data class SignUpRequest(
val name: String,
val calendarType: String,
val birthDate: String,
val birthTime: String,
val gender: String,
) {
companion object {
fun fromState(name: String, calendarType: String, birthDate: String, birthTime: String, gender: String): SignUpRequest {
return SignUpRequest(
name = name,
calendarType = when (calendarType) {
"양력" -> "SOLAR"
"음력" -> "LUNAR"
else -> "UNKNOWN"
},
birthDate = birthDate,
birthTime = birthTime,
gender = when (gender) {
"남성" -> "MALE"
"여성" -> "FEMALE"
else -> "UNKNOWN"
},
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.yapp.data.remote.dto.response

import com.yapp.domain.model.User
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UserResponse(
@SerialName("id") val userId: Long,
val name: String,
val calendarType: String,
val birthDate: String,
val birthTime: String?,
val gender: String,

)

fun UserResponse.toDomain(): User {
return User(
id = userId,
name = name,
calendarType = when (calendarType) {
"LUNAR" -> "음력"
"SOLAR" -> "양력"
else -> "알 수 없음"
},
birthDate = birthDate.toFormattedDate(),
birthTime = birthTime ?: "시간모름",
gender = when (gender) {
"MALE" -> "남"
"FEMALE" -> "여"
else -> "알 수 없음"
},
)
}

private fun String.toFormattedDate(): String {
val parts = this.split("-") // "2000-01-01" → ["2000", "01", "01"]
val year = parts[0] + "년"
val month = parts[1].toInt().toString() + "월"
val day = parts[2].toInt().toString() + "일"
return "$year $month $day"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.yapp.data.remote.repositoryimpl

import android.util.Log
import com.yapp.data.remote.datasource.SignUpDataSource
import com.yapp.data.remote.dto.request.SignUpRequest
import com.yapp.domain.repository.SignUpRepository
import javax.inject.Inject

class SignUpRepositoryImpl @Inject constructor(
private val signUpDataSource: SignUpDataSource,
) : SignUpRepository {

override suspend fun postSignUp(
name: String,
calendarType: String,
birthDate: String,
birthTime: String,
gender: String,
): Result<Long> {
val request = SignUpRequest.fromState(name, calendarType, birthDate, birthTime, gender)

return signUpDataSource.postSignUp(request).map { userId ->
Log.d("SignUpRepository", "회원가입 성공! userId=$userId")
userId
}
}
}
Loading
Loading