본문으로 건너뛰기

"Research" 태그로 연결된 11개 게시물개의 게시물이 있습니다.

AI 연구, 논문 기반 기술 인사이트와 실전 적용 사례를 다룹니다.

모든 태그 보기

실시간 스트리밍 렌더링 최적화 - Canvas, Web Worker, OffscreenCanvas 기반 EVA 아키텍처 개선기

· 약 6분
junhyung yoo
junhyung yoo
Product Developer

안녕하세요. EVA 팀에서 프론트엔드 개발을 담당하고 있는 유준형입니다.

EVA 서비스의 핵심 기능 중 하나는 수십 대의 카메라 영상을 실시간으로 확인하는 실시간 스트리밍입니다. 단순히 영상을 짧게 확인하는 수준을 넘어, 현장을 장시간 관제해야 하는 사용자가 늘어남에 따라 예상치 못한 성능 병목 현상이 발생하기 시작했습니다.

"화면을 오래 켜두면 브라우저가 점점 느려지다가 결국 탭이 죽어버려요."

이 문제를 해결하기 위해 저희 팀이 진행했던 Canvas, Web Worker, 그리고 OffscreenCanvas를 이용한 렌더링 구조 개선 여정을 공유하고자 합니다.


1. 배경: 왜 오래 켜둘수록 문제가 생겼을까?

초기 EVA의 스트리밍 방식은 가장 보편적인 <img> 태그와 Blob(Object URL)의 조합이었습니다.

기존 방식 (Blob 기반 렌더링)

  1. 서버로부터 MJPEG 스트림 데이터를 Blob 형태로 수신합니다.
  2. URL.createObjectURL(blob)으로 임시 URL을 생성합니다.
  3. <img> 태그의 src에 할당하여 브라우저가 이미지를 그리게 합니다.

구현은 매우 간단했지만, '장시간 관제'라는 특수한 환경에서 두 가지 치명적인 문제가 드러났습니다.

  • 메모리 오버헤드: 매 프레임(초당 30회 내외)마다 고유한 URL 문자열이 생성됩니다. revokeObjectURL을 호출하더라도 브라우저 내부의 이미지 캐시와 가비지 컬렉터(GC)의 지연으로 인해 메모리 점유율이 우상향하며 결국 Out of Memory(OOM) 오류를 유발했습니다.
  • 메인 스레드 블로킹: 이미지의 디코딩 과정이 메인 스레드(UI 스레드)에서 발생합니다. 고해상도 영상을 처리할 때 이벤트 루프가 지연되면서 클릭이나 스크롤 같은 UI 반응이 느려지는 Jank 현상이 발생했습니다.

2. 네트워크 탭 분석: MJPEG의 정체

성능 개선을 위해 가장 먼저 분석한 것은 네트워크 레이어였습니다. MJPEG 스트리밍은 일반적인 HTTP 요청과 다릅니다.

multipart/x-mixed-replace

MJPEG은 Content-Type: multipart/x-mixed-replace; boundary=... 헤더를 사용합니다. 이는 단일 HTTP 연결을 통해 서버가 끊임없이 이미지 프레임을 밀어주는 방식입니다.

  • 네트워크 탭의 특징: 요청이 완료되지 않고 계속 'Pending' 상태로 유지됩니다. 브라우저는 연결을 끊지 않고 계속해서 들어오는 바이너리 데이터를 받아들입니다.
  • 바이너리 데이터 구조: 각 프레임은 특정 boundary 문자열로 구분된 JPEG 바이너리 데이터(0xFF 0xD8 ... 0xFF 0xD9)입니다.

기존 방식은 이 거대한 바이너리 덩어리를 통째로 Blob으로 만들어 메인 스레드에서 파싱했기 때문에, 데이터가 쌓일수록 브라우저의 부담은 기하급수적으로 늘어날 수밖에 없는 구조였습니다.


3. 1차 개선: Canvas와 createImageBitmap

저희는 먼저 브라우저의 가비지 컬렉터에 의존하던 메모리 관리 방식을 명시적 관리 방식으로 전환하기 위해 Canvas API를 도입했습니다.

비동기 비트맵 렌더링

createImageBitmap API는 이미지를 화면에 그리기 전에 백그라운드에서 비동기적으로 디코딩할 수 있게 해줍니다.

