Skip to content

웹소켓 클라이언트 리팩토링 & 재연결 로직 구현#653

Merged
kanghun1121 merged 33 commits intodevfrom
refactor/websocket-dh
Oct 30, 2025
Merged

웹소켓 클라이언트 리팩토링 & 재연결 로직 구현#653
kanghun1121 merged 33 commits intodevfrom
refactor/websocket-dh

Conversation

@kanghun1121
Copy link
Member

@kanghun1121 kanghun1121 commented Oct 26, 2025

#️⃣ 연관된 이슈

ex) <#1377914257735421952>, <#1377914203255607316>

📝 작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  • 웹소켓 클라이언트 리팩토링
  • 웹소켓 재연결 로직

이해를 돕기 위해서 예제를 하나 만들어봤습니다. Reconnectable WebSocket이 궁금하시다면 참고해주세요!

Reconnectable WebSocket 정리한 노션 링크

웹소켓 클라이언트를 리팩토링 하면서 웹소켓 객체를 하나로 줄였습니다.

ReconnectableWebSocketClient , BaseWebSocketClient

->

WebSocketClient

웹소켓 상태를 Task에서 관찰하도록 구현했습니다. 상황에 따라서 알맞은 상태가 전달되고 그에 맞는 작업을 진행합니다.

private var stateTask: Task<Void, Error>?

/// WebSocket의 상태 변화를 관찰하고 각 상태에 맞는 동작을 수행합니다.
private func observeState() {
    stateTask = Task {
        for await state in stateStream {
            switch state {
            case .connecting:
                debugPrint("Connecting")
                continue
            case .connected:
                debugPrint("Connected")
                receive()
                checkingAlive()
            case .failed, .closed:
                debugPrint("Closed")
                release()
            case .reconnecting:
                debugPrint("Reconnecting")
                await reconnect()
            }
        }
    }
}

재연결시에 다시 웹소켓에 메시지 보내기

재연결이 완료되면 웹소켓에 다시 메시지를 전달해야 하기 때문에 ViewModel 쪽에서도 웹소켓의 상태를 관찰해야 합니다.

하나의 AsyncStream 에 두 개의 소비자가 있으면 데이터가 나눠서 전달되는 문제가 생기기 때문에 BroadCaster를 만들었습니다.

public class AsyncStreamBroadcaster<Element> {
    private var continuations: [AsyncStream<Element>.Continuation] = []

    public func stream() -> AsyncStream<Element> {
        AsyncStream { continuation in
            continuations.append(continuation)
        }
    }

    @BroadCaster
    public func send(_ element: Element) async {
        for c in continuations {
            c.yield(element)
        }
    }

    public func finish() {
        for c in continuations {
            c.finish()
        }
    }
}

아래 코드와 같이 웹소켓 상태가 connected 가 될 때 다시 웹소켓에 메시지를 보내도록 반응형으로 구현했습니다.

// MarketStore.swift

private func observeState() async {
    for await state in tickerService.subscribeStateStream() {
        if case .connected = state {
            if !subscriptionSnapshot.isEmpty {
                await sendTicket(subscriptionSnapshot)
            }
            
            // 시세가
            self.tickerStreamTask = Task {
                await consume()
            }
        }
    }
}

스크린샷 (선택)

2025-10-27.1.17.20.mov

💬 리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

실기기로 테스트 부탁드립니다!

실기기와 Mac에 USB로 연결하면 네트워크를 끊어도 정상적인 테스트가 가능합니다.

@kanghun1121 kanghun1121 requested a review from a team October 26, 2025 16:20
@kanghun1121 kanghun1121 added Feature 기능 관련 작업 Fix 오류 버그 수정 labels Oct 26, 2025
Copy link
Member

@jihyeonjjang jihyeonjjang left a comment

Choose a reason for hiding this comment

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

웹소켓 연결은 다시 잘 되는 것 같습니다!
근데 네트워크가 재연결되어도 사진처럼(사진 업로드가 안돼서 디스코드로 공유드리겠습니다!) 오프라인 헤더가 사라지지 않습니다

Simulator Screenshot - iPhone 17 - 2025-10-27 at 14 33 14

@kanghun1121 kanghun1121 force-pushed the refactor/websocket-dh branch from cfe95f0 to 5b776e2 Compare October 29, 2025 14:35
Copy link
Collaborator

@leekangho0 leekangho0 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다

@kanghun1121 kanghun1121 merged commit 88e9272 into dev Oct 30, 2025
1 check passed
@kanghun1121 kanghun1121 deleted the refactor/websocket-dh branch October 30, 2025 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature 기능 관련 작업 Fix 오류 버그 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants