Skip to content

[#602] 앱이 독에 내려갔을 때 Todo Fetch 요청이 되는 현상을 해결한다#657

Closed
opficdev wants to merge 4 commits into
developfrom
refactor/#602-anonymous-background-widget-fetch
Closed

[#602] 앱이 독에 내려갔을 때 Todo Fetch 요청이 되는 현상을 해결한다#657
opficdev wants to merge 4 commits into
developfrom
refactor/#602-anonymous-background-widget-fetch

Conversation

@opficdev

@opficdev opficdev commented Jun 28, 2026

Copy link
Copy Markdown
Owner

🔗 연관된 이슈

🎯 의도

  • 앱이 백그라운드로 전환될 때 위젯 갱신을 위해 불필요한 Todo fetch가 반복되는 문제 완화

📝 작업 내용

📌 요약

  • 앱 백그라운드 진입 시 매번 위젯 동기화를 수행하던 흐름을 날짜 변경 및 Todo 변경 여부 기준으로 제한
  • 현재 설정된 위젯 종류에 따라 Today 또는 Heatmap에 필요한 데이터만 fetch하도록 위젯 동기화 범위 축소
  • DevLog 작업 규칙에 커밋 메시지 작성 기준 보강

🔍 상세

  • DevLogAppscenePhase == .background 처리에서 날짜 변경 또는 대기 중인 위젯 동기화 요청이 있을 때만 .syncRequested 발행
  • Todo 추가, 수정, 삭제, 복구 성공 시 즉시 위젯 fetch를 수행하지 않고 WidgetSyncEventBus에 동기화 요청 상태만 기록
  • WidgetSyncEventHandler에서 WidgetCenter의 현재 설정 정보를 조회해 Today widget이 있으면 Today snapshot만, Heatmap widget이 있으면 Heatmap snapshot만 갱신
  • 현재 위젯 설정 조회 실패 시 기존 동작 보존을 위해 Today와 Heatmap snapshot 모두 갱신
  • WidgetConfigurationProvider 계약 및 WidgetConfigurationProviderImpl 구현 추가
  • 관련 repository, event bus, widget sync handler 테스트 갱신

작업 노트

  • 설정된 위젯 종류만 확인하는 방식은 fetch 범위를 줄이는 보호 장치로는 유효하지만 위젯 제거 직후 WidgetCenter의 현재 설정 정보가 즉시 반영되지 않을 수 있어 트리거 자체를 막는 기준으로는 불충분

  • 날짜 기반 안전장치는 앱이 실행된 상태로 날짜가 넘어가 Today widget의 분류 기준일이 바뀌는 경우를 보완하기 위한 장치

  • 콘텐츠 추가, 수정, 삭제 기반 안전장치는 서버 데이터가 사용자 조작 없이 바뀌지 않는 현재 구조에서 실제 source data 변경이 있었던 경우에만 백그라운드 위젯 fetch를 허용하기 위한 장치

  • 최종 구조는 트리거 제한과 fetch 범위 제한을 분리해 백그라운드 진입 시점에는 “갱신 필요 여부”를 판단하고 실제 동기화 단계에서는 “설정된 위젯 종류”를 기준으로 필요한 fetch만 수행하는 구성

  • configurationProvider가 반환한 현재 위젯 종류에 따라 fetch 호출 수가 달라지도록 구성

    • Today widget만 설정된 경우: Today snapshot 갱신에 필요한 2회 fetch만 수행 (dueDate, updatedAt)
    • Heatmap widget만 설정된 경우: Heatmap snapshot 갱신에 필요한 3회 fetch만 수행 (createdAt, completedAt, deletedAt)
    • Today widget과 Heatmap widget이 모두 설정된 경우: 기존과 동일하게 5회 fetch 수행
    • 현재 위젯 설정 조회 실패 시: 기존 동작 보존을 위해 5회 fetch 수행

📸 영상 / 이미지 (Optional)

@opficdev opficdev self-assigned this Jun 28, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +14 to 21
private let lock = NSLock()
private var isRequested = false

public init() { }

public func publish(_ event: WidgetSyncEvent) {
subject.send(event)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

confirmRequest() 메서드는 반환값을 무시하고 호출하는 경우가 많으므로, 호출부에서 _ =를 사용하지 않고도 경고 없이 호출할 수 있도록 @discardableResult 속성을 추가하는 것이 좋습니다.

Suggested change
func confirmRequest() -> Bool
@discardableResult
func confirmRequest() -> Bool

isRequested = true
}

public func confirmRequest() -> Bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

프로토콜 정의에 맞춰 구현체에도 @discardableResult 속성을 추가하여 반환값을 생략할 수 있도록 합니다.

Suggested change
public func confirmRequest() -> Bool {
@discardableResult
public func confirmRequest() -> Bool {

// 콘텐츠 변경 여부와 관계없이 기존 syncRequested 흐름을 즉시 허용한다.
guard Calendar.current.isDate(syncDate, inSameDayAs: now) else {
syncDate = now
_ = bus.confirmRequest()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

confirmRequest()에 @discardableResult 속성이 추가되면, 불필요한 _ = 대입 식을 제거하고 더 깔끔하게 호출할 수 있습니다.

Suggested change
_ = bus.confirmRequest()
bus.confirmRequest()

@opficdev opficdev closed this Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

앱이 독에 내려갔을 때 Todo Fetch 요청이 되는 현상을 해결한다

1 participant