---
title: "Ladybird 브라우저 2026년 4월 업데이트: PDF, HTML 파서, JS 엔진, 리눅스 GPU 렌더링까지 한꺼번에 진전"
published: 2026-05-02T20:46:30.000Z
canonical: https://jeff.news/article/2235
---
# Ladybird 브라우저 2026년 4월 업데이트: PDF, HTML 파서, JS 엔진, 리눅스 GPU 렌더링까지 한꺼번에 진전

Ladybird가 2026년 4월 한 달 동안 35명의 기여자로부터 333개 PR을 병합하며 브라우저 엔진 전반을 크게 개선했다. 인라인 PDF 뷰어, 히스토리 기반 주소창 자동완성, 스트리밍 HTML 파싱, 오프스레드 자바스크립트 컴파일, 독립 iframe 래스터화, GTK4 프론트엔드, 리눅스 dmabuf 기반 GPU 페인팅 등이 포함됐다.

## 브라우저 엔진다운 업데이트가 쏟아짐

- Ladybird가 2026년 4월 한 달 동안 333개 PR을 병합함. 기여자는 35명이고, 이 중 7명은 Ladybird에 처음 커밋한 사람들임.
  - 새 후원으로 Human Rights Foundation의 “AI for Individual Rights” 프로그램에서 5만 달러를 받음.
  - Jakub Stęplowski도 1천 달러를 후원함.
  - 오픈 웹을 목표로 하는 독립 브라우저 프로젝트가 실제 개발 속도를 계속 끌어올리는 중임.

- 사용자 눈에 바로 보이는 기능도 꽤 늘었음. 인라인 PDF 뷰어와 주소창 자동완성이 대표적임.
  - PDF는 번들된 `pdf.js` 뷰어로 브라우저 안에서 바로 렌더링됨. 페이지 이동, 텍스트 선택, 확대, 문서 내 검색까지 지원함.
  - 주소창은 방문 기록을 바탕으로 favicon, 제목, 검색 엔진 단축키, URL 완성을 섞어 보여줌.
  - 뒤에서는 SQLite 기반 `HistoryStore`가 탐색 기록, 제목, favicon, 방문 횟수, 마지막 방문 시간을 저장함.

## 로딩 파이프라인이 진짜 브라우저처럼 변하는 중

- HTML 파서가 이제 응답 본문 전체를 기다리지 않고 청크 단위로 처리함.
  - 바이트가 스트리밍 텍스트 디코더를 지나 토크나이저로 들어가고, 입력이 끊기면 멈췄다가 다음 청크가 오면 다시 이어감.
  - 이전에는 전체 body를 받은 뒤에야 파싱을 시작하는 구조였음.
  - 웹 페이지 로딩에서 “받으면서 파싱하기”는 체감 성능에 꽤 중요한 차이를 만듦.

- speculative HTML parser도 들어감. 동기 외부 스크립트 때문에 메인 파서가 막혔을 때, 별도 토크나이저가 아직 파싱 안 된 HTML을 앞질러 훑음.
  - `<script src>`, `<link rel=stylesheet|preload>`, `<img src>` 같은 리소스를 미리 찾아 fetch를 걸 수 있음.
  - `<base href>`, template, foreign content 같은 까다로운 케이스도 처리함.
  - 이후 document preload map과 연결해서, speculative parser가 찾은 리소스와 일반 파서가 나중에 찾는 리소스가 중복 요청되지 않게 함.

```mermaid
sequenceDiagram
    participant 네트워크
    participant 메인파서
    participant 추측파서
    participant 프리로드맵
    participant 리소스로더
    네트워크->>메인파서: HTML 청크 전달
    메인파서->>메인파서: 토큰화 중 동기 스크립트에서 대기
    메인파서->>추측파서: 남은 입력 스캔 요청
    추측파서->>프리로드맵: 스크립트·스타일·이미지 후보 등록
    프리로드맵->>리소스로더: 중복 없이 미리 가져오기
    리소스로더-->>메인파서: 이후 파싱 시 캐시된 리소스 사용
```

- 자바스크립트 컴파일도 메인 스레드에서 일부 빠져나감.
  - fetched script의 top-level bytecode generation이 백그라운드 스레드 풀에서 돌아감.
  - VM이나 GC heap을 건드리는 부분은 여전히 메인 스레드에 남겨둠.
  - YouTube 로딩만 봐도 약 200밀리초의 메인 스레드 시간이 백그라운드로 이동했다고 함.

> [!IMPORTANT]
> Ladybird의 이번 업데이트는 단순 기능 추가가 아니라 브라우저 로딩 파이프라인 자체를 갈아끼우는 쪽임. HTML 스트리밍 파싱, speculative fetch, 오프스레드 JS 컴파일이 같이 들어가면서 실제 사이트 로딩 체감에 영향을 줄 만한 변경이 많음.

## 자바스크립트 엔진 성능 개선도 빽빽함

