# task-1016.1: Lightpanda 헤드리스 브라우저 심층 분석 + 인포키워드 적용 방안

**작성일**: 2026-03-25
**작성자**: 다그다(Dagda), 개발3팀장
**팀원**: 루(백엔드), 브리짓(프론트엔드), 아네(UX/UI), 모리건(테스터)

---

## 요약 (Executive Summary)

Lightpanda는 Zig로 작성된 헤드리스 브라우저로, 렌더링 파이프라인을 의도적으로 제거하여 Chrome 대비 11배 빠르고 9배 적은 메모리를 사용한다. 그러나 **렌더링 엔진이 없어 실제 스크린샷 생성이 불가능**하다. `Page.captureScreenshot` API는 존재하지만 **하드코딩된 더미 PNG**를 반환할 뿐이다. 인포키워드의 핵심 기능인 네이버 검색결과 스크린샷에는 **적용 불가(No-Go)**이며, 텍스트 기반 크롤링(ThreadAuto 등)에는 적용 가치가 있다.

---

## Phase 1: GitHub 레포 심층 분석

### 1. 아키텍처 분석

#### 프로젝트 구조

```
lightpanda-io/browser (v1.0.0-dev, Zig 0.15.2)
├── build.zig             # Zig 빌드 (V8, libcurl, html5ever 링킹)
├── build.zig.zon         # 패키지 매니페스트
├── src/
│   ├── main.zig          # 진입점 (serve/fetch/mcp 모드 분기)
│   ├── Server.zig        # CDP WebSocket 서버
│   ├── App.zig           # 전역 상태 (Platform, Network, Snapshot)
│   ├── ArenaPool.zig     # Arena 메모리 풀 (메모리 절감 핵심)
│   ├── slab.zig          # 슬랩 할당자
│   ├── browser/
│   │   ├── Browser.zig   # 브라우저 인스턴스 (V8 Env 포함)
│   │   ├── Page.zig      # 페이지 DOM 상태
│   │   ├── Runner.zig    # tick 루프 (이벤트 드리븐 실행)
│   │   ├── StyleManager.zig  # CSS 가시성 처리 (4속성만)
│   │   ├── js/           # V8 통합 (Env, Context, Bridge, Snapshot)
│   │   ├── parser/       # html5ever FFI (Rust 정적 라이브러리)
│   │   └── webapi/       # Web API 구현 (200+ 파일)
│   ├── cdp/              # CDP 프로토콜 (17개 도메인)
│   ├── network/          # poll 기반 이벤트 루프
│   └── mcp/              # Model Context Protocol 서버
```

#### Chromium/WebKit과의 근본적 차이

Chromium 렌더링 파이프라인:
```
HTML → DOM → Style → Layout → Paint → GPU Composite → Screen
```

Lightpanda 파이프라인:
```
HTML(html5ever) → DOM → JS실행(V8) → CDP응답
                    ↑
              CSS(가시성만)
```

**제거된 요소**: Layout Engine, Paint/Rasterization, GPU Compositing, 이미지 디코딩, 폰트 래스터라이징
**유지된 요소**: DOM Tree, V8 JS Engine, HTTP/2 (libcurl), CSS 파싱(가시성 판단용만)

Chromium은 멀티프로세스(Browser + Renderer + GPU + Utility), Lightpanda는 **단일 프로세스 2스레드**(Main: I/O 루프, Worker: JS 실행).

#### JavaScript 엔진: V8 (확인)

- `build.zig.zon`에서 `lightpanda-io/zig-v8-fork v0.3.4` 참조
- `src/browser/js/js.zig`: `pub const v8 = @import("v8").c;`
- V8 Isolate를 직접 래핑 (`src/browser/js/Isolate.zig`)
- SpiderMonkey, 자체 엔진이 **아님** — Google V8 포크 사용
- V8 스냅샷 (`src/browser/js/Snapshot.zig`): Web API 템플릿을 빌드 시 직렬화하여 바이너리에 embed → 즉시 시작

#### CSS 렌더링 방식

Lightpanda의 CSS 엔진(`StyleManager.zig`)은 **4개 속성만 처리**:
- `display: none`
- `visibility: hidden/collapse`
- `opacity: 0`
- `pointer-events: none`

나머지 수천 개 CSS 속성은 CSSOM 객체에 문자열로 저장되지만 실제 계산하지 않는다. `:hover`, `:active`, `:focus` 등 인터랙션 기반 pseudo-class는 아예 무시. `getComputedStyle()`은 레이아웃 계산 없이 최소한의 응답만 제공.

