[#602] 앱이 독에 내려갔을 때 Todo Fetch 요청이 되는 현상을 해결한다#657
Conversation
There was a problem hiding this comment.
Code Review
이번 풀 리퀘스트는 활성화된 위젯 종류에 따라 필요한 스냅샷만 선택적으로 갱신하도록 개선하고, 앱이 백그라운드로 진입할 때 날짜 변경 여부 및 동기화 요청 상태를 확인하여 효율적으로 동기화를 수행하도록 수정합니다. 이에 대해 WidgetSyncEventBusImpl에서 스레드 안전성을 확보하고 데드락을 방지하기 위해 NSRecursiveLock을 도입하고 publish(_:) 메서드를 보호할 것을 제안했습니다. 또한, confirmRequest() 메서드에 @discardableResult 속성을 추가하여 불필요한 _ = 대입 식을 제거하고 코드를 더 깔끔하게 개선할 것을 권장했습니다.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| private let lock = NSLock() | ||
| private var isRequested = false | ||
|
|
||
| public init() { } | ||
|
|
||
| public func publish(_ event: WidgetSyncEvent) { | ||
| subject.send(event) | ||
| } |
There was a problem hiding this comment.
WidgetSyncEventBusImpl에서 PassthroughSubject는 스레드 안전(Thread-safe)하지 않으므로, 여러 스레드에서 동시에 publish(_:)를 호출할 경우 크래시가 발생할 수 있습니다.
또한, 이벤트 버스의 특성상 이벤트를 구독하는 측(Subscriber)에서 동기적으로 다시 이벤트를 발행하거나 상태를 확인할 가능성이 있습니다. 이때 일반 NSLock을 사용하면 동일한 스레드에서 락을 중복 취득하려다 데드락(Deadlock)이 발생할 수 있습니다.
따라서 NSLock 대신 NSRecursiveLock을 사용하고, publish(_:) 메서도 락으로 보호하여 스레드 안전성을 확보하는 것을 권장합니다.
private let lock = NSRecursiveLock()
private var isRequested = false
public init() { }
public func publish(_ event: WidgetSyncEvent) {
lock.lock()
defer { lock.unlock() }
subject.send(event)
}| public protocol WidgetSyncEventBus { | ||
| func publish(_ event: WidgetSyncEvent) | ||
| func request() | ||
| func confirmRequest() -> Bool |
There was a problem hiding this comment.
confirmRequest() 메서드는 반환값을 무시하고 호출하는 경우가 많으므로, 호출부에서 _ =를 사용하지 않고도 경고 없이 호출할 수 있도록 @discardableResult 속성을 추가하는 것이 좋습니다.
| func confirmRequest() -> Bool | |
| @discardableResult | |
| func confirmRequest() -> Bool |
| isRequested = true | ||
| } | ||
|
|
||
| public func confirmRequest() -> Bool { |
There was a problem hiding this comment.
프로토콜 정의에 맞춰 구현체에도 @discardableResult 속성을 추가하여 반환값을 생략할 수 있도록 합니다.
| public func confirmRequest() -> Bool { | |
| @discardableResult | |
| public func confirmRequest() -> Bool { |
| // 콘텐츠 변경 여부와 관계없이 기존 syncRequested 흐름을 즉시 허용한다. | ||
| guard Calendar.current.isDate(syncDate, inSameDayAs: now) else { | ||
| syncDate = now | ||
| _ = bus.confirmRequest() |
There was a problem hiding this comment.
confirmRequest()에 @discardableResult 속성이 추가되면, 불필요한 _ = 대입 식을 제거하고 더 깔끔하게 호출할 수 있습니다.
| _ = bus.confirmRequest() | |
| bus.confirmRequest() |
🔗 연관된 이슈
🎯 의도
📝 작업 내용
📌 요약
🔍 상세
DevLogApp의scenePhase == .background처리에서 날짜 변경 또는 대기 중인 위젯 동기화 요청이 있을 때만.syncRequested발행WidgetSyncEventBus에 동기화 요청 상태만 기록WidgetSyncEventHandler에서WidgetCenter의 현재 설정 정보를 조회해 Today widget이 있으면 Today snapshot만, Heatmap widget이 있으면 Heatmap snapshot만 갱신WidgetConfigurationProvider계약 및WidgetConfigurationProviderImpl구현 추가작업 노트
설정된 위젯 종류만 확인하는 방식은 fetch 범위를 줄이는 보호 장치로는 유효하지만 위젯 제거 직후
WidgetCenter의 현재 설정 정보가 즉시 반영되지 않을 수 있어 트리거 자체를 막는 기준으로는 불충분날짜 기반 안전장치는 앱이 실행된 상태로 날짜가 넘어가 Today widget의 분류 기준일이 바뀌는 경우를 보완하기 위한 장치
콘텐츠 추가, 수정, 삭제 기반 안전장치는 서버 데이터가 사용자 조작 없이 바뀌지 않는 현재 구조에서 실제 source data 변경이 있었던 경우에만 백그라운드 위젯 fetch를 허용하기 위한 장치
최종 구조는 트리거 제한과 fetch 범위 제한을 분리해 백그라운드 진입 시점에는 “갱신 필요 여부”를 판단하고 실제 동기화 단계에서는 “설정된 위젯 종류”를 기준으로 필요한 fetch만 수행하는 구성
configurationProvider가 반환한 현재 위젯 종류에 따라 fetch 호출 수가 달라지도록 구성dueDate,updatedAt)createdAt,completedAt,deletedAt)📸 영상 / 이미지 (Optional)