- C++/Rust 전환 작업이 마무리된 뒤, 4월에는 자바스크립트 엔진 최적화가 대거 들어갔음.
  - JS-to-JS 호출에서 `Call`, `Return`, `End` 명령이 흔한 케이스에서는 AsmInt 어셈블리 인터프리터 안에 머물도록 개선됨.
  - ARM64에서는 레지스터 저장과 복원에 hand-tuned `ldp/stp` paired load/store를 사용함.
  - 네이티브 함수 호출도 새 `RawNativeFunction` 변형을 통해 직접 디스패치함.

- 레지스터 할당기 하나 바꿨는데 x.com 로딩에서 큰 병목이 사라짐.
  - 기존 `Generator::allocate_register`는 가장 낮은 번호의 빈 레지스터를 찾으려고 free pool을 스캔했음.
  - x.com 로딩 중 이 함수만 약 800밀리초를 쓰고 있었음.
  - 이제는 단순 LIFO 스택으로 바뀜.

- `for-in` 반복도 캐시가 들어감. 객체의 enumerable key snapshot을 저장해뒀다가 조건이 같으면 재사용함.
  - receiver의 shape, indexed storage, prototype chain이 그대로면 다시 펼치지 않음.
  - Speedometer 2는 67.7에서 73.6으로, Speedometer 3는 4.11에서 4.22로 올랐음.

- 자잘해 보이지만 실제 성능에 큰 영향을 주는 개선도 많음.
  - 파서가 lexer, parser, scope collector 사이에서 identifier name을 zero-copy로 공유해 파싱이 1.14배 빨라지고 RSS가 282MB 줄어듦.
  - 짧은 문자열 연결에서 rope를 건너뛰는 최적화로 `a + b` 루프가 2.13배 빨라짐.
  - lexical-this arrow function이 호출마다 function environment를 만들지 않게 되어 마이크로벤치마크에서 2.13배 개선됨.
  - maptiler.com 로딩 중 GC 시간은 property-table marking 최적화로 1.3초 줄어듦.

## 렌더링, 네트워크, 스타일 무효화까지 손댐

- iframe 렌더링 구조도 바뀜. 각 Navigable이 자기 스레드에서 독립적으로 래스터화함.
  - 이전에는 iframe이 부모 display list 안에 중첩 display list로 동기 페인트됐음.
  - 이제 부모 display list는 iframe의 래스터 결과를 `ExternalContentSource`로 참조함.
  - iframe 변경이 부모 display list 재기록을 매번 요구하지 않게 됐고, 향후 iframe을 별도 샌드박스 프로세스로 옮기기 위한 준비도 됨.

- 리눅스 Vulkan 빌드에서는 GPU 페인팅 경로가 훨씬 말이 되게 바뀜.
  - 이전에는 WebContent가 GPU-backed Skia surface에 그려도, UI 프로세스와 공유하는 버퍼가 CPU bitmap이라 매 프레임 GPU-to-CPU readback이 발생했음.
  - 이제 `SharedImage`가 리눅스 dmabuf 핸들을 담을 수 있어 front/back buffer가 UI 프로세스까지 GPU-resident 상태로 유지됨.
  - 매 프레임 복사 비용을 줄이는, 브라우저 렌더링 엔진 입장에서는 꽤 중요한 변경임.

- DNS와 네트워크 큐도 실제 사이트에서 터지던 병목을 줄였음.
  - `getaddrinfo`가 더 이상 이벤트 루프를 막지 않고, LibDNS가 thread pool에서 A/AAAA 쿼리를 병렬로 날림.
  - RequestServer에서 WebContent가 네트워크보다 느릴 때 response data를 비우는 과정이 O(n²)였고, YouTube 영상 로딩 중 `memcpy`에 약 30초를 쓰던 문제가 있었음.
  - `AllocatingMemoryStream`을 singly-linked chunk list로 바꿔 소비를 O(1)로 만들었음.

- 스타일 무효화는 `:has()` 때문에 난이도가 확 올라간 영역인데, Ladybird가 여기서도 꽤 구체적인 숫자를 냄.
  - Reddit rule cache rebuild는 13.2초에서 3.2초로 줄어듦.
  - Reddit infinite scroll에서 불필요한 recompute가 11% 감소함.
  - Intel ISA PDF의 `:has()` child-list visit은 71,000회에서 1,600회로 줄었고, pdf.js 로딩에서 약 650밀리초를 아낌.

## 실제 사이트 호환성과 WPT 점수

- 이번 달 가장 눈에 띄는 실제 사이트 개선은 Reddit과 YouTube임.
  - Reddit 이미지 갤러리 캐러셀이 동작하고, TextDecoderStream 덕분에 SPA가 링크 클릭을 먹어버리던 문제가 해결돼 댓글을 열 수 있게 됨.
  - YouTube는 오프스레드 JS 컴파일, WOFF2 압축 해제 오프스레드화, `@font-face` fetch fanout 감소, RequestServer 메모리 churn 수정, zero-copy TransferArrayBuffer가 누적 효과를 냄.
  - 초기 로딩에서 `@font-face` fetch는 177개에서 약 9개로 줄었다고 함.

