10.8. WebSockets를 이용한 양방향 제어¶
Server-Sent Events는 푸시 전용입니다. 소유자가 대시보드에서 “지금 바로 스냅샷 저장”이나 “트리거 카운터 재설정”을 탭하면, 대시보드는 카메라로 메시지를 보내야 합니다. 그것이 WebSockets입니다. 하나의 TCP 소켓에서 프레임화된 메시지가 양방향으로 흐릅니다.
10.8.1. /control 라우트¶
microdot.websocket.with_websocket()은 WebSocket 업그레이드 핸드셰이크를 수행하고 핸들러에 WebSocket 객체를 넘겨줍니다. 핸들러는 명령을 읽고 확인 응답을 보내면서 무한히 반복합니다:
from microdot.websocket import with_websocket
from microdot.websocket import WebSocketError
import json
@app.get('/control')
@with_websocket
async def control(request, ws):
while True:
try:
msg = await ws.receive()
except WebSocketError:
break
try:
cmd = json.loads(msg)
except ValueError:
await ws.send({'error': 'bad json'})
continue
if cmd.get('cmd') == 'snapshot_now':
if latest_jpeg:
path = '/sdcard/snaps/manual-{}.jpg'.format(
int(time.time()))
with open(path, 'wb') as f:
f.write(latest_jpeg)
await ws.send({'ok': True, 'saved': path})
else:
await ws.send({'ok': False, 'reason': 'no frame yet'})
elif cmd.get('cmd') == 'reset':
state['trigger_count'] = 0
await ws.send({'ok': True, 'counters': 'reset'})
else:
await ws.send({'error': 'unknown command'})
receive()는 텍스트 프레임에 대해서는 문자열을, 바이너리 프레임에 대해서는 바이트를 반환합니다. 브라우저 측의 WebSocket.send(...)는 기본적으로 텍스트를 보내므로, JSON으로 인코딩된 명령이 자연스러운 선택입니다.
send()는 문자열, 바이트, 또는 JSON 직렬화가 가능한 모든 것을 받습니다. dict는 JSON 텍스트 프레임으로 전송됩니다.
WebSocketError는 클라이언트가 연결을 끊을 때(정상 종료, 네트워크 끊김, 또는 프로토콜 오류) 발생합니다. 핸들러는 루프를 빠져나와 반환하고, microdot가 소켓을 정리합니다.
10.8.2. 대시보드가 명령을 보냅니다¶
슬라이더 옆에 버튼 두 개가 index.html에 들어갑니다:
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
그리고 app.js는 WebSocket을 한 번 열고 두 버튼을 모두 send에 연결합니다:
const proto = location.protocol === 'https:' ? 'wss://' : 'ws://';
const ws = new WebSocket(proto + location.host + '/control');
document.getElementById('snap-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'snapshot_now'})));
document.getElementById('reset-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'reset'})));
ws.addEventListener('message', (e) => {
console.log('cam:', JSON.parse(e.data));
});
ws://와 wss:// 사이의 선택은 http://와 https://의 선택을 그대로 반영합니다. WebSocket은 동일한 TLS 처리를 상속합니다. HTTPS가 적용되어 있으면 대시보드는 자동으로 wss://를 통해 연결합니다.
10.8.3. SSE와 WebSockets 중 무엇을 선택할지¶
카메라가 푸시하고 브라우저는 듣기만 할 때 – 알림, 텔레메트리, 상태 변경 – SSE를 사용하세요. 전송 방식은 평범한 HTTP이고, 클라이언트 측은 한 줄(new EventSource)이며, 재연결은 자동입니다.
브라우저도 푸시해야 할 때 – 버튼, 키 입력 속도로 업데이트를 보내는 슬라이더, 다음 요청을 기다리면 너무 느린 모든 경우 – WebSockets를 사용하세요. 연결은 양방향이고 프레임화되어 있지만, API는 양쪽 모두에서 더 복잡합니다.
이제 카메라는 상호작용하는 대상이 되었습니다 – 지켜보고, 이벤트를 내보내며, 명령을 받아들입니다.