카테고리 없음

cloudflared 사용

kjw81024 2026. 8. 7. 16:54

사람 수 카운트는 POST api 로 5초에 한 번씩 숫자만 보내주면 되는 형식 

 

대표님 (ㄷㄷ) 요구사항으로 추가되는 기능이 CCTV 처럼 카메라가 비추는 화면을 확인할 수 있으면 좋을 것 같다고 하심 

 

나는 그냥 사진 post 하던거랑 비슷하게 프레임 하나하나 post 하면 되는건가? 했는데 숫자 하나 보내는거랑은 다르다고 해서 ai 한테 무슨 차이인지 물어봤다 

 

1) 프레임마다 POST 를 따로 날리면 요청마다 헤더 + 왕복시간 + 초당 10~15 프레임 뽑으려면 부담이 커짐

2) count POST 요청은 5초에 한번이라 가능하지만... 얘는 초당 몇 장씩 백엔드 -> 프론트 로 넘어가는거니까 트래픽 과도 

 

-> 터널 URL 로 MJPEG 스트림을 순서대로 받는 개념은 똑같지만, 요청을 매번 새로 여는 것이 아니라 연결 하나를 계속 열어둔 채로 프레임을 이어붙여 보낸느 방식이 필요해짐 (요청-응답 오버헤드 TCP, TLS handshake 등이 X)

추가로 영상 스트림 주소 (camera_url) 이 노트북 로컬 IP 로 적혀있으면 같은 wifi 에 있는 사람만 배포서버 front 에 띄워지는 영상을 볼 수 있는 문제가 있기 때문에 

로컬 스트림도 외부에서 접근 가능한 주소 필요 !!!! 

 

결론: cloudflared 를 씁시다...


먼저 cloudflared 는 터널링 도구

내 로컬 서버 localhost 를 외부 접근 가능 공개 URL 로 노출시켜주는 역할

 

(1) cloudflared 가 내 컴퓨터에서 cloudflare edge server 로 outbound 연결을 먼저 맺고 

(2) 연결을 하면 공개 URL 이 생김 

(3) 그 URL 로 들어오는 요청을 내 localhost 포트로 그대로 relay 

-> 내가 따로 inbound port 열 필요 X, 

 

ngrok 이랑 비슷한 느낌

포트포워딩, 공유기 설정 없이 바로 사용할 수 있음 (공개 IP 없어도 , CGNAT 이어도 가능) 

[카메라] → [메인 스레드] ─ YOLO 검출 → count 백엔드로 POST
                        └ 프레임 JPEG 인코딩 → 공유 버퍼
[서브 스레드(HTTP 서버)] ─ GET /video_feed → 공유 버퍼를 MJPEG로 스트리밍

 

설치방법...

최근에 맥이랑 윈도우 왔다갔다 쓰려니까 정신 나갈 것 같다........

 

둘 다 적어놓는걸로 

# mac
brew install cloudflared

# windows
winget install --id Cloudflare.cloudflared

 

실행은 

cloudflared tunnel --url http://localhost:8090

이렇게 하면 된다 

이런 화면이 쭉쭉 내려가는데 (시간이 약간 소요됨)

첫번째 네모에 있는 quick tunnel 메시지 아래 https:// xxxx.trycloudflare.com 주소가 공개 URL 이다 

 

이제 저 공개 URL 을 복사해서

python3 camera_daemon.py \
    --source 0 \
    --public-url https://ind-agent-poison-founded.trycloudflare.com/video_feed \ 
    --backend https://xxx/api/crowd/live

이런식으로 명령을 보내주면 영상이 송출되는것을 확인할 수 있음

 

(참고로 이 때 생기는 공개 url 은 cloudflared 를 실행시키는 터미널이 꺼지면 같이 꺼지니까 매번 실행시켜서 새로운 값을 넣어줘야함)


사용한 코드는 대충  아래 부분을 필수적으로 넣어줬다 

_latest_jpeg = None
_lock = threading.Lock()

# --- MJPEG 서버 (stdlib만, flask 불필요) ---
class MJPEGHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "multipart/x-mixed-replace; boundary=frame")
        self.end_headers()
        while True:
            with _lock:
                jpg = _latest_jpeg
            if jpg is None:
                time.sleep(0.05); continue
            self.wfile.write(b"--frame\r\n")
            self.wfile.write(b"Content-Type: image/jpeg\r\n\r\n")
            self.wfile.write(jpg + b"\r\n")

# 카메라 루프를 안 막게 데몬 스레드로 서버 실행
server = ThreadingHTTPServer(("0.0.0.0", 8090), MJPEGHandler)
threading.Thread(target=server.serve_forever, daemon=True).start()

# --- 카메라 루프 (카메라 소유자) ---
while True:
    ret, frame = cap.read()
    results = model(frame, classes=[0])           # YOLO 사람 검출
    count = len(results[0].boxes)                 # → 백엔드로 POST

    ok, buf = cv2.imencode(".jpg", results[0].plot())  # 박스 그린 프레임
    if ok:
        with _lock:
            _latest_jpeg = buf.tobytes()          # 공유 버퍼 갱신

... 사실 AI 한테 짜달라고 하면 잘 짜준다 

짠 코드를 적당히 읽어보고 어색한 부분 물어보면서 고치면 됨...!!!!


 

좀 밤티같긴 하지만 잘 나옴, 인식도 잘 되고 프레임도 나쁘지않게 들어온다 

 

원래 목표였던 제 3의 client 도 잘 보이는지 확인

핸드폰 캡처
배터리 23퍼센트.ㅎ

데이터 키고도 잘 보인다 화면 !!

 

돌리다보면 cloudflared 를 켜둔 터미널창에 아래와 같은 오류가 뜨는데 (가끔????)

 

시각 에러 의미 심각도
07:15 connection refused 8090에 서버 없음 ❌ (근데 이제 해결됨)
07:26 stream canceled, code 0 브라우저가 스트림 연결 정상 종료 ⚠️ 대체로 무해

요런거라고 하고 

나는 자동으로 해결됐다 아마 초반에 렉걸려서 8090 서버 못 찾아서 생긴 문제인듯?!