Cheondi
개발 · 알고리즘·CS

재접속 버튼은 누가 책임져야 할까

여러 화면이 각자 재접속을 시작하던 구조를 하나의 연결 관리자와 의도 이벤트로 정리한 기록입니다.

  • #reconnect
  • #ownership
  • #concurrency

연결 오류 팝업과 상단 배너, 설정 화면에 각각 재접속 버튼이 있었다. 어느 버튼을 눌러도 같은 함수를 호출한다고 생각했지만 각 화면이 자신의 로딩 플래그와 팝업 상태를 관리했다. 빠르게 두 버튼을 누르면 연결 시도가 겹치고 한쪽은 성공했는데 다른 쪽이 실패 팝업을 다시 띄웠다.

버튼이 여러 개여도 실제 재접속 작업의 주인은 하나여야 했다.

UI는 의도만 전달했다

화면은 연결 방법을 알지 않고 사용자가 재시도를 원한다는 명령만 보냈다.

reconnectButton.onClick.AddListener(() =>
    connectionRecovery.RequestReconnect(ReconnectSource.User));

연결 관리자는 현재 상태에 따라 새 작업을 시작하거나 진행 중인 작업을 반환했다. UI는 공통 상태를 구독해 버튼과 로딩 표시를 바꿨다.

한 번의 작업을 공유했다

동시에 여러 요청이 와도 실제 복구 파이프라인은 하나만 실행했다.

Task currentReconnect;

public Task RequestReconnect()
{
    if (currentReconnect is { IsCompleted: false })
        return currentReconnect;
    return currentReconnect = RunReconnectAsync();
}

완료 후 참조 정리와 예외 전달을 명확히 하고 앱 종료 시 취소했다.

자동 재접속과 사용자 요청을 구분했다

자동 재접속이 backoff 대기 중일 때 사용자가 버튼을 누르면 즉시 한 번 시도할 수 있지만 무한 클릭은 제한했다. 인증 만료 상태에서는 버튼을 비활성화하고 로그인 행동을 안내했다.

연결 성공 이벤트를 받은 모든 화면은 자신의 로컬 팝업을 임의로 열고 닫지 않고 공통 상태에서 결과를 반영했다.

화면이 닫힌 뒤에도 완료 콜백이 돌아와 없는 UI를 만지는 문제가 있어 구독 수명도 정리했다. 연결 관리자의 작업은 앱 수명으로 유지되지만 화면 구독은 활성 기간에만 존재했다. 작업의 수명과 표현의 수명을 같게 두지 않았다.

테스트는 자동 재접속 중 연속 클릭, 두 화면에서 동시 요청, 인증 만료, 앱 백그라운드 전환을 포함했다. 실제 연결 시도 수가 한 번인지 로그와 상태 전이로 확인했다.

책임 하나가 화면 여러 개를 단순하게 했다

예전에는 버튼별 코드를 고치며 중복을 줄이려 했다. 실제 문제는 코드 줄 수보다 연결 상태를 결정하는 주체가 여러 명이라는 데 있었다.

이 작업 뒤 공유 자원을 움직이는 UI가 여러 곳에 있으면 누가 상태의 최종 소유자인지 먼저 찾는다. 버튼은 많아도 작업과 전이는 한 곳에서 관리해야 경쟁 상태와 모순된 안내를 줄일 수 있었다.