// @src/entities/devices/components/stream/MJPEGStream.tsx
// 캔버스에 비트맵을 그린 직후 즉시 메모리 해제
const bitmap = await createImageBitmap(blob);
ctx.drawImage(bitmap, 0, 0);
bitmap.close(); // 명시적으로 메모리 반환

이 방식의 핵심은 bitmap.close()입니다. 개발자가 직접 사용이 끝난 비트맵 리소스를 파괴함으로써 메모리 점유율을 일정하게 유지할 수 있게 되었습니다. 또한 <img> 태그의 src 변경 시 발생하는 리플로우(Reflow) 를 제거하고 GPU 가속을 활용하는 캔버스 드로잉으로 전환하며 렌더링 효율을 높였습니다.


4. 2차 개선: Web Worker를 통한 연산 분리

렌더링은 가벼워졌지만, 스트림 데이터를 수신하고 바이너리에서 JPEG 프레임을 찾아내는(Boundary Parsing) 작업은 여전히 메인 스레드의 몫이었습니다. 초당 수백만 바이트의 데이터를 실시간으로 문자열 검색하는 것은 CPU에 큰 부담을 줍니다.

이를 해결하기 위해 Web Worker를 도입하여 "데이터 처리는 백그라운드에서, 화면 그리기는 메인에서" 라는 역할 분담을 적용했습니다.

데이터 전송 최적화 (Transferable Objects)

워커에서 메인 스레드로 대량의 이미지를 보낼 때 데이터를 복제(Copy)하면 심각한 성능 저하가 발생합니다. 저희는 Transferable Objects 기능을 사용하여 데이터 복사 없이 메모리의 소유권만 이전하는 Zero-copy 방식을 택했습니다.


5. 최종 개선: OffscreenCanvas의 도입

하지만 여전히 최종 드로잉 작업은 메인 스레드에서 일어나야 했습니다. 마지막 퍼즐 조각은 OffscreenCanvas였습니다. 이 API를 사용하면 캔버스의 제어권 자체를 워커로 넘길 수 있습니다.

메인 스레드가 블락되어도(왼쪽) 워커에서 동작하는 이미지 처리는 중단 없이 실시간으로 반영됩니다. (출처: 카카오 테크 블로그)

렌더링 부하 0%를 향하여

transferControlToOffscreen()을 통해 제어권을 넘긴 후, 워커 내부에서 직접 렌더링을 수행합니다.

// @src/entities/devices/components/stream/mjpeg.worker.ts
const bitmap = await createImageBitmap(blob);
if (ctx && canvas) {
// 워커가 직접 캔버스에 드로잉 (메인 스레드 간섭 0%)
ctx.drawImage(bitmap, 0, 0);

if (config.showArea && config.area) {
drawPolygonArea(ctx, config.area); // 영역 표시 로직도 워커에서 수행
}
}
bitmap.close();

이 구조에서는 메인 스레드에 아무리 무거운 작업이 걸려도, 스트리밍 영상은 별도의 스레드에서 끊김 없이 독립적으로 재생됩니다.

🌐 브라우저 호환성 및 자동 분기 처리

OffscreenCanvas는 강력한 기능을 제공하지만, 브라우저마다 지원 여부가 다릅니다. EVA 서비스에서는 사용자 환경을 고려하여 이를 자동으로 감지하고 분기 처리하도록 구현되었습니다.

브라우저지원 버전비고
Chrome69+최우선 지원
Edge79+Chromium 기반 버전부터 지원
Firefox105+105 버전부터 기본 활성화
Safari16.4+최신 macOS/iOS 환경 권장
Opera56+-

EVA의 맞춤 렌더링 전략:

  • 최신 브라우저: OffscreenCanvas를 활성화하여 메인 스레드 부하를 0%로 유지합니다.
  • 하위 브라우저(예: Safari 15 이하): 기능을 감지하여 1차 개선안인 메인 스레드 Canvas 렌더링 방식으로 자동 전환(Fallback)합니다.

이를 통해 어떤 브라우저 환경에서도 끊김 없는 스트리밍 경험을 보장합니다.


6. 추가 최적화: 버퍼 재사용과 파싱 속도 향상

