Moli: Chrome Headless보다 가벼우면서 웹 페이지 데이터 활용에 적합한 AI 에이전트용 헤드리스 브라우저

Moli 소개

에이전트에게 웹을 보여주려고 헤드리스(Headless) 브라우저를 띄우면, 정작 필요한 것은 페이지의 텍스트나 DOM 노드 하나인데 브라우저는 레이아웃과 페인트까지 계속 유지합니다. 이러다보면 페이지 하나를 가져오는데 수백 MiB의 메모리와 여러 개의 프로세스가 필요하고, 여러 에이전트들이 동시에 여러 웹 페이지들에 접근하는 순간, 이러한 자원은 그대로 곱해집니다. 그렇다고 HTTP 요청만으로 웹 페이지를 가져오다보면 자바스크립트를 사용하여 브라우저에서 그려지는 내용를 가져오지 못합니다. 이는 크롤링(Crawling) 및 브라우저 조작 에이전트들이 늘 마주하는 문제입니다.

이번에 소개할 Moli는 이러한 비용 구조를 해결하는 것을 목표로 하는 헤드리스 브라우저 프로젝트입니다. 자바스크립트, DOM, CSS, 네트워크, 저장소를 갖춘 완전한 웹 런타임(Runtime)을 유지하면서, 레이아웃과 페인트는 요청이 실제로 필요로 할 때만 계산합니다. 즉, 네이티브 DOM(Document Object Model)과 스타일 상태를 단일 진실 공급원(SSoT, Single Source of Truth)으로 두고, 화면 좌표나 스크린샷처럼 픽셀이 있어야 답할 수 있는 요청에만 레이아웃 트리를 새로 만들어 확인합니다.

Moli는 Rust로 작성된 독립적인 브라우저 커널을 기반으로 한 프로젝트로, 대부분의 헤드리스 브라우저가 사용하는 Chromium을 감싼 래퍼(Wrapper)가 아닙니다. 명령줄과 CDP, WebDriver Classic, WebDriver BiDi 네 가지 경로로 쓸 수 있고 Linux, macOS, Windows를 지원합니다. 또한, Moli의 개발 및 공개를 담당한 Lexmount는 Moli를 기반으로 한 관리형 클라우드 런타임인 Lexmount Browser도 함께 운영합니다. 물론, Moli 프로젝트는 클라우드 제품 없이도 오픈소스 브라우저만으로 온전히 쓸 수 있다고 명시하고 있습니다.

Moli가 요청을 처리하는 방식

Moli는 "에이전트가 무엇을 요청했는가"에 따라 작업량이 달라지는 구조로 설계되어 있습니다. 즉, 에이전트의 요청에 따라 실제 수행하는 일은 다음과 같습니다:

에이전트 요청 Moli가 하는 일
HTML과 마크다운 추출, DOM 질의, 자바스크립트 실행, 네트워크와 저장소 확인 브라우저 런타임 상태를 직접 읽고 레이아웃과 페인트는 건드리지 않음
요소의 박스 크기 읽기, 좌표 히트 테스트, 좌표 기반 입력 레이아웃을 한 번 계산하고 가장 최근의 고정된 레이아웃 트리만 보관
스크린샷 촬영, 스크린캐스트 갱신 현재 DOM과 스타일에서 다시 만들어 프레임을 그려서 사용 (프레임은 사용 후 폐기)

첫 좌표 요청이 들어오면 현재 DOM과 스타일에서 작업용 레이아웃 트리를 만들고, 그 기하 정보를 DOM과 독립적인 불변 구조인 FrozenLayoutTree로 만든 다음 그 최신본 하나만 남깁니다. 이후 평범한 좌표 읽기는 페이지가 바뀌었더라도 이 고정본을 재사용할 수 있지만, 스크린샷과 스크린캐스트는 언제나 다시 만들어 교체하며 오래된 페인트 결과를 재사용하지 않습니다. 또한, 증분 갱신되는 레이아웃 트리, 손상 그래프, 유지되는 디스플레이 리스트, GPU 합성기, 상주 윈도우 등은 모두 없습니다.

