-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] API 호출 오류 핸들링 개선 #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9096ecc
a68d0bd
9e2a51d
05159c6
1877369
a3362fe
c09b185
63f4a0e
6cc8dec
dd60769
f587b8c
d140199
ab61862
886200a
019859d
8281771
7701831
d09d4b7
b77cbf5
2c91d5c
7da9ca8
6412fd5
a16114d
31e0c99
e58a3a9
f64d784
ae15754
b221c36
04055fc
cb24faf
855d229
976744f
fb4b49c
55cadf0
1689f2e
f214504
0ec4086
f61979e
1d40b3c
d08ea51
bc9208d
60ffc27
d12a307
be68944
1bdf871
73c652a
dd79dde
4503516
265a86b
3c525bd
f206d82
fcb7647
2c86c4f
7397c53
9ff0cb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| package com.eatssu.android.data.model | ||
|
|
||
| import com.eatssu.android.data.model.ApiResult.Failure | ||
| import com.eatssu.android.data.model.ApiResult.NetworkError | ||
| import com.eatssu.android.data.model.ApiResult.Success | ||
| import com.eatssu.android.data.model.ApiResult.UnknownError | ||
| import java.io.IOException | ||
|
|
||
| sealed interface ApiResult<out T> { | ||
| // API가 성공했을 때 | ||
| data class Success<T>(val data: T) : ApiResult<T> | ||
|
|
||
| // 서버에서 에러 반환 | ||
| data class Failure( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val responseCode: Int, | ||
| val message: String? | ||
| ) : ApiResult<Nothing> | ||
|
|
||
| // 소켓 연결 끊김, 타임아웃 등 네트워크 예외인 IOException 처리 | ||
| data class NetworkError( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val exception: IOException | ||
| ) : ApiResult<Nothing> | ||
|
|
||
| // IOException을 제외한 예외 처리 | ||
| data class UnknownError( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val exception: Throwable | ||
| ) : ApiResult<Nothing> | ||
| } | ||
|
|
||
| // ApiResult가 성공인지 아닌지 여부 확인 | ||
| fun ApiResult<Unit>.isSuccess(): Boolean = this is Success | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅋㅋㅋㅋ 올려주신 짤처럼 저도 HTTP Response Code만 신뢰할 수 없으니, BaseResponse라는 래퍼 정보를 기반으로 오류 처리하려고 고민을 많이 했어요.
이렇게 BaseResponse까지 검증해서 처리하고 있어요! |
||
|
|
||
| // 성공한 경우 데이터 반환, 실패한 경우 빈 리스트 반환 | ||
| fun <TElement, TList : List<TElement>> ApiResult<TList>.orEmptyList(): List<TElement> = | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| when (this) { | ||
| is Success -> data | ||
| else -> emptyList() | ||
| } | ||
|
|
||
| // 성공한 경우 데이터 반환, 실패한 경우 기본값 반환 | ||
| fun <T> ApiResult<T>.orElse(default: T): T = when (this) { | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| is Success -> data | ||
| else -> default | ||
| } | ||
|
|
||
| // 성공한 경우 데이터 반환, 실패한 경우 null 반환 | ||
| fun <T> ApiResult<T>.orNull(): T? = when (this) { | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| is Success -> data | ||
| else -> null | ||
| } | ||
|
|
||
| // ApiResult가 Success일 때 데이터를 변환 | ||
| fun <T, R> ApiResult<T>.map(transform: (T) -> R): ApiResult<R> = when (this) { | ||
| is Success<T> -> Success(transform(data)) | ||
| is Failure -> Failure(responseCode, message) | ||
| is NetworkError -> NetworkError(exception) | ||
| is UnknownError -> UnknownError(exception) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.model.isSuccess | ||
| import com.eatssu.android.data.service.HealthCheckService | ||
| import com.eatssu.android.domain.repository.HealthCheckRepository | ||
| import javax.inject.Inject | ||
|
|
||
| class HealthCheckRepositoryImpl @Inject constructor( | ||
| private val healthCheckService: HealthCheckService | ||
| ) : HealthCheckRepository { | ||
| override suspend fun checkHealth(): Boolean { | ||
| return healthCheckService.checkHealth().isSuccess() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,39 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.dto.response.BaseResponse | ||
| import com.eatssu.android.data.dto.response.MenuOfMealResponse | ||
| import com.eatssu.android.data.dto.response.MenusInformation | ||
| import com.eatssu.android.data.dto.response.mapTodayMenuResponseToMenu | ||
| import com.eatssu.android.data.dto.response.toDomain | ||
| import com.eatssu.android.data.model.map | ||
| import com.eatssu.android.data.model.orEmptyList | ||
| import com.eatssu.android.data.service.MealService | ||
| import com.eatssu.android.domain.model.Menu | ||
| import com.eatssu.android.domain.repository.MealRepository | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.flow | ||
| import com.eatssu.common.enums.Restaurant | ||
| import com.eatssu.common.enums.Time | ||
| import javax.inject.Inject | ||
|
|
||
| class MealRepositoryImpl @Inject constructor( | ||
| private val mealService: MealService, | ||
| ) : MealRepository { | ||
|
|
||
| override suspend fun getTodayMeal( //todo 분기처리 어떻게 할지? | ||
| override suspend fun getTodayMeal( | ||
| date: String, | ||
| restaurant: String, | ||
| time: String | ||
| ): Flow<List<List<String>>> { | ||
| return flow { | ||
| try { | ||
| val response = mealService.getTodayMeal2(date, restaurant, time) | ||
| ): List<List<String>> { | ||
| return mealService.getTodayMeal(date, restaurant, time).orEmptyList().toDomain() | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 응답이 성공적이라면 Result.success()로 감싸서 Flow로 반환 | ||
| if (response.isSuccess == true) { | ||
| response.result?.let { emit(it.toDomain()) } // 성공시 데이터를 반환 | ||
| } else { | ||
| // 실패한 경우에는 Result.failure()로 실패 정보 반환 | ||
| emit(emptyList()) | ||
| } | ||
| } catch (e: Exception) { | ||
| // 네트워크 오류 또는 예외가 발생한 경우에는 Result.failure()로 반환 | ||
| // emit(ApiResult.Failure(e)) | ||
| } | ||
| } | ||
| override suspend fun getTodayMenuList( | ||
| date: String, | ||
| restaurant: Restaurant, | ||
| time: Time | ||
| ): List<Menu> { | ||
| return mealService.getTodayMeal(date, restaurant.toString(), time.toString()) | ||
| .map { it.mapTodayMenuResponseToMenu() } | ||
| .orEmptyList() | ||
| } | ||
|
|
||
| override suspend fun getMenuInfoByMealId(mealId: Long): Flow<BaseResponse<MenuOfMealResponse>> = | ||
| flow { | ||
| emit(mealService.getMenuInfoByMealId(mealId)) | ||
| } | ||
| override suspend fun getMenuInfoByMealId(mealId: Long): List<MenusInformation> = | ||
| mealService.getMenuInfoByMealId(mealId).map { it.briefMenus }.orEmptyList() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.dto.response.mapFixedMenuResponseToMenu | ||
| import com.eatssu.android.data.model.map | ||
| import com.eatssu.android.data.model.orEmptyList | ||
| import com.eatssu.android.data.service.MenuService | ||
| import com.eatssu.android.domain.model.Menu | ||
| import com.eatssu.android.domain.repository.MenuRepository | ||
| import com.eatssu.common.enums.Restaurant | ||
| import javax.inject.Inject | ||
|
|
||
| class MenuRepositoryImpl @Inject constructor( | ||
| private val menuService: MenuService | ||
| ) : MenuRepository { | ||
| override suspend fun getFixedMenuList(restaurant: Restaurant): List<Menu> { | ||
| return menuService.getFixMenu(restaurant.toString()) | ||
| .map { it.mapFixedMenuResponseToMenu() } | ||
| .orEmptyList() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,15 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.dto.request.ReportRequest | ||
| import com.eatssu.android.data.dto.response.BaseResponse | ||
| import com.eatssu.android.data.model.isSuccess | ||
| import com.eatssu.android.data.service.ReportService | ||
| import com.eatssu.android.domain.repository.ReportRepository | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.flow | ||
| import javax.inject.Inject | ||
|
|
||
| class ReportRepositoryImpl @Inject constructor(private val reportService: ReportService) : | ||
| ReportRepository { | ||
|
|
||
| override suspend fun reportReview(body: ReportRequest): Flow<BaseResponse<Void>> = | ||
| flow { | ||
| emit(reportService.reportReview(body)) | ||
| } | ||
| override suspend fun reportReview(body: ReportRequest): Boolean = | ||
| reportService.reportReview(body).isSuccess() | ||
|
|
||
| } |

Uh oh!
There was an error while loading. Please reload this page.