
Github: denev6/stream-yolo
FlutterFastAPI, net/http, OpenCV, ONNXDocker, Prometheus, Grafana, AWS EC2, Nginx, Github ActionsClaude Code, Gemini Pro, VS Code저는 단순 연구보다 실제 서비스 개발에 더 관심이 많아요. 그래서 비전 모델을 다룰 때면 종종 실시간 처리 파이프라인을 만들었어요. 자연스럽게 CPU-bound 작업을 할 때마다 Python의 성능이 아쉽게 느껴졌어요.
Python은 인터프리터 언어라서 컴파일 언어보다 실행 속도가 느릴 수밖에 없어요. 여기에 GIL로 인한 성능 제약과 reference count 기반 GC까지 더해져, 이 부분은 Python 커뮤니티에서 오랫동안 단점으로 논의되어 왔어요. 물론 Python 3.11 이후로 GIL 개선 논의가 진행되고 있지만, 아직은 한계가 분명해요.
반면 Go는 빠르고 가벼운 언어로 잘 알려져 있어요. 우선 컴파일 언어라서 인터프리터 단계를 거치지 않아요. 가장 큰 장점은 goroutine이에요. goroutine은 대규모 병렬 처리에 최적화된 경량 스레드 개념이에요. 최소한의 메모리만 점유하면서 효율적인 스케줄링으로 수십만 개의 작업을 동시에 관리할 수 있어요. 당근팀도 Go를 도입해 CPU 성능을 최적화할 수 있었다고 소개하고 있어요. FastAPI(Python)와 Go를 비교한 벤치마크를 봐도, Go가 더 적은 자원으로 더 많은 일을 처리한다는 점을 확인할 수 있어요.
여기서 자연스럽게 이런 의문이 생겼어요. 그렇다면 내가 만드는 AI 서비스도 Go로 구축하면 더 빨라질까? 이 궁금증을 해결하기 위해 YOLO를 이용한 실시간 객체 인식 서버를 구축했어요. 각각 Python과 Go로 서버를 구현하고 동일한 부하를 줬을 때 지연 시간과 CPU 사용률을 비교하려고 했어요.

실시간 객체 인식 서버는 연속된 이미지 스트림을 받아 처리해야 해요. 일반적인 RESTful API는 매 요청마다 중복된 헤더 정보를 전송하는 오버헤드가 크고, HTTP의 stateless 특성 때문에 실시간 스트리밍에 비효율적이에요.
그래서 WebSocket을 사용해 한 번의 연결만으로 양방향 데이터 전송이 가능하도록 구현했어요. 이렇게 하면 연결을 재설정하는 비용을 줄여 통신 지연 시간을 줄이고, 실시간성을 확보할 수 있어요.
Python 서버는 FastAPI를 사용했어요. 실험을 보면 FastAPI가 Flask, Django 등 다른 Python 프레임워크보다 좋은 성능을 보인다는 것을 알 수 있어요. 빠른 속도 덕분에 최근 Python 커뮤니티에서 가장 인기 있는 프레임워크이기도 해요. Go 서버는 표준 라이브러리인 net/http와 gorilla/websocket을 사용했어요. 이 조합은 Go 커뮤니티에서 오랫동안 사용되어 온, 안정적인 표준 스택이에요.
이미지가 입력으로 들어오면 먼저 OpenCV로 전처리를 수행해요. Python과 Go 모두 내부적으로는 C++로 구현된 OpenCV 코어를 사용해요. Python에서는 opencv-python이 Python 래퍼를 통해 C++ 구현 함수를 호출해요. Go에서는 GoCV가 cgo 기반 바인딩으로 C++ 구현체에 직접 연결해 함수를 호출해요. 다시 말해 두 방식 모두 동일한 C++ 백엔드를 기반으로 동작해요.
전처리가 끝난 이미지는 YOLO26n으로 객체를 검출해요. 26n은 경량화된 모델이라 CPU 환경에서 실험하기에 적합하다는 장점이 있어요. Python과 Go에서 동일한 모델을 사용하기 위해 PyTorch(.pt) 모델을 ONNX 형식으로 변환했어요. 이후 두 언어 모두 ONNX Runtime을 이용해 모델 추론을 수행할 수 있도록 구성했어요.
실험의 일관성을 위해 전체 환경은 Docker로 구성했고, CPU 모니터링에는 Prometheus와 Grafana를 사용했어요.
먼저 각 서버의 지연 시간을 비교하기 위해 20명의 사용자가 동시에 요청을 보내는 상황을 재현했어요. 실험은 애플 M4 환경에서 진행했고, Python 3.12와 Go 1.28을 사용했어요. 결과는 아래와 같아요.
| Metric | Go Server | Python Server |
|---|---|---|
| Average Latency | 413.09 ms | 437.10 ms |
| Average FPS | 2.53 ms | 2.35 ms |
| P95 Latency | 542.26 ms | 549.43 ms |
| P99 Latency | 613.48 ms | 623.11 ms |
Go 서버를 사용했을 때 평균 FPS가 7.66% 개선되었어요. 그 외 모든 지표에서도 Go가 Python보다 더 좋은 성능을 보였어요.
추가로 리소스 사용량을 확인하기 위해 CPU 점유율도 측정했어요. M4는 10코어 CPU이기 때문에, 이 점을 감안해 20명이 아닌 6명이 동시에 접속하는 상황을 재현하고 지표를 측정했어요.

