diff --git a/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicApiErrorParser.kt b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicApiErrorParser.kt new file mode 100644 index 000000000..9b35ba325 --- /dev/null +++ b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicApiErrorParser.kt @@ -0,0 +1,40 @@ +package com.mashup.gabbangzip.sharedalbum.data.base + +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import okhttp3.ResponseBody +import retrofit2.HttpException + +object PicApiErrorParser { + private val moshi = Moshi.Builder().build() + + // PicResponse의 타입을 동적으로 처리하기 위해 ParameterizedType 생성 + private val type = Types.newParameterizedType(PicResponse::class.java, Any::class.java) + private val adapter = moshi.adapter>(type) + + fun parse(e: Throwable): Throwable { + return when (e) { + is HttpException -> { + val error = e.response() + ?.errorBody() + ?.let { parseErrorResponse(it) } + PicApiException(error) + } + + else -> e + } + } + + private fun parseErrorResponse(errorResponseBody: ResponseBody): PicErrorResponse? { + return try { + val response = adapter.fromJson(errorResponseBody.string()) + if (response != null && !response.isSuccess) { + response.errorResponse + } else { + null + } + } catch (e: Exception) { + null + } + } +} diff --git a/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicResponse.kt b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicResponse.kt index fc59b61ee..96891d966 100644 --- a/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicResponse.kt +++ b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/base/PicResponse.kt @@ -21,8 +21,19 @@ data class PicErrorResponse( val message: String, ) +data class PicApiException( + val errorResponse: PicErrorResponse?, +) : RuntimeException() { + override val message: String + get() = errorResponse?.message.orEmpty() +} + suspend fun callApi( execute: suspend () -> PicResponse, ): T { - return execute().data ?: throw IllegalStateException() + return runCatching { + execute().data ?: throw NoSuchElementException() + }.getOrElse { e -> + throw PicApiErrorParser.parse(e) + } } diff --git a/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/repository/GroupRepositoryImpl.kt b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/repository/GroupRepositoryImpl.kt index 9481f0078..a66b5644f 100644 --- a/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/com/mashup/gabbangzip/sharedalbum/data/repository/GroupRepositoryImpl.kt @@ -1,11 +1,13 @@ package com.mashup.gabbangzip.sharedalbum.data.repository +import com.mashup.gabbangzip.sharedalbum.data.base.PicApiException import com.mashup.gabbangzip.sharedalbum.data.base.callApi import com.mashup.gabbangzip.sharedalbum.data.common.toS3Url import com.mashup.gabbangzip.sharedalbum.data.dto.request.CreateGroupRequest import com.mashup.gabbangzip.sharedalbum.data.dto.request.EnterGroupRequest import com.mashup.gabbangzip.sharedalbum.data.dto.response.group.toDomainModel import com.mashup.gabbangzip.sharedalbum.data.service.GroupService +import com.mashup.gabbangzip.sharedalbum.domain.base.PicException import com.mashup.gabbangzip.sharedalbum.domain.model.GroupParam import com.mashup.gabbangzip.sharedalbum.domain.model.group.GroupDomainModel import com.mashup.gabbangzip.sharedalbum.domain.model.group.GroupInfoDomainModel @@ -37,7 +39,19 @@ class GroupRepositoryImpl @Inject constructor( override suspend fun enterGroupByCode(code: String): Long { val request = EnterGroupRequest(code) - return callApi { groupService.enterGroupByCode(request) }.groupId + return runCatching { + callApi { groupService.enterGroupByCode(request) }.groupId + }.getOrElse { e -> + throw if (e is PicApiException && e.errorResponse != null) { + when (e.errorResponse.code) { + CODE_NOT_EXIST -> PicException.InvalidGroupCodeException + CODE_ARGUMENT_NOT_VALID -> PicException.GroupOverflowException + else -> e + } + } else { + e + } + } } override suspend fun getGroupList(): List { @@ -55,4 +69,9 @@ class GroupRepositoryImpl @Inject constructor( override suspend fun withdrawGroup(groupId: Long): WithdrawalGroupDomainModel { return callApi { groupService.withdrawGroup(groupId) }.toDomainModel() } + + companion object { + private const val CODE_NOT_EXIST = "C001_NOT_EXIST" + private const val CODE_ARGUMENT_NOT_VALID = "C009_ARGUMENT_NOT_VALID" + } } diff --git a/domain/src/main/java/com/mashup/gabbangzip/sharedalbum/domain/base/PicException.kt b/domain/src/main/java/com/mashup/gabbangzip/sharedalbum/domain/base/PicException.kt index e506ffd7c..ff53519c7 100644 --- a/domain/src/main/java/com/mashup/gabbangzip/sharedalbum/domain/base/PicException.kt +++ b/domain/src/main/java/com/mashup/gabbangzip/sharedalbum/domain/base/PicException.kt @@ -3,4 +3,6 @@ package com.mashup.gabbangzip.sharedalbum.domain.base sealed class PicException : Throwable() { data object LoginRequiredException : PicException() data object UnknownException : PicException() + data object GroupOverflowException : PicException() + data object InvalidGroupCodeException : PicException() } diff --git a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeActivity.kt b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeActivity.kt index 2f3ee6d09..f0a1ac37b 100644 --- a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeActivity.kt +++ b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeActivity.kt @@ -18,7 +18,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.mashup.gabbangzip.sharedalbum.presentation.R import com.mashup.gabbangzip.sharedalbum.presentation.theme.SharedAlbumTheme import com.mashup.gabbangzip.sharedalbum.presentation.ui.common.PicSnackbarHost import com.mashup.gabbangzip.sharedalbum.presentation.ui.common.model.PicSnackbarType @@ -59,11 +58,13 @@ class InvitationCodeActivity : ComponentActivity() { when (state.isInvitationSuccessful) { true -> navigateToMainActivity() false -> { - LaunchedEffect(snackbarHostState) { - snackbarHostState.showPicSnackbar( - type = PicSnackbarType.WARNING, - message = getString(R.string.enter_group_by_code_failure_message), - ) + LaunchedEffect(state.errorMessageRes) { + state.errorMessageRes?.let { message -> + snackbarHostState.showPicSnackbar( + type = PicSnackbarType.WARNING, + message = getString(message), + ) + } } } diff --git a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeUiState.kt b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeUiState.kt index d69558806..bbf16fc5a 100644 --- a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeUiState.kt +++ b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeUiState.kt @@ -1,6 +1,9 @@ package com.mashup.gabbangzip.sharedalbum.presentation.ui.invitation +import androidx.annotation.StringRes + data class InvitationCodeUiState( val isLoading: Boolean = false, val isInvitationSuccessful: Boolean? = null, + @StringRes val errorMessageRes: Int? = null, ) diff --git a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeViewModel.kt b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeViewModel.kt index 931811419..d48243779 100644 --- a/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeViewModel.kt +++ b/presentation/src/main/java/com/mashup/gabbangzip/sharedalbum/presentation/ui/invitation/InvitationCodeViewModel.kt @@ -2,7 +2,9 @@ package com.mashup.gabbangzip.sharedalbum.presentation.ui.invitation import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.mashup.gabbangzip.sharedalbum.domain.base.PicException import com.mashup.gabbangzip.sharedalbum.domain.usecase.invitation.EnterGroupByInvitationCodeUseCase +import com.mashup.gabbangzip.sharedalbum.presentation.R import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -21,7 +23,7 @@ class InvitationCodeViewModel @Inject constructor( _uiState.update { state -> state.copy( isLoading = true, - isInvitationSuccessful = null, + errorMessageRes = null, ) } viewModelScope.launch { @@ -39,9 +41,18 @@ class InvitationCodeViewModel @Inject constructor( state.copy( isLoading = false, isInvitationSuccessful = false, + errorMessageRes = mapErrorMessageRes(e), ) } } } } + + private fun mapErrorMessageRes(e: Throwable): Int { + return when (e) { + is PicException.InvalidGroupCodeException -> R.string.enter_group_by_code_failure_not_found + is PicException.GroupOverflowException -> R.string.enter_group_by_code_failure_overflow + else -> R.string.error_network + } + } } diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 4ea7a9185..d814558f0 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -122,7 +122,8 @@ 그룹 들어가기 초대코드를 입력해주세요 예) A12B0EHQ - 존재하지 않는 초대코드에요. + 존재하지 않는 초대코드에요. + 그룹이 가득 찼어요. 내 PIC을 골랐어요! 모든 사람이 PIC을 완료하면\n네컷 사진이 만들어져요