From f0bd8f55b0a452deb25dec4329224fae326f2c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Fri, 25 Jul 2025 21:58:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?#313=20-=20refactor=20:=20PolicyRepository?= =?UTF-8?q?=EC=9D=98=20getPolicyDetail=20=ED=95=A8=EC=88=98=EC=9D=98=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=EA=B0=92=EC=9D=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/PolicyRepositoryImpl.kt | 17 ++---- .../repository/PolicyRepositoryTest.kt | 52 +++++++++++++++++++ .../dataapi/repository/PolicyRepository.kt | 2 +- 3 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt diff --git a/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt b/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt index 9aedc322..c442345e 100644 --- a/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt +++ b/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt @@ -3,12 +3,12 @@ package com.youthtalk.repository import com.core.dataapi.repository.PolicyRepository import com.core.exception.NoDataException import com.youthtalk.data.PolicyService -import com.youthtalk.dto.PolicyDetailResponse import com.youthtalk.dto.policy.PolicyResponse import com.youthtalk.mapper.toData import com.youthtalk.mapper.toDomain import com.youthtalk.model.policy.Policy import com.youthtalk.model.policy.PolicyDetail +import com.youthtalk.utils.ErrorUtils.createResult import com.youthtalk.utils.ErrorUtils.throwableError import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -19,19 +19,8 @@ class PolicyRepositoryImpl @Inject constructor( private val policyService: PolicyService ) : PolicyRepository { - override fun getPolicyDetail(policyId: Long): Flow = flow { - runCatching { - policyService.getPolicyDetail(policyId) - } - .onSuccess { response -> - response.data?.let { - emit(it.toData()) - } ?: throw NoDataException("no Data") - } - .onFailure { - Timber.e("getPolicyDetail $it") - throwableError(it) - } + override suspend fun getPolicyDetail(policyId: Long): Result = createResult { + policyService.getPolicyDetail(policyId).data?.toData() ?: throw NoDataException() } override fun getRecentlyViewPolicies(): Flow> = flow { diff --git a/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt b/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt new file mode 100644 index 00000000..6512e678 --- /dev/null +++ b/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt @@ -0,0 +1,52 @@ +package com.youthtalk.repository + +import com.core.dataapi.repository.PolicyRepository +import com.youthtalk.data.PolicyService +import com.youthtalk.dto.CommonResponse +import com.youthtalk.dto.PolicyDetailResponse +import com.youthtalk.mapper.toData +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@RunWith(MockitoJUnitRunner::class) +class PolicyRepositoryTest { + + private lateinit var sut: PolicyRepository + + @Mock + private lateinit var policyService: PolicyService + + @Before + fun setUp() { + sut = PolicyRepositoryImpl(policyService) + } + + @Test + fun givenPolicyId_whenGetPolicyDetail_thenReturnsPolicy() { + runTest { + // given + val policyId = 3L + val policyDetailResponse = createPolicyDetailResponse() + + whenever(policyService.getPolicyDetail(policyId)).thenReturn(CommonResponse(200, "정책 조회에 성공하였습니다.", "S04", policyDetailResponse)) + + // when + val result = sut.getPolicyDetail(policyId).getOrThrow() + + // then + assertEquals(policyDetailResponse.toData(), result) + verify(policyService).getPolicyDetail(policyId) + } + } + + private fun createPolicyDetailResponse(): PolicyDetailResponse { + return PolicyDetailResponse("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", true, "", "") + } +} diff --git a/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt b/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt index 1b434be3..724088d7 100644 --- a/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt +++ b/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt @@ -5,6 +5,6 @@ import com.youthtalk.model.policy.PolicyDetail import kotlinx.coroutines.flow.Flow interface PolicyRepository { - fun getPolicyDetail(policyId: Long): Flow + suspend fun getPolicyDetail(policyId: Long): Result fun getRecentlyViewPolicies(): Flow> } From d4fd1cd21f91671e34c8a96fadd92ac9cf7f522e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Fri, 25 Jul 2025 21:59:59 +0900 Subject: [PATCH 2/4] =?UTF-8?q?#313=20-=20refactor=20:=20PolicyDetail?= =?UTF-8?q?=EC=9D=84=20=EB=B0=9B=EB=8A=94=20ViewModel=EC=97=90=EC=84=9C=20?= =?UTF-8?q?Result=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20=EA=B0=9C?= =?UTF-8?q?=EB=B3=84=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/usercase/policydetail/GetPolicyDetailUseCase.kt | 3 +-- .../main/java/com/core/home/viewmodel/NewPolicyViewModel.kt | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/domain/src/main/java/com/core/domain/usercase/policydetail/GetPolicyDetailUseCase.kt b/core/domain/src/main/java/com/core/domain/usercase/policydetail/GetPolicyDetailUseCase.kt index e3796533..5e9392cc 100644 --- a/core/domain/src/main/java/com/core/domain/usercase/policydetail/GetPolicyDetailUseCase.kt +++ b/core/domain/src/main/java/com/core/domain/usercase/policydetail/GetPolicyDetailUseCase.kt @@ -3,10 +3,9 @@ package com.core.domain.usercase.policydetail import com.core.dataapi.repository.PolicyRepository import com.youthtalk.model.policy.PolicyDetail import javax.inject.Inject -import kotlinx.coroutines.flow.Flow class GetPolicyDetailUseCase @Inject constructor( private val policyRepository: PolicyRepository ) { - operator fun invoke(policyId: Long): Flow = policyRepository.getPolicyDetail(policyId) + suspend operator fun invoke(policyId: Long): Result = policyRepository.getPolicyDetail(policyId) } diff --git a/feature/home/src/main/java/com/core/home/viewmodel/NewPolicyViewModel.kt b/feature/home/src/main/java/com/core/home/viewmodel/NewPolicyViewModel.kt index 98d72093..81df4e28 100644 --- a/feature/home/src/main/java/com/core/home/viewmodel/NewPolicyViewModel.kt +++ b/feature/home/src/main/java/com/core/home/viewmodel/NewPolicyViewModel.kt @@ -62,10 +62,7 @@ class NewPolicyViewModel @Inject constructor( viewModelScope.launch { state.value.policyId?.let { policyId -> getPolicyDetailUseCase(policyId) - .catch { - Timber.e("NewPolicyViewModel refresh error $it") - } - .collectLatest { policyDetail -> + .onSuccess { policyDetail -> setState { copy( newPolicies = newPolicies.copy( From 19b2219570f271b44c01a2009c20055744fc109a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Fri, 25 Jul 2025 22:04:43 +0900 Subject: [PATCH 3/4] =?UTF-8?q?#313=20-=20refactor=20:=20PolicyRepository?= =?UTF-8?q?=EC=9D=98=20=EC=B5=9C=EA=B7=BC=EC=97=90=20=EB=B3=B8=20=EC=A0=95?= =?UTF-8?q?=EC=B1=85=20=ED=95=A8=EC=88=98=EC=9D=98=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20flow=EC=97=90=EC=84=9C=20Result=EB=A1=9C?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/PolicyRepositoryImpl.kt | 20 ++----------------- .../repository/PolicyRepositoryTest.kt | 16 +++++++++++++++ .../dataapi/repository/PolicyRepository.kt | 3 +-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt b/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt index c442345e..2e77b3e6 100644 --- a/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt +++ b/core/data/src/main/java/com/youthtalk/repository/PolicyRepositoryImpl.kt @@ -3,17 +3,12 @@ package com.youthtalk.repository import com.core.dataapi.repository.PolicyRepository import com.core.exception.NoDataException import com.youthtalk.data.PolicyService -import com.youthtalk.dto.policy.PolicyResponse import com.youthtalk.mapper.toData import com.youthtalk.mapper.toDomain import com.youthtalk.model.policy.Policy import com.youthtalk.model.policy.PolicyDetail import com.youthtalk.utils.ErrorUtils.createResult -import com.youthtalk.utils.ErrorUtils.throwableError import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import timber.log.Timber class PolicyRepositoryImpl @Inject constructor( private val policyService: PolicyService @@ -23,18 +18,7 @@ class PolicyRepositoryImpl @Inject constructor( policyService.getPolicyDetail(policyId).data?.toData() ?: throw NoDataException() } - override fun getRecentlyViewPolicies(): Flow> = flow { - runCatching { - policyService.getRecentlyViewPolicies() - } - .onSuccess { response -> - response.data?.let { data -> - emit(data.map { it.toDomain() }) - } ?: throw NoDataException("no Data") - } - .onFailure { - Timber.e("PolicyRepositoryImpl getRecentlyViewPolicies error $it") - throwableError(it) - } + override suspend fun getRecentlyViewPolicies(): Result> = createResult { + policyService.getRecentlyViewPolicies().data?.map { it.toDomain() } ?: throw NoDataException() } } diff --git a/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt b/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt index 6512e678..3f1bfa43 100644 --- a/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt +++ b/core/data/src/test/java/com/youthtalk/repository/PolicyRepositoryTest.kt @@ -5,6 +5,7 @@ import com.youthtalk.data.PolicyService import com.youthtalk.dto.CommonResponse import com.youthtalk.dto.PolicyDetailResponse import com.youthtalk.mapper.toData +import com.youthtalk.model.policy.Policy import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Before @@ -46,6 +47,21 @@ class PolicyRepositoryTest { } } + @Test + fun given_whenGetRecentViewPolicy_thenReturnsPolicy() { + runTest { + // given + whenever(policyService.getRecentlyViewPolicies()).thenReturn(CommonResponse(200, "요청에 성공하였습니다.", "S01", listOf())) + + // when + val result = sut.getRecentlyViewPolicies().getOrThrow() + + // then + assertEquals(listOf(), result) + verify(policyService).getRecentlyViewPolicies() + } + } + private fun createPolicyDetailResponse(): PolicyDetailResponse { return PolicyDetailResponse("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", true, "", "") } diff --git a/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt b/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt index 724088d7..209ea3f9 100644 --- a/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt +++ b/core/dataApi/src/main/java/com/core/dataapi/repository/PolicyRepository.kt @@ -2,9 +2,8 @@ package com.core.dataapi.repository import com.youthtalk.model.policy.Policy import com.youthtalk.model.policy.PolicyDetail -import kotlinx.coroutines.flow.Flow interface PolicyRepository { suspend fun getPolicyDetail(policyId: Long): Result - fun getRecentlyViewPolicies(): Flow> + suspend fun getRecentlyViewPolicies(): Result> } From e520dd60a3c0886591a845121e40c15367984d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Fri, 25 Jul 2025 22:15:02 +0900 Subject: [PATCH 4/4] =?UTF-8?q?#313=20-=20feat=20:=20=EC=B5=9C=EA=B7=BC?= =?UTF-8?q?=EC=97=90=20=EB=B3=B8=20=EC=A0=95=EC=B1=85=EA=B0=92=EC=9D=84=20?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=20ViewModel,=20UseCase=EB=A5=BC=20=20Result?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=9C=EB=B3=84=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../policy/GetRecentlyViewPolicesUseCase.kt | 2 +- .../home/viewmodel/PopularPolicyViewModel.kt | 5 +-- .../policy/viewmodel/PolicyViewModel.kt | 16 ++++----- .../viewmodel/RecentlyViewPolicyViewModel.kt | 10 +++--- .../viewmode/PolicyDetailViewModel.kt | 33 +++++++------------ 5 files changed, 24 insertions(+), 42 deletions(-) diff --git a/core/domain/src/main/java/com/core/domain/usercase/policy/GetRecentlyViewPolicesUseCase.kt b/core/domain/src/main/java/com/core/domain/usercase/policy/GetRecentlyViewPolicesUseCase.kt index 1d04cfc3..e1efba70 100644 --- a/core/domain/src/main/java/com/core/domain/usercase/policy/GetRecentlyViewPolicesUseCase.kt +++ b/core/domain/src/main/java/com/core/domain/usercase/policy/GetRecentlyViewPolicesUseCase.kt @@ -6,5 +6,5 @@ import javax.inject.Inject class GetRecentlyViewPolicesUseCase @Inject constructor( private val policyRepository: PolicyRepository ) { - operator fun invoke() = policyRepository.getRecentlyViewPolicies() + suspend operator fun invoke() = policyRepository.getRecentlyViewPolicies() } diff --git a/feature/home/src/main/java/com/core/home/viewmodel/PopularPolicyViewModel.kt b/feature/home/src/main/java/com/core/home/viewmodel/PopularPolicyViewModel.kt index a5145410..334f64cb 100644 --- a/feature/home/src/main/java/com/core/home/viewmodel/PopularPolicyViewModel.kt +++ b/feature/home/src/main/java/com/core/home/viewmodel/PopularPolicyViewModel.kt @@ -50,10 +50,7 @@ class PopularPolicyViewModel @Inject constructor( viewModelScope.launch { state.value.policyId?.let { policyId -> getPolicyDetailUseCase(policyId) - .catch { - Timber.e("PopularPolicyViewModel refresh error $it") - } - .collectLatest { policyDetail -> + .onSuccess { policyDetail -> setState { copy( policies = policies.map { policy -> diff --git a/feature/policy/src/main/java/com/feature/policy/viewmodel/PolicyViewModel.kt b/feature/policy/src/main/java/com/feature/policy/viewmodel/PolicyViewModel.kt index ce1ec75f..685e654a 100644 --- a/feature/policy/src/main/java/com/feature/policy/viewmodel/PolicyViewModel.kt +++ b/feature/policy/src/main/java/com/feature/policy/viewmodel/PolicyViewModel.kt @@ -68,8 +68,7 @@ class PolicyViewModel @Inject constructor( setState { copy( recentlyPolicies = recentlyPolicies - .map { - policy -> + .map { policy -> if (policy.policyId == policyId) policy.copy(scrap = !scrap) else policy } ) @@ -81,13 +80,12 @@ class PolicyViewModel @Inject constructor( private fun refresh() { viewModelScope.launch { getRecentlyViewPolicesUseCase() - .catch { - Timber.e("PolicyViewModel refresh error $it") - } - .collectLatest { policies -> + .onSuccess { policies -> setState { copy(recentlyPolicies = policies) } + }.onFailure { + Timber.e("error $it") } } } @@ -164,8 +162,8 @@ class PolicyViewModel @Inject constructor( private fun initData() { val today = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(state.value.selectedDay) viewModelScope.launch { + val recentViewPolicies = getRecentlyViewPolicesUseCase() combine( - getRecentlyViewPolicesUseCase(), getUserUseCase(), combine( postSpecPoliciesUseCase(SearchFilter(applyDue = today), PolicyType.POLICY_TAB_DEADLINE), @@ -179,10 +177,10 @@ class PolicyViewModel @Inject constructor( ) { categoryPolicies, allCount -> Pair(categoryPolicies, allCount) } - ) { recentlyViewPolicies, user, deadlineInfo, categoryInfo -> + ) { user, deadlineInfo, categoryInfo -> PolicyUiState.initState.copy( user = user, - recentlyPolicies = recentlyViewPolicies, + recentlyPolicies = recentViewPolicies.getOrThrow(), deadlinePolicies = deadlineInfo.first.cachedIn(viewModelScope), deadlineCount = deadlineInfo.second, allCount = categoryInfo.second, diff --git a/feature/policy/src/main/java/com/feature/policy/viewmodel/RecentlyViewPolicyViewModel.kt b/feature/policy/src/main/java/com/feature/policy/viewmodel/RecentlyViewPolicyViewModel.kt index fcb55887..3f5de79c 100644 --- a/feature/policy/src/main/java/com/feature/policy/viewmodel/RecentlyViewPolicyViewModel.kt +++ b/feature/policy/src/main/java/com/feature/policy/viewmodel/RecentlyViewPolicyViewModel.kt @@ -59,8 +59,7 @@ class RecentlyViewPolicyViewModel @Inject constructor( setState { copy( policies = policies - .map { - policy -> + .map { policy -> if (policy.policyId == policyId) policy.copy(scrap = !scrap) else policy } ) @@ -72,11 +71,10 @@ class RecentlyViewPolicyViewModel @Inject constructor( private fun initData() { viewModelScope.launch { getRecentlyViewPolicesUseCase() - .catch { - Timber.e("RecentlyViewPolicyViewModel initData error $it") - } - .collectLatest { + .onSuccess { setState { copy(isLoading = false, policies = it) } + }.onFailure { + Timber.e("error : $it") } } } diff --git a/feature/policydetail/src/main/java/com/feature/policydetail/viewmode/PolicyDetailViewModel.kt b/feature/policydetail/src/main/java/com/feature/policydetail/viewmode/PolicyDetailViewModel.kt index 587c4ad3..ed1733ab 100644 --- a/feature/policydetail/src/main/java/com/feature/policydetail/viewmode/PolicyDetailViewModel.kt +++ b/feature/policydetail/src/main/java/com/feature/policydetail/viewmode/PolicyDetailViewModel.kt @@ -21,7 +21,6 @@ import java.time.LocalDateTime import javax.inject.Inject import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import timber.log.Timber @@ -197,29 +196,19 @@ class PolicyDetailViewModel @Inject constructor( private fun initData(policyId: Long) { viewModelScope.launch { val commentInfo = getPolicyDetailCommentUseCase(policyId) + val policyDetail = getPolicyDetailUseCase(policyId) - if (commentInfo.isFailure) { - Timber.e("commentInfoError ${commentInfo.exceptionOrNull()?.message}") - return@launch - } - - combine( - getPolicyDetailUseCase(policyId), - getUserUseCase() - ) { policyDetail, user -> - PolicyDetailUiState( - isLoading = false, - user = user, - policyDetail = policyDetail, - commentInfo = commentInfo.getOrThrow(), - policyId = policyId - ) - } + getUserUseCase() .catch { - Timber.e("PolicyDetailViewModel initData error $it") - } - .collectLatest { - setState { it } + Timber.e("error $it") + }.collectLatest { user -> + PolicyDetailUiState( + isLoading = false, + user = user, + policyDetail = policyDetail.getOrThrow(), + commentInfo = commentInfo.getOrThrow(), + policyId = policyId + ) } } }