같은 부하를 줬을 때 Go 서버가 CPU를 약 7.3% 적게 사용하는 모습을 확인할 수 있었어요. 이를 통해 Go가 Python보다 빠르고 리소스를 더 효율적으로 사용하는 방향으로 동작한다는 점을 알 수 있어요.
다만 그렇다고 해서 항상 Go가 정답이라고 보기는 어려워요. 예를 들어 Go를 OpenCV에 바인딩하는 과정에서 CPU 아키텍처 차이 때문에 빌드가 되지 않는 등의 개발 이슈가 있었어요. 반면 Python은 별도의 환경 구성이 필요 없고, pip로 설치해서 바로 사용할 수 있어서 윈도우 환경에서도 문제 없이 작동했어요.
또 YOLO가 예측한 클래스를 문자열 레이블로 변환하는 과정을 비교해보면, Go 소스코드는 이 과정을 직접 문자열을 조작해 값을 찾는 방식으로 구현해야 해서 코드가 꽤 길어져요. 반면 Python에서는 같은 기능을 단 4줄로 처리했어요. 이런 차이 때문에 머신러닝 분야에서는 Python의 개발 편의성과 방대한 커뮤니티 덕분에 개발 난이도가 상대적으로 낮아요.
정리하면, 프로덕션 환경에서 리소스와 성능이 중요하다면 Go가 유리해요. 반대로 빠른 프로토타이핑이 중요한 실험 환경에서는 Python이 더 적합해요. 특히 학술 연구처럼 코드 수정이 잦고, 대규모 트래픽을 견딜 필요가 크지 않은 상황이라면 Python을 선택하는 것이 합리적이에요.
마지막 단계에서는 완성된 서버를 실제 프로덕트 형태로 배포해보려고 했어요. 다양한 플랫폼에서 활용하기 위해 Flutter를 선택했어요. Flutter로 작성한 코드를 안드로이드와 iOS 환경에서 각각 빌드하고 테스트했어요. 아래 사진은 아이폰에서 실행한 모습이에요.

서버 배포에는 AWS EC2를 사용했어요. 앞서 설명했듯 Docker 이미지를 ARM 아키텍처에 맞춰 작성해 두었기 때문에, 그대로 실행할 수 있는 t4g.small 인스턴스를 선택했어요. 혼자 테스트하는 용도이고 YOLO26n을 사용한다는 점을 고려했을 때, small 사양이면 충분하다고 판단했어요. 추가로 Github Actions를 이용해 CI/CD 환경도 구축했어요.

전체 흐름을 정리하면 아래 그림과 같아요.

이번 프로젝트는 Claude Code와 Gemini Pro를 함께 활용해 개발했어요. AI 도구를 사용하니 프론트엔드부터 배포까지 모든 작업을 이틀 만에 끝낼 수 있었어요. 그리고 AI 모델마다 잘하는 영역이 다르다는 점도 경험할 수 있었어요.
예를 들어 Flutter 개발 중에 bounding box가 화면 좌상단에 몰리는 문제가 있었어요. iOS 카메라 데이터(bgra8888)의 이미지 처리 방식과 안드로이드/iOS 센서의 이미지 회전 방식 차이가 겹치면서 발생한 이슈였어요. Claude Code에는 하루치 토큰을 모두 사용해 보면서까지 질문했지만, Thinking 내용을 보면 합리적으로 접근하고 있음에도 결국 같은 자리만 맴도는 모습을 보였어요. 이후 Gemini에게 스크린샷과 코드를 함께 넘겨주니 3번 정도의 대화만에 문제를 해결할 수 있었어요.
최근 PyCon이나 GopherCon 같은 컨퍼런스에 가보면, 많은 발표자분들이 코드를 AI로 작성했다고 이야기하세요. 이제는 어떤 AI 툴을 어떻게 골라 쓰느냐 자체도 개발 역량의 중요한 일부가 되어가고 있다는 생각이 들어요.