Moli는 많은 리소스를 사용하는 비싼 작업은 기본적으로 꺼져 있어 명시적으로 켜야 합니다. 기본 상태에서는 실제 레이아웃 없이 형식만 호환되는 결정적 기하 값을 돌려주고, --layout 을 붙이면 실제 레이아웃과 히트 테스트, 좌표 입력, 스크린샷이 켜집니다. 이미지, 폰트, 오디오, 비디오 같은 선택적 리소스도 --resource 나 개별 플래그로 켜야 받아옵니다.

Moli의 아키텍처

Moli는 브라우저 엔진의 각 층을 Rust 생태계의 검증된 구성 요소를 바탕으로 구현하였습니다. 네트워크 전송은 libcurl, HTML 파싱은 html5ever, 자바스크립트 실행은 rusty_v8을 통한 V8, 셀렉터와 캐스케이드는 Servo의 Stylo, 박스와 텍스트 레이아웃은 TaffyParley, 소프트웨어 렌더링은 Vello CPU 백엔드와 usvg 가 맡습니다.

웹 런타임 쪽에서 이미 갖춰진 범위도 넓습니다. 스트리밍 HTML 파싱과 네이티브 DOM, 모듈과 타이머, 마이크로태스크, 이벤트, iframe과 워커, CSS 캐스케이드, Fetch와 XHR, WebSocket, 쿠키, WebCrypto, 그리고 프로필 단위로 분리된 localStorage와 IndexedDB, OPFS가 동작합니다. CDP와 WebDriver Classic, WebDriver BiDi가 같은 커널과 스케줄러를 공유하기 때문에 ChromeDriver나 geckodriver를 따로 설치할 필요도 없습니다.

Moli의 성능 측정 결과

Moli 저장소에서는 두 가지 측정 데이터를 바탕으로 한 측정 결과를 공개하고 있습니다. 첫 번째는 중국과 해외 주요 사이트에서 고른 공개 URL 192개를 대상으로 한 크롤링 테스트입니다. 자바스크립트 실행 후 의미 있는 내용이 나와야 성공으로 세며, HTTP 200 응답이나 차단 페이지, 로그인 벽, 빈 응답, 껍데기만 있는 애플리케이션은 성공으로 치지 않습니다:

브라우저 유효 페이지 성공률 중간 소요 시간 중간 RSS
Moli 103 53.6% 1.43초 73 MiB
Chrome Headless 101 52.6% 1.43초 773 MiB
Lightpanda 85 44.3% 0.97초 40 MiB
Obscura 57 29.7% 1.30초 39 MiB

테스트 결과, Moli는 Chrome Headless와 성공률과 소요 시간이 비슷하면서 메모리를 10분의 1 수준으로 사용했습니다. Moli보다 더 가벼운 LightpandaObscura의 경우에는 메모리를 절반 가량만 사용했지만, 유효 페이지 수에서 차이가 납니다. 즉, 완전한 웹 런타임을 유지하는 비용과 페이지를 실제로 읽어내는 능력 사이에 트레이드 오프(Trade-off) 관계가 있다는 것입니다.


두 번째 결과는 에이전트 작업을 흉내 낸 측정으로, 같은 작업을 Chromium과 비교했습니다:

항목 Moli Chromium
CDP 준비 시간 34.85 ms 169.37 ms
에피소드 활성 구간 p50 33.40 ms 57.13 ms
최대 PSS 102.46 MiB 348.82 MiB
최대 프로세스 및 스레드 수 1 / 24 11 / 123

프로세스가 하나로 유지된다는 점은 에이전트를 병렬로 띄울 때 특히 크게 작용합니다. 표준 준수 측면에서는 에이전트 브라우저 범위를 검증하는 현재 WPT 선택 집합(WPT selection)에서 한 번의 실행에 161만 2천개(1.612M) 테스트가 통과했다고 밝히고 있습니다. 다만 이러한 결과 수치는 모두 Moli 개발사인 Lexmount가 자체적으로 측정해 공개한 값이므로, 자신의 대상 사이트와 작업 형태로 다시 재어 보는 편이 안전합니다.

Moli는 누구에게 유용한가