성능은 디테일에서 결정됩니다. 워커 내부 로직에서도 몇 가지 최적화를 더했습니다.

  1. 고정 버퍼 재사용: 매번 새로운 Uint8Array를 생성하는 대신, 고정된 크기의 버퍼를 재사용하고 copyWithin을 사용하여 데이터를 관리했습니다. 이는 가비지 컬렉션(GC) 발생 빈도를 대폭 줄여줍니다.
  2. indexOf 기반 고속 파싱: 바이너리 데이터에서 매칭 바이트를 찾을 때 단순 루프 대신 내장 indexOf를 활용하여 불필요한 바이트 검사를 건너뛰도록 구현했습니다. 단순 연산만으로도 프레임 드랍을 획기적으로 줄일 수 있었습니다.

7. 결론: 더 견고해진 EVA 모니터링 환경

이번 최적화 작업을 통해 EVA 서비스는 다음과 같은 결과를 얻었습니다.

  • 메모리 안정성: 장시간 구동 시에도 메모리 점유율이 일정하게 유지되며 OOM 오류가 사라졌습니다.
  • UI 반응성: 고해상도 스트리밍 중에도 메뉴 이동, 버튼 클릭 등 UI 조작이 네이티브 앱 수준으로 부드러워졌습니다.
  • 안정적인 프레임: 스레드 분리를 통해 네트워크 지연이나 메인 스레드 부하와 무관하게 일정한 프레임 레이트를 확보했습니다.

프론트엔드 성능 최적화의 핵심은 **"브라우저의 메인 스레드를 얼마나 자유롭게 유지하느냐"**에 있다는 것을 다시 한번 체감할 수 있는 프로젝트였습니다.

긴 글 읽어주셔서 감사합니다!


참고 기술 요약

  • Web Workers API: 백그라운드 스레드에서 연산 수행
  • OffscreenCanvas: 메인 스레드로부터 독립된 렌더링
  • createImageBitmap: 비동기 이미지 디코딩 및 명시적 메모리 관리
  • Transferable Objects: 복사 비용 없는 고속 데이터 전송

참고 링크

Multi-Frame 기반 VLM 탐지: 단일 이미지 한계를 넘어 시간적 맥락으로

· 약 7분
Gyulim Gu
Gyulim Gu
Tech Leader
Seongwoo Kong
Seongwoo Kong
AI Specialist
Taehoon Park
Taehoon Park
AI Specialist
Jisu Kang
Jisu Kang
AI Specialist

단일 프레임은 충분한가?

최근 Vision-Language Model(VLM)은 단일 이미지에 대한 이해 능력에서 매우 높은 성능을 보여주고 있습니다. 대규모 멀티모달 모델들은 다중 이미지와 텍스트 조건을 함께 처리하는 구조를 제시하며, 멀티 프레임 기반 추론 가능성을 이론적으로 확장해왔습니다.

그러나 실제 산업 현장의 탐지 시나리오는 연구 환경과 다르게 훨씬 복잡합니다. 단일 프레임으로는 충분해 보이던 문제도, 실제 운영 환경에서는 다양한 오탐과 경계 사례를 만들어냅니다.

예를 들어, 사람이 바닥에 누워 있는 장면이 있습니다. 그 순간만 보면 쓰러짐으로 판단하기 쉽습니다. 하지만 바로 직전 프레임에서는 스트레칭을 하고 있었을 수도 있고, 작업 도중 잠시 자세를 바꾼 것일 수도 있습니다.

야간 환경에서는 렌즈 플레어나 조명 반사, 빛 번짐 현상이 화재의 색상 패턴과 유사하게 나타나 단일 이미지 기준으로는 화재로 오탐되는 경우도 존재합니다. 사람조차 한 장의 스냅샷만 보고는 확신하기 어려운 상황에서, 모델에게 단일 프레임만을 제공하는 것은 구조적으로 한계를 가질 수밖에 없습니다.



이러한 사례는 공통적으로 “맥락 부족”이라는 문제를 공유합니다.

VLM에게 '멀티 태스킹'을 가르치는 법: 시나리오 분해를 통한 상황 인지 능력 고도화

· 약 8분
Hyunchan Moon
Hyunchan Moon
AI Specialist

