실시간 통신
Connect Base 실시간 통신 심화 가이드입니다.
WebSocket 아키텍처
카테고리/룸 패턴
WebSocket Server
├── 카테고리: chat
│ ├── 룸: room-1
│ ├── 룸: room-2
│ └── 룸: room-3
├── 카테고리: notifications
│ └── 룸: user-123
└── 카테고리: presence
└── 룸: online-users연결 관리
typescript
// Public Key 만으로 연결 (게스트)
await cb.realtime.connect()
// 또는 앱 멤버 토큰으로 연결
await cb.realtime.connect({
accessToken: 'user_access_token', // 앱 멤버 JWT
userId: 'user_123', // 표시용 식별자 (선택)
maxRetries: 10, // 자동 재연결 횟수
retryInterval: 1000 // 재연결 간격 (ms)
})
// 연결 종료
cb.realtime.disconnect()
// 상태 확인
console.log(cb.realtime.connectionId) // 현재 연결 ID
console.log(cb.realtime.appId) // 앱 IDPub/Sub 패턴
카테고리 구독
📌 카테고리는 콘솔의 실시간 → 카테고리 에서 미리 만들어두어야 합니다. "룸"이라는 별도 개념은 없으며, 룸별로 분리하려면
chat-room-1,chat-room-2처럼 카테고리 이름으로 구분합니다.
typescript
// 카테고리 구독 (반환된 Subscription 객체로 send/onMessage 사용)
const chat = await cb.realtime.subscribe('chat-room-123')
// 메시지 수신 핸들러 등록 — 반환된 함수로 해제 가능
const off = chat.onMessage<{ text: string; userId: string }>((msg) => {
console.log('새 메시지:', msg.data.text, 'sentAt:', msg.sentAt)
})
// 핸들러 제거
off()메시지 발행
typescript
// 발신자도 echo 받음 (기본값)
await chat.send({
text: '안녕하세요!',
userId: 'user_123'
})
// 발신자 자신은 echo 안 받음
await chat.send({ text: '...' }, { includeSelf: false })히스토리 조회
typescript
// persist=true 로 설정된 카테고리만 메시지가 저장됩니다
if (chat.info.persist) {
const history = await chat.getHistory<{ text: string }>(50)
history.messages.forEach((m) => {
console.log(m.from, m.data.text, new Date(m.sentAt).toISOString())
})
}구독 해제
typescript
await chat.unsubscribe()Presence / Typing
cb.realtime 가 presence/typing 의 단일 SoT(Single Source of Truth) 입니다.
cb.database.setPresence / subscribePresence 는 v2.0.0 에서 제거되었습니다.
typescript
// 본인 온라인 상태 publish
await cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
// 다른 사용자 상태 구독
const unsub = await cb.realtime.subscribePresence('user_123', (info) => {
console.log(info.userId, info.status, info.eventType) // join | leave | update
})
// 룸 단위 typing indicator
await cb.realtime.startTyping('room-123')
await cb.realtime.onTypingChange('room-123', (typing) => {
console.log(typing.users)
})WebRTC (화상/음성)
cb.webrtc.connect(...) 한 메서드로 1:1 / 그룹 통화 모두 지원합니다.
typescript
// 1) 로컬 스트림 가져오기
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
// 2) WebRTC 시그널링 서버에 연결
await cb.webrtc.connect({
roomId: 'live:room-123',
userId: 'user-456',
isBroadcaster: true, // 송출자 / false 면 시청자
localStream
})
// 3) 이벤트 핸들러
cb.webrtc.onRemoteStream((peerId, stream) => {
// 새 비디오 요소에 stream 부착
console.log('peer joined:', peerId)
})
cb.webrtc.onPeerJoined((peerId, info) => {
console.log('피어 입장:', peerId, info)
})
cb.webrtc.onPeerLeft((peerId) => {
console.log('피어 퇴장:', peerId)
})
cb.webrtc.onStateChange((state) => {
console.log('연결 상태:', state)
})
// 4) 종료
cb.webrtc.disconnect()룸 ID 명명 규칙:
live:xxx— 라이브 스트리밍 (broadcaster + viewers)call:xxx— 1:1 또는 그룹 통화
실시간 패턴
채팅
typescript
const chat = await cb.realtime.subscribe('chat-room-123')
// 메시지 전송
async function sendMessage(text: string) {
await chat.send({
id: crypto.randomUUID(),
text,
userId: currentUser.id,
userName: currentUser.name
})
}
// 메시지 수신
chat.onMessage<{ text: string; userName: string }>((msg) => {
addMessageToUI(msg.data)
})실시간 협업 (커서 공유)
typescript
const collab = await cb.realtime.subscribe('collab-doc-123')
// 내 커서 위치 발행 (echo 없음으로 전송량 절반 절약)
document.addEventListener('mousemove', (e) => {
void collab.send(
{ userId: currentUser.id, x: e.clientX, y: e.clientY },
{ includeSelf: false }
)
})
// 다른 사용자 커서 수신
collab.onMessage<{ userId: string; x: number; y: number }>((msg) => {
updateCursorPosition(msg.data.userId, msg.data.x, msg.data.y)
})실시간 대시보드
typescript
const metrics = await cb.realtime.subscribe('metrics')
// 서버 → 클라이언트 메트릭 푸시는 보통 백엔드에서 NATS publish
metrics.onMessage<{ cpu: number; memory: number; requests: number }>((msg) => {
updateDashboard(msg.data)
})