StepProject

Jeom·2025년 11월 5일

StepProject

목록 보기
1/2
post-thumbnail

[iOS / RxSwift] WebSocket 실시간 시세 연동 & MyStocks 화면 구현

오늘의 작업 요약

  • WebSocketManager: Starscream 기반 STOMP 연결 구현
  • LoginViewModel / APIService: RxSwift + MVVM 로그인 로직 완성
  • MyStocksViewController: 코인 리스트 UI (SnapKit + RxDataBinding)
  • CoinService: 서버 연동 코드 개선 (URLSession.insecure 사용, SSL 임시처리)

주요 구현 내용

🕸 WebSocketManager.swift

STOMP 프로토콜 없이 Starscream 기반 WebSocket으로 직접 연결

final class WebSocketManager: WebSocketDelegate {
    static let shared = WebSocketManager()
    private init() {}

    private var socket: WebSocket?
    private let coinSubject = PublishSubject<Coin>()
    var coinStream: Observable<Coin> { coinSubject.asObservable() }

    func connect(token: String) {
        var request = URLRequest(url: URL(string: "wss://<서버도메인>/ws-stomp")!)
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        socket = WebSocket(request: request)
        socket?.delegate = self
        socket?.connect()
    }

    func didReceive(event: WebSocketEvent, client: WebSocketClient) {
        switch event {
        case .connected:
            print("✅ Connected to WebSocket")
            send(message: "CONNECT\naccept-version:1.2\n\n\u{0000}")
        case .text(let text):
            handleMessage(text)
        default:
            break
        }
    }
}

APIService.swift

SSL 자체 인증서 문제로 인해 임시로 URLSession.insecure 사용

extension URLSession {
    static var insecure: URLSession = {
        let config = URLSessionConfiguration.default
        let delegate = InsecureSessionDelegate()
        return URLSession(configuration: config, delegate: delegate, delegateQueue: nil)
    }()
}

final class InsecureSessionDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let trust = challenge.protectionSpace.serverTrust {
            completionHandler(.useCredential, URLCredential(trust: trust))
        } else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}

⚠️ 이 코드는 개발용 임시 SSL 우회용이며, 실제 도메인 SSL 적용 후 반드시 제거해야 함.


CoinService.swift

상위 종목 + 요약 정보를 조합하여 코인 리스트 생성

final class CoinService {
    private let baseUrl = "https://<서버도메인>"

    func fetchTopSymbols(limit: Int = 10) -> Single<[String]> {
        let url = URL(string: "\(baseUrl)/api/coins/top?n=\(limit)")!
        return Single.create { single in
            let task = URLSession.insecure.dataTask(with: url) { data, _, error in
                if let error = error {
                    single(.failure(error))
                    return
                }
                guard let data = data,
                      let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
                      let symbols = json["symbols"] as? [String] else {
                    single(.failure(NSError(domain: "InvalidResponse", code: -1)))
                    return
                }
                single(.success(symbols))
            }
            task.resume()
            return Disposables.create { task.cancel() }
        }
    }
}

트러블슈팅

문제원인해결 방법
❌ SSL Error (-1200)자체 서명 인증서 사용URLSession.insecure로 임시 우회
❌ InvalidResponse서버 응답 구조 불일치CoinResponse 구조체 수정

다음 계획

  • WebSocket 실시간 /topic/price/BTCUSDT 반영
  • Portfolio API 연동 → 보유 종목 자동 표시
  • SSL 정식 인증 후 URLSession.insecure 제거
  • 차트 (Candlestick) 화면 추가

회고

오늘은 RxSwift + MVVM + WebSocket 구조를 완성한 날이었다.
SSL 문제와 인증 오류 때문에 애를 먹었지만,
결국 서버 연동과 UI 구성까지 성공적으로 마무리했다.
내일부터는 실시간 가격 반영과 차트 데이터로 확장 예정 💪

profile
iOS 개발노트

0개의 댓글