EVA 핵심은 "화재", "낙상", "교통사고" 등 화면 속에서 동시 다발적으로 일어나는 위급 상황을 놓치지 않고 '이해' 하는 것입니다. 하지만 아무리 뛰어난 VLM(Vision-Language Model)이라도 한 번에 너무 많은 것을 물어보면 인지 능력이 급격히 떨어지는 현상이 발생합니다.[2,3]

본 포스트에서는 텍스트-비디오 검색 분야의 최신 연구인 Q₂E (Query-to-Event Decomposition)[1] 논문을 참고하여, VLM이 단일 화면 내의 복합적인 시나리오를 깊이 있게 인지하도록 만드는 '시나리오 분해(Scenario Decomposition)' 기법을 소개합니다.

이미지에서 언어로, 언어에서 판단으로: 카메라 컨텍스트로 VLM 성능 끌어올리기

· 약 7분
Minjun Son
Minjun Son
POSTECH
Jisu Kang
Jisu Kang
AI Specialist

캠퍼스, 안전을 넘어 지능을 갖다: EVA와 함께하는 Postech Living Lab 프로젝트로 손민준 군(지도 교수 고영명 님)과 협동 연구한 주제입니다.


사용자의 한 줄 질의를 더 똑똑하게: 이미지 컨텍스트로 언어를 보강하는 법

EVA는 수백~수천 대의 스마트 카메라로 이상 상황을 감지하는 시스템입니다. 우리는 VLM/LLM을 활용해 카메라 컨텍스트를 자동으로 추론하고, 이를 프롬프트에 녹여 넣어 탐지하고자 하는 이미지의 상황이 반영된(camera-context; 카메라 컨텍스트 기반) 이상 탐지 파이프라인을 만들었습니다. 단일 프레임으로 추출한 카메라 컨텍스트를 VLLM의 사전 지식으로 활용했을 때, 기존 베이스라인 대비 의미 있는 정확도 향상과 더 깊은 해석 가능성을 확인했습니다.

사용자 피드백 데이터 기반 Instruction Tuning을 통한 성능 고도화

· 약 10분
Jaechan Lee
Jaechan Lee
POSTECH
Yura Shin
Yura Shin
AI Specialist

캠퍼스, 안전을 넘어 지능을 갖다: EVA와 함께하는 Postech Living Lab 프로젝트로 이재찬 군(지도 교수 고영명 님)과 협동 연구한 주제입니다.


🎯 서론: 피드백을 '사후 보정'에서 '사고 능력 강화'로 전환하다

EVA가 이미지를 판단할 때, 운영자들은 "이 경우는 안전조끼가 맞아. 왜 헷갈린 거지?" 또는 "여기서는 경보가 나야 하는 것 아닌가?"와 같은 구체적인 피드백을 제공합니다. 이 피드백에는 단순한 정오답을 넘어, 사람이 판단에 이른 이유와 문맥이 담겨 있습니다.

그동안 EVA는 이러한 피드백을 별도의 Vector DB에 저장하여 유사 상황 발생 시 Alert 여부를 보정하는 방식으로 활용해 왔습니다. 이 방식은 신속한 적용이 가능하다는 장점이 있었지만, 모델 자체의 추론 능력을 개선하지 못하고 오류를 사후적으로 필터링하는 구조적 한계를 가지고 있었습니다.

우리는 이 문제를 근본적으로 해결하기 위해 접근 방식을 완전히 바꿨습니다. 사용자 피드백을 단순한 오류 보고가 아니라, 모델이 추론 과정에 직접 활용하여 시각적 사고(Visual Reasoning) 능력을 강화할 수 있는 Instruction 데이터로 재구성한 것입니다.

이 글에서는 사용자 피드백 데이터를 활용한 VLM 기반 Instruction Tuning이 기존의 Vector DB 중심 접근의 한계를 어떻게 극복하고, 모델의 시각적 추론 능력을 어떻게 개선하는지를 중심으로 이야기하려고 합니다.

의도 파악 기반 Chat 명령어 수행의 성능 향상

· 약 6분
Yura Shin
Yura Shin
AI Specialist

서론

사용자는 Chat Agent에게 단순한 텍스트를 입력합니다. "모니터링 시작해주세요.", "사람에 대한 threshold를 0.6으로 맞춰줘.", "타겟 리스트에 나무 추가해."

