Skip to content

Commit 10828cf

Browse files
committed
refactor: 과도한 refresh 요청 방지를 위한 cancelId 추가
1 parent 14b24ce commit 10828cf

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

Application/DevLogPresentation/Sources/Home/List/TodoListFeature.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ struct TodoListFeature {
8989

9090
enum CancelID: Hashable {
9191
case debounce
92+
case fetch
9293
case request
9394
}
9495

@@ -261,12 +262,15 @@ private extension TodoListFeature {
261262
))
262263
await send(.setHasMore(page.items.count == query.pageSize && page.nextCursor != nil))
263264
await send(.loading(.end(target: .default, mode: .delayed)))
265+
} catch is CancellationError {
266+
return
264267
} catch {
265268
await send(.setAlert(true))
266269
await send(.loading(.end(target: .default, mode: .delayed)))
267270
}
268271
}
269272
)
273+
.cancellable(id: CancelID.fetch, cancelInFlight: true)
270274
}
271275

272276
func setSearchTextEffect(state: inout State) -> Effect<Action> {

Application/DevLogPresentation/Tests/Home/TodoListFeatureTestDoubles.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,50 @@ final class TodoListFetchTodosUseCaseSpy: FetchTodosUseCase {
194194
}
195195
}
196196

197+
actor TodoListDelayedFirstFetchTodosUseCaseSpy: FetchTodosUseCase {
198+
private let pages: [TodoPage]
199+
private var queries = [TodoQuery]()
200+
private var cursors = [TodoCursor?]()
201+
private var cancelledCallIndices = [Int]()
202+
203+
init(pages: [TodoPage]) {
204+
self.pages = pages
205+
}
206+
207+
func execute(_ query: TodoQuery, cursor: TodoCursor?) async throws -> TodoPage {
208+
let callIndex = queries.count
209+
queries.append(query)
210+
cursors.append(cursor)
211+
212+
if callIndex == 0 {
213+
do {
214+
try await Task.sleep(for: .seconds(1))
215+
} catch is CancellationError {
216+
cancelledCallIndices.append(callIndex)
217+
throw CancellationError()
218+
}
219+
}
220+
221+
if pages.count <= callIndex {
222+
return pages.last ?? TodoPage(items: [], nextCursor: nil)
223+
}
224+
225+
return pages[callIndex]
226+
}
227+
228+
func calledQueries() -> [TodoQuery] {
229+
queries
230+
}
231+
232+
func calledCursors() -> [TodoCursor?] {
233+
cursors
234+
}
235+
236+
func cancelledCalls() -> [Int] {
237+
cancelledCallIndices
238+
}
239+
}
240+
197241
final class TodoListFetchTodoByIdUseCaseSpy: FetchTodoByIdUseCase {
198242
var todos: [Todo]
199243
var error: Error?

Application/DevLogPresentation/Tests/Home/TodoListFeatureTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ struct TodoListFeatureTests {
6363
#expect(!adapter.hasMore)
6464
}
6565

66+
@Test("새 목록 조회는 이전 요청을 취소하고 마지막 응답만 반영한다")
67+
func 새_목록_조회는_이전_요청을_취소하고_마지막_응답만_반영한다() async {
68+
let firstTodo = makeTodoListTodo(id: "todo-first", number: 1)
69+
let secondTodo = makeTodoListTodo(id: "todo-second", number: 2)
70+
let fetchSpy = TodoListDelayedFirstFetchTodosUseCaseSpy(pages: [
71+
TodoPage(items: [firstTodo], nextCursor: nil),
72+
TodoPage(items: [secondTodo], nextCursor: nil)
73+
])
74+
let adapter = TodoListStoreTestAdapter(fetchUseCase: fetchSpy)
75+
76+
await adapter.onAppear()
77+
await adapter.setSortTarget(.updatedAt)
78+
79+
await waitUntil(timeout: .seconds(2)) {
80+
adapter.todos == [TodoListItem(from: secondTodo)!]
81+
}
82+
83+
let queries = await fetchSpy.calledQueries()
84+
let cancelledCalls = await fetchSpy.cancelledCalls()
85+
86+
#expect(adapter.todos == [TodoListItem(from: secondTodo)!])
87+
#expect(queries.map(\.sortTarget) == [.createdAt, .updatedAt])
88+
#expect(cancelledCalls == [0])
89+
#expect(!adapter.showAlert)
90+
}
91+
6692
@Test("필터와 정렬 액션은 query와 적용 필터 수를 갱신한다")
6793
func 필터와_정렬_액션은_query와_적용_필터_수를_갱신한다() async {
6894
let adapter = TodoListStoreTestAdapter()

0 commit comments

Comments
 (0)