크롤링 파이프라인, 브라우저를 조작하는 에이전트, 검색-증강 생성(RAG)의 수집 단계, 평가 환경과 강화학습 환경처럼 브라우저 여러 개를, 동시에, 오래 실행해야 하는 작업에 잘 맞습니다. 이런 작업의 요청 대부분이 웹 페이지의 구조를 읽는 작업이기 때문에, 레이아웃을 건너뛰는 Moli의 설계 이득이 그대로 나타나고, 프로세스 하나로 유지되는 점이 동시 실행 수를 늘립니다. Playwright 코드를 이미 갖고 있다면 CDP로 접속 대상만 바꿔 시험해 볼 수도 있어 도입 비용도 낮습니다.

반대로 자동화된 테스트와 같이 사람이 보는 화면을 기반으로 한 사용이 목적이라면 적절하지 않습니다. 프로젝트는 경계를 분명히 밝혀 두었는데, GUI 브라우저와 상주 윈도우, GPU 합성기가 없고 Chrome과의 픽셀 단위 일치를 목표로 하지 않으며 Canvas와 WebGL, 미디어 재생의 고품질 구현도 범위 밖입니다. --layout 이 지원하는 것은 소프트웨어 스크린샷과 래스터 기반의 CDP PDF 생성이고, Chrome의 모든 스크린샷 및 인쇄 모드가 구현되어 있지는 않습니다. 시각적 회귀 테스트나 픽셀 비교가 목적인 작업은 여전히 Chromium 쪽이 맞습니다.

또한, Moli는 자동화 스크립트가 잘못된 성공을 전제로 계속 진행하는 사고를 막기 위해, 지원하지 않는 프로토콜 경로는 그냥 넘어가지 않고 명시적인 오류를 반환합니다.

Moli 설치와 사용법

Linux와 macOS에서는 설치 스크립트를 실행합니다:

curl --proto '=https' --tlsv1.2 -fsSL \
  https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh

Windows에서는 PowerShell에서 실행합니다:

irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex

페이지를 마크다운으로 출력하거나, 모델이 읽기 좋은 압축된 의미 트리(Semantic Tree)로 바로 받을 수 있습니다:

moli fetch \
  --dump markdown \
  --wait-until done \
  https://example.com

moli fetch \
  --dump semantic_tree_text \
  --wait-selector body \
  https://example.com

스크린샷과 PDF처럼 시각적 출력이 필요한 경우, 실행 시 --layout 인자를 함께 붙입니다:

moli fetch --layout --dump screenshot https://example.com > page.png
moli fetch --layout --dump pdf https://example.com > page.pdf

자동화 서버는 moli serve 로 띄우며, 같은 엔드포인트가 CDP와 WebDriver Classic, WebDriver BiDi를 함께 제공합니다. Playwright는 CDP로 곧장 붙습니다:

import { chromium } from "playwright";

const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();

await page.goto("https://example.com");
console.log(await page.locator("body").innerText());

await browser.close();

에이전트에게 설치부터 맡기고 싶다면 저장소의 skills/ 디렉토리에 moli-webfetchmoli-cdp-server 스킬이 들어 있습니다. 에이전트에게 이 경로를 알려주고 스킬을 설치하게 한 뒤 사용하면 됩니다.

Moli의 라이선스

Moli는 Apache-2.0MIT 라이선스 중 원하는 쪽을 골라 사용할 수 있는 이중 라이선스로 공개되어 있어, 개인 및 상업적 목적으로 자유롭게 사용할 수 있습니다.

단, 함께 포함된 서드파티 구성 요소는 각자의 라이선스를 따르므로, 상업적 사용 전에는 주의해야 합니다.

:house: Lexmount Browser 홈페이지 (Moli 기반의 관리형 클라우드 서비스)

:github: Moli 프로젝트 GitHub 저장소

더 읽어보기




이 글은 GPT 모델로 정리한 초안을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 댓글로 알려주시기를 부탁드립니다. :hugs:

이 도구를 직접 설치해 사용해보셨다면, :pytorch:파이토치 한국 사용자 모임:south_korea: 회원들을 위해 경험이나 팁을 댓글로 남겨주세요! :folded_hands: