Skip to content

Commit 296a7f3

Browse files
authored
Merge pull request #568 from projects200/refactor/fcm-repository-naming-566
[Refactor] POST login 호출을 FcmRepository.registerToken으로 이동
2 parents 99f1e98 + 95ef800 commit 296a7f3

14 files changed

Lines changed: 92 additions & 292 deletions

File tree

app/src/main/java/com/project200/undabang/main/MainViewModel.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package com.project200.undabang.main
33
import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.project200.common.utils.NetworkMonitor
6-
import com.project200.domain.model.BaseResult
6+
import com.project200.domain.model.FcmTokenSyncResult
77
import com.project200.domain.model.UpdateCheckResult
88
import com.project200.domain.usecase.CheckForUpdateUseCase
99
import com.project200.domain.usecase.ClearSessionUseCase
1010
import com.project200.domain.usecase.GetMemberIdUseCase
11-
import com.project200.domain.usecase.LoginUseCase
11+
import com.project200.domain.usecase.SyncFcmTokenUseCase
1212
import com.project200.undabang.oauth.AuthManager
1313
import com.project200.undabang.oauth.AuthStateManager
1414
import com.project200.undabang.oauth.TokenRefreshResult
@@ -30,7 +30,7 @@ class MainViewModel
3030
@Inject
3131
constructor(
3232
private val checkForUpdateUseCase: CheckForUpdateUseCase,
33-
private val loginUseCase: LoginUseCase,
33+
private val syncFcmTokenUseCase: SyncFcmTokenUseCase,
3434
private val networkMonitor: NetworkMonitor,
3535
private val authManager: AuthManager,
3636
private val authStateManager: AuthStateManager,
@@ -47,7 +47,7 @@ class MainViewModel
4747
private var wasOffline = !networkMonitor.isCurrentlyConnected()
4848

4949
private var registrationJob: Job? = null
50-
private var serverLoginPending = false
50+
private var registrationPending = false
5151

5252
init {
5353
observeNetworkReconnection()
@@ -145,7 +145,7 @@ class MainViewModel
145145
viewModelScope.launch {
146146
networkMonitor.networkState.collect { isOnline ->
147147
if (isOnline && wasOffline && _entryState.value is EntryState.Content) {
148-
if (serverLoginPending) {
148+
if (registrationPending) {
149149
ensureFcmRegistration() // 실패했던 FCM 등록 재시도
150150
}
151151
recheckForUpdateOnReconnect()
@@ -172,17 +172,17 @@ class MainViewModel
172172

173173
// FCM 토큰 등록
174174
private fun ensureFcmRegistration() {
175-
// 재시도 중 재연결이 겹치면 POST /login이 중복 발행 방지
175+
// 재시도 중 재연결이 겹치면 등록 요청이 중복 발행되는 것을 방지
176176
if (registrationJob?.isActive == true) return
177-
serverLoginPending = true
177+
registrationPending = true
178178
registrationJob =
179179
viewModelScope.launch {
180-
val result = loginUseCase()
181-
if (result is BaseResult.Success) {
182-
serverLoginPending = false
183-
Timber.d("서버 로그인(FCM 등록) 성공")
180+
// SKIPPED는 보낼 토큰이 없는 상태 - 대기를 닫지 않고 재연결 때 재시도
181+
if (syncFcmTokenUseCase() == FcmTokenSyncResult.SUCCESS) {
182+
registrationPending = false
183+
Timber.d("FCM 토큰 등록 성공")
184184
} else {
185-
Timber.w("서버 로그인 실패 - 재연결 시 재시도 예정")
185+
Timber.w("FCM 토큰 등록 실패 - 재연결 시 재시도 예정")
186186
}
187187
}
188188
}

app/src/test/java/com/project200/undabang/main/MainViewModelTest.kt

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package com.project200.undabang.main
22

33
import com.google.common.truth.Truth.assertThat
44
import com.project200.common.utils.NetworkMonitor
5-
import com.project200.domain.model.BaseResult
5+
import com.project200.domain.model.FcmTokenSyncResult
66
import com.project200.domain.model.UpdateCheckResult
77
import com.project200.domain.usecase.CheckForUpdateUseCase
88
import com.project200.domain.usecase.ClearSessionUseCase
99
import com.project200.domain.usecase.GetMemberIdUseCase
10-
import com.project200.domain.usecase.LoginUseCase
10+
import com.project200.domain.usecase.SyncFcmTokenUseCase
1111
import com.project200.undabang.oauth.AuthManager
1212
import com.project200.undabang.oauth.AuthStateManager
1313
import com.project200.undabang.oauth.TokenRefreshResult
@@ -43,7 +43,7 @@ class MainViewModelTest {
4343
private lateinit var mockCheckForUpdateUseCase: CheckForUpdateUseCase
4444

4545
@MockK
46-
private lateinit var mockLoginUseCase: LoginUseCase
46+
private lateinit var mockSyncFcmTokenUseCase: SyncFcmTokenUseCase
4747

4848
@MockK
4949
private lateinit var mockNetworkMonitor: NetworkMonitor
@@ -85,7 +85,7 @@ class MainViewModelTest {
8585
private fun createViewModel(): MainViewModel {
8686
return MainViewModel(
8787
checkForUpdateUseCase = mockCheckForUpdateUseCase,
88-
loginUseCase = mockLoginUseCase,
88+
syncFcmTokenUseCase = mockSyncFcmTokenUseCase,
8989
networkMonitor = mockNetworkMonitor,
9090
authManager = mockAuthManager,
9191
authStateManager = mockAuthStateManager,
@@ -122,7 +122,7 @@ class MainViewModelTest {
122122
// Given
123123
stubNoUpdate()
124124
stubAuthState(isAuthorized = true, needsRefresh = false)
125-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
125+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
126126

127127
// When: init이 진입을 시작한다 — 별도 호출 없음
128128
viewModel = createViewModel()
@@ -131,7 +131,7 @@ class MainViewModelTest {
131131
// Then
132132
assertThat(viewModel.entryState.value).isEqualTo(EntryState.Content)
133133
coVerify(exactly = 1) { mockCheckForUpdateUseCase() }
134-
coVerify(exactly = 1) { mockLoginUseCase() }
134+
coVerify(exactly = 1) { mockSyncFcmTokenUseCase() }
135135
}
136136

137137
@Test
@@ -147,7 +147,7 @@ class MainViewModelTest {
147147

148148
// Then
149149
assertThat(viewModel.entryState.value).isEqualTo(EntryState.Login)
150-
coVerify(exactly = 0) { mockLoginUseCase() }
150+
coVerify(exactly = 0) { mockSyncFcmTokenUseCase() }
151151
}
152152

153153
@Test
@@ -157,7 +157,7 @@ class MainViewModelTest {
157157
stubNoUpdate()
158158
stubAuthState(isAuthorized = true, needsRefresh = true)
159159
coEvery { mockAuthManager.refreshAccessToken() } returns TokenRefreshResult.Success(mockk())
160-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
160+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
161161

162162
// When
163163
viewModel = createViewModel()
@@ -182,7 +182,7 @@ class MainViewModelTest {
182182

183183
// Then
184184
assertThat(viewModel.entryState.value).isEqualTo(EntryState.Login)
185-
coVerify(exactly = 0) { mockLoginUseCase() }
185+
coVerify(exactly = 0) { mockSyncFcmTokenUseCase() }
186186
}
187187

188188
@Test
@@ -193,7 +193,7 @@ class MainViewModelTest {
193193
stubAuthState(isAuthorized = true, needsRefresh = true)
194194
coEvery { mockAuthManager.refreshAccessToken() } returns
195195
TokenRefreshResult.Error(AuthorizationException.GeneralErrors.NETWORK_ERROR)
196-
coEvery { mockLoginUseCase() } returns BaseResult.Error("NETWORK_ERROR", "오프라인")
196+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.FAILURE
197197

198198
// When
199199
viewModel = createViewModel()
@@ -232,7 +232,7 @@ class MainViewModelTest {
232232

233233
// Then
234234
assertThat(viewModel.entryState.value).isEqualTo(EntryState.Login)
235-
coVerify(exactly = 0) { mockLoginUseCase() }
235+
coVerify(exactly = 0) { mockSyncFcmTokenUseCase() }
236236
}
237237

238238
@Test
@@ -265,7 +265,7 @@ class MainViewModelTest {
265265

266266
// Then: 게이트 닫힘 — 토큰 판정·FCM 등록 미진행
267267
assertThat(viewModel.entryState.value).isEqualTo(EntryState.ForceUpdate(fromReconnect = false))
268-
coVerify(exactly = 0) { mockLoginUseCase() }
268+
coVerify(exactly = 0) { mockSyncFcmTokenUseCase() }
269269
}
270270

271271
@Test
@@ -275,7 +275,7 @@ class MainViewModelTest {
275275
coEvery { mockCheckForUpdateUseCase() } returns
276276
Result.success(UpdateCheckResult.UpdateAvailable(isForceUpdate = false))
277277
stubAuthState(isAuthorized = true)
278-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
278+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
279279

280280
// When
281281
viewModel = createViewModel()
@@ -296,7 +296,7 @@ class MainViewModelTest {
296296
// Given
297297
coEvery { mockCheckForUpdateUseCase() } returns Result.failure(Exception("Network error"))
298298
stubAuthState(isAuthorized = true)
299-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
299+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
300300

301301
// When
302302
viewModel = createViewModel()
@@ -314,7 +314,7 @@ class MainViewModelTest {
314314
// Given: 정상 진입 완료
315315
stubNoUpdate()
316316
stubAuthState(isAuthorized = true)
317-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
317+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
318318
viewModel = createViewModel()
319319
testDispatcher.scheduler.advanceUntilIdle()
320320
assertThat(viewModel.entryState.value).isEqualTo(EntryState.Content)
@@ -337,7 +337,7 @@ class MainViewModelTest {
337337
forceLogoutFlow.emit(Unit) // 갱신 도중 강제 로그아웃 발생
338338
TokenRefreshResult.Error(AuthorizationException.GeneralErrors.NETWORK_ERROR) // 진입은 오프라인 경로로 완주 시도
339339
}
340-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
340+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
341341

342342
// When
343343
viewModel = createViewModel()
@@ -356,7 +356,7 @@ class MainViewModelTest {
356356
every { mockNetworkMonitor.isCurrentlyConnected() } returns false
357357
stubNoUpdate()
358358
stubAuthState(isAuthorized = true)
359-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
359+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
360360
viewModel = createViewModel()
361361
testDispatcher.scheduler.advanceUntilIdle()
362362

@@ -374,7 +374,7 @@ class MainViewModelTest {
374374
// Given: 온라인 시작(setUp 기본값)으로 Content 진입
375375
stubNoUpdate()
376376
stubAuthState(isAuthorized = true)
377-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
377+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
378378
viewModel = createViewModel()
379379
testDispatcher.scheduler.advanceUntilIdle()
380380

@@ -419,7 +419,7 @@ class MainViewModelTest {
419419
Result.success(UpdateCheckResult.NoUpdateNeeded) andThen
420420
Result.success(UpdateCheckResult.UpdateAvailable(isForceUpdate = true))
421421
stubAuthState(isAuthorized = true)
422-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
422+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
423423
viewModel = createViewModel()
424424
testDispatcher.scheduler.advanceUntilIdle()
425425

@@ -440,7 +440,7 @@ class MainViewModelTest {
440440
Result.success(UpdateCheckResult.NoUpdateNeeded) andThen
441441
Result.failure(Exception("Network error"))
442442
stubAuthState(isAuthorized = true)
443-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
443+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
444444
viewModel = createViewModel()
445445
testDispatcher.scheduler.advanceUntilIdle()
446446

@@ -461,9 +461,9 @@ class MainViewModelTest {
461461
// Given: 처음엔 실패, 이후엔 성공
462462
stubNoUpdate()
463463
stubAuthState(isAuthorized = true)
464-
coEvery { mockLoginUseCase() } returns
465-
BaseResult.Error("NETWORK_ERROR", "네트워크 오류") andThen
466-
BaseResult.Success(Unit)
464+
coEvery { mockSyncFcmTokenUseCase() } returns
465+
FcmTokenSyncResult.FAILURE andThen
466+
FcmTokenSyncResult.SUCCESS
467467
viewModel = createViewModel()
468468
testDispatcher.scheduler.advanceUntilIdle()
469469

@@ -473,7 +473,7 @@ class MainViewModelTest {
473473
testDispatcher.scheduler.advanceUntilIdle()
474474

475475
// Then: 진입 1회 + 재시도 1회
476-
coVerify(exactly = 2) { mockLoginUseCase() }
476+
coVerify(exactly = 2) { mockSyncFcmTokenUseCase() }
477477
}
478478

479479
@Test
@@ -482,7 +482,7 @@ class MainViewModelTest {
482482
// Given
483483
stubNoUpdate()
484484
stubAuthState(isAuthorized = true)
485-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
485+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
486486
viewModel = createViewModel()
487487
testDispatcher.scheduler.advanceUntilIdle()
488488

@@ -492,18 +492,18 @@ class MainViewModelTest {
492492
testDispatcher.scheduler.advanceUntilIdle()
493493

494494
// Then: 진입 1회뿐
495-
coVerify(exactly = 1) { mockLoginUseCase() }
495+
coVerify(exactly = 1) { mockSyncFcmTokenUseCase() }
496496
}
497497

498498
@Test
499-
fun `FCM 등록 - 토큰이 없어 실패하면 대기가 닫히지 않고 재연결 때 재시도한다`() =
499+
fun `FCM 등록 - 보낼 토큰이 없으면 대기가 닫히지 않고 재연결 때 재시도한다`() =
500500
runTest {
501-
// Given: FCM 토큰이 없어 서버 로그인이 막힌다
501+
// Given: 저장된 토큰이 없어 SKIPPED로 끝난다. 그사이 토큰이 도착하면 다음엔 성공한다
502502
stubNoUpdate()
503503
stubAuthState(isAuthorized = true)
504-
coEvery { mockLoginUseCase() } returns
505-
BaseResult.Error("NO_FCM_TOKEN", "FCM 토큰이 없어 서버 로그인을 보내지 않았습니다.") andThen
506-
BaseResult.Success(Unit)
504+
coEvery { mockSyncFcmTokenUseCase() } returns
505+
FcmTokenSyncResult.SKIPPED andThen
506+
FcmTokenSyncResult.SUCCESS
507507
viewModel = createViewModel()
508508
testDispatcher.scheduler.advanceUntilIdle()
509509

@@ -512,8 +512,8 @@ class MainViewModelTest {
512512
networkStateFlow.emit(true)
513513
testDispatcher.scheduler.advanceUntilIdle()
514514

515-
// Then: 성공으로 닫히지 않았으므로 재시도가 한 번 더 나간다
516-
coVerify(exactly = 2) { mockLoginUseCase() }
515+
// Then: SKIPPED는 성공이 아니므로 대기가 열려 있고 재시도가 나간다
516+
coVerify(exactly = 2) { mockSyncFcmTokenUseCase() }
517517
}
518518

519519
@Test
@@ -522,9 +522,9 @@ class MainViewModelTest {
522522
// Given: 등록 요청이 느리게 진행 중
523523
stubNoUpdate()
524524
stubAuthState(isAuthorized = true)
525-
coEvery { mockLoginUseCase() } coAnswers {
525+
coEvery { mockSyncFcmTokenUseCase() } coAnswers {
526526
delay(10_000) // 응답 지연 — 이 사이 재연결이 겹친다
527-
BaseResult.Error("NETWORK_ERROR", "타임아웃")
527+
FcmTokenSyncResult.FAILURE
528528
}
529529
viewModel = createViewModel()
530530
testDispatcher.scheduler.runCurrent() // 진입 시작 → 등록 요청 발사(지연 중)
@@ -535,7 +535,7 @@ class MainViewModelTest {
535535
testDispatcher.scheduler.runCurrent()
536536

537537
// Then: 실행 중 가드(registrationJob.isActive)가 두 번째 발사를 막는다
538-
coVerify(exactly = 1) { mockLoginUseCase() }
538+
coVerify(exactly = 1) { mockSyncFcmTokenUseCase() }
539539
}
540540

541541
@Test
@@ -544,7 +544,7 @@ class MainViewModelTest {
544544
// Given
545545
stubNoUpdate()
546546
stubAuthState(isAuthorized = true)
547-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
547+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
548548
every { mockNetworkMonitor.isCurrentlyConnected() } returns true
549549
viewModel = createViewModel()
550550
testDispatcher.scheduler.advanceUntilIdle()
@@ -559,7 +559,7 @@ class MainViewModelTest {
559559
// Given
560560
stubNoUpdate()
561561
stubAuthState(isAuthorized = true)
562-
coEvery { mockLoginUseCase() } returns BaseResult.Success(Unit)
562+
coEvery { mockSyncFcmTokenUseCase() } returns FcmTokenSyncResult.SUCCESS
563563
every { mockNetworkMonitor.isCurrentlyConnected() } returns false
564564
viewModel = createViewModel()
565565
testDispatcher.scheduler.advanceUntilIdle()

data/src/main/java/com/project200/data/dto/FcmTokenDTO.kt

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)