#### 11배 빠르고 9배 메모리 절약인 이유

**속도 11배의 원인:**
1. 렌더링 파이프라인 완전 제거 (Layout, Paint, Composite 생략)
2. V8 스냅샷으로 즉시 시작 (~20ms vs Chrome ~600ms)
3. Arena Pool 메모리 할당: 포인터 이동 1회 O(1), 페이지 완료 시 일괄 리셋
4. 단일 프로세스 (IPC 오버헤드 없음)
5. 이미지/폰트 다운로드·디코딩 불필요

**메모리 9배 절감의 원인:**
1. 렌더링 관련 자료구조(Layout Tree, Paint Layer, GPU Texture) 없음
2. ArenaPool + SlabAllocator: malloc 오버헤드/단편화 없음, 메모리 재사용
3. DOM 노드에 레이아웃 정보 미포함 (순수 논리 구조만)
4. 단일 프로세스 (Chrome의 프로세스당 50MB+ 기본 오버헤드 없음)
5. 불필요 프로토콜 제거 (FTP, SMTP 등 disabled된 libcurl 빌드)

### 2. 호환성 분석

#### Playwright 연결 방식 (CDP 프로토콜)

```bash
# Lightpanda CDP 서버 시작
./lightpanda serve --host 127.0.0.1 --port 9222
```

```python
# Playwright 연결 (connect_over_cdp 방식)
from playwright.async_api import async_playwright

async with async_playwright() as p:
    browser = await p.chromium.connect_over_cdp("ws://127.0.0.1:9222")
```

CDP 엔드포인트: `ws://127.0.0.1:9222/`
`/json/version`에서 `webSocketDebuggerUrl` 반환. Chrome 124로 위장(`PRODUCT = "Chrome/124.0.6367.29"`).

#### 구현된 CDP 도메인 (17개)

Page, DOM, Runtime, Target, Network, Emulation, Input, Browser, Fetch, Accessibility, CSS, Log, Storage, Security, Performance, Inspector, LP(Lightpanda 전용)

#### Playwright API 지원 현황

**지원 (실제 동작):**
- `page.goto()` — Page.navigate, waitUntil 라이프사이클 이벤트(DOMContentLoaded, load, networkIdle) 지원
- `page.evaluate()` — Runtime.evaluate → V8 Inspector 위임, 완전 동작
- `page.content()` — DOM.getOuterHTML, 완전 동작
- `page.query_selector()` — DOM.querySelector, 완전 동작
- `page.click()` — Input.dispatchMouseEvent, 완전 동작
- `page.fill()` — Input.dispatchKeyEvent + insertText, 완전 동작
- `page.wait_for_selector()` — Runtime.evaluate 기반 polling으로 간접 동작
- Network 인터셉트, Cookie 관리 — 완전 지원

**미지원 또는 가짜 응답:**
- **`page.screenshot()`** — ★ **API는 응답하나 항상 동일한 하드코딩 더미 PNG 반환**
- `page.screenshot(full_page=True)` — captureBeyondViewport 파라미터 무시, 동일 더미 이미지
- `page.screenshot(type="jpeg")` — `-32000 에러` 반환
- `browser.new_context(viewport=...)` — Emulation.setDeviceMetricsOverride가 **noop** (파라미터 무시)
- `browser.new_context(user_agent=...)` — 명시적으로 `"ignored"` 로그 출력
- `page.pdf()` — 미구현
- `getLayoutMetrics` — 1920×1080 하드코딩

#### ★ 스크린샷 기능 심층 분석 (핵심 발견)

`src/cdp/domains/page.zig`:
```zig
// 빌드 시 정적으로 임베드되는 더미 PNG
const screenshot_png = @embedFile("screenshot.png");

fn captureScreenshot(cmd: anytype) !void {
    // ... format/quality/clip 등 파라미터는 무시 ...
    // ★ 실제 렌더링 없이 고정 PNG를 Base64로 반환
    return cmd.sendResult(.{
        .data = base64Encode(screenshot_png),
    }, .{});
}
```

- 임베드된 `screenshot.png`: 16,697바이트, 1920×1080, 8비트 인덱스 컬러 (실질적으로 플레이스홀더)
- Playwright가 `screenshot()`을 호출하면 에러 없이 정상 응답 → **실제 테스트하면 모든 스크린샷이 동일한 이미지**
- GitHub Issue #492에서 개발팀 공식 답변: *"Lightpanda browser doesn't handle graphical rendering, so it can't generate screenshots."*
- 이슈는 Close 상태, 구현 계획 없음

