10.1. 첫 번째 엔드포인트

카메라가 흥미로운 일을 하기 전에, 먼저 네트워크의 나머지 부분이 카메라에 도달할 수 있어야 합니다. 서버가 살아 있음을 증명하는 가장 간단한 방법은 JSON을 반환하는 단일 경로 HTTP 엔드포인트입니다:

from microdot import Microdot

app = Microdot()

frame_count = 0
trigger_count = 0

@app.get('/status')
async def status(request):
    return {'frames': frame_count, 'triggers': trigger_count}

app.run(host='0.0.0.0', port=80)

IDE에서 실행하세요. LAN의 다른 어떤 컴퓨터에서든 http://<cam-ip>/status 를 엽니다. 브라우저에는 다음이 표시됩니다:

{"frames": 0, "triggers": 0}

카운터는 자리표시자일 뿐이며 아직 아무것도 건드리지 않습니다. 하지만 요청이 네트워크를 건너갔고, 카메라가 이를 라우팅하고, 핸들러를 실행하고, JSON을 다시 보냈습니다.

10.1.1. 각 줄이 하는 일

스크립트당 하나의 microdot.Microdot 인스턴스를 둡니다. 이 인스턴스는 라우팅 테이블, 오류 핸들러, 그리고 생명주기(시작, 서비스, 중지)를 소유합니다. 큰 앱은 여러 Python 모듈로 나뉘지만 여전히 하나의 app 객체를 공유합니다.

@app.get('/status') 는 라우트 데코레이터입니다. 여기서는 microdot.Microdot.get() 만 사용합니다. post(), put(), delete() 는 카메라가 쓰기 작업을 받기 시작하는 이후 페이지에서 등장합니다.

모든 라우트 핸들러는 asyncio 코루틴이며 첫 번째 인자로 요청을 받습니다. 핸들러가 반드시 request 를 사용해야 하는 것은 아니지만(이 핸들러는 이를 무시합니다), 시그니처의 일관성을 위해 매개변수는 항상 존재합니다.

dict를 반환하는 것이 JSON을 보내는 가장 짧은 방법입니다. Microdot는 dict를 자동으로 JSON으로 직렬화하고 응답에 Content-Type: application/json 을 설정합니다. 문자열을 반환하면 text/plain 을 보냅니다. microdot.Response 를 명시적으로 반환하는 것이 긴 형식이며, 본문이 바이너리이거나 응답에 커스텀 헤더가 필요할 때 쓰입니다.

app.run(host='0.0.0.0', port=80) 가 서버를 시작합니다. 0.0.0.0카메라가 가진 모든 인터페이스에서 수신 대기 한다는 뜻으로, 유선 이더넷과 wifi STA가 둘 다 켜져 있다면 둘 모두에서 수신합니다. 포트 80은 HTTP 기본값이므로 브라우저가 포트 번호를 입력할 필요가 없습니다.

10.1.2. 요청 하나, 처음부터 끝까지

휴대폰이 카메라로 TCP 연결을 열고 HTTP 요청을 보내면, 카메라가 이를 파싱하고, 라우팅하고, 핸들러를 실행한 다음 응답을 다시 씁니다.

휴대폰이 TCP 연결을 열고, 요청 라인과 헤더를 쓴 뒤 기다립니다. 카메라는 소켓에서 바이트를 읽어 microdot.Request 객체로 파싱하고, 경로와 메서드를 라우팅 테이블과 대조한 다음, 핸들러 코루틴을 await하고, 반환된 것이 무엇이든 직렬화하고, 상태 라인과 헤더와 본문을 다시 소켓으로 쓰고, 연결을 닫거나(HTTP/1.0 기본값) 재사용합니다(Connection: keep-alive 가 있는 HTTP/1.1). 전체 교환에 걸리는 시간은 네트워크 왕복 시간에 핸들러가 한 일을 더한 정도입니다.

10.1.3. 블로킹에 관한 참고 사항

run()블로킹 입니다. 서버가 멈출 때까지 절대 반환하지 않습니다. 단일 목적 서버라면 괜찮습니다. 프레임도 캡처하거나 다른 코루틴도 실행하는 앱은 대신 asyncio.run() 안에서 start_server() 를 사용하여 HTTP 서버가 나머지 모든 것과 루프를 공유하도록 합니다.

앱이 하나의 URL에 응답합니다.