겉으로 보기엔 단순한 대화지만, LLM 내부에서 이 요청을 처리하기 위해 수행해야하는 작업은 훨씬 복잡합니다.

LLM은 먼저 사용자의 요청이 어떤 종류의 작업인지 스스로 분류해야 합니다.

"이건 Target 설정인가? 시나리오 편집인가? 아니면 단순 조회인가?"

그 다음, 해당 태스크에 필요한 파라미터를 정확히 추출하고, 값이 허용 범위에 있는지 검증하며, 잘못된 값이면 이유까지 사용자 친화적으로 설명해야 합니다.

즉, 사람이 여러 단계에 걸쳐 순차적으로 판단해야 할 일을, 기존 LLM 구조는 한 번의 호출로 모두 처리하도록 설계되어 있었습니다.

이 방식은 외형상 깔끔해 보였지만, 실제로는 예측하기 어려운 문제를 반복적으로 만들어냈습니다.

  • 태스크 타입을 잘못 분류하여 엉뚱한 작업을 수행
  • 다른 태스크의 규칙이 섞여 충돌 발생
  • 잘못 추출한 파라미터가 그대로 통과
  • 규칙이 복잡하게 얽혀 유지보수 비용 상승

결국, 근본적인 문제를 해결하기 위해 LangGraph 기반 Multi-Node Routing 구조로 Chat Agent를 재설계하게 되었습니다.

From One-Shot Decisions to Two-Stage Reasoning

· 약 7분
Seongwoo Kong
Seongwoo Kong
AI Specialist
Jisu Kang
Jisu Kang
AI Specialist
Keewon Jeong
Keewon Jeong
Solution Architect

한 번에 모든 것을 판단하기보다, 단계 별로 신중하게

AI가 카메라 화면 한 장을 보고 판단을 내리는 과정은 생각보다 복잡합니다. 사용자는 자연스럽게 “사람이 쓰러지면 알려주세요”, “마스크를 쓰지 않은 작업자를 알려주세요”처럼 간단한 요청을 하지만, AI는 이 요청을 처리하기 위해 사진 분석, 조건 충족 여부 판단, 예외 상황 고려, 최종 결정, 이유 설명까지 여러 과정을 단 한 번에 수행해야 합니다.

EVA에서는 이를 해결하기 위해 사용자의 요청을 탐지 조건(Detection)예외 조건(Exception) 으로 구조화하는 Enriched Input 방식을 도입했고 성능이 크게 좋아졌습니다. 하지만 입력을 구조화 했음에도 불구하고, 여러 요청을 처리하는 과정에서 AI가 여전히 모순된 판단을 내리는 경우가 있었습니다.

즉, 문제는 단순히 조건을 구조화 하는 것 뿐만 아니라, AI가 여러 판단을 한 번에 수행해야 한다는 방식 자체에 있었던 것입니다. 그래서 EVA는 기존의 One-Shot 방식이 가진 한계를 넘어, 두 단계로 나누어 판단하는 Two-Stage Reasoning 구조를 새롭게 도입했습니다.

본 포스트에서는

  • 구조화만으로 해결되지 않았던 문제
  • One-Shot 판단이 가진 근본적 한계
  • 두 단계로 판단을 나누었을 때 AI가 더 잘 작동하는 이유
  • 실제 실험으로 확인한 개선 효과

를 중심으로 Two-Stage 구조의 도입 과정을 소개합니다.

Turning Simple User Requests into AI-Understandable Instructions

· 약 10분
Seongwoo Kong
Seongwoo Kong
AI Specialist
Jisu Kang
Jisu Kang
AI Specialist
Keewon Jeong
Keewon Jeong
Solution Architect

사용자 의도가 명확해지면, AI의 판단도 더욱 명확해집니다

EVA는 사용자가 자연어로 입력한 시나리오를 기반으로 동작하는 시스템입니다.

EVA가 안정적이고 정확한 판단을 내리기 위해서는 사용자의 시나리오가 AI에게 명확하게 이해할 수 있는 형태로 전달되는 것이 매우 중요합니다.

하지만 우리가 일상적으로 사용하는 자연어 표현은 사람에게는 단순하고 명확해 보이더라도, AI 입장에서는 모호한 경우가 많습니다. 이러한 간극이 바로 AI의 오작동이나 부정확한 판단의 원인이 됩니다.