### 3. 성숙도 평가

- **최신 릴리스**: v0.2.6 (2025-03-14)
- **공식 상태**: Beta
- **GitHub**: 24.6k stars, 988 forks, 71개 오픈 이슈
- **라이선스**: AGPL-3.0 (내부 사용 무제한, SaaS 제공 시 소스 공개 의무)
- **플랫폼**: Linux x86_64/ARM64, macOS ARM64 (Windows는 WSL2 필요)

**알려진 주요 제한사항:**
- WebSocket API 미구현 (#1952)
- CDP WebSocketDebuggerUrl 0.0.0.0 반환 (#1922)
- 멀티클라이언트 CDP 연결 붕괴 (#1892)
- Angular/React 복잡 SPA 부분적 미지원 (#1947)
- Cloudflare Turnstile 차단 (#1869)
- IntersectionObserver 미지원

**프로덕션 판단:**
- 텍스트 기반 크롤링/스크래핑: 조건부 사용 가능
- 스크린샷/PDF 필요 워크로드: **사용 불가**
- 복잡 SPA/봇 감지 사이트: 불안정

### 4. 설치 및 실행

```bash
# Linux x86_64 바이너리 다운로드
curl -L -o lightpanda \
  https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux
chmod a+x ./lightpanda

# CDP 서버 시작
LIGHTPANDA_DISABLE_TELEMETRY=true ./lightpanda serve \
  --host 127.0.0.1 --port 9222

# Docker
docker run -d -p 9222:9222 lightpanda/browser:nightly
```

Playwright 연결:
```python
async with async_playwright() as p:
    browser = await p.chromium.connect_over_cdp("ws://127.0.0.1:9222")
    context = await browser.new_context()
    page = await context.new_page()
    await page.goto("https://example.com", wait_until="networkidle")
    title = await page.title()
    await browser.close()
```

### 5. 벤치마크 데이터

**로컬 벤치마크 (100페이지):**
- Lightpanda 2.3초, Chrome 25.2초 → **11배 빠름**
- Lightpanda 24MB, Chrome 207MB → **9배 메모리 절약**

**실제 네트워크 벤치마크 (933페이지, 25병렬):**
- Lightpanda 4.81초, Chrome 46.70초 → **~10배 빠름**
- Lightpanda 123-215MB, Chrome 2GB → **~10-16배 메모리 절약**

**100병렬:** Chrome 69분 이상, Lightpanda 4.45초

벤치마크 조건: AWS EC2 m5.large (8GB RAM). Lightpanda 자체 공개 수치이므로 독립 검증 필요.

---

## Phase 2: 인포키워드 적용 분석

### 현재 시스템 분석

**파일**: `/home/jay/projects/InfoKeyword/worker/reporter/screenshot.py`

핵심 코드:
```python
async with async_playwright() as pw:
    browser = await pw.chromium.launch(
        executable_path=CHROMIUM_PATH,
        headless=True,
        args=["--no-sandbox", "--disable-dev-shm-usage"],
    )
    context = await browser.new_context(
        viewport={"width": 1280, "height": 900},
        user_agent=_USER_AGENT,
    )
    page = await context.new_page()
    await page.goto(url, wait_until="domcontentloaded", timeout=30_000)
    await page.wait_for_timeout(2_000)
    await page.screenshot(path=screenshot_path, full_page=True)  # ← 핵심
```

사용하는 Playwright 기능:
- `chromium.launch(headless=True)` — 로컬 Chromium 실행
- `new_context(viewport, user_agent)` — 뷰포트/UA 설정
- `page.goto(wait_until="domcontentloaded")` — 페이지 로드
- `page.wait_for_timeout(2000)` — 추가 대기
- `page.screenshot(full_page=True)` — **풀페이지 스크린샷** ← 핵심 기능

### 적용 가능성 평가: No-Go

#### 코드 변경 범위

이론적으로 `p.chromium.launch()` → `p.chromium.connect_over_cdp("ws://127.0.0.1:9222")` 변경만으로 연결 가능.

**그러나:**
1. `page.screenshot(full_page=True)` — Lightpanda는 **하드코딩된 더미 PNG 반환**. 실제 네이버 검색결과 이미지가 아님.
2. `new_context(viewport={"width": 1280, "height": 900})` — **noop**, 뷰포트 미적용.
3. `new_context(user_agent=_USER_AGENT)` — **ignored**, UA 미적용.

→ 연결은 되지만 **출력물이 모두 동일한 1920×1080 플레이스홀더 이미지**. 인포키워드의 존재 의미인 "네이버 검색결과 시각적 캡처"가 불가능.

#### 리스크 평가

| 리스크 | 심각도 | 설명 |
|--------|--------|------|
| 스크린샷 불가 | **치명적** | 렌더링 엔진 부재로 실제 스크린샷 생성 원천 불가 |
| 네이버 JS 호환성 | 높음 | 네이버 검색결과의 복잡한 SPA 구조, JS 의존도 높음 |
| 뷰포트/UA 미적용 | 높음 | Emulation API가 noop → 일관된 결과 보장 불가 |
| 한글 폰트 렌더링 | 해당 없음 | 렌더링 자체를 하지 않으므로 논의 불필요 |
| Beta 안정성 | 높음 | 프로덕션 크래시, WebSocket 미지원 등 |

### 기타 활용 가능 영역

**스크린샷이 필요 없는 작업에는 Lightpanda가 유효하다.**

| 시스템 | 현재 방식 | Lightpanda 적용 가능성 | 예상 이점 |
|--------|---------|---------------------|----------|
| ThreadAuto 크롤링 | RSS 외 웹 크롤링 | **높음** — 텍스트 추출, JS 실행 가능 | 메모리 10배 절감, 속도 10배 향상 |
| 인포키워드 데이터 수집 | 검색 결과 파싱(비스크린샷) | **높음** — evaluate()로 DOM 데이터 추출 | 동시 처리량 대폭 증가 |
| MediScan 심평원 PDF | Playwright로 PDF 다운로드 | **낮음** — PDF API 미구현 | 해당 없음 |
| 대시보드 자동화 | 웹 데이터 수집 | **중간** — 단순 데이터 수집은 가능 | 서버 리소스 절감 |

---

## Phase 3: 적용 로드맵

### Go/No-Go 판단

**인포키워드 스크린샷: No-Go (확정)**
- 근거: 렌더링 엔진 부재로 실제 스크린샷 생성이 원천적으로 불가능
- Lightpanda Issue #492 공식 답변: 구현 계획 없음

**텍스트 기반 크롤링 용도: Conditional Go**
- 근거: V8 JS 엔진 완전 지원, DOM API 동작, 메모리/속도 10배 개선
- 조건: PoC 테스트 통과 시

### PoC 실행 계획 (텍스트 기반 크롤링 용도)

1. **Lightpanda 바이너리 설치** (1일)
   - Linux x86_64 바이너리 다운로드
   - CDP 서버 기동 확인

2. **네이버 검색결과 텍스트 추출 테스트** (1일)
   - `page.evaluate()`로 검색 결과 제목/URL/설명 추출
   - 한글 텍스트 정상 반환 확인
   - 네이버 JS 렌더링 호환성 확인

3. **ThreadAuto 크롤링 PoC** (2일)
   - 기존 Chrome headless 대비 성능 비교
   - 메모리 사용량 측정
   - 안정성 테스트 (100+ 페이지 연속 처리)

4. **결과 평가** (1일)
   - Go: Chrome fallback 유지하면서 Lightpanda 우선 사용
   - No-Go: Chrome headless 유지

### 단계적 전환 방안 (텍스트 기반 크롤링에만 해당)

```
Phase A: 듀얼 모드 구현
  - Lightpanda CDP → 텍스트 추출 시도
  - 실패 시 → Chrome fallback
  - 성공률/속도/메모리 모니터링

Phase B: 안정화 (2-4주)
  - 성공률 99%+ 달성 확인
  - 에러 패턴 분석 및 대응

Phase C: 기본 전환
  - Lightpanda를 기본으로, Chrome을 fallback으로
  - 스크린샷 필요 작업만 Chrome 유지
```

---

## 생성/수정 파일 목록

- `/home/jay/workspace/memory/reports/task-1016.1.md` — 본 보고서 (신규)

## 테스트 결과

- 리서치/분석 전용 작업 (코드 수정 없음)
- 테스트 해당 없음

## 발견 이슈 및 해결

범위 외 이슈 없음. 리서치 전용 작업.

## 비고

- Lightpanda는 "스크린샷을 찍지 않는 브라우저"로 설계됨. 인포키워드의 핵심 가치인 시각적 캡처에는 부적합하나, 텍스트 기반 크롤링/데이터 추출에서는 Chrome 대비 10배 이상의 성능 개선 잠재력이 있음.
- 2026년 3월 현재 Beta 상태이며, 6-12개월 내 안정성 크게 향상 예상 (24.6k stars, 활발한 개발).
- AGPL-3.0 라이선스: 내부 자동화 도구 사용은 제한 없음.
