From bf650a86c336625a6b23d2a3d8c583bbc66051c7 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:16:05 +0900 Subject: [PATCH 1/8] refactor: migrate kotlinx-serialization --- app/build.gradle.kts | 8 +-- .../dto/request/ChangeNicknameRequest.kt | 5 +- .../dto/request/CheckValidTokenRequest.kt | 5 +- .../dto/request/LoginWithKakaoRequest.kt | 10 ++-- .../remote/dto/request/ModifyReviewRequest.kt | 15 +++--- .../data/remote/dto/request/ReportRequest.kt | 10 ++-- .../dto/request/UserDepartmentRequest.kt | 3 ++ .../dto/request/WriteMealReviewRequest.kt | 21 ++++---- .../dto/request/WriteMenuReviewRequest.kt | 19 ++++--- .../data/remote/dto/response/BaseResponse.kt | 14 ++--- .../remote/dto/response/CollegeResponse.kt | 10 ++-- .../remote/dto/response/DepartmentResponse.kt | 10 ++-- .../data/remote/dto/response/ImageResponse.kt | 8 +-- .../data/remote/dto/response/MealResponse.kt | 17 +++--- .../dto/response/MealReviewInfoResponse.kt | 25 +++++---- .../dto/response/MealReviewListResponse.kt | 38 ++++++++------ .../remote/dto/response/MenuOfMealResponse.kt | 11 ++-- .../data/remote/dto/response/MenuResponse.kt | 20 ++++--- .../dto/response/MenuReviewInfoResponse.kt | 25 +++++---- .../dto/response/MenuReviewListResponse.kt | 38 ++++++++------ .../remote/dto/response/MyNickNameResponse.kt | 8 +-- .../dto/response/MyReviewListResponse.kt | 30 ++++++----- .../dto/response/PartnershipResponse.kt | 52 +++++++++---------- .../response/PartnershipRestaurantResponse.kt | 45 +++++++--------- .../data/remote/dto/response/TokenResponse.kt | 10 ++-- .../response/UserCollegeDepartmentResponse.kt | 14 ++--- .../FirebaseRemoteConfigRepositoryImpl.kt | 25 +++------ .../com/eatssu/android/di/NetworkModule.kt | 20 +++++-- .../android/di/network/ApiResultCall.kt | 20 +++---- .../di/network/ApiResultCallAdapter.kt | 7 +-- .../di/network/ApiResultCallAdapterFactory.kt | 6 +-- .../domain/model/PartnershipRestaurant.kt | 8 ++- .../android/domain/model/RestaurantInfo.kt | 4 +- .../android/domain/model/WidgetMealInfo.kt | 12 ++++- .../presentation/map/MapFragmentView.kt | 13 ++--- .../android/presentation/map/MapViewModel.kt | 23 ++++---- .../map/component/MapRestaurantBottomSheet.kt | 11 ++-- .../presentation/map/model/PlaceType.kt | 9 ---- .../widget/MealInfoStateDefinition.kt | 46 +++------------- build.gradle.kts | 1 + core/common/build.gradle.kts | 3 ++ .../com/eatssu/common/enums/Restaurant.kt | 9 ++-- .../java/com/eatssu/common/enums/StoreType.kt | 10 ++++ .../main/java/com/eatssu/common/enums/Time.kt | 6 --- gradle/libs.versions.toml | 9 ++-- 45 files changed, 370 insertions(+), 343 deletions(-) delete mode 100644 app/src/main/java/com/eatssu/android/presentation/map/model/PlaceType.kt create mode 100644 core/common/src/main/java/com/eatssu/common/enums/StoreType.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0036a99dd..28c3ccf1e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,6 +3,7 @@ import java.util.Properties plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) alias(libs.plugins.compose.compiler) alias(libs.plugins.google.services) alias(libs.plugins.firebase.crashlytics) @@ -185,10 +186,7 @@ dependencies { //retrofit2: 서버통신 implementation(libs.retrofit) - implementation(libs.retrofit.converter.gson) - // Gson for JSON parsing - implementation(libs.gson) //OkHttp: 통신 로그 확인하기 위함 implementation(libs.okhttp) @@ -265,6 +263,10 @@ dependencies { // Paging3 implementation(libs.androidx.paging.runtime) implementation(libs.androidx.paging.compose) + + // Kotlin Serialization + implementation(libs.kotlinx.serialization.json) + implementation(libs.retrofit.kotlinx.serialization.converter) } configurations.all { diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ChangeNicknameRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ChangeNicknameRequest.kt index d2c05eb49..05b3d776c 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ChangeNicknameRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ChangeNicknameRequest.kt @@ -1,5 +1,8 @@ package com.eatssu.android.data.remote.dto.request +import kotlinx.serialization.Serializable + +@Serializable data class ChangeNicknameRequest( val nickname: String, -) \ No newline at end of file +) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/CheckValidTokenRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/CheckValidTokenRequest.kt index 6ac07d306..4b8917549 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/CheckValidTokenRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/CheckValidTokenRequest.kt @@ -1,5 +1,8 @@ package com.eatssu.android.data.remote.dto.request +import kotlinx.serialization.Serializable + +@Serializable data class CheckValidTokenRequest( val token: String, -) \ No newline at end of file +) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/LoginWithKakaoRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/LoginWithKakaoRequest.kt index 27a0dbd06..cdb9d22a2 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/LoginWithKakaoRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/LoginWithKakaoRequest.kt @@ -1,15 +1,17 @@ package com.eatssu.android.data.remote.dto.request import com.eatssu.common.enums.DeviceType -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class LoginWithKakaoRequest( - @SerializedName("email") + @SerialName("email") val email: String, - @SerializedName("providerId") + @SerialName("providerId") val providerId: String, - @SerializedName("deviceType") + @SerialName("deviceType") val deviceType: DeviceType, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ModifyReviewRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ModifyReviewRequest.kt index 3ed94c53f..7fcb2f0c0 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ModifyReviewRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ModifyReviewRequest.kt @@ -1,16 +1,19 @@ package com.eatssu.android.data.remote.dto.request -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class ModifyReviewRequest( - @SerializedName("rating") val rating: Int? = null, - @SerializedName("menuLikes") val menuLikes: List = arrayListOf(), - @SerializedName("content") val content: String? = null + @SerialName("rating") val rating: Int? = null, + @SerialName("menuLikes") val menuLikes: List = arrayListOf(), + @SerialName("content") val content: String? = null ) { + @Serializable data class MenuLikes( - @SerializedName("menuId") val menuId: Long? = null, - @SerializedName("isLike") val isLike: Boolean? = null + @SerialName("menuId") val menuId: Long? = null, + @SerialName("isLike") val isLike: Boolean? = null ) } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ReportRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ReportRequest.kt index 18d013e74..8ccf2d64a 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/ReportRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/ReportRequest.kt @@ -1,14 +1,16 @@ package com.eatssu.android.data.remote.dto.request -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class ReportRequest( - @SerializedName("reviewId") + @SerialName("reviewId") val reviewId: Long, - @SerializedName("reportType") + @SerialName("reportType") val reportType: String, - @SerializedName("content") + @SerialName("content") val content: String, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/UserDepartmentRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/UserDepartmentRequest.kt index 5fd3ec56c..4afa9aeda 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/UserDepartmentRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/UserDepartmentRequest.kt @@ -1,5 +1,8 @@ package com.eatssu.android.data.remote.dto.request +import kotlinx.serialization.Serializable + +@Serializable data class UserDepartmentRequest( val departmentId: Int ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMealReviewRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMealReviewRequest.kt index 608c8c64e..8b977ee31 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMealReviewRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMealReviewRequest.kt @@ -1,17 +1,20 @@ package com.eatssu.android.data.remote.dto.request -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable //별점은 필수 값 나머지는 옵션 +@Serializable data class WriteMealReviewRequest( - @SerializedName("mealId") val mealId: Long, - @SerializedName("rating") val rating: Int, - @SerializedName("menuLikes") val menuLikes: List?, - @SerializedName("content") val content: String, - @SerializedName("imageUrls") val imageUrls: List + @SerialName("mealId") val mealId: Long, + @SerialName("rating") val rating: Int, + @SerialName("menuLikes") val menuLikes: List?, + @SerialName("content") val content: String, + @SerialName("imageUrls") val imageUrls: List ) { + @Serializable data class MenuLikes( - @SerializedName("menuId") val menuId: Long, - @SerializedName("isLike") val isLike: Boolean + @SerialName("menuId") val menuId: Long, + @SerialName("isLike") val isLike: Boolean ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMenuReviewRequest.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMenuReviewRequest.kt index d0f269f59..c2ad65a8f 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMenuReviewRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/request/WriteMenuReviewRequest.kt @@ -1,16 +1,19 @@ package com.eatssu.android.data.remote.dto.request -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable //별점은 필수 값 나머지는 옵션 +@Serializable data class WriteMenuReviewRequest( - @SerializedName("rating") val rating: Int, - @SerializedName("menuLike") val menuLike: MenuLike?, - @SerializedName("content") val content: String, - @SerializedName("imageUrls") val imageUrls: List, + @SerialName("rating") val rating: Int, + @SerialName("menuLike") val menuLike: MenuLike?, + @SerialName("content") val content: String, + @SerialName("imageUrls") val imageUrls: List, ) { + @Serializable data class MenuLike( - @SerializedName("menuId") val menuId: Long, - @SerializedName("isLike") val isLike: Boolean + @SerialName("menuId") val menuId: Long, + @SerialName("isLike") val isLike: Boolean ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt index dc1dff8f0..aabaa0632 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt @@ -1,10 +1,12 @@ package com.eatssu.android.data.remote.dto.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class BaseResponse( - @SerializedName("isSuccess") var isSuccess: Boolean? = null, - @SerializedName("code") var code: Int? = null, - @SerializedName("message") var message: String? = null, - @SerializedName("result") var result: T? = null, -) \ No newline at end of file + @SerialName("isSuccess") var isSuccess: Boolean? = null, + @SerialName("code") var code: Int? = null, + @SerialName("message") var message: String? = null, + @SerialName("result") var result: T? = null, +) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt index 8614cb69c..e91a06c71 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt @@ -1,12 +1,14 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.College -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class CollegeResponse( - @SerializedName("id") + @SerialName("id") val collegeId: Int?, - @SerializedName("name") + @SerialName("name") val collegeName: String? ) @@ -15,4 +17,4 @@ fun CollegeResponse.toDomain(): College? { val id = collegeId ?: return null val name = collegeName ?: return null return College(collegeId = id, collegeName = name) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt index 976ac558c..ce43dd8dc 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt @@ -1,12 +1,14 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Department -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class DepartmentResponse( - @SerializedName("id") + @SerialName("id") val departmentId: Int?, - @SerializedName("name") + @SerialName("name") val departmentName: String?, ) @@ -15,4 +17,4 @@ fun DepartmentResponse.toDomain(): Department? { val id = departmentId ?: return null val name = departmentName ?: return null return Department(departmentId = id, departmentName = name) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/ImageResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/ImageResponse.kt index 6b7b9d7f8..1d8bf3793 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/ImageResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/ImageResponse.kt @@ -1,7 +1,9 @@ package com.eatssu.android.data.remote.dto.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class ImageResponse( - @SerializedName("url") val url: String? = null, -) \ No newline at end of file + @SerialName("url") val url: String? = null, +) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt index 797a39186..8769cf87f 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt @@ -1,20 +1,23 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Menu -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable private const val MENU_SEPARATOR = ", " +@Serializable data class GetMealResponse( - @SerializedName("mealId") var mealId: Long? = null, - @SerializedName("price") var price: Int? = null, - @SerializedName("rating") var rating: Double? = null, - @SerializedName("briefMenus") var briefMenus: List = emptyList(), + @SerialName("mealId") var mealId: Long? = null, + @SerialName("price") var price: Int? = null, + @SerialName("rating") var rating: Double? = null, + @SerialName("briefMenus") var briefMenus: List = emptyList(), ) +@Serializable data class MenusInformationList( - @SerializedName("menuId") var menuId: Long? = null, - @SerializedName("name") var name: String? = null, + @SerialName("menuId") var menuId: Long? = null, + @SerialName("name") var name: String? = null, ) fun List.mapTodayMenuResponseToMenu(): List { diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewInfoResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewInfoResponse.kt index 24faca6ec..e3457cca7 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewInfoResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewInfoResponse.kt @@ -1,22 +1,25 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.ReviewInfo -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlin.math.round +@Serializable data class MealReviewInfoResponse( - @SerializedName("menuNames") val menuNames: List? = null, - @SerializedName("totalReviewCount") val totalReviewCount: Int? = null, - @SerializedName("rating") val rating: Double? = null, - @SerializedName("likeCount") val likeCount: Int? = null, - @SerializedName("reviewRatingCount") val reviewRatingCount: ReviewRatingCount? = ReviewRatingCount(), + @SerialName("menuNames") val menuNames: List? = null, + @SerialName("totalReviewCount") val totalReviewCount: Int? = null, + @SerialName("rating") val rating: Double? = null, + @SerialName("likeCount") val likeCount: Int? = null, + @SerialName("reviewRatingCount") val reviewRatingCount: ReviewRatingCount? = ReviewRatingCount(), ) { + @Serializable data class ReviewRatingCount( - @SerializedName("oneStarCount") val oneStarCount: Int? = null, - @SerializedName("twoStarCount") val twoStarCount: Int? = null, - @SerializedName("threeStarCount") val threeStarCount: Int? = null, - @SerializedName("fourStarCount") val fourStarCount: Int? = null, - @SerializedName("fiveStarCount") val fiveStarCount: Int? = null, + @SerialName("oneStarCount") val oneStarCount: Int? = null, + @SerialName("twoStarCount") val twoStarCount: Int? = null, + @SerialName("threeStarCount") val threeStarCount: Int? = null, + @SerialName("fourStarCount") val fourStarCount: Int? = null, + @SerialName("fiveStarCount") val fiveStarCount: Int? = null, ) } diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewListResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewListResponse.kt index f3560c176..b33adb81d 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewListResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealReviewListResponse.kt @@ -1,28 +1,32 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Review -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class MealReviewListResponse( - @SerializedName("numberOfElements") val numberOfElements: Int? = null, - @SerializedName("hasNext") val hasNext: Boolean? = null, - @SerializedName("dataList") val dataList: List = arrayListOf() + @SerialName("numberOfElements") val numberOfElements: Int? = null, + @SerialName("hasNext") val hasNext: Boolean? = null, + @SerialName("dataList") val dataList: List = arrayListOf() ) { + @Serializable data class DataList( - @SerializedName("reviewId") val reviewId: Long? = null, - @SerializedName("menuList") val menuList: List = arrayListOf(), - @SerializedName("writerId") val writerId: Long? = null, - @SerializedName("isWriter") val isWriter: Boolean? = null, - @SerializedName("writerNickname") val writerNickname: String? = null, - @SerializedName("rating") val rating: Int? = null, - @SerializedName("writtenAt") val writtenAt: String? = null, - @SerializedName("content") val content: String? = null, - @SerializedName("imageUrls") val imageUrls: List = arrayListOf(), + @SerialName("reviewId") val reviewId: Long? = null, + @SerialName("menuList") val menuList: List = arrayListOf(), + @SerialName("writerId") val writerId: Long? = null, + @SerialName("isWriter") val isWriter: Boolean? = null, + @SerialName("writerNickname") val writerNickname: String? = null, + @SerialName("rating") val rating: Int? = null, + @SerialName("writtenAt") val writtenAt: String? = null, + @SerialName("content") val content: String? = null, + @SerialName("imageUrls") val imageUrls: List = arrayListOf(), ) { + @Serializable data class MenuList( - @SerializedName("id") val id: Long? = null, - @SerializedName("name") val name: String? = null, - @SerializedName("isLike") val isLike: Boolean? = null, + @SerialName("id") val id: Long? = null, + @SerialName("name") val name: String? = null, + @SerialName("isLike") val isLike: Boolean? = null, ) } } @@ -48,4 +52,4 @@ fun MealReviewListResponse?.toDomain(): List { imgUrl = data.imageUrls.firstOrNull(), ) } ?: emptyList() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuOfMealResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuOfMealResponse.kt index d637423e8..093532989 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuOfMealResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuOfMealResponse.kt @@ -1,16 +1,19 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.MenuMini -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class MenuOfMealResponse( - @SerializedName("menuList") val menuList: ArrayList = arrayListOf() + @SerialName("menuList") val menuList: ArrayList = arrayListOf() ) +@Serializable data class MenuList( - @SerializedName("menuId") val menuId: Long? = null, - @SerializedName("name") val name: String? = null + @SerialName("menuId") val menuId: Long? = null, + @SerialName("name") val name: String? = null ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt index 352f9cd58..ceed27f85 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt @@ -1,29 +1,33 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Menu -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import timber.log.Timber +@Serializable data class GetFixedMenuResponse( - @SerializedName("categoryMenuListCollection") val categoryMenuListCollection: ArrayList = arrayListOf(), + @SerialName("categoryMenuListCollection") val categoryMenuListCollection: ArrayList = arrayListOf(), ) +@Serializable data class CategoryMenuListCollection( - @SerializedName("category") val category: String? = null, - @SerializedName("menus") val menus: ArrayList = arrayListOf(), + @SerialName("category") val category: String? = null, + @SerialName("menus") val menus: ArrayList = arrayListOf(), ) +@Serializable data class MenuInformationList( - @SerializedName("menuId") var menuId: Long? = null, - @SerializedName("name") val name: String? = null, - @SerializedName("price") val price: Int? = null, - @SerializedName("rating") val rating: Double? = null, + @SerialName("menuId") var menuId: Long? = null, + @SerialName("name") val name: String? = null, + @SerialName("price") val price: Int? = null, + @SerialName("rating") val rating: Double? = null, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewInfoResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewInfoResponse.kt index 518b960a4..88e9d8546 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewInfoResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewInfoResponse.kt @@ -1,22 +1,25 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.ReviewInfo -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlin.math.round +@Serializable data class MenuReviewInfoResponse( - @SerializedName("menuName") val menuName: String? = null, - @SerializedName("totalReviewCount") val totalReviewCount: Int? = null, - @SerializedName("rating") val rating: Double? = null, - @SerializedName("likeCount") val likeCount: Int? = null, - @SerializedName("reviewRatingCount") val reviewRatingCount: ReviewRatingCount? = ReviewRatingCount(), + @SerialName("menuName") val menuName: String? = null, + @SerialName("totalReviewCount") val totalReviewCount: Int? = null, + @SerialName("rating") val rating: Double? = null, + @SerialName("likeCount") val likeCount: Int? = null, + @SerialName("reviewRatingCount") val reviewRatingCount: ReviewRatingCount? = ReviewRatingCount(), ) { + @Serializable data class ReviewRatingCount( - @SerializedName("oneStarCount") val oneStarCount: Int? = null, - @SerializedName("twoStarCount") val twoStarCount: Int? = null, - @SerializedName("threeStarCount") val threeStarCount: Int? = null, - @SerializedName("fourStarCount") val fourStarCount: Int? = null, - @SerializedName("fiveStarCount") val fiveStarCount: Int? = null, + @SerialName("oneStarCount") val oneStarCount: Int? = null, + @SerialName("twoStarCount") val twoStarCount: Int? = null, + @SerialName("threeStarCount") val threeStarCount: Int? = null, + @SerialName("fourStarCount") val fourStarCount: Int? = null, + @SerialName("fiveStarCount") val fiveStarCount: Int? = null, ) } diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewListResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewListResponse.kt index 34d758c2d..46aee3015 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewListResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuReviewListResponse.kt @@ -1,28 +1,32 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Review -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class MenuReviewListResponse( - @SerializedName("numberOfElements") val numberOfElements: Int? = null, - @SerializedName("hasNext") val hasNext: Boolean? = null, - @SerializedName("dataList") val dataList: List = arrayListOf(), + @SerialName("numberOfElements") val numberOfElements: Int? = null, + @SerialName("hasNext") val hasNext: Boolean? = null, + @SerialName("dataList") val dataList: List = arrayListOf(), ) { + @Serializable data class DataList( - @SerializedName("reviewId") val reviewId: Long? = null, - @SerializedName("menu") val menu: Menu? = Menu(), - @SerializedName("writerId") val writerId: Long? = null, - @SerializedName("isWriter") val isWriter: Boolean? = null, - @SerializedName("writerNickname") val writerNickname: String? = null, - @SerializedName("rating") val rating: Int? = null, - @SerializedName("writtenAt") val writtenAt: String? = null, - @SerializedName("content") val content: String? = null, - @SerializedName("imageUrls") val imageUrls: List = arrayListOf(), + @SerialName("reviewId") val reviewId: Long? = null, + @SerialName("menu") val menu: Menu? = Menu(), + @SerialName("writerId") val writerId: Long? = null, + @SerialName("isWriter") val isWriter: Boolean? = null, + @SerialName("writerNickname") val writerNickname: String? = null, + @SerialName("rating") val rating: Int? = null, + @SerialName("writtenAt") val writtenAt: String? = null, + @SerialName("content") val content: String? = null, + @SerialName("imageUrls") val imageUrls: List = arrayListOf(), ) { + @Serializable data class Menu( - @SerializedName("id") val id: Long? = null, - @SerializedName("name") val name: String? = null, - @SerializedName("isLike") val isLike: Boolean? = null + @SerialName("id") val id: Long? = null, + @SerialName("name") val name: String? = null, + @SerialName("isLike") val isLike: Boolean? = null ) } } @@ -46,4 +50,4 @@ fun MenuReviewListResponse?.toDomain(): List { imgUrl = data.imageUrls.firstOrNull(), ) } ?: emptyList() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt index b4bf93915..cd5771588 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt @@ -1,8 +1,10 @@ package com.eatssu.android.data.remote.dto.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class MyNickNameResponse( - @SerializedName("nickname") var nickname: String? = null, - @SerializedName("provider") var provider: String, + @SerialName("nickname") var nickname: String? = null, + @SerialName("provider") var provider: String, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyReviewListResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyReviewListResponse.kt index 8b04a8f40..bffd19db3 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyReviewListResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyReviewListResponse.kt @@ -1,26 +1,30 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Review -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class MyReviewListResponse( - @SerializedName("numberOfElements") val numberOfElements: Int? = null, - @SerializedName("hasNext") val hasNext: Boolean? = null, - @SerializedName("dataList") val dataList: ArrayList? = arrayListOf() + @SerialName("numberOfElements") val numberOfElements: Int? = null, + @SerialName("hasNext") val hasNext: Boolean? = null, + @SerialName("dataList") val dataList: ArrayList? = arrayListOf() ) { + @Serializable data class DataList( - @SerializedName("reviewId") val reviewId: Long? = null, - @SerializedName("rating") val rating: Int? = null, - @SerializedName("writtenAt") val writtenAt: String? = null, - @SerializedName("content") val content: String? = null, - @SerializedName("imageUrls") val imageUrls: ArrayList = arrayListOf(), - @SerializedName("menuList") val menuList: ArrayList = arrayListOf() + @SerialName("reviewId") val reviewId: Long? = null, + @SerialName("rating") val rating: Int? = null, + @SerialName("writtenAt") val writtenAt: String? = null, + @SerialName("content") val content: String? = null, + @SerialName("imageUrls") val imageUrls: ArrayList = arrayListOf(), + @SerialName("menuList") val menuList: ArrayList = arrayListOf() ) { + @Serializable data class MenuList( - @SerializedName("id") val id: Long? = null, - @SerializedName("name") val name: String? = null, - @SerializedName("isLike") val isLike: Boolean? = null + @SerialName("id") val id: Long? = null, + @SerialName("name") val name: String? = null, + @SerialName("isLike") val isLike: Boolean? = null ) } } diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt index 247681e63..2b20686ea 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt @@ -1,39 +1,42 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.Partnership -import com.eatssu.android.domain.model.RestaurantType -import com.google.gson.annotations.SerializedName +import com.eatssu.common.enums.StoreType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class PartnershipResponse( - @SerializedName("storeName") + @SerialName("storeName") val storeName: String?, - @SerializedName("longitude") + @SerialName("longitude") val longitude: Double?, - @SerializedName("latitude") + @SerialName("latitude") val latitude: Double?, - @SerializedName("restaurantType") - val restaurantType: String?, - @SerializedName("partnershipInfos") + @SerialName("restaurantType") + val restaurantType: StoreType?, + @SerialName("partnershipInfos") val partnershipInfos: List ){ + @Serializable data class PartnershipInfo( - @SerializedName("id") + @SerialName("id") val id: Int?, - @SerializedName("partnershipType") - val partnershipType: String?, - @SerializedName("collegeName") + @SerialName("partnershipType") + val partnershipType: StoreType?, + @SerialName("collegeName") val collegeName: String?, - @SerializedName("departmentName") + @SerialName("departmentName") val departmentName: String?, - @SerializedName("likeCount") + @SerialName("likeCount") val likeCount: Int?, - @SerializedName("isLiked") + @SerialName("isLiked") val isLiked: Boolean?, - @SerializedName("description") + @SerialName("description") val description: String?, - @SerializedName("startDate") + @SerialName("startDate") val startDate: String?, - @SerializedName("endDate") + @SerialName("endDate") val endDate: String? ) } @@ -43,18 +46,11 @@ fun PartnershipResponse.toDomain(): Partnership = storeName = storeName ?: "", longitude = longitude ?: 126.95661313346206, latitude = latitude ?: 37.49517278813046, - restaurantType = restaurantType ?.let { - when(it) { - "CAFE" -> RestaurantType.CAFE - "RESTAURANT" -> RestaurantType.RESTAURANT - "PUB" -> RestaurantType.PUB - else -> RestaurantType.RESTAURANT - } - } ?: RestaurantType.RESTAURANT, + restaurantType = restaurantType ?: StoreType.RESTAURANT, partnershipInfos = partnershipInfos.map { Partnership.PartnershipInfo( id = it.id ?: -1, - partnershipType = it.partnershipType ?: "", + partnershipType = it.partnershipType ?: StoreType.RESTAURANT, collegeName = it.collegeName ?: "", departmentName = it.departmentName ?: "", likeCount = it.likeCount ?: 0, @@ -64,4 +60,4 @@ fun PartnershipResponse.toDomain(): Partnership = endDate = it.endDate ?: "" ) } - ) \ No newline at end of file + ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt index 1f021d350..0bf22d326 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt @@ -1,35 +1,37 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.PartnershipRestaurant -import com.eatssu.android.domain.model.RestaurantType -import com.google.gson.annotations.SerializedName +import com.eatssu.common.enums.StoreType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class PartnershipRestaurantResponse( - @SerializedName("id") + @SerialName("id") val id: Int?, - @SerializedName("partnershipType") + @SerialName("partnershipType") val partnershipType: String?, - @SerializedName("storeName") + @SerialName("storeName") val storeName: String?, - @SerializedName("description") + @SerialName("description") val description: String?, - @SerializedName("startDate") + @SerialName("startDate") val startDate: String?, - @SerializedName("endDate") + @SerialName("endDate") val endDate: String?, - @SerializedName("restaurantType") - val restaurantType: String?, - @SerializedName("longitude") + @SerialName("restaurantType") + val restaurantType: StoreType?, + @SerialName("longitude") val longitude: Double?, - @SerializedName("latitude") + @SerialName("latitude") val latitude: Double?, - @SerializedName("collegeName") + @SerialName("collegeName") val collegeName: String?, - @SerializedName("departmentName") + @SerialName("departmentName") val departmentName: String?, - @SerializedName("partnershipLikeCount") + @SerialName("partnershipLikeCount") val partnershipLikeCount: Int?, - @SerializedName("likedByUser") + @SerialName("likedByUser") val likedByUser: Boolean?, ) @@ -41,18 +43,11 @@ fun PartnershipRestaurantResponse.toDomain(): PartnershipRestaurant = description = description ?: "", startDate = startDate ?: "", endDate = endDate ?: "", - restaurantType = restaurantType ?.let { - when (it) { - "CAFE" -> RestaurantType.CAFE - "RESTAURANT" -> RestaurantType.RESTAURANT - "PUB" -> RestaurantType.PUB - else -> RestaurantType.RESTAURANT - } - } ?: RestaurantType.RESTAURANT, + restaurantType = restaurantType ?: StoreType.RESTAURANT, longitude = longitude ?: 126.95661313346206, latitude = latitude ?: 37.49517278813046, collegeName = collegeName ?: "", departmentName = departmentName ?: "", partnershipLikeCount = partnershipLikeCount ?: 0, likedByUser = likedByUser ?: false - ) \ No newline at end of file + ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt index e54bcc643..92cc0596a 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt @@ -1,16 +1,18 @@ package com.eatssu.android.data.remote.dto.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class TokenResponse( - @SerializedName("accessToken") + @SerialName("accessToken") val accessToken: String, - @SerializedName("refreshToken") + @SerialName("refreshToken") val refreshToken: String, ) fun TokenResponse.toDomain() = com.eatssu.android.domain.model.Token( accessToken = accessToken, refreshToken = refreshToken, -) \ No newline at end of file +) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt index 5b9867b47..e2cacdbeb 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt @@ -2,16 +2,18 @@ package com.eatssu.android.data.remote.dto.response import com.eatssu.android.domain.model.College import com.eatssu.android.domain.model.Department -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable data class UserCollegeDepartmentResponse( - @SerializedName("departmentId") + @SerialName("departmentId") val departmentId: Int?, - @SerializedName("departmentName") + @SerialName("departmentName") val departmentName: String?, - @SerializedName("collegeId") + @SerialName("collegeId") val collegeId: Int?, - @SerializedName("collegeName") + @SerialName("collegeName") val collegeName: String?, ) @@ -25,4 +27,4 @@ fun UserCollegeDepartmentResponse.toDomain(): Pair? { College(collegeId = colId, collegeName = colName), Department(departmentId = deptId, departmentName = deptName) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt b/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt index 2aad4884f..f14292a84 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt @@ -6,14 +6,15 @@ import com.eatssu.android.domain.repository.FirebaseRemoteConfigRepository import com.eatssu.common.enums.Restaurant import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings -import com.google.gson.Gson import kotlinx.coroutines.tasks.await +import kotlinx.serialization.json.Json import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class FirebaseRemoteConfigRepositoryImpl @Inject constructor( + private val json: Json ) : FirebaseRemoteConfigRepository { private val instance = FirebaseRemoteConfig.getInstance() @@ -50,28 +51,16 @@ class FirebaseRemoteConfigRepositoryImpl @Inject constructor( } private fun getCafeteriaInfo(): List { - val json = instance.getString("cafeteria_information") - return runCatching { parseCafeteriaJson(json) } + val jsonString = instance.getString("cafeteria_information") + return runCatching { parseCafeteriaJson(jsonString) } .onFailure { Timber.e(it, "cafeteria_information JSON 파싱 실패") } .getOrDefault(emptyList()) } - private fun parseCafeteriaJson(json: String): List { + private fun parseCafeteriaJson(jsonString: String): List { return try { - val gson = Gson() - val dtoList = gson.fromJson(json, Array::class.java) ?: emptyArray() - - dtoList.map { dto -> - RestaurantInfo( - enum = dto.enum, - name = dto.name, - location = dto.location, - image = dto.image, - time = dto.time, - etc = dto.etc - ).also { - Timber.d("Loaded cafeteria info: $it") - } + json.decodeFromString>(jsonString).also { + Timber.d("Loaded cafeteria info: $it") } } catch (e: Exception) { Timber.e(e, "Failed to parse cafeteria JSON") diff --git a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt index 6f20c2d0a..8cf034e00 100644 --- a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt +++ b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt @@ -13,13 +13,14 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.ResponseBody import okhttp3.logging.HttpLoggingInterceptor import retrofit2.CallAdapter import retrofit2.Converter import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory import java.lang.reflect.Type import javax.inject.Qualifier import javax.inject.Singleton @@ -50,6 +51,14 @@ annotation class NoToken @InstallIn(SingletonComponent::class) object NetworkModule { + @Singleton + @Provides + fun provideJson(): Json = Json { + ignoreUnknownKeys = true + coerceInputValues = true + isLenient = true + } + // 토큰이 필요한 okhttpClient @Singleton @Provides @@ -89,7 +98,8 @@ object NetworkModule { @Singleton @Provides - fun provideCallAdapterFactory(): CallAdapter.Factory = ApiResultCallAdapterFactory() + fun provideCallAdapterFactory(json: Json): CallAdapter.Factory = + ApiResultCallAdapterFactory(json) // 토큰이 필요한 retrofit @Singleton @@ -97,10 +107,11 @@ object NetworkModule { fun provideAuthRetrofit( okHttpClient: OkHttpClient, callAdapterFactory: CallAdapter.Factory, + json: Json ): Retrofit { return Retrofit.Builder().client(okHttpClient).baseUrl(BASE_URL) .addCallAdapterFactory(callAdapterFactory) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) .addConverterFactory(NullOnEmptyConverterFactory()) .build() } @@ -112,10 +123,11 @@ object NetworkModule { fun provideNoAuthRetrofit( @NoToken okHttpClient: OkHttpClient, callAdapterFactory: CallAdapter.Factory, + json: Json ): Retrofit { return Retrofit.Builder().client(okHttpClient).baseUrl(BASE_URL) .addCallAdapterFactory(callAdapterFactory) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) .addConverterFactory(NullOnEmptyConverterFactory()) .build() } diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt index f859f58c8..cb871c6fb 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt @@ -3,7 +3,6 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse import com.eatssu.android.presentation.base.NetworkErrorEventBus -import com.google.gson.Gson import okhttp3.Request import okio.Timeout import retrofit2.Call @@ -16,13 +15,10 @@ import java.lang.reflect.Type @Suppress("UNCHECKED_CAST") class ApiResultCall( private val call: Call>, - private val responseType: Type + private val responseType: Type, + private val json: kotlinx.serialization.json.Json ) : Call> { - companion object { - private val gson = Gson() - } - override fun enqueue(callback: Callback>) { call.enqueue(object : Callback> { override fun onResponse( @@ -65,12 +61,13 @@ class ApiResultCall( // errorBody를 JSON으로 파싱 시도 if (!errorBodyString.isNullOrEmpty()) { try { - val reader = com.google.gson.stream.JsonReader(java.io.StringReader(errorBodyString)) - reader.isLenient = true - val errorResponse = gson.fromJson>(reader, BaseResponse::class.java) + val errorResponse = + json.decodeFromString>( + errorBodyString + ) // BaseResponse 형태인지 확인 (isSuccess가 false이고 code와 message가 있는 경우) - if (errorResponse?.isSuccess == false && + if (errorResponse.isSuccess == false && errorResponse.code != null && errorResponse.message != null ) { @@ -132,7 +129,7 @@ class ApiResultCall( override fun isExecuted(): Boolean = call.isExecuted - override fun clone(): Call> = ApiResultCall(call.clone(), responseType) + override fun clone(): Call> = ApiResultCall(call.clone(), responseType, json) override fun isCanceled(): Boolean = call.isCanceled @@ -146,4 +143,3 @@ class ApiResultCall( override fun timeout(): Timeout = call.timeout() } - diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapter.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapter.kt index 5077b50a8..614ffc201 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapter.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapter.kt @@ -2,19 +2,20 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse +import kotlinx.serialization.json.Json import retrofit2.Call import retrofit2.CallAdapter import java.lang.reflect.Type class ApiResultCallAdapter( private val baseResponseType: Type, - private val dataType: Type + private val dataType: Type, + private val json: Json, ) : CallAdapter, Call>> { override fun responseType(): Type = baseResponseType override fun adapt(call: Call>): Call> { - return ApiResultCall(call, dataType) + return ApiResultCall(call, dataType, json) } } - diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapterFactory.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapterFactory.kt index 67d048691..12c9f3689 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapterFactory.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCallAdapterFactory.kt @@ -2,13 +2,14 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse +import kotlinx.serialization.json.Json import retrofit2.Call import retrofit2.CallAdapter import retrofit2.Retrofit import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -class ApiResultCallAdapterFactory : CallAdapter.Factory() { +class ApiResultCallAdapterFactory(private val json: Json) : CallAdapter.Factory() { override fun get( returnType: Type, @@ -40,7 +41,6 @@ class ApiResultCallAdapterFactory : CallAdapter.Factory() { override fun getActualTypeArguments(): Array = arrayOf(successType) override fun getOwnerType(): Type? = null } - return ApiResultCallAdapter(baseResponseType, successType) + return ApiResultCallAdapter(baseResponseType, successType, json) } } - diff --git a/app/src/main/java/com/eatssu/android/domain/model/PartnershipRestaurant.kt b/app/src/main/java/com/eatssu/android/domain/model/PartnershipRestaurant.kt index 14819ffb2..e642cb921 100644 --- a/app/src/main/java/com/eatssu/android/domain/model/PartnershipRestaurant.kt +++ b/app/src/main/java/com/eatssu/android/domain/model/PartnershipRestaurant.kt @@ -1,5 +1,7 @@ package com.eatssu.android.domain.model +import com.eatssu.common.enums.StoreType + data class PartnershipRestaurant( val id: Int, val partnershipType: String, @@ -7,7 +9,7 @@ data class PartnershipRestaurant( val description: String, val startDate: String, val endDate: String, - val restaurantType: RestaurantType, + val storeType: StoreType, val longitude: Double, val latitude: Double, val collegeName: String, @@ -15,7 +17,3 @@ data class PartnershipRestaurant( val partnershipLikeCount: Int, val likedByUser: Boolean, ) - -enum class RestaurantType { - CAFE, RESTAURANT, PUB -} diff --git a/app/src/main/java/com/eatssu/android/domain/model/RestaurantInfo.kt b/app/src/main/java/com/eatssu/android/domain/model/RestaurantInfo.kt index 3e66f1b55..bb6d0300a 100644 --- a/app/src/main/java/com/eatssu/android/domain/model/RestaurantInfo.kt +++ b/app/src/main/java/com/eatssu/android/domain/model/RestaurantInfo.kt @@ -1,7 +1,9 @@ package com.eatssu.android.domain.model import com.eatssu.common.enums.Restaurant +import kotlinx.serialization.Serializable +@Serializable data class RestaurantInfo( val enum: Restaurant, val name: String, @@ -9,4 +11,4 @@ data class RestaurantInfo( val image: String, val time: String, val etc: String, -) \ No newline at end of file +) diff --git a/app/src/main/java/com/eatssu/android/domain/model/WidgetMealInfo.kt b/app/src/main/java/com/eatssu/android/domain/model/WidgetMealInfo.kt index f49cc3d5a..7945da411 100644 --- a/app/src/main/java/com/eatssu/android/domain/model/WidgetMealInfo.kt +++ b/app/src/main/java/com/eatssu/android/domain/model/WidgetMealInfo.kt @@ -1,11 +1,17 @@ package com.eatssu.android.domain.model import com.eatssu.common.enums.Restaurant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable - +@Serializable sealed interface WidgetMealInfo { + @Serializable + @SerialName("Loading") object Loading : WidgetMealInfo + @Serializable + @SerialName("Available") data class Available( val breakfast: List>, val lunch: List>, @@ -13,5 +19,7 @@ sealed interface WidgetMealInfo { val restaurant: Restaurant, ) : WidgetMealInfo + @Serializable + @SerialName("Unavailable") object Unavailable : WidgetMealInfo -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt index 08b16c838..efb83efb6 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt @@ -3,13 +3,11 @@ package com.eatssu.android.presentation.map import android.Manifest -import android.R.id.message import android.app.Activity import android.content.Context import android.content.ContextWrapper import android.content.Intent import android.content.pm.PackageManager -import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image @@ -47,7 +45,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver @@ -56,7 +53,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.eatssu.android.R import com.eatssu.android.domain.model.Partnership -import com.eatssu.android.domain.model.RestaurantType import com.eatssu.android.presentation.MainState import com.eatssu.android.presentation.MainViewModel import com.eatssu.android.presentation.map.component.DepartmentBottomSheet @@ -71,6 +67,7 @@ import com.eatssu.common.UiEvent import com.eatssu.common.UiState import com.eatssu.common.UiText import com.eatssu.common.enums.ScreenId +import com.eatssu.common.enums.StoreType import com.eatssu.common.enums.ToastType import com.eatssu.design_system.theme.EatssuTheme import com.eatssu.design_system.theme.Gray300 @@ -327,7 +324,7 @@ internal fun MapScreen( if (partnershipSheetState.isVisible) { mapState.restaurantPartnershipInfo?.let { info -> - mapState.placeType?.let { placeType -> + mapState.storeType?.let { storeType -> EventLogger.clickPartnerRestaurant( college = collegeId, @@ -337,7 +334,7 @@ internal fun MapScreen( MapRestaurantBottomSheet( storeName = info.storeName, - placeType = placeType, + storeType = storeType, mapRestaurantList = mapState.restaurantInfoList, onDismiss = { onHidePartnershipSheet() @@ -414,8 +411,8 @@ internal fun MapScreen( ) ) { val iconRes = when (partnership.restaurantType) { - RestaurantType.CAFE -> R.drawable.ic_map_marker_cafe - RestaurantType.PUB -> R.drawable.ic_map_marker_pub + StoreType.CAFE -> R.drawable.ic_map_marker_cafe + StoreType.PUB -> R.drawable.ic_map_marker_pub else -> R.drawable.ic_map_marker_restaurant } diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt index 1ad6ede3b..3a3dbe74a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt @@ -2,18 +2,18 @@ package com.eatssu.android.presentation.map import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.R import com.eatssu.android.domain.model.Partnership import com.eatssu.android.domain.model.PartnershipRestaurant -import com.eatssu.android.domain.model.RestaurantType import com.eatssu.android.domain.repository.PartnershipRepository import com.eatssu.android.domain.usecase.user.GetPartnershipDetailUseCase import com.eatssu.android.domain.usecase.user.GetUserCollegeDepartmentUseCase import com.eatssu.android.presentation.map.component.FilterType -import com.eatssu.android.presentation.map.model.PlaceType import com.eatssu.android.presentation.map.model.RestaurantInfo import com.eatssu.common.EventLogger import com.eatssu.common.UiEvent import com.eatssu.common.UiState +import com.eatssu.common.enums.StoreType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -24,11 +24,19 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject + +val StoreType.iconRes: Int + get() = when (this) { + StoreType.CAFE -> R.drawable.ic_map_cafe + StoreType.RESTAURANT -> R.drawable.ic_map_restaurant + StoreType.PUB -> R.drawable.ic_map_pub + } + data class MapState( val partnerships: List = emptyList(), val restaurantPartnershipInfo: PartnershipRestaurant? = null, val restaurantInfoList: List = emptyList(), - val placeType: PlaceType? = null, + val storeType: StoreType? = null, val selectedFilter: FilterType = FilterType.Mine, val filterChangeResult: FilterChangeResult? = null, ) { @@ -186,18 +194,11 @@ class MapViewModel @Inject constructor( ) } - // Domain 모델(RestaurantType)을 UI 모델(PlaceType)로 변환 - val placeType = when (representative.restaurantType) { - RestaurantType.CAFE -> PlaceType.CAFE - RestaurantType.RESTAURANT -> PlaceType.RESTAURANT - RestaurantType.PUB -> PlaceType.PUB - } - _uiState.value = UiState.Success( data.copy( restaurantPartnershipInfo = representative, restaurantInfoList = restaurantInfoList, - placeType = placeType + storeType = representative.storeType ) ) } diff --git a/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt b/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt index 179eeee2a..b0fff816a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/component/MapRestaurantBottomSheet.kt @@ -35,10 +35,11 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.eatssu.android.R -import com.eatssu.android.presentation.map.model.PlaceType +import com.eatssu.android.presentation.map.iconRes import com.eatssu.android.presentation.map.model.RestaurantInfo import com.eatssu.android.presentation.util.TrackScreenViewEvent import com.eatssu.common.enums.ScreenId +import com.eatssu.common.enums.StoreType import com.eatssu.design_system.theme.EatssuTheme import com.eatssu.design_system.theme.Gray200 import com.eatssu.design_system.theme.Gray400 @@ -50,7 +51,7 @@ import com.eatssu.design_system.theme.Primary @Composable fun MapRestaurantBottomSheet( storeName: String, - placeType: PlaceType, + storeType: StoreType, mapRestaurantList: List, onDismiss: () -> Unit = {} ) { @@ -115,7 +116,7 @@ fun MapRestaurantBottomSheet( contentAlignment = Alignment.Center ) { Image( - painter = painterResource(id = placeType.iconRes), + painter = painterResource(id = storeType.iconRes), contentDescription = storeName, modifier = Modifier.size(13.dp) ) @@ -124,7 +125,7 @@ fun MapRestaurantBottomSheet( Spacer(modifier = Modifier.width(4.dp)) Text( - text = placeType.placeCategory, + text = storeType.value, style = EatssuTheme.typography.caption3, color = Gray400, ) @@ -225,7 +226,7 @@ fun MapRestaurantBottomSheetPreview() { MapRestaurantBottomSheet( storeName = "현선이네", mapRestaurantList = dummyList, - placeType = PlaceType.RESTAURANT + storeType = StoreType.RESTAURANT ) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/map/model/PlaceType.kt b/app/src/main/java/com/eatssu/android/presentation/map/model/PlaceType.kt deleted file mode 100644 index 0a0f80f4e..000000000 --- a/app/src/main/java/com/eatssu/android/presentation/map/model/PlaceType.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.eatssu.android.presentation.map.model - -import com.eatssu.android.R - -enum class PlaceType(val placeCategory: String, val iconRes: Int) { - CAFE("카페", R.drawable.ic_map_cafe), - RESTAURANT("음식점", R.drawable.ic_map_restaurant), - PUB("주점", R.drawable.ic_map_pub), -} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/MealInfoStateDefinition.kt b/app/src/main/java/com/eatssu/android/presentation/widget/MealInfoStateDefinition.kt index 25f1247b7..679134b9a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/MealInfoStateDefinition.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/MealInfoStateDefinition.kt @@ -8,14 +8,11 @@ import androidx.datastore.core.Serializer import androidx.datastore.dataStoreFile import androidx.glance.state.GlanceStateDefinition import com.eatssu.android.domain.model.WidgetMealInfo -import com.eatssu.common.enums.Restaurant -import com.google.gson.Gson -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.google.gson.reflect.TypeToken import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import timber.log.Timber import java.io.File import java.io.InputStream @@ -55,56 +52,27 @@ object MealInfoStateDefinition : GlanceStateDefinition { } object MealInfoSerializer : Serializer { - private val gson = Gson() - override val defaultValue = WidgetMealInfo.Loading + override val defaultValue: WidgetMealInfo = WidgetMealInfo.Loading override suspend fun readFrom(input: InputStream): WidgetMealInfo { return try { val jsonRaw = input.readBytes().decodeToString() Timber.d("readFrom: raw = '$jsonRaw'") if (jsonRaw.isBlank()) return defaultValue - val obj = JsonParser.parseString(jsonRaw).asJsonObject - when (obj.get("type").asString) { - "Loading" -> WidgetMealInfo.Loading - "Unavailable" -> WidgetMealInfo.Unavailable - "Available" -> { - val mealListType = object : TypeToken>>() {}.type - val breakfast = - gson.fromJson>>(obj.get("breakfast"), mealListType) - val lunch = - gson.fromJson>>(obj.get("lunch"), mealListType) - val dinner = - gson.fromJson>>(obj.get("dinner"), mealListType) - val restaurant = Restaurant.valueOf(obj.get("restaurant").asString) - WidgetMealInfo.Available(breakfast, lunch, dinner, restaurant) - } - else -> defaultValue - } + Json.decodeFromString(jsonRaw) } catch (e: Exception) { Timber.e("Serialization error: ${e.message}") - throw CorruptionException("Could not read data: ${e.message}") + throw CorruptionException("Could not read data: ${e.message}", e) } } override suspend fun writeTo(t: WidgetMealInfo, output: OutputStream) { - val obj = JsonObject() - when (t) { - is WidgetMealInfo.Loading -> obj.addProperty("type", "Loading") - is WidgetMealInfo.Unavailable -> obj.addProperty("type", "Unavailable") - is WidgetMealInfo.Available -> { - obj.addProperty("type", "Available") - obj.add("breakfast", gson.toJsonTree(t.breakfast)) - obj.add("lunch", gson.toJsonTree(t.lunch)) - obj.add("dinner", gson.toJsonTree(t.dinner)) - obj.addProperty("restaurant", t.restaurant.name) - } - } - val json = gson.toJson(obj) + val json = Json.encodeToString(t) Timber.d("[writeTo] json = $json") output.use { it.write(json.encodeToByteArray()) } } } -} \ No newline at end of file +} diff --git a/build.gradle.kts b/build.gradle.kts index 2e64d8ad7..17153a001 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.google.services) apply false alias(libs.plugins.firebase.crashlytics) apply false diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 537fef772..a2a0fe9d1 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) alias(libs.plugins.google.services) } @@ -44,4 +45,6 @@ dependencies { // Firebase implementation(platform(libs.firebase.bom)) implementation(libs.firebase.analytics) + + implementation(libs.kotlinx.serialization.json) } \ No newline at end of file diff --git a/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt b/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt index 0e037e7d0..f5928d17d 100644 --- a/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt +++ b/core/common/src/main/java/com/eatssu/common/enums/Restaurant.kt @@ -3,7 +3,9 @@ package com.eatssu.common.enums import androidx.annotation.StringRes import com.eatssu.common.R import com.eatssu.common.UiText +import kotlinx.serialization.Serializable +@Serializable enum class Restaurant( val value: String, @field:StringRes val displayNameResId: Int, @@ -25,10 +27,5 @@ enum class Restaurant( fun getVariableRestaurantList(): List { return entries.filter { it.menuType == MenuType.VARIABLE } } - - @Deprecated("다국어 지원을 위해 displayNameResId 사용", ReplaceWith("entries.find { it.name == enumName }?.displayNameResId")) - fun fromRestaurantEnumName(enumName: String): String { - return "" - } } -} \ No newline at end of file +} diff --git a/core/common/src/main/java/com/eatssu/common/enums/StoreType.kt b/core/common/src/main/java/com/eatssu/common/enums/StoreType.kt new file mode 100644 index 000000000..d35667d93 --- /dev/null +++ b/core/common/src/main/java/com/eatssu/common/enums/StoreType.kt @@ -0,0 +1,10 @@ +package com.eatssu.common.enums + +import kotlinx.serialization.Serializable + +@Serializable +enum class StoreType(val value: String) { + CAFE("카페"), + RESTAURANT("음식점"), + PUB("주점"), +} diff --git a/core/common/src/main/java/com/eatssu/common/enums/Time.kt b/core/common/src/main/java/com/eatssu/common/enums/Time.kt index e894f254c..e2068a3f6 100644 --- a/core/common/src/main/java/com/eatssu/common/enums/Time.kt +++ b/core/common/src/main/java/com/eatssu/common/enums/Time.kt @@ -4,10 +4,4 @@ enum class Time(val value: String, val korean: String) { MORNING("breakfast", "조식"), LUNCH("lunch", "중식"), DINNER("dinner", "석식"); - - companion object { - fun fromTimeEnumName(enumName: String): String { - return entries.find { it.name == enumName }?.korean ?: "" - } - } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8077dd9bf..ddcb82521 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,8 +37,6 @@ datastore = "1.0.0" composeBom = "2024.04.01" lifecycle = "2.7.0" retrofit = "2.9.0" -converter-gson = "2.9.0" -gson = "2.10.1" okhttp = "4.12.0" glide = "4.15.1" glide-compiler = "4.12.0" @@ -62,6 +60,8 @@ glanceAppwidgetPreview = "1.1.1" glancePreview = "1.1.1" posthog = "3.+" paging = "3.3.6" +kotlinx-serialization-json = "1.7.3" +retrofit-kotlinx-serialization-converter = "1.0.0" [libraries] @@ -103,6 +103,7 @@ androidx-navigation-ui = { module = "androidx.navigation:navigation-ui", version # coroutines kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } # glance androidx-glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glanceAppwidget" } @@ -124,10 +125,9 @@ coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" # retrofit, okhttp, gson retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } -retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "converter-gson" } okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } okhttp-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } -gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } +retrofit-kotlinx-serialization-converter = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofit-kotlinx-serialization-converter" } # hilt hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } @@ -169,6 +169,7 @@ androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", android-application = { id = "com.android.application", version.ref = "android" } android-library = { id = "com.android.library", version.ref = "android" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-android" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin-android" } google-services = { id = "com.google.gms.google-services", version.ref = "google-services" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } From e0bb02a66f120733be343c1d4774d0ac6c9818dd Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:26:23 +0900 Subject: [PATCH 2/8] chore: change enum --- .../android/data/remote/dto/response/PartnershipResponse.kt | 4 ++-- .../data/remote/dto/response/PartnershipRestaurantResponse.kt | 2 +- .../main/java/com/eatssu/android/domain/model/Partnership.kt | 4 +++- .../domain/usecase/user/GetPartnershipDetailUseCase.kt | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt index 2b20686ea..cb6e46743 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt @@ -23,7 +23,7 @@ data class PartnershipResponse( @SerialName("id") val id: Int?, @SerialName("partnershipType") - val partnershipType: StoreType?, + val partnershipType: String?, @SerialName("collegeName") val collegeName: String?, @SerialName("departmentName") @@ -50,7 +50,7 @@ fun PartnershipResponse.toDomain(): Partnership = partnershipInfos = partnershipInfos.map { Partnership.PartnershipInfo( id = it.id ?: -1, - partnershipType = it.partnershipType ?: StoreType.RESTAURANT, + partnershipType = it.partnershipType ?: "", collegeName = it.collegeName ?: "", departmentName = it.departmentName ?: "", likeCount = it.likeCount ?: 0, diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt index 0bf22d326..ed8bbc628 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt @@ -43,7 +43,7 @@ fun PartnershipRestaurantResponse.toDomain(): PartnershipRestaurant = description = description ?: "", startDate = startDate ?: "", endDate = endDate ?: "", - restaurantType = restaurantType ?: StoreType.RESTAURANT, + storeType = restaurantType ?: StoreType.RESTAURANT, longitude = longitude ?: 126.95661313346206, latitude = latitude ?: 37.49517278813046, collegeName = collegeName ?: "", diff --git a/app/src/main/java/com/eatssu/android/domain/model/Partnership.kt b/app/src/main/java/com/eatssu/android/domain/model/Partnership.kt index 6677c3c5a..3d2cfd825 100644 --- a/app/src/main/java/com/eatssu/android/domain/model/Partnership.kt +++ b/app/src/main/java/com/eatssu/android/domain/model/Partnership.kt @@ -1,10 +1,12 @@ package com.eatssu.android.domain.model +import com.eatssu.common.enums.StoreType + data class Partnership( val storeName: String, val longitude: Double, val latitude: Double, - val restaurantType: RestaurantType, + val restaurantType: StoreType, val partnershipInfos: List ) { data class PartnershipInfo( diff --git a/app/src/main/java/com/eatssu/android/domain/usecase/user/GetPartnershipDetailUseCase.kt b/app/src/main/java/com/eatssu/android/domain/usecase/user/GetPartnershipDetailUseCase.kt index d4d3fc510..bfda70a3d 100644 --- a/app/src/main/java/com/eatssu/android/domain/usecase/user/GetPartnershipDetailUseCase.kt +++ b/app/src/main/java/com/eatssu/android/domain/usecase/user/GetPartnershipDetailUseCase.kt @@ -23,7 +23,7 @@ class GetPartnershipDetailUseCase @Inject constructor() { description = info.description, startDate = info.startDate, endDate = info.endDate, - restaurantType = matched.restaurantType, + storeType = matched.restaurantType, longitude = matched.longitude, latitude = matched.latitude, collegeName = info.collegeName, From 8f993892c79edb00e2f094ef70b8012abb03edf5 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:26:40 +0900 Subject: [PATCH 3/8] chore: change Gson to Json --- app/src/main/java/com/eatssu/android/di/NetworkModule.kt | 1 + .../main/java/com/eatssu/android/di/network/ApiResultCall.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt index 8cf034e00..a7088ddeb 100644 --- a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt +++ b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt @@ -9,6 +9,7 @@ import com.eatssu.android.di.network.TokenInterceptor import com.eatssu.android.domain.usecase.auth.GetAccessTokenUseCase import com.eatssu.android.domain.usecase.auth.LogoutUseCase import com.eatssu.android.domain.usecase.auth.ReissueAndStoreTokenUseCase +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt index cb871c6fb..8df0f5841 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt @@ -3,6 +3,7 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse import com.eatssu.android.presentation.base.NetworkErrorEventBus +import kotlinx.serialization.json.Json import okhttp3.Request import okio.Timeout import retrofit2.Call @@ -16,7 +17,7 @@ import java.lang.reflect.Type class ApiResultCall( private val call: Call>, private val responseType: Type, - private val json: kotlinx.serialization.json.Json + private val json: Json ) : Call> { override fun enqueue(callback: Callback>) { From cee31fd7bc8163db188f7fb12867c4090d2787e4 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:34:44 +0900 Subject: [PATCH 4/8] fix: MissingFieldException by defaultValue(null) --- .../dto/response/PartnershipResponse.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt index cb6e46743..f4107fc30 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipResponse.kt @@ -8,36 +8,36 @@ import kotlinx.serialization.Serializable @Serializable data class PartnershipResponse( @SerialName("storeName") - val storeName: String?, + val storeName: String? = null, @SerialName("longitude") - val longitude: Double?, + val longitude: Double? = null, @SerialName("latitude") - val latitude: Double?, + val latitude: Double? = null, @SerialName("restaurantType") - val restaurantType: StoreType?, + val restaurantType: StoreType? = null, @SerialName("partnershipInfos") - val partnershipInfos: List + val partnershipInfos: List = emptyList() ){ @Serializable data class PartnershipInfo( @SerialName("id") - val id: Int?, + val id: Int? = null, @SerialName("partnershipType") - val partnershipType: String?, + val partnershipType: String? = null, @SerialName("collegeName") - val collegeName: String?, + val collegeName: String? = null, @SerialName("departmentName") - val departmentName: String?, + val departmentName: String? = null, @SerialName("likeCount") - val likeCount: Int?, + val likeCount: Int? = null, @SerialName("isLiked") - val isLiked: Boolean?, + val isLiked: Boolean? = null, @SerialName("description") - val description: String?, + val description: String? = null, @SerialName("startDate") - val startDate: String?, + val startDate: String? = null, @SerialName("endDate") - val endDate: String? + val endDate: String? = null ) } From 62c3af8577e03df8eb48676dce8b8129852c1442 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:48:40 +0900 Subject: [PATCH 5/8] fix: Prevent MissingFieldException by adding default values to DTOs --- .../remote/dto/response/CollegeResponse.kt | 4 +-- .../remote/dto/response/DepartmentResponse.kt | 4 +-- .../remote/dto/response/MyNickNameResponse.kt | 2 +- .../response/PartnershipRestaurantResponse.kt | 26 +++++++++---------- .../response/UserCollegeDepartmentResponse.kt | 8 +++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt index e91a06c71..fce32a694 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/CollegeResponse.kt @@ -7,9 +7,9 @@ import kotlinx.serialization.Serializable @Serializable data class CollegeResponse( @SerialName("id") - val collegeId: Int?, + val collegeId: Int? = null, @SerialName("name") - val collegeName: String? + val collegeName: String? = null ) // 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt index ce43dd8dc..c7f9eb06b 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/DepartmentResponse.kt @@ -7,9 +7,9 @@ import kotlinx.serialization.Serializable @Serializable data class DepartmentResponse( @SerialName("id") - val departmentId: Int?, + val departmentId: Int? = null, @SerialName("name") - val departmentName: String?, + val departmentName: String? = null, ) // 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt index cd5771588..48dcddf58 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt @@ -6,5 +6,5 @@ import kotlinx.serialization.Serializable @Serializable data class MyNickNameResponse( @SerialName("nickname") var nickname: String? = null, - @SerialName("provider") var provider: String, + @SerialName("provider") var provider: String? = null, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt index ed8bbc628..6a0516b55 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/PartnershipRestaurantResponse.kt @@ -8,31 +8,31 @@ import kotlinx.serialization.Serializable @Serializable data class PartnershipRestaurantResponse( @SerialName("id") - val id: Int?, + val id: Int? = null, @SerialName("partnershipType") - val partnershipType: String?, + val partnershipType: String? = null, @SerialName("storeName") - val storeName: String?, + val storeName: String? = null, @SerialName("description") - val description: String?, + val description: String? = null, @SerialName("startDate") - val startDate: String?, + val startDate: String? = null, @SerialName("endDate") - val endDate: String?, + val endDate: String? = null, @SerialName("restaurantType") - val restaurantType: StoreType?, + val restaurantType: StoreType? = null, @SerialName("longitude") - val longitude: Double?, + val longitude: Double? = null, @SerialName("latitude") - val latitude: Double?, + val latitude: Double? = null, @SerialName("collegeName") - val collegeName: String?, + val collegeName: String? = null, @SerialName("departmentName") - val departmentName: String?, + val departmentName: String? = null, @SerialName("partnershipLikeCount") - val partnershipLikeCount: Int?, + val partnershipLikeCount: Int? = null, @SerialName("likedByUser") - val likedByUser: Boolean?, + val likedByUser: Boolean? = null, ) fun PartnershipRestaurantResponse.toDomain(): PartnershipRestaurant = diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt index e2cacdbeb..de4a23528 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/UserCollegeDepartmentResponse.kt @@ -8,13 +8,13 @@ import kotlinx.serialization.Serializable @Serializable data class UserCollegeDepartmentResponse( @SerialName("departmentId") - val departmentId: Int?, + val departmentId: Int? = null, @SerialName("departmentName") - val departmentName: String?, + val departmentName: String? = null, @SerialName("collegeId") - val collegeId: Int?, + val collegeId: Int? = null, @SerialName("collegeName") - val collegeName: String?, + val collegeName: String? = null, ) // 이 함수가 null을 반환하는 경우, 이 함수를 호출하는 UserRepositoryImpl에서 mapNotNull로 걸러짐 From d9dedcd865b9f949d45f4c49cdfbe08c1c65d837 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Tue, 10 Feb 2026 14:58:34 +0900 Subject: [PATCH 6/8] chore: change var to val --- .../android/data/remote/dto/response/BaseResponse.kt | 8 ++++---- .../android/data/remote/dto/response/MealResponse.kt | 12 ++++++------ .../android/data/remote/dto/response/MenuResponse.kt | 2 +- .../data/remote/dto/response/MyNickNameResponse.kt | 4 ++-- .../data/remote/dto/response/TokenResponse.kt | 3 ++- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt index aabaa0632..c9eb56338 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/BaseResponse.kt @@ -5,8 +5,8 @@ import kotlinx.serialization.Serializable @Serializable data class BaseResponse( - @SerialName("isSuccess") var isSuccess: Boolean? = null, - @SerialName("code") var code: Int? = null, - @SerialName("message") var message: String? = null, - @SerialName("result") var result: T? = null, + @SerialName("isSuccess") val isSuccess: Boolean? = null, + @SerialName("code") val code: Int? = null, + @SerialName("message") val message: String? = null, + @SerialName("result") val result: T? = null, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt index 8769cf87f..1d7ce4bf0 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MealResponse.kt @@ -8,16 +8,16 @@ private const val MENU_SEPARATOR = ", " @Serializable data class GetMealResponse( - @SerialName("mealId") var mealId: Long? = null, - @SerialName("price") var price: Int? = null, - @SerialName("rating") var rating: Double? = null, - @SerialName("briefMenus") var briefMenus: List = emptyList(), + @SerialName("mealId") val mealId: Long? = null, + @SerialName("price") val price: Int? = null, + @SerialName("rating") val rating: Double? = null, + @SerialName("briefMenus") val briefMenus: List = emptyList(), ) @Serializable data class MenusInformationList( - @SerialName("menuId") var menuId: Long? = null, - @SerialName("name") var name: String? = null, + @SerialName("menuId") val menuId: Long? = null, + @SerialName("name") val name: String? = null, ) fun List.mapTodayMenuResponseToMenu(): List { diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt index ceed27f85..53bbe70e4 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MenuResponse.kt @@ -24,7 +24,7 @@ data class CategoryMenuListCollection( @Serializable data class MenuInformationList( - @SerialName("menuId") var menuId: Long? = null, + @SerialName("menuId") val menuId: Long? = null, @SerialName("name") val name: String? = null, @SerialName("price") val price: Int? = null, @SerialName("rating") val rating: Double? = null, diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt index 48dcddf58..086e5f9e3 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/MyNickNameResponse.kt @@ -5,6 +5,6 @@ import kotlinx.serialization.Serializable @Serializable data class MyNickNameResponse( - @SerialName("nickname") var nickname: String? = null, - @SerialName("provider") var provider: String? = null, + @SerialName("nickname") val nickname: String? = null, + @SerialName("provider") val provider: String? = null, ) diff --git a/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt b/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt index 92cc0596a..07f0562bd 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/dto/response/TokenResponse.kt @@ -1,5 +1,6 @@ package com.eatssu.android.data.remote.dto.response +import com.eatssu.android.domain.model.Token import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -12,7 +13,7 @@ data class TokenResponse( val refreshToken: String, ) -fun TokenResponse.toDomain() = com.eatssu.android.domain.model.Token( +fun TokenResponse.toDomain() = Token( accessToken = accessToken, refreshToken = refreshToken, ) From 268e2a0da0c8dca8ca2bf70559eeca8e58cefd27 Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Thu, 12 Feb 2026 12:07:04 +0900 Subject: [PATCH 7/8] chore: apply strict json serialization and handle serialization errors --- app/src/main/java/com/eatssu/android/di/NetworkModule.kt | 3 --- .../java/com/eatssu/android/di/network/ApiResultCall.kt | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt index a7088ddeb..b434a2da0 100644 --- a/app/src/main/java/com/eatssu/android/di/NetworkModule.kt +++ b/app/src/main/java/com/eatssu/android/di/NetworkModule.kt @@ -55,9 +55,6 @@ object NetworkModule { @Singleton @Provides fun provideJson(): Json = Json { - ignoreUnknownKeys = true - coerceInputValues = true - isLenient = true } // 토큰이 필요한 okhttpClient diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt index 8df0f5841..bbf18a30e 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt @@ -3,6 +3,7 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse import com.eatssu.android.presentation.base.NetworkErrorEventBus +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import okhttp3.Request import okio.Timeout @@ -41,6 +42,11 @@ class ApiResultCall( ApiResult.NetworkError(error) } + is SerializationException, is IllegalArgumentException -> { + Timber.e(error, "Serialization Error") + ApiResult.UnknownError(error) + } + else -> ApiResult.UnknownError(error) } callback.onResponse( From 32c65de6c456a6c2865cfabae96679ac37f79eec Mon Sep 17 00:00:00 2001 From: Yu Jin Date: Thu, 12 Feb 2026 12:20:35 +0900 Subject: [PATCH 8/8] feat: report serialization exceptions to crashlytics --- .../main/java/com/eatssu/android/di/network/ApiResultCall.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt index bbf18a30e..ad924f364 100644 --- a/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt +++ b/app/src/main/java/com/eatssu/android/di/network/ApiResultCall.kt @@ -3,6 +3,7 @@ package com.eatssu.android.di.network import com.eatssu.android.data.model.ApiResult import com.eatssu.android.data.remote.dto.response.BaseResponse import com.eatssu.android.presentation.base.NetworkErrorEventBus +import com.google.firebase.crashlytics.FirebaseCrashlytics import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import okhttp3.Request @@ -44,6 +45,7 @@ class ApiResultCall( is SerializationException, is IllegalArgumentException -> { Timber.e(error, "Serialization Error") + FirebaseCrashlytics.getInstance().recordException(error) ApiResult.UnknownError(error) }