이를 해결하기 위해 EVA에서는 사용자의 간단한 요청을 자동으로 구조화된 표현(Structured Query)으로 확장하는 기술을 개발하고 적용했습니다.

본 포스트에서는

  1. 왜 단순한 자연어 요청이 AI에게는 어려운지,
  2. 어떻게 쿼리를 재구성하여 AI의 이해도를 높일 수 있는지,
  3. 실제 현장 적용 시 얼마나 성능이 개선되었는지,

를 중심으로, 사용자의 의도를 구조화 하는 실질적인 방법과 효과를 공유하고자 합니다.

vLLM 완전 정복: EVA를 위한 최적화

· 약 19분
Taehoon Park
Taehoon Park
AI Specialist

이번 글에서는 EVA에서 LLM 서비스를 제공하기 위해 최적화한 과정을 알아보려 합니다. EVA에 맞게 LLM을 서빙하기 위해 vLLM을 도입한 사례 및 서빙 핵심 기술을 구체적으로 설명합니다.




1. GPU 리소스 효율화의 필요성

처음 LLM을 쓸 때 대부분은 GPT / Gemini / Claude 같은 클라우드 LLM부터 접하게 됩니다. 모델 운영에 대한 걱정 없이 성능이 가장 좋은 최신 모델을 url과 api key만 있으면 누구나 사용할 수 있기 때문입니다. 하지만 API 사용 비용이 지속적으로 발생하고 데이터가 외부로 전송되기에 개인 정보나 사내 문서 등 보안에 대한 위험성을 동반합니다. 조금만 스케일이 커지면 자연스레 이런 생각이 듭니다.

“이 정도면 그냥 우리 서버에 모델 올려서 쓰는 게 낫지 않나…?”

로컬 환경에서 쓸 수 있는 LLM도 Alibaba의 Qwen, Meta의 LLaMA 등 다양한 모델이 있습니다. 오픈 소스인 LLM이 많은 만큼 최신 성능의 새로운 모델이 빠르게 출시되며 선택의 폭이 굉장히 넓습니다. 하지만 이를 서비스에 적용하기 위해서는 여러가지 문제점이 있습니다.

먼저 LLM을 그냥 돌리면 추론 속도가 너무 느립니다. 이는 autoregressive 모델인 LLM의 특성 때문입니다. 추론 속도를 획기적으로 줄일 수 있는 KV Cache, Paged Attention 등의 다양한 기술이 있습니다. 이러한 개념들을 적용한 오픈소스가 여러 가지가 있는데 EVA는 vLLM을 사용합니다. 여러 오픈소스마다 각 각 지원하는 모델 범위가 다르고 사용 편의성에서도 큰 차이를 보입니다. 이제부터 EVA가 왜 vLLM을 사용했는지 알아보겠습니다.

Pose Estimation으로 사람 탐지 오탐 해결하기

· 약 12분
Euisuk Chung
Euisuk Chung
AI Specialist

들어가며

"저기 사람이 있어요!" AI 비전 시스템이 자신 있게 보고했습니다. 하지만 화면 속에는 빈 의자와 그 위에 걸쳐진 코트뿐이었습니다.

AI의 사람 인식 기술은 놀랍도록 발전했지만, 화려한 데모 영상과 달리 실제 현장은 훨씬 더 혼란스럽습니다. 특히 우리가 주로 다루는 환경에서는 그 문제가 더 두드러졌습니다.

  • 🏢 사무실: 빈 의자와 의자에 걸린 옷들
  • 🔬 실험실: 실험복이나 가운이 걸린 공간
  • 💼 근무 환경: 회의실, 휴게실 등 사람이 비어 있는 구역

이런 오탐(False Positive)은 단순히 “조금 틀린 결과”가 아니라, 시스템 전체의 신뢰도와 효율성에 직접적인 영향을 줍니다.

예를 들어, 에너지 절약 시스템은 잘못된 인원 수를 기준으로 조명과 냉난방을 제어하고, 보안 시스템은 ‘없는 사람’을 감시하느라 리소스를 낭비할 수도 있습니다.

예시. 빈 의자를 "앉아있는 사람"으로 오인한 사례