- WPT 점수는 2,003,537에서 2,067,263으로 증가함. 숫자로는 63,726개 서브테스트 증가임.
  - 다만 이번 달 WPT에 공식 ECMAScript conformance suite인 test262가 upstream으로 들어오면서 53,207개 JS 서브테스트가 추가된 효과가 큼.
  - Ladybird는 이 중 52,045개를 통과해 97.8% pass rate를 기록함.
  - 그래도 test262 import 효과를 제외해도 약 11,700개 정도는 진짜 브라우저 플랫폼 진전이라고 설명함.

- 기타 변경도 웹 엔진 만드는 사람들이 좋아할 만한 것들로 가득함.
  - Rust는 이제 필수이고 `ENABLE_RUST` 빌드 옵션은 제거됨.
  - GN 빌드 시스템도 제거되어 CMake가 단일 기준이 됨.
  - C++와 Rust 코드가 시스템 allocator를 따로 쓰지 않고 mimalloc v2의 단일 인스턴스를 공유함.
  - ShadowRoot에 `innerHTML`을 설정할 때 문서 전체 레이아웃 트리를 무효화하지 않게 되어 pomax.github.io/bezierinfo의 layout-and-paint 시간이 21% 감소함.

---
## 기술 맥락

- Ladybird가 이번 달에 고른 방향은 “보이는 기능”과 “엔진 내부 병목 제거”를 같이 밀어붙이는 거예요. PDF 뷰어, 주소창 자동완성, GTK4 프론트엔드는 사용자가 바로 느끼는 영역이고, 스트리밍 HTML 파싱, JS 오프스레드 컴파일, 스타일 무효화 최적화는 페이지 로딩과 반응성을 좌우하는 내부 경로예요.

- HTML 파서 변경이 중요한 이유는 브라우저가 네트워크 응답을 다 받은 뒤 움직이면 너무 늦기 때문이에요. 청크가 들어오는 즉시 토큰화하고, 동기 스크립트에서 막히면 speculative parser가 리소스를 미리 찾아 fetch하는 구조가 실제 브라우저들이 쓰는 성능 전략에 가까워요. Ladybird가 preload map까지 연결한 건 중복 요청을 피하려는 디테일이고요.

- 자바스크립트 엔진 쪽은 작은 병목이 큰 사이트에서 어떻게 눈덩이처럼 커지는지 보여줘요. 레지스터 할당 함수 하나가 x.com 로딩에서 800밀리초를 쓰고, GC marking 하나가 maptiler.com에서 1.3초를 먹는 식이에요. 그래서 이번 업데이트는 화려한 새 문법보다 호출 경로, 캐시, 문자열 표현, 배열 처리 같은 낮은 레벨의 최적화가 많아요.

- 렌더링 경로에서는 dmabuf가 핵심이에요. GPU로 그린 뒤 UI 프로세스에 넘기려고 매 프레임 CPU bitmap으로 읽어오면 GPU 가속의 장점이 많이 사라지거든요. dmabuf로 GPU 버퍼를 그대로 공유하면 브라우저 프로세스 구조를 유지하면서도 복사 비용을 줄일 수 있어요.

- 스타일 무효화 최적화는 `:has()`가 왜 브라우저 엔진에 부담인지 잘 보여줘요. 예전 선택자는 대체로 아래로만 보면 됐지만, `:has()`는 자식 변화가 조상의 매칭 결과를 바꿀 수 있어요. Ladybird가 Reddit과 pdf.js 같은 실제 워크로드에서 방문 수와 재계산 수를 줄인 건 표준 기능 구현 이후의 진짜 난이도에 들어갔다는 신호예요.

## 핵심 포인트

- 4월 한 달간 333개 PR이 병합됐고 35명이 기여했으며 이 중 7명은 첫 기여자
- pdf.js 기반 인라인 PDF 뷰어와 SQLite 기반 브라우징 히스토리 저장소가 추가됨
- HTML 파서는 전체 응답을 기다리지 않고 청크 단위로 파싱하며, speculative parser가 리소스를 미리 가져옴
- 자바스크립트 엔진은 호출 경로, 레지스터 할당, for-in 캐시, 문자열 처리, GC 등에서 다수의 성능 개선을 얻음
- WPT 점수는 2,003,537에서 2,067,263으로 63,726개 서브테스트 증가

## 인사이트

Ladybird는 이제 ‘취미 브라우저’라기보다 진짜 브라우저 엔진이 부딪히는 문제를 정면으로 풀고 있다. HTML 파싱, 스타일 무효화, 자바스크립트 엔진, GPU 버퍼 공유, 프로세스 분리 준비까지 한 달 업데이트에 다 들어간다는 게 꽤 압도적이다.
