티스토리 뷰

Swift

Swift - WebRTC 구현(3)

DevDiana 2026. 7. 24. 12:18

안녕하세요 오늘은 WebRTC의 연결에 이어 채팅을 구현해보려고 합니다.

 

제일 어려운 연결 부분은 해결했으니 바로 구현해보도록 하겠습니다.

 


 

✅ 채팅 구현

연결을 전부 구현해놨기 때문에 채팅은 어렵지 않습니다.

우선 이전에 만들어놨던 WebTRCClient의 Delegate에 text 전송을 위한 데이터 타입들을 생성해줍니다.

이후 text 외에도 다른 타입들을 추가할 예정이므로 MessageKind를 생성해줍니다.

enum MessageKind: String, Codable {
    case text
}

struct Envelope: Codable {
    let kind: MessageKind
    let text: String?
}

struct ChatMessage: Identifiable {
    let id = UUID()
    let text: String
    let isMine: Bool
}

 

그리고 이전에 구현해놨던 WebRTCClient로 돌아와 Delegate에 데이터 전송을 위한 함수를 추가해줍니다.

protocol WebRTCClientDelegate: AnyObject {
    func webRTCClient(_ client: WebRTCClient, didGenerate candidate: RTCIceCandidate)
    func webRTCClient(_ client: WebRTCClient, didChange state: RTCIceConnectionState)
    func webRTCClientDidOpenDataChannel(_ client: WebRTCClient)
    
    // 추가!
    func webRTCClient(_ client: WebRTCClient, didReceive envelope: Envelope)
}

 

이렇게 함수가 추가되었다면 이를 사용하는 ViewModel에도 함수를 추가해줍니다.

func send(_ text: String) {
        let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return }
        webRTC?.send(text: trimmed)
        messages.append(ChatMessage(text: trimmed, isMine: true))
}

 

그리고 마지막으로 WebRTCClient Delegate를 사용하기 위한 함수를 추가해주면

nonisolated func webRTCClient(_ client: WebRTCClient, didReceive envelope: Envelope) {
        Task { @MainActor in
            switch envelope.kind {
            case .text:
                if let text = envelope.text {
                    self.messages.append(ChatMessage(text: text, isMine: false))
                }
            }
        }
    }

 

주요 코드는 끝이 납니다.

 

그리고 이제 View로 돌아가 기본적인 채팅 UI를 구현해주면 아래와 같은 채팅 화면을 확인할 수 있습니다.

 

 

이렇게 기본적인 WebRTC를 사용한 채팅을 구현해봤습니다.

 

확실히 이론만 봤을 때는 이해가 잘 되지 않았지만 기능을 직접 구현해보니 각 기능들이 어떤 과정으로 동작하는 지 이해가 되는 그려지는 느낌입니다.

 

실무에서 WebRTC를 사용하는 곳은 카카오톡 보이스톡, 디스코드 통화 등이 있습니다.

WebRTC의 경우 정확한 시작 시점과 종료 시점에 대한 알림이 있어야 하고 보이스톡의 통화 받기, 종료 버튼 등이 이런 기능을 수행합니다.

그리고 보이스톡과 디스코드 등은 전화를 건 쪽과 받는 쪽이 명확하게 구분이 되어 있으므로 전화를 건 사람과 받는 사람 각각이 Caller, Callee 역할을 담당합니다.

 

하지만 가끔 앱이 강제로 종료되거나 배터리가 없이 휴대폰이 강제로 꺼져버리는 경우가 있습니다. 이 경우에는 종료에 대한 알람이 가지 않기 때문에 서버에서는 연결이 종료되었다는 것을 알 수 없죠.

 

이런 경우를 방지하기 위해 서버에서는 안전장치를 둡니다.

 

  • TTL(자동 만료) — 문서에 만료 시각을 넣어두면 Firestore가 알아서 지웁니다. Realtime Database에는 onDisconnect 라는 게 있어서 연결이 끊기면 서버가 대신 정리해줍니다.
  • 세션 ID를 매번 새로 생성 — test-room 같은 고정 이름을 사용하지 않습니다. UUID를 쓰면 옛 문서가 남아 있어도 새 통화와 충돌할 일이 없죠.

 

물론 통화는 좀더 복잡한 구조를 거칩니다. 통화의 경우 주로 앱이 종료되어 있는 상황에서 발생하므로 단순 WebRTC나 알림으로는 불가능하고 운영체제 단에서 통화가 왔음을 휴대폰에게 먼저 알려주어야 합니다.

그리고 iOS의 경우 이를 VoIP로 처리하고 있고 이는 우선순위가 최우선이며 이 알림을 받는 경우 CallKit에 알람을 받았음을 신고해주어야 합니다.

 

다음 글에는 이런 부분을 고려하여 화상 통화를 구현해보고 Firestore을 사용하며 어색한 동작들을 수정해보려고 합니다.

 

감사합니다.

'Swift' 카테고리의 다른 글

Swift - VoIP Push(PushKit) & CallKit  (0) 2026.07.27
Swift - WebRTC 구현(2)  (0) 2026.07.22
Swift - WebRTC 구현(1)  (0) 2026.07.20
Swift - WebRTC란?  (0) 2026.07.19
Swift - API 호출 실패 시 재시도  (0) 2026.07.07
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함