diff --git a/app/src/main/java/org/sopt/and/api/ApiFactory.kt b/app/src/main/java/org/sopt/and/api/ApiFactory.kt index 8da299d..99d6251 100644 --- a/app/src/main/java/org/sopt/and/api/ApiFactory.kt +++ b/app/src/main/java/org/sopt/and/api/ApiFactory.kt @@ -6,6 +6,7 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.sopt.and.BuildConfig +import org.sopt.and.api.service.HobbyService import org.sopt.and.api.service.LoginService import org.sopt.and.api.service.UserService import retrofit2.Retrofit @@ -35,4 +36,5 @@ object ApiFactory { object ServicePool { val userService = ApiFactory.create() val loginService = ApiFactory.create() + val hobbyService = ApiFactory.create() } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/api/dto/HobbyDto.kt b/app/src/main/java/org/sopt/and/api/dto/HobbyDto.kt new file mode 100644 index 0000000..ba29b3d --- /dev/null +++ b/app/src/main/java/org/sopt/and/api/dto/HobbyDto.kt @@ -0,0 +1,16 @@ +package org.sopt.and.api.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseHobbySuccessDto( + @SerialName("result") + val result: HobbyData +) + +@Serializable +data class HobbyData( + @SerialName("hobby") + val hobby: String +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/api/service/HobbyService.kt b/app/src/main/java/org/sopt/and/api/service/HobbyService.kt new file mode 100644 index 0000000..530a0f4 --- /dev/null +++ b/app/src/main/java/org/sopt/and/api/service/HobbyService.kt @@ -0,0 +1,13 @@ +package org.sopt.and.api.service + +import okhttp3.ResponseBody +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Header + +interface HobbyService { + @GET("/user/my-hobby") + fun getMyHobby( + @Header("token") token: String + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/navigate/NavGraph.kt b/app/src/main/java/org/sopt/and/navigate/NavGraph.kt index 6f27d30..0c4c5eb 100644 --- a/app/src/main/java/org/sopt/and/navigate/NavGraph.kt +++ b/app/src/main/java/org/sopt/and/navigate/NavGraph.kt @@ -17,6 +17,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState import org.sopt.and.component.bar.BottomBar import org.sopt.and.ui.home.HomeScreen import org.sopt.and.ui.my.MyScreen +import org.sopt.and.ui.my.MyViewModel import org.sopt.and.ui.search.SearchScreen import org.sopt.and.ui.signin.SignInScreen import org.sopt.and.ui.signin.SignInViewModel @@ -27,6 +28,7 @@ import org.sopt.and.ui.signup.SignUpViewModel fun NavGraph(navController: NavHostController) { val signUpViewModel: SignUpViewModel = viewModel() val signInViewModel: SignInViewModel = viewModel() + val myViewModel: MyViewModel = viewModel() val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route @@ -65,7 +67,12 @@ fun NavGraph(navController: NavHostController) { signUpViewModel = signUpViewModel ) } - composable(Routes.My.route) { MyScreen(navController) } + composable(Routes.My.route) { + MyScreen( + navController, + myViewModel = myViewModel + ) + } composable(Routes.Search.route) { SearchScreen(navController) } composable(Routes.Home.route) { HomeScreen(navController) } } diff --git a/app/src/main/java/org/sopt/and/ui/my/MyScreen.kt b/app/src/main/java/org/sopt/and/ui/my/MyScreen.kt index 66eb13c..001adc2 100644 --- a/app/src/main/java/org/sopt/and/ui/my/MyScreen.kt +++ b/app/src/main/java/org/sopt/and/ui/my/MyScreen.kt @@ -15,6 +15,10 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -28,15 +32,22 @@ import androidx.navigation.NavHostController import org.sopt.and.R import org.sopt.and.component.BuyTextButton import org.sopt.and.component.MyPageItem -import org.sopt.and.ui.signin.SignInViewModel @Composable fun MyScreen( - navController: NavHostController + navController: NavHostController, + myViewModel: MyViewModel ) { val context = LocalContext.current - val sharedPreferences = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE) - val token = sharedPreferences.getString("token", "") + val sharedPreferences = remember { + context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE) + } + + LaunchedEffect(Unit) { + myViewModel.getUserHobby(sharedPreferences) + } + + val hobby by myViewModel.hobby.collectAsState() Column( modifier = Modifier.fillMaxSize() @@ -62,7 +73,7 @@ fun MyScreen( contentScale = ContentScale.Fit ) Text( - text = stringResource(R.string.my_nickname, token!!), + text = stringResource(R.string.my_nickname, hobby), color = Color.White, modifier = Modifier.weight(1f) ) diff --git a/app/src/main/java/org/sopt/and/ui/my/MyViewModel.kt b/app/src/main/java/org/sopt/and/ui/my/MyViewModel.kt new file mode 100644 index 0000000..5fdd17d --- /dev/null +++ b/app/src/main/java/org/sopt/and/ui/my/MyViewModel.kt @@ -0,0 +1,58 @@ +package org.sopt.and.ui.my + +import android.content.SharedPreferences +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json +import okhttp3.ResponseBody +import org.sopt.and.api.ServicePool +import org.sopt.and.api.dto.ResponseErrorDto +import org.sopt.and.api.dto.ResponseHobbySuccessDto +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class MyViewModel(): ViewModel() { + private val hobbyService = ServicePool.hobbyService + + private val _hobby = MutableStateFlow("") + val hobby: StateFlow get() = _hobby + + fun getUserHobby(sharedPreferences: SharedPreferences) { + val token = sharedPreferences.getString("token", null) + if (token.isNullOrEmpty()) { + Log.e("MyViewModel", "토큰 없음") + return + } + + hobbyService.getMyHobby(token).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + val successBody = response.body()?.string() + val successDto = Json.decodeFromString(successBody ?: "") + viewModelScope.launch { + _hobby.emit(successDto.result.hobby) + } + } else { + val errorBody = response.errorBody()?.string() + val errorDto = errorBody?.let { Json.decodeFromString(it) } + val errorMessage = when (response.code()) { + 401 -> "토큰이 없습니다." + 403 -> "유효하지 않은 토큰입니다." + 404 -> "잘못된 경로로 요청했습니다." + else -> "알 수 없는 오류가 발생했습니다." + } + Log.e("MyViewModel", errorMessage) + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.e("MyViewModel", "Network error: ${t.message}") + } + }) + } +} \ No newline at end of file