웹 개발 데일리 브리핑 — 2026-11-12
Soshy··조회수 —

기준 시점: 2026-11-12 06:00 KST
조사 범위: 전일 2026-11-11 06:00 ~ 당일 2026-11-12 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
React, Next.js, Vercel, Vue, Nuxt, Angular, Svelte, SvelteKit, Astro, Remix의 공식 블로그와 릴리스 채널을 확인했다.
이번 기간에는 새로운 주요 안정 버전이나 중요한 프론트엔드 API의 정식 출시를 확인하지 못했다.
Next.js에서는 Canary 개발과 관련 Pull Request가 계속 진행되고 있었지만, 개발 브랜치의 변경을 새로운 안정 기능의 출시로 취급하지 않았다.
Astro의 최근 안정 패치인 7.4.9와 7.4.10, Vue 3.5.45, Next.js 16.5 계열의 주요 실험적 변경은 이전 브리핑에서 이미 다뤘으므로 반복하지 않았다.
한편 프론트엔드 빌드 도구인 Vite에서는 11월 11일 정식 패치인 8.3.8이 공개됐다.
이번 패치는 Inline Worker의 자산 처리와 Sass 로딩, Windows 환경의 개발 도구 실행에 관한 수정이 중심이다. 프레임워크의 새로운 기능 출시와는 구분해 개발 도구 및 보안 섹션에서 다룬다.
이번 추천 글에서는 GPU를 사용하는 웹 UI의 번들 크기와 실제 렌더링 비용을 비교한 자료, SVG Filter를 이용한 상품 이미지 색상 변환의 한계를 분석한 자료를 소개한다.
두 자료 모두 단순히 새로운 라이브러리를 소개하기보다 실제 구현과 측정 결과를 바탕으로 설계상의 트레이드오프를 검토한다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome·Chromium, Firefox·Mozilla, WebKit·Safari의 공식 공개 자료와 JavaScript·TypeScript·CSS, 웹 성능 및 접근성 관련 발표를 확인했다.
이번 24시간에 해당하는 새로운 주요 웹 표준의 정식 채택이나 주요 브라우저 API의 안정화 발표는 확인하지 못했다.
WebKit의 공식 블로그에서 확인한 최신 Safari Technology Preview 256은 11월 4일 공개된 자료다. 따라서 이번 조사 기간의 신규 발표로 포함하지 않았다.
또한 Firefox 160의 WebTransport 표준 정합성 개선과 보안 수정은 11월 11일 브리핑에서 이미 다뤘으므로 반복하지 않았다.
웹 플랫폼의 구현 Commit과 정식 브라우저 릴리스는 구분해야 한다.
예를 들어 브라우저 엔진의 개발 브랜치에 새로운 CSS 기능이 추가됐더라도 실제 안정 버전에서 기본적으로 사용할 수 있는지는 별도로 확인해야 한다.
이번 조사에서는 해당 조건을 충족하는 중요한 신규 발표를 확인하지 못했다.
다만 이번 추천 글에서는 이미 제공되는 웹 플랫폼 기능과 관련해 실제 운영상의 문제를 분석한 자료를 선정했다.
특히 WebGPU API가 존재하더라도 실제 GPU Adapter를 확보하지 못할 수 있다는 점과, WebAssembly로 자동 전환된 사실을 애플리케이션이 명시적으로 기록해야 한다는 문제를 살펴본다.
3️⃣ 백엔드·인프라
Cloudflare Hyperdrive, 데이터베이스 연결과 설정 수 한도 확대 — Configuration 100개·원본 연결 1,500개 지원
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 플랫폼 한도 변경
- 적용 대상: Cloudflare Hyperdrive, Workers Paid 요금제
- 주요 변경: Configuration 한도 25개에서 100개, Configuration당 Origin Connection 한도 100개에서 1,500개
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-11-hyperdrive-raised-limits/
Cloudflare가 Hyperdrive의 설정 수와 데이터베이스 연결 한도를 크게 확대했다.
이번 변경은 새로운 데이터베이스 드라이버나 Query Cache 알고리즘을 출시한 것이 아니다.
기존 Hyperdrive를 통해 더 많은 데이터베이스와 더 큰 규모의 연결 Pool을 관리할 수 있도록 리소스 한도를 변경한 것이다.
특히 Cloudflare Workers에서 PostgreSQL과 같은 외부 데이터베이스를 사용하는 애플리케이션에 영향을 줄 수 있다.
Hyperdrive의 역할
일반적인 서버 애플리케이션에서는 데이터베이스 Connection Pool을 이용한다.
예를 들어 Node.js API 서버가 PostgreSQL에 연결한다고 가정해 보자.
모든 HTTP 요청마다 새로운 데이터베이스 연결을 생성하면 다음 작업이 반복될 수 있다.
- 네트워크 연결
- TLS Handshake
- 데이터베이스 인증
- Query 실행
- 연결 종료
이러한 작업에는 비용이 발생한다.
따라서 일반적인 서버에서는 연결을 재사용하기 위해 Connection Pool을 유지할 수 있다.
하지만 Cloudflare Workers처럼 여러 위치에서 실행되는 서버리스 환경에서는 연결 재사용이 더 복잡해질 수 있다.
Hyperdrive는 Workers와 원본 데이터베이스 사이에서 연결을 Pooling하고 일부 Query 결과를 Cache하는 역할을 제공한다.
Configuration 수 확대
기존 Workers Paid 요금제의 Configuration 한도는 다음과 같았다.
25개
새로운 한도는 다음과 같다.
100개
Configuration은 Hyperdrive가 연결할 원본 데이터베이스와 관련 설정을 관리하는 단위다.
예를 들어 하나의 SaaS 서비스가 여러 고객의 데이터베이스를 분리해 관리한다고 가정해 보자.
고객별로 서로 다른 데이터베이스에 연결해야 할 수 있다.
기존 한도에서는 Configuration을 25개까지만 생성할 수 있었다.
새로운 정책에서는 같은 계정에서 최대 100개를 생성할 수 있다.
따라서 여러 데이터베이스를 이용하는 애플리케이션에서 Hyperdrive를 활용할 수 있는 범위가 확대됐다.
하지만 Configuration 한도가 네 배로 증가했다는 사실이 실제 데이터베이스의 최대 처리량도 네 배로 증가한다는 의미는 아니다.
각 데이터베이스의 리소스와 연결 정책은 별도로 적용된다.
Origin Connection 한도 확대
두 번째 변경은 실제 원본 데이터베이스에 연결할 수 있는 Connection 수다.
기존 한도는 다음과 같았다.
100개
새로운 한도는 다음과 같다.
1,500개
이는 Hyperdrive Configuration 하나가 원본 데이터베이스에 생성할 수 있는 연결 수의 최대 설정 범위다.
예를 들어 많은 Worker가 같은 PostgreSQL 데이터베이스에 접근한다고 가정해 보자.
기존에는 Hyperdrive가 원본 데이터베이스에 최대 100개의 연결을 유지할 수 있었다.
하지만 실제 부하가 증가해 더 많은 동시 Query를 처리해야 한다면 해당 한도가 병목이 될 수 있었다.
새로운 정책에서는 필요한 경우 연결 수를 1,500개까지 구성할 수 있다.
다만 중요한 점이 있다.
최대 연결 수가 15배로 증가했다고 해서 실제 Query 처리량이 15배 증가하는 것은 아니다.
데이터베이스는 연결 하나마다 메모리와 실행 자원을 사용할 수 있다.
따라서 연결 수를 지나치게 늘리면 오히려 데이터베이스의 성능이 나빠질 수 있다.
데이터베이스의 max_connections
Cloudflare는 Hyperdrive의 Origin Connection Limit을 실제 데이터베이스의 연결 한도보다 낮게 설정하도록 권장한다.
예를 들어 PostgreSQL의 max_connections가 500이라고 가정해 보자.
그런데 Hyperdrive의 한도를 1,500으로 설정했다면 실제 데이터베이스가 해당 수의 연결을 모두 받아들일 수 없다.
또한 다른 애플리케이션과 관리 도구도 데이터베이스 연결을 사용한다.
따라서 Hyperdrive가 데이터베이스 연결을 모두 점유하지 않도록 여유를 남겨야 한다.
실제 연결 수를 결정할 때는 다음 사항이 영향을 준다.
- 데이터베이스의 최대 연결 수
- Query의 평균 실행 시간
- 동시 요청 수
- CPU 및 메모리 용량
- 다른 서비스가 사용하는 연결 수
따라서 이번 변경은 더 큰 Connection Pool을 사용할 수 있게 만든 것이지 모든 사용자에게 큰 Pool을 사용하도록 권장하는 것은 아니다.
설정 인터페이스
새로운 Origin Connection Limit은 다음 방식으로 관리할 수 있다.
- Cloudflare Dashboard
- Wrangler
- REST API
따라서 기존 Hyperdrive를 사용하는 프로젝트도 실제 필요에 따라 설정을 조정할 수 있다.
다만 이번 발표에는 자동으로 모든 Configuration의 한도를 1,500개로 변경한다는 설명이 없다.
실제로 사용하려는 연결 한도는 해당 Configuration의 설정을 확인해야 한다.
적용 범위와 한계
이번 변경은 Workers Paid 요금제를 대상으로 한다.
공식 발표에는 다음 수치가 포함되지 않았다.
- Query 지연 시간의 감소율
- Connection Pool의 실제 처리량 증가율
- CPU 및 메모리 절감량
- 최대 부하 환경에서의 P95 또는 P99 지연
따라서 한도 확대 자체를 성능 개선 Benchmark로 해석해서는 안 된다.
이번 업데이트는 여러 원본 데이터베이스를 사용하는 Workers 애플리케이션과 높은 동시성의 데이터베이스 작업에서 Hyperdrive를 활용할 수 있는 설정 범위를 확대한 플랫폼 변경이다.
Cloudflare Durable Objects, 객체 재배치 이후 불필요한 연결 경유 제거 — 위치 조회 과정의 P99 지연 개선
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 플랫폼 내부 최적화
- 적용 대상: Cloudflare Workers, Durable Objects
- 변경 유형: 객체 위치 조회 및 연결 경로 최적화
- 성능: 특정 요청의 P99 연결 지연 감소 가능. 정량적인 감소 수치는 미공개
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-11-durable-objects-request-routing/
Cloudflare가 Durable Object에 연결할 때 사용하는 내부 Routing 과정을 최적화했다.
이번 변경은 새로운 Durable Object API를 추가하는 기능이 아니다.
실제 객체의 실행 위치를 찾는 과정에서 불필요하게 생성하던 중간 연결을 제거하는 것이다.
특히 Durable Object가 다른 데이터센터로 이동한 이후의 요청에서 영향을 줄 수 있다.
Durable Object의 실행 위치
Durable Object는 특정 ID에 연결된 실행 단위다.
예를 들어 하나의 채팅방을 하나의 Durable Object로 관리한다고 가정해 보자.
사용자는 같은 채팅방 ID로 요청한다.
하지만 실제 Durable Object가 어느 데이터센터에서 실행되는지는 Cloudflare가 결정할 수 있다.
따라서 Worker가 해당 Durable Object를 호출하려면 실제 실행 위치를 찾아야 한다.
Cloudflare는 이러한 위치 정보를 내부 네트워크에서 Cache할 수 있다.
Cache에 위치가 존재한다면 해당 정보를 이용해 연결할 수 있다.
문제는 객체가 이전 위치에서 다른 데이터센터로 이동한 이후다.
기존 Forwarding Record
예를 들어 Durable Object가 데이터센터 A에서 실행되고 있었다고 가정해 보자.
이후 Cloudflare는 해당 객체를 데이터센터 B에서 실행하도록 변경했다.
하지만 일부 위치 정보 Cache에는 아직 이전 정보가 남아 있을 수 있다.
이 경우 데이터센터 A에는 새로운 위치인 B를 가리키는 Forwarding Record가 존재할 수 있다.
기존 동작은 다음과 같았다.
- 호출자가 데이터센터 A에 객체 위치를 문의한다.
- A는 B를 가리키는 Forwarding Record를 확인한다.
- A가 B의 Durable Object에 먼저 연결한다.
- A가 해당 연결을 통해 요청을 중계한다.
- 호출자는 이후 B에 대한 직접 연결도 생성한다.
즉 실제 객체의 위치를 찾는 과정에서 중간 연결이 불필요하게 발생할 수 있었다.
이러한 연결에는 추가적인 네트워크 왕복 시간이 필요하다.
변경된 Routing 방식
새로운 구현에서는 다음과 같이 동작한다.
- 호출자가 데이터센터 A에 위치를 문의한다.
- A는 Forwarding Record에서 실제 목적지 B를 확인한다.
- A가 B에 직접 연결하지 않고 목적지 정보만 반환한다.
- 호출자가 B에 직접 연결한다.
따라서 중간 데이터센터에서 실제 Durable Object에 연결하는 작업을 생략한다.
Cloudflare는 이러한 변경이 객체 재배치와 관련된 요청의 P99 연결 지연을 줄일 수 있다고 설명한다.
P99는 요청의 99%가 해당 시간 안에 완료되는 지연 시간 지표다.
따라서 평균적인 요청보다 드물게 느려지는 요청을 분석할 때 사용할 수 있다.
전체 요청이 빨라지는 것은 아님
이번 최적화에는 명확한 적용 범위가 존재한다.
모든 Durable Object 요청의 연결 경로가 변경된 것은 아니다.
기존 위치 정보가 정확하게 Cache돼 있다면 이번 변경의 영향이 작을 수 있다.
실제로 영향을 받는 상황은 다음과 같다.
- 객체가 다른 데이터센터로 이동
- 호출자가 최신 객체 위치를 Cache하지 못함
- 이전 데이터센터에 Forwarding Record가 존재
- 해당 기록을 이용해 새로운 목적지를 찾는 경우
또한 객체의 이동 횟수와 실제 트래픽의 분포에 따라 효과가 달라질 수 있다.
Cloudflare는 P99 지연을 줄일 수 있다고 설명하지만 실제 감소 시간을 밀리초 단위로 공개하지 않았다.
따라서 이번 최적화가 모든 Durable Object 호출의 P99를 특정 비율만큼 개선했다고 단정할 수 없다.
기존 API와의 호환성
이번 변경은 Cloudflare의 내부 Routing에 관한 것이다.
따라서 개발자가 다음 작업을 수행할 필요는 없다.
- Wrangler 설정 변경
- Durable Object Namespace 변경
- 기존 RPC 메서드 수정
- 별도의 Compatibility Flag 추가
하지만 기존 위치에서 새로운 위치로 이동하는 과정의 비용이 모두 제거된 것은 아니다.
이번 변경은 그 과정에서 발생할 수 있는 불필요한 중간 연결을 생략한 것이다.
또한 Durable Object의 실행 위치를 개발자가 항상 고정할 수 있다는 의미도 아니다.
이번 발표는 Durable Object의 실제 실행 위치가 변경된 이후 연결을 복구하는 과정에서 불필요한 네트워크 왕복을 제거하도록 내부 Routing을 최적화한 업데이트다.
Vercel AI Gateway, Gemini Decision 1 지원 — 텍스트 생성 대신 Boolean·선택지·점수 반환
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 발표 주체: Vercel
- 모델 제공업체: Google
- 적용 대상: Vercel AI Gateway, AI SDK 7
- 모델 식별자:
google/gemini-decision-1 - 기능 단계: AI Gateway에서 모델 제공. AI SDK의 관련 함수는 Experimental API
- 원문: https://vercel.com/changelog/gemini-decision-1-now-available-on-ai-gateway
Vercel이 AI Gateway에서 Gemini Decision 1을 사용할 수 있도록 지원한다고 발표했다.
이번 변경은 일반적인 텍스트 생성 모델을 하나 더 제공하는 업데이트와 다르다.
Gemini Decision 1은 입력을 바탕으로 특정 질문에 대한 판단 결과를 반환하는 모델이다.
대표적인 결과는 다음과 같다.
- Boolean
- Choice
- Score
즉 장문의 자연어 답변을 생성하는 대신 특정 조건에 관한 판단 결과를 반환하도록 설계됐다.
일반적인 텍스트 생성과의 차이
예를 들어 고객지원 서비스에서 다음 요청이 도착했다고 가정해 보자.
같은 요금이 두 번 결제됐습니다. 중복 결제를 환불해 주세요.
일반적인 LLM에는 다음과 같이 질문할 수 있다.
이 사용자가 환불을 요청했나요?
모델은 다음과 같은 자연어 답변을 생성할 수 있다.
네, 사용자는 중복 결제에 대한 환불을 요청하고 있습니다.
하지만 실제 백엔드에서 필요한 값은 단순히 Boolean일 수 있다.
true
이 경우 자연어 답변을 생성하고 다시 Parsing하는 과정이 추가될 수 있다.
Decision Model은 이러한 종류의 판단을 직접 반환하도록 설계됐다.
따라서 고객지원 요청의 분류나 도구 선택과 같은 기능에서 활용할 수 있다.
AI SDK의 decide()
Vercel은 AI SDK 7에서 다음 API를 소개한다.
experimental_decide
이를 decide라는 이름으로 가져와 사용할 수 있다.
대표적인 사용 구조는 다음과 같다.
- 모델 선택
- 판단할 입력 데이터 전달
- 판단 질문 정의
- 결과 형식 지정
- 실제 판단 결과 확인
예를 들어 다음 질문을 정의할 수 있다.
requestsRefund
결과 타입은 Boolean이다.
이후 다음 값에서 실제 판단 결과를 확인할 수 있다.
result.answers.requestsRefund
이러한 방식은 자연어 답변의 표현이 매번 조금씩 달라지는 문제를 줄일 수 있다.
다만 Boolean을 반환한다는 사실만으로 판단이 반드시 정확하다는 의미는 아니다.
실제 모델은 입력의 의미를 잘못 해석할 수 있다.
따라서 중요한 업무에서는 판단 결과에 대한 검증과 오류 처리 정책이 필요하다.
선택지와 점수
Decision Model은 Boolean뿐 아니라 선택지와 점수도 반환할 수 있다.
예를 들어 고객지원 요청을 다음 분류 가운데 하나로 구분할 수 있다.
- 결제
- 배송
- 계정
- 기술지원
또는 여러 검색 결과에 점수를 부여해 순위를 정하는 방식으로 활용할 수 있다.
AI 에이전트에서는 어떤 도구를 사용할지 결정하는 데 사용할 수 있다.
예를 들어 다음 두 도구가 있다고 가정해 보자.
- 문서 검색
- 주문 조회
사용자의 질문이 주문 상태에 관한 것이라면 주문 조회 도구를 선택할 수 있다.
하지만 이러한 구조를 실제 보안 권한의 검증에 그대로 사용해서는 안 된다.
예를 들어 사용자가 다른 사람의 주문을 조회할 권한이 있는지는 AI 모델의 판단이 아니라 실제 서버의 인가 정책으로 결정해야 한다.
멀티모달 입력
이번 모델은 텍스트 외의 입력도 처리할 수 있다.
Vercel이 공개한 지원 범위는 다음과 같다.
| 인터페이스 | 입력 |
|---|---|
| AI SDK 7 | 텍스트, 이미지, 오디오, 비디오, PDF |
| TypeSafe-compatible API | 텍스트, 이미지 |
| Decisions API | 텍스트, 이미지 |
따라서 Gemini Decision 1이 여러 종류의 데이터를 지원한다는 사실과 실제 API에서 모든 형식을 사용할 수 있다는 사실은 구분해야 한다.
예를 들어 PDF 문서에서 특정 조건을 판단하려면 AI SDK 7의 지원 경로를 사용해야 한다.
TypeSafe-compatible API에서 같은 PDF 입력이 자동으로 지원된다고 가정해서는 안 된다.
출력 Token이 없다는 의미
Vercel은 이번 모델이 출력 Token을 생성하지 않고 Boolean과 Choice, Score를 반환한다고 설명한다.
하지만 이것이 모든 요청의 비용이 없다는 의미는 아니다.
실제 입력 데이터의 처리와 모델 추론에는 계산 자원이 필요하다.
따라서 실제 요금과 처리량은 해당 모델의 가격 정책과 사용량에 따라 달라질 수 있다.
또한 이번 발표에는 다음 비교 수치가 포함되지 않았다.
- 일반적인 LLM 분류 방식과의 정확도 차이
- 실제 요청의 P50 또는 P95 지연 시간
- 대규모 분류 작업의 처리량
- 동일한 데이터셋에서의 독립적인 평가 결과
따라서 Decision Model이 모든 분류 작업에서 더 빠르거나 정확하다고 단정할 수 없다.
AI Gateway와 사용량 관리
이번 모델은 기존 AI Gateway에서 사용할 수 있다.
Decision 호출도 일반적인 모델 요청과 함께 다음 정보에 포함된다.
- 요청 로그
- 사용량 추적
- 비용 및 Budget
따라서 실제 애플리케이션에서 Decision 호출을 별도의 시스템으로 완전히 분리해 관리하지 않아도 된다.
하지만 실제 모델을 이용한 정책 판단과 서버의 최종 비즈니스 권한 검증은 서로 다른 역할을 수행한다.
이번 발표는 자연어 생성보다 구조화된 판단이 필요한 AI 웹 애플리케이션에서 새로운 Decision Model을 기존 AI SDK 및 Gateway에 연결하도록 지원 범위를 확대한 업데이트다.
이번 조사 범위에서 이외에 포함할 만한 주요 신규 백엔드·인프라 발표를 확인하지 못했습니다.
AWS, Google Cloud, Azure의 공식 발표와 데이터베이스·API·서버리스 관련 주요 업데이트를 조사했다.
이번 기간에 해당하는 중요한 신규 정식 기능을 공식 원문과 함께 확인하지 못한 항목은 포함하지 않았다.
Cloudflare의 API Shield에서는 Web Assets의 Operation별 트래픽·오류·지연·응답 크기·보안 관련 Metric View가 추가됐다.
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-11-web-assets-metric-views/
이 기능은 기존 API Endpoint의 분석 화면을 확장한 것이다. 실제 API의 동작이나 보안 규칙을 변경하는 기능이 아니므로 별도의 주요 소식으로 선정하지 않았다.
4️⃣ 개발 도구 및 보안
Node.js 26.13.1·24.22.3·22.23.5 긴급 보안 릴리스 — HTTP/2·HTTPS·TLS에서 다수의 정보 유출·서버 종료·인증 우회 문제 수정
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 발표 주체: Node.js Project
- 안정화 단계: 지원 중인 세 릴리스 계열의 정식 보안 업데이트
- 대상: Node.js 22.x, 24.x, 26.x
- 최고 심각도: High
- 공식 보안 권고: https://nodejs.org/zh-cn/blog/vulnerability/november-2026-security-releases
- Node.js 26.13.1: https://nodejs.org/ko/blog/release/v26.13.1
- Node.js 24.22.3: https://nodejs.org/ko/blog/release/v24.22.3
- Node.js 22.23.5: https://nodejs.org/id/blog/release/v22.23.5
Node.js가 2026년 11월 11일 세 개의 지원 릴리스 계열에 대한 보안 업데이트를 공개했다.
이번 조사 기간에서 가장 중요한 보안 발표다.
특히 HTTP/2와 HTTPS, TLS 관련 취약점이 다수 수정됐다.
이 가운데 일부는 원격의 인증되지 않은 공격자가 서버 프로세스를 종료시키거나 다른 연결의 메모리 내용을 노출시킬 수 있는 문제와 관련된다.
또한 호스트 이름에 따라 서로 다른 클라이언트 인증서를 요구하는 mTLS 환경에서 인증 정책을 우회할 수 있던 문제도 포함됐다.
이번 릴리스는 일반적인 성능 패치나 기능 추가와 구분해야 한다.
실제 공개된 취약점에 대한 수정이 포함된 정식 보안 릴리스다.
세 릴리스 계열의 수정 버전
공식 보안 수정 버전은 다음과 같다.
| 계열 | 수정 버전 | 코드명 |
|---|---|---|
| Node.js 26 | 26.13.1 | Lithium |
| Node.js 24 | 24.22.3 | Krypton |
| Node.js 22 | 22.23.5 | Jod |
Node.js 26과 24는 LTS 계열이다.
실제로 사용하는 애플리케이션의 Runtime 버전에 맞는 수정 버전으로 변경해야 한다.
하지만 이번 보안 권고에 나열된 모든 CVE가 세 계열에 똑같이 영향을 주는 것은 아니다.
예를 들어 일부 HTTP/2 취약점은 Node.js 24와 26에만 영향을 준다.
반면 HTTPS의 특정 메모리 노출 문제는 세 계열 모두에 영향을 준다.
따라서 실제 서비스의 영향 범위를 분석할 때는 사용 중인 버전과 관련 기능을 함께 확인해야 한다.
HTTP/2 Session 종료 중 프로세스가 종료되는 문제
대표적인 High 취약점은 다음과 같다.
CVE-2026-123863
이 취약점은 HTTP/2 Session을 종료하는 과정에서 발생한다.
Node.js는 HTTP/2 연결을 처리하기 위해 여러 Stream과 관련 메모리를 관리한다.
예를 들어 하나의 HTTP/2 연결에서 여러 HTTP 요청이 동시에 처리될 수 있다.
문제는 Stream이 Session의 관리 목록에서 제거됐지만 실제 메모리 자원의 정리가 완료되지 않은 상황이었다.
이후 Session Destructor가 실행되면서 내부 메모리 사용량이 0이라고 가정한다.
하지만 실제로는 해당 메모리가 남아 있을 수 있다.
그 결과 내부 Assertion이 실패해 Node.js 프로세스가 종료될 수 있었다.
공식 권고에 따르면 인증되지 않은 원격 클라이언트가 해당 상황을 유발할 수 있다.
특히 요청 Body가 모두 도착하기 전에 서버가 응답을 반환하는 일반적인 HTTP/2 Handler에서도 문제가 발생할 수 있다.
이번 취약점의 영향 범위는 다음과 같다.
- Node.js 24.x
- Node.js 26.x
Node.js 22.x는 공식 권고에서 이 CVE의 영향 대상에 포함되지 않았다.
HTTPS의 메모리 정보 유출
이번 발표에서 가장 심각하게 검토할 필요가 있는 문제 가운데 하나는 다음 취약점이다.
CVE-2026-123864
이 취약점은 HTTPS 처리 과정에서 발생하는 Re-entrancy 문제와 관련된다.
Re-entrancy는 어떤 함수가 아직 실행 중인데 같은 처리 흐름이 다시 진입하는 상황이다.
Node.js의 TLS 처리와 HTTP Parser가 서로 영향을 주면서 일부 메모리를 잘못 읽을 수 있었다.
공식 보안 권고는 인증되지 않은 클라이언트가 다른 연결에 속한 메모리를 읽을 가능성을 설명한다.
여기에는 다음 데이터가 포함될 수 있다.
- 다른 사용자의 요청 내용
- 다른 연결의 메모리
- TLS 관련 비밀 데이터
하지만 이 취약점이 모든 HTTPS 서버에서 항상 같은 방식으로 악용된다는 의미는 아니다.
공식 권고는 다음과 같은 실행 조건을 제시한다.
- 요청 Body가 완전히 도착하기 전에
res.flushHeaders()호출 - 이후 비어 있지 않은 데이터를 포함해
res.end()호출 - 요청 Body를 다시 반환하거나 저장하는 경로 존재
따라서 실제 애플리케이션의 HTTP Handler 구조에 따라 영향이 달라질 수 있다.
또한 이 문제는 HTTPS 처리 경로와 관련된다.
일반적인 암호화되지 않은 node:http 자체가 같은 방식으로 영향을 받는 것은 아니라고 명시돼 있다.
영향을 받는 계열은 다음과 같다.
- Node.js 22.x
- Node.js 24.x
- Node.js 26.x
HTTP/2 연결의 메모리 및 File Descriptor 누수
다음 취약점도 High로 분류됐다.
CVE-2026-123865
이 문제는 HTTP/2 Session이 종료되는 과정에서 발생한다.
예를 들어 클라이언트가 HTTP/2 연결의 종료 절차를 시작했다고 가정해 보자.
그런데 연결이 종료되는 동안 Protocol Error가 발생할 수 있다.
이때 내부 nghttp2 라이브러리는 연결을 종료한 상태로 처리한다.
하지만 Node.js는 해당 종료 사실을 정확하게 인식하지 못할 수 있었다.
그 결과 다음 자원이 해제되지 않을 수 있었다.
- Session 관련 메모리
- File Descriptor
- Socket
공식 권고에서는 일부 Socket이 CLOSE_WAIT 상태로 유지될 수 있다고 설명한다.
이러한 연결이 누적되면 실제 서버의 사용 가능한 자원이 감소할 수 있다.
결과적으로 새로운 요청을 처리하지 못할 가능성이 있다.
이번 취약점은 세 지원 계열에 모두 영향을 준다.
HTTP/2 응답 메모리의 Use-after-free
다음 취약점도 High로 분류됐다.
CVE-2026-123866
Use-after-free는 이미 해제된 메모리를 다시 사용하는 문제다.
이번 문제는 HTTP/2 Stream을 정리하는 과정에서 발생할 수 있었다.
예를 들어 서버가 클라이언트에 응답을 전송하고 있다고 가정해 보자.
하지만 실제 Socket에는 아직 전송하지 못한 데이터가 남아 있다.
이때 클라이언트가 요청을 취소한다.
서버는 해당 Stream을 정리한다.
그런데 실제 전송 작업에서는 이전 Stream의 메모리를 계속 참조할 수 있었다.
그 결과 이미 해제된 메모리나 다른 데이터가 클라이언트에 전송될 수 있었다.
공식 권고는 다른 연결의 Response Buffer 내용까지 노출될 가능성을 설명한다.
다만 원격 클라이언트가 단독으로 문제를 유발할 수 있는지와 애플리케이션의 Timeout 처리가 필요한지는 Node.js 버전에 따라 달라질 수 있다.
이번 취약점은 세 지원 계열 모두에 영향을 준다.
TLS Server의 원격 프로세스 종료
다음 취약점은 Node.js 24와 26에 영향을 준다.
CVE-2026-126051
TLS Server는 연결을 수립하는 과정에서 여러 Callback을 사용할 수 있다.
대표적으로 다음과 같다.
SNICallbackOCSPRequestnewSessionresumeSession
문제는 해당 Callback이 실제로 실행되기 전에 연결이 종료될 수 있다는 점이다.
기존 구현에서는 Socket이 이미 제거됐더라도 지연된 Callback이 실행될 수 있었다.
그 결과 처리되지 않은 예외가 발생해 서버 프로세스가 종료될 수 있었다.
공식 권고에 따르면 인증되지 않은 원격 클라이언트가 관련 실행 흐름을 유발할 수 있었다.
이번 수정은 연결의 종료 상태와 지연된 Callback의 실행 관계를 보완한다.
TLS 1.2의 mTLS 인증 우회
다음 취약점은 High로 분류됐다.
CVE-2026-123872
이 취약점은 TLS 1.2 Renegotiation과 SNI(Server Name Indication)의 관계에서 발생한다.
SNI는 하나의 서버에서 여러 호스트 이름을 처리할 때 실제 요청 대상의 호스트 이름을 전달하는 데 사용할 수 있다.
예를 들어 다음 두 도메인이 있다고 가정해 보자.
- 일반 사용자 API
- 관리자 API
서버는 두 도메인에 서로 다른 클라이언트 인증서 정책을 적용할 수 있다.
관리자 API에는 더 엄격한 인증서를 요구한다.
문제는 기존 TLS 연결을 Renegotiation하는 과정에서 호스트 이름을 변경할 수 있던 상황이었다.
공식 권고에 따르면 일반 호스트에 접근할 수 있는 클라이언트가 Renegotiation을 통해 더 엄격한 인증이 필요한 호스트에 접근할 가능성이 있었다.
이번 수정은 연결의 인증 정책과 호스트 이름이 잘못 결합되는 문제를 방지한다.
영향 대상은 세 계열 모두다.
HTTP/2 파일 전송 관련 문제
파일을 전송하는 API에도 여러 수정이 포함됐다.
대표적인 메서드는 다음과 같다.
respondWithFile()respondWithFD()
이러한 API는 HTTP/2에서 파일을 응답으로 전달하는 데 사용할 수 있다.
문제는 파일을 전송하는 동안 클라이언트가 요청을 취소하는 경우다.
다음 두 CVE가 관련된다.
| CVE | 문제 | 영향 계열 |
|---|---|---|
| CVE-2026-123874 | 전송 중인 Stream의 Use-after-free | 24.x, 26.x |
| CVE-2026-123875 | 취소된 전송의 Null Pointer 참조로 프로세스 종료 | 22.x, 24.x, 26.x |
이러한 문제는 일반적인 모든 파일 다운로드에서 반드시 발생한다는 의미는 아니다.
실제로 Node.js HTTP/2의 해당 API를 사용하는지에 따라 영향이 달라진다.
하지만 파일 전송 요청의 취소처럼 정상적으로도 발생할 수 있는 상황에서 서버의 안정성 문제가 발생할 수 있었다는 점이 중요하다.
HTTP/2의 메모리 사용량 제한 우회
이번 보안 권고에는 Medium으로 분류된 메모리 문제도 존재한다.
CVE-2026-123868
HTTP/2에서는 클라이언트가 데이터를 서버에 전송할 수 있다.
하지만 서버가 해당 데이터를 즉시 읽지 않는 경우가 존재할 수 있다.
이때 내부 Queue에 여러 Buffer가 쌓일 수 있다.
문제는 기존 구현에서 이러한 데이터가 Session의 메모리 사용량 계산에 정확하게 포함되지 않았다는 것이다.
따라서 다음 설정만으로 실제 사용량을 제한하기 어려울 수 있었다.
maxSessionMemory
공식 권고는 작은 데이터 Frame을 매우 많이 전송하는 방식으로 실제 서버 메모리를 크게 증가시킬 수 있었다고 설명한다.
이 문제는 Node.js 24와 26에 영향을 준다.
이번 수정은 읽지 않은 Stream의 데이터도 실제 메모리 사용량 계산에 반영하도록 변경한다.
Proxy 인증 정보의 로그 노출
다음 취약점도 수정됐다.
CVE-2026-123871
Node.js에서는 Proxy를 이용해 외부 HTTP 요청을 전달할 수 있다.
Proxy URL에는 사용자 이름과 비밀번호가 포함될 수 있다.
문제는 잘못된 Proxy 설정으로 오류가 발생했을 때 인증 정보가 오류 메시지에 포함될 수 있었다는 것이다.
대표적인 오류 코드는 다음과 같다.
ERR_PROXY_INVALID_CONFIG
예를 들어 애플리케이션이 Container에서 실행되고 있다고 가정해 보자.
Proxy 설정에 오류가 발생한다.
Node.js는 관련 오류를 stderr에 출력한다.
이후 Container 로그가 중앙 Log Aggregation 서비스로 전달된다.
그런데 오류 메시지에 Proxy Credential이 포함돼 있다면 실제 인증 정보가 로그에 저장될 수 있다.
이번 수정은 해당 오류 경로에서 인증 정보를 제거한다.
영향 대상은 세 계열 모두다.
HTTPS Agent의 TLS Session 재사용 정책
다음 취약점도 포함됐다.
CVE-2026-123877
Node.js의 https.Agent는 TLS Session을 Cache하고 재사용할 수 있다.
문제는 서로 다른 인증서 신뢰 정책을 사용하는 요청이 같은 Session Cache Key를 사용할 수 있던 상황이었다.
예를 들어 첫 번째 요청은 기본 CA Trust Store를 사용한다.
두 번째 요청은 명시적으로 다른 CA 정책을 지정한다.
하지만 두 요청이 같은 Cache Key를 사용한다면 이전에 인증된 TLS Session이 더 엄격한 정책의 요청에 재사용될 수 있었다.
이번 수정은 이러한 정책이 서로 다른 Cache Key를 사용하도록 변경한다.
이는 인증서 검증의 정확성과 연결되는 문제다.
Permission Model과 기타 수정
공식 권고에는 Low 취약점도 포함돼 있다.
대표적으로 다음 영역이다.
- 재귀적인
fs.watch()에서 허용하지 않은 하위 파일 이름 노출 zlib.params()와 Stream 종료가 겹쳤을 때 프로세스 종료- 파일시스템 Permission Model의 경로 매칭 오류
이러한 취약점은 실제 실행 환경과 옵션에 따라 영향이 달라진다.
예를 들어 재귀적인 파일 감시 문제는 macOS 및 Windows의 특정 권한 구성과 연결된다.
따라서 모든 Node.js 애플리케이션에서 동일한 공격이 가능한 것은 아니다.
또한 보안 권고와 개별 버전 릴리스 노트에는 일부 Low 취약점의 CVE 식별자 표기에 차이가 있어, 번호별 세부 영향 범위를 확인할 때는 실제 사용 버전의 릴리스 노트도 함께 참고해야 한다.
포함된 의존성 업데이트
이번 보안 릴리스에는 Node.js 자체 코드뿐 아니라 관련 의존성의 업데이트도 포함됐다.
공식 보안 권고에서 확인한 대상은 다음과 같다.
- llhttp: 22.x, 24.x, 26.x
- Undici: 24.x, 26.x
- SQLite: 22.x, 24.x
Node.js 26.13.1의 릴리스 노트에서는 다음 버전이 확인된다.
- llhttp 9.4.4
- Undici 8.12.4
- SQLite 3.54.1
- Brotli 1.2.1
Node.js 24.22.3에서는 Undici 7.30.2가 포함됐다.
이러한 의존성 변경은 실제 Node.js 바이너리에 포함된 구성요소의 버전에 관한 것이다.
애플리케이션이 별도로 설치한 npm 패키지의 버전까지 모두 자동으로 변경하는 것은 아니다.
실제 Next.js·Express 서비스의 영향
이번 보안 업데이트는 Node.js를 사용하는 웹 서버와 관련된다.
하지만 실제 영향 범위를 분석할 때는 어떤 계층이 TLS와 HTTP/2 연결을 처리하는지 구분해야 한다.
예를 들어 다음 구조가 있다고 가정해 보자.
- 사용자 브라우저
- Cloudflare 또는 Load Balancer
- Node.js 애플리케이션 서버
TLS와 HTTP/2 연결이 Load Balancer에서 종료되고 Node.js 서버에는 일반적인 HTTP/1.1 요청만 전달될 수 있다.
이 경우 외부 사용자가 Node.js의 HTTP/2 Session을 직접 조작하는 경로는 제한될 수 있다.
반면 Node.js 서버가 직접 HTTPS나 HTTP/2를 처리한다면 관련 취약점의 영향 범위가 달라질 수 있다.
또한 애플리케이션이 직접 HTTPS 요청을 보내는 경우에는 https.Agent와 같은 클라이언트 기능도 사용한다.
따라서 단순히 Express나 Next.js를 사용한다는 사실만으로 모든 CVE가 동일하게 적용된다고 판단해서는 안 된다.
실제 Node.js 버전과 네트워크 연결 구조, 사용하는 API를 확인해야 한다.
보안 릴리스의 범위
이번 업데이트는 지원 중인 22.x, 24.x, 26.x를 대상으로 한다.
오래된 End-of-life 버전은 이번 정식 보안 업데이트의 대상이 아니다.
따라서 지원이 종료된 버전을 사용하는 프로젝트는 단순한 패치 버전 변경만으로 같은 수정 사항을 모두 받을 수 있다고 가정해서는 안 된다.
공식 보안 권고는 이번 수정의 최고 심각도를 High로 분류했다.
하지만 모든 취약점의 실제 악용 여부나 공개된 공격 코드의 존재를 확인한 것은 아니다.
또한 이번 릴리스에는 보안 패치를 적용한 이후 일반적인 HTTP 서버의 성능이 얼마나 변경됐는지에 관한 정량적인 Benchmark가 포함되지 않았다.
이번 발표는 Node.js의 네트워크 프로토콜과 메모리 관리, TLS 인증 및 권한 검사에서 발생할 수 있던 여러 위험을 수정하기 위해 세 지원 계열의 새로운 정식 버전을 제공한 중요한 보안 업데이트다.
Vite 8.3.8 정식 패치 — Inline Worker 자산 누락과 Sass 중복 Import 수정
- 발표일: 2026-11-11 — GitHub 표시 시각 02:46, 정확한 시간대 미확인
- 발표 주체: Vite
- 버전: 8.3.8
- 안정화 단계: 정식 안정 패치
- 적용 대상: Vite Build, Bundled Dev Server, CSS 및 Sass, Windows
- 원문: https://github.com/vitejs/vite/releases/tag/v8.3.8
Vite가 8.3.8을 공개했다.
이번 릴리스는 새로운 주요 Bundler 기능을 추가하는 버전이 아니다.
실제 개발 서버와 빌드 과정에서 발생하던 오류를 수정하고 일부 초기화 비용을 줄인 패치다.
특히 Inline Worker에서 참조하는 자산이 빌드 결과물에 포함되지 않던 문제가 수정됐다.
Inline Worker의 자산 처리
일반적인 웹 애플리케이션에서는 Web Worker를 사용할 수 있다.
예를 들어 다음과 같은 작업이다.
- 대용량 데이터 변환
- 이미지 처리
- 문서 Parsing
- 복잡한 계산
Worker는 메인 스레드와 별도의 실행 환경에서 코드를 처리할 수 있다.
하지만 Worker 역시 다른 정적 자산을 참조할 수 있다.
예를 들어 다음과 같은 파일이다.
- WebAssembly
- JSON
- 추가 JavaScript Module
기존 Vite의 Bundled Dev 환경에서는 Inline Worker가 로딩 과정에서 참조하는 자산을 올바르게 출력하지 못할 수 있었다.
이번 수정은 이러한 자산을 빌드 결과에 포함하도록 한다.
따라서 실제 Worker가 실행된 이후 필요한 파일을 찾지 못하는 문제를 줄일 수 있다.
다만 모든 종류의 Worker 사용 방식이 이번 수정에 영향을 받는 것은 아니다.
실제 문제는 특정 Bundled Dev 및 Inline Worker의 자산 참조 경로와 연결된다.
Sass 초기화 비용
이번 릴리스에는 다음 성능 관련 변경도 포함됐다.
Import Sass once instead of per file
기존 구현에서는 여러 CSS 파일을 처리하면서 Sass Module을 반복해서 가져오는 작업이 발생할 수 있었다.
새로운 방식은 Sass를 한 번만 가져오도록 변경한다.
예를 들어 SCSS 파일이 매우 많은 프로젝트를 생각할 수 있다.
각 파일을 처리할 때 같은 Sass Module을 반복해서 초기화한다면 불필요한 비용이 발생할 수 있다.
이번 변경은 이러한 반복 작업을 줄이기 위한 것이다.
하지만 공식 릴리스에는 Sass 파일 수에 따른 실제 빌드 시간 비교 결과가 포함되지 않았다.
따라서 모든 SCSS 프로젝트의 빌드가 일정 비율만큼 빨라진다고 단정해서는 안 된다.
Type-only Import와 설정 로더
Vite의 Configuration Loader에서도 수정이 이루어졌다.
Type-only Import에는 실제 실행 시점의 Module이 존재하지 않을 수 있다.
이번 패치에서는 이러한 Import에 대해 Native Loader Compatibility Check를 불필요하게 수행하지 않도록 변경했다.
이러한 변경은 TypeScript를 사용하는 Vite 설정 파일의 분석 정확성과 연결된다.
Windows UNC 경로
Windows 관련 수정도 포함됐다.
대표적으로 다음 작업이다.
yarn config get실행 시 Console Window 숨김- UNC 경로가 실제로 확인될 때까지
net use실행 지연
UNC 경로는 네트워크 공유 위치를 표현하는 데 사용할 수 있다.
예를 들어 다음과 같은 경로다.
\\server\share
이러한 경로를 실제로 사용하지 않는 상황에서 관련 명령을 미리 실행하면 불필요한 작업이 발생할 수 있다.
이번 변경은 필요한 경우에만 해당 명령을 실행하도록 한다.
Release Note의 변경과 Revert
이번 릴리스의 변경 기록에는 다음 항목이 함께 포함돼 있다.
- Transform Hook Metadata에서
magicString과ast유지 - 해당 변경을 되돌리는 Revert
따라서 변경 기록에 최초 수정 PR이 포함돼 있다는 이유만으로 실제 최종 릴리스에서도 해당 수정이 유지됐다고 판단해서는 안 된다.
실제로 같은 릴리스 과정에서 되돌린 변경은 최종 결과물과 구분해야 한다.
이는 릴리스 노트를 읽을 때 개별 Commit뿐 아니라 후속 Revert까지 확인해야 한다는 사례다.
적용 범위와 한계
이번 업데이트는 정식 패치다.
하지만 중요한 신규 API의 안정화나 새로운 빌드 엔진 출시를 의미하지 않는다.
공식 발표에는 전체 빌드 시간이나 HMR 처리량의 정량적인 Benchmark가 포함되지 않았다.
이번 릴리스는 Worker 자산 출력의 정확성과 CSS 전처리 및 설정 로딩의 안정성을 개선한 유지보수 업데이트다.
GitHub, 개인 계정 Repository에 Issue Types·Custom Fields 정식 제공 — 25개 유형과 25개 필드 설정 가능
- 발표일: 2026-11-11 — 정확한 게시 시각 미확인
- 발표 주체: GitHub
- 안정화 단계: Generally Available
- 적용 대상: 개인 GitHub 계정이 소유한 Repository
- 주요 기능: Issue Type, Custom Issue Field, Projects 연동, API 및 Webhook
- 원문: https://github.blog/changelog/2026-11-11-issue-types-and-fields-are-available-for-personal-accounts/
GitHub가 개인 계정에서 소유한 Repository에도 Issue Type과 Custom Field를 정식 제공한다.
기존에는 조직이나 팀 단위에서 사용하는 계획 관리 기능이 개인 개발자의 Repository에서는 제한될 수 있었다.
이번 변경은 해당 기능을 개인 계정까지 확대한 것이다.
특히 개인 프로젝트와 오픈소스 Repository를 관리하는 개발자에게 영향을 줄 수 있다.
Issue Type
일반적인 GitHub Issue는 버그나 작업, 기능 요청 등 다양한 목적으로 사용될 수 있다.
기존에는 Label로 이러한 분류를 관리하는 방식이 널리 사용됐다.
예를 들어 다음 Label이다.
- bug
- enhancement
- task
하지만 Label은 실제 Issue Type과 다른 역할도 수행할 수 있다.
예를 들어 Priority와 Component, 상태 등을 Label로 관리할 수 있다.
이번 기능에서는 Issue의 종류를 별도 필드로 지정할 수 있다.
기본 제공 Type은 다음과 같다.
- Bug
- Task
- Feature
개발자는 이를 변경할 수 있다.
최대 25개의 Issue Type을 생성할 수 있다.
Custom Issue Field
Issue마다 추가 정보를 지정할 수도 있다.
기본 필드는 다음과 같다.
- Priority
- Effort
- Start date
- Target date
예를 들어 다음과 같은 구조를 생각할 수 있다.
하나의 기능 개발 Issue가 존재한다.
개발자는 해당 Issue에 다음 정보를 기록한다.
- 우선순위: High
- 예상 작업량: 3
- 시작일: 11월 12일
- 목표일: 11월 16일
이러한 정보를 자유로운 본문 텍스트 대신 구조화된 Field로 관리할 수 있다.
최대 25개의 Custom Field를 만들 수 있다.
지원하는 Field Type은 다음과 같다.
- Text
- Number
- Date
- Single-select
- Multi-select
따라서 단순한 분류뿐 아니라 실제 일정과 작업량도 관리할 수 있다.
Issue Type별 필드 구성
Custom Field는 특정 Issue Type에 연결할 수 있다.
예를 들어 다음과 같은 설정이다.
Bug에는 Severity와 재현 환경을 연결한다.
Feature에는 Priority와 Effort를 연결한다.
이렇게 하면 Issue의 종류에 따라 필요한 정보를 구분해 관리할 수 있다.
또한 Type이 없는 Issue에서도 특정 필드를 사용할 수 있도록 구성할 수 있다.
하지만 Custom Field를 추가한다는 사실만으로 모든 사용자가 해당 정보를 반드시 입력하도록 강제하는 것은 아니다.
실제 입력 정책과 Issue Template의 구성을 함께 고려해야 한다.
GitHub Projects 연동
이번 필드는 GitHub Projects에서도 사용할 수 있다.
예를 들어 다음 작업이 가능하다.
- 특정 Issue Type만 표시
- Priority 기준 정렬
- 목표 날짜를 기준으로 필터
- 여러 Issue의 필드 일괄 수정
- 새로운 View 생성
따라서 개인 개발자도 여러 Repository의 Issue를 하나의 계획 관리 구조에서 활용할 수 있다.
API 및 자동화
이번 기능은 다음 인터페이스에서도 사용할 수 있다.
- REST API
- GraphQL API
- Webhook
예를 들어 CI에서 특정 작업의 결과를 확인한 뒤 Issue의 Custom Field를 변경하는 자동화를 구현할 수 있다.
또는 외부 프로젝트 관리 시스템과 GitHub Issue의 상태를 동기화할 수 있다.
다만 어떤 API로 어떤 필드를 수정할 수 있는지는 실제 권한과 API Schema에 영향을 받는다.
따라서 개인 계정에서 기능이 제공된다는 사실과 외부 애플리케이션이 모든 정보를 수정할 권한을 가진다는 사실은 구분해야 한다.
Label과 Milestone의 생성
이번 발표에는 작은 UI 개선도 포함됐다.
Issue를 작성하거나 수정하는 화면에서 필요한 Label 또는 Milestone을 바로 생성할 수 있다.
기존에는 Repository의 별도 Label 및 Milestone 관리 화면으로 이동해야 할 수 있었다.
이번 변경은 이러한 이동을 줄인다.
하지만 GitHub Projects의 모든 계획 기능이 이번 발표에서 처음 도입된 것은 아니다.
이번 업데이트는 이미 제공되던 Issue Type과 Field를 개인 계정으로 확대하고 관련 UI를 개선한 것이다.
적용 범위와 한계
이번 기능은 Generally Available 상태다.
하지만 다음과 같은 기능을 새롭게 보장하는 것은 아니다.
- Issue의 자동 우선순위 판단
- 예상 작업량의 자동 계산
- 실제 개발 일정의 자동 예측
- 코드 변경과 Issue의 완전한 자동 동기화
Custom Field는 구조화된 정보를 저장하고 관리하는 기능이다.
실제 데이터를 어떻게 입력하고 유지할지는 프로젝트의 작업 방식에 따라 달라진다.
이번 발표는 개인 개발자와 오픈소스 유지보수자도 Issue를 단순한 목록이 아니라 구조화된 계획 데이터로 관리할 수 있도록 GitHub의 프로젝트 관리 기능을 확장한 정식 업데이트다.
이번 조사 범위에서 이외에 포함할 만한 주요 신규 개발 도구 및 보안 발표를 확인하지 못했습니다.
Deno, Bun, webpack, Rollup, esbuild, Babel, Biome, ESLint, Prettier, npm, pnpm, Yarn, GitLab과 주요 테스트·배포 도구의 릴리스 및 공식 변경 기록을 검토했다.
또한 OWASP와 주요 공급망 보안 관련 공개 자료를 확인했다.
이번 기간에 해당하는 새로운 중요한 취약점이나 정식 수정 버전을 공식 원문으로 함께 확인할 수 없는 항목은 포함하지 않았다.
Vite의 개발 브랜치에서는 11월 11일에도 WASM과 CSS Import, Rolldown Dev Server 관련 변경이 이루어지고 있었다.
하지만 이러한 Commit이 모두 Vite 8.3.8에 포함된 것은 아니다.
따라서 개발 브랜치의 변경을 해당 정식 버전의 기능으로 소개하지 않았다.
📚 추천 글
GPU Style Sheets vs. Three.js_ Building a 3D hero
- 저자: Ikeh Akinyemi
- 매체: LogRocket
- 게시일: 2026-11-08
- 기술: JavaScript, CSS-like DSL, GPU Style Sheets, Three.js, WebGL2, WebGPU, Vite
- 원문: https://blog.logrocket.com/gpu-style-sheets-vs-three-js-building-a-3d-hero/
- 실험 코드: https://github.com/Ikeh-Akinyemi/gss-vs-threejs
3D 효과가 포함된 웹사이트의 Hero Section을 구현하면서 GPU Style Sheets와 Three.js의 코드 구조, JavaScript 번들 크기, 실제 GPU 렌더링 비용을 비교한 기술 글이다.
이번 글의 특징은 Bundle 크기만으로 어떤 기술이 더 빠르다고 판단하지 않는다는 점이다.
저자는 동일한 목적의 Hero Section을 두 방식으로 구현했다.
그리고 실제 JavaScript 크기와 GPU 처리 시간을 각각 측정했다.
결과는 흥미로웠다.
GPU Style Sheets의 JavaScript 번들은 Three.js보다 훨씬 작았다.
하지만 실제 GPU에서 하나의 Frame을 그리는 작업은 오히려 더 오래 걸렸다.
GPU Style Sheets의 역할
GPU Style Sheets(GSS)는 CSS와 비슷한 문법으로 3D Scene을 정의하는 도구다.
저자가 사용한 버전은 다음과 같다.
gss-lang 0.0.7
이번 버전은 초기 개발 단계다.
공식 README에서도 문법이 변경될 수 있다고 안내한다.
GSS에서는 다음과 같은 형태로 3D 객체를 선언할 수 있다.
- Sphere
- Torus
- Cube
- Group
이후 CSS와 비슷한 Selector로 해당 객체의 스타일을 지정한다.
예를 들어 다음 속성을 정의할 수 있다.
- 색상
- 크기
- 이동
- 회전
- Material
- Animation
또한 CSS와 비슷한 다음 기능을 활용할 수 있다.
@keyframes:hover:has()- Media Query
sibling-index()sibling-count()
하지만 중요한 점이 있다.
GSS는 브라우저의 기본 CSS가 3D Scene을 직접 렌더링하도록 만드는 기술이 아니다.
실제로는 GSS Compiler와 JavaScript Runtime이 동작한다.
Compiler는 GSS 문법을 GPU에서 실행할 수 있는 Shader로 변환한다.
이후 WebGL2 또는 WebGPU를 이용해 Scene을 렌더링한다.
Signed Distance Function
GSS는 3D 객체를 표현하는 데 Signed Distance Function(SDF)을 사용한다.
SDF는 특정 위치에서 객체의 표면까지의 거리를 계산하는 함수다.
예를 들어 구체를 생각해 보자.
임의의 점과 구체 중심의 거리를 계산한다.
그리고 구체의 반지름을 뺀다.
이러한 값으로 구체의 내부와 외부, 표면까지의 거리를 표현할 수 있다.
GSS는 이러한 정보를 이용해 실제 화면의 Pixel을 계산한다.
이 방식은 일반적인 Polygon Mesh 렌더링과 다르다.
예를 들어 Three.js에서는 많은 Triangle로 구체를 구성할 수 있다.
반면 SDF 기반 구현에서는 실제 거리 함수를 이용해 구체의 표면을 계산한다.
이러한 구조에서는 일부 형태를 수학적으로 간단하게 표현할 수 있다.
하지만 실제 렌더링 비용은 Pixel 수와 Ray의 계산 횟수에 영향을 받는다.
실제 Hero Section
저자가 구현한 Scene에는 다음 객체가 존재한다.
- 가운데 떠 있는 Sphere
- 유리 Material을 사용하는 Ring
- 빛나는 작은 구체
- 주변에 배치된 12개의 Cube
각 객체에는 Animation이 적용됐다.
예를 들어 가운데 Sphere는 위아래로 움직인다.
주변 Cube는 각자의 위치와 회전값을 가진다.
또한 사용자가 Sphere 위에 마우스를 올리면 일부 객체의 크기와 회전이 변경된다.
GSS에서는 이러한 동작을 CSS와 비슷한 규칙으로 작성했다.
반면 Three.js에서는 다음 기능을 이용했다.
- Scene
- Camera
- Mesh
- Material
- Lighting
- Animation Loop
- Raycaster
따라서 Three.js 구현에서는 더 많은 JavaScript 설정이 필요했다.
하지만 코드의 길이가 짧다는 사실만으로 실제 GPU 처리량이 낮아지는 것은 아니다.
빌드 시점 컴파일
저자는 GSS를 두 방식으로 실행했다.
첫 번째는 브라우저에서 GSS 소스를 컴파일하는 방식이다.
두 번째는 Vite의 GSS Plugin을 이용해 빌드 시점에 미리 컴파일하는 방식이다.
두 방식에는 중요한 차이가 있다.
브라우저에서 컴파일하려면 Compiler 코드도 클라이언트에 전달해야 한다.
반면 빌드 시점에 컴파일하면 실제 사용자에게는 Runtime과 컴파일된 Scene을 제공할 수 있다.
따라서 빌드 시점 컴파일은 클라이언트 JavaScript 크기를 줄일 수 있다.
저자는 실제로 세 구성을 비교했다.
JavaScript Bundle 크기
측정 결과는 다음과 같았다.
| 구현 | 원본 크기 | Gzip | Brotli |
|---|---|---|---|
| GSS, 빌드 시점 컴파일 | 400.7KB | 87.4KB | 24.5KB |
| GSS, 브라우저에서 컴파일 | 330.7KB | 111.0KB | 94.4KB |
| Three.js | 573.4KB | 140.9KB | 115.2KB |
이번 실험에서는 빌드 시점에 컴파일한 GSS가 가장 작은 Brotli 결과를 생성했다.
Three.js의 115.2KB와 비교하면 약 79% 작았다.
하지만 이 수치를 해석할 때는 몇 가지 제한사항이 있다.
첫 번째는 JavaScript 파일만 비교했다는 점이다.
다음 파일은 제외했다.
- HTML
- CSS
- Poster Image
- 브라우저 컴파일용 GSS 원본 파일
따라서 해당 수치는 실제 페이지 전체의 다운로드 크기가 아니다.
두 번째는 압축 설정이다.
Brotli에서는 Quality 11을 사용했다.
실제 CDN과 서버가 같은 설정으로 압축한다는 보장은 없다.
세 번째는 두 Hero의 실제 Material과 Lighting 구현이 완전히 동일하지 않다는 점이다.
따라서 이 결과를 같은 시각적 품질의 완전히 동일한 구현 비교로 해석해서는 안 된다.
Brotli 압축 Window의 영향
이번 글에서 흥미로운 부분은 압축 설정에 따른 결과 차이다.
저자는 GSS의 Scene 데이터가 반복적인 Shader 코드를 많이 포함한다는 사실을 확인했다.
Brotli는 이러한 반복 데이터를 효과적으로 압축할 수 있다.
하지만 실제 Window 크기에 따라 결과가 달라졌다.
저자가 측정한 값은 다음과 같다.
| Brotli Window | GSS 빌드 시점 컴파일 결과 |
|---|---|
| 4MiB | 24.5KB |
| 32KiB | 73.8KB |
같은 JavaScript를 압축했는데 결과가 약 세 배 차이 났다.
즉 GSS의 매우 작은 압축 크기에는 실제 Compressor의 설정도 크게 기여했다.
따라서 개발자는 로컬 Benchmark의 압축 결과만 확인하지 말고 실제 배포된 JavaScript의 전송 크기를 확인해야 한다.
대표적으로 다음 HTTP Header가 중요하다.
content-encoding
실제 GPU 처리 시간
저자는 JavaScript Bundle 크기뿐 아니라 GPU에서 하나의 Frame을 처리하는 시간도 측정했다.
사용한 환경은 다음과 같다.
- WebGL2
- 1920×1080
- Pixel Density 1
- GSS Adaptive Density 비활성화
- Intel Iris Xe
- NVIDIA T4
각 실험은 다음 순서로 진행됐다.
- 3초 Warm-up
- 8초 동안 측정
- 조건별 세 번 반복
- 실제 측정의 중앙값 사용
결과는 다음과 같았다.
| 구현 | Iris Xe, 밝은 모드 | Iris Xe, 어두운 모드 | NVIDIA T4, 밝은 모드 | NVIDIA T4, 어두운 모드 |
|---|---|---|---|---|
| GSS Hero | 30.9ms | 39.1ms | 7.8ms | 9.8ms |
| GSS, Shadow 제거 | 17.9ms | 26.4ms | 5.8ms | 6.4ms |
| GSS, Shadow 제거 및 Matte 적용 | 12.8ms | 21.5ms | 5.7ms | 5.7ms |
| Three.js Hero | 7.4ms | 8.4ms | 4ms 미만 | 4ms 미만 |
이번 실험에서는 Three.js가 더 낮은 GPU 처리 시간을 기록했다.
특히 Intel Iris Xe에서는 GSS의 전체 Hero가 Three.js보다 약 4.2~4.7배 많은 GPU 시간을 사용했다.
하지만 두 구현은 Material과 Antialiasing 방식이 다르다.
따라서 해당 차이를 GSS와 Three.js 렌더링 엔진 자체의 일반적인 성능 차이로 해석해서는 안 된다.
60FPS와 GPU 처리 시간
일반적인 60FPS 화면에서는 하나의 Frame에 약 16.7ms의 시간이 주어진다.
하지만 실제 화면에서는 GPU 작업만 수행하는 것이 아니다.
브라우저는 다음 작업도 처리해야 한다.
- JavaScript
- Layout
- Style Calculation
- Input Event
- 다른 Render Pass
따라서 GPU 작업 자체가 30ms 이상이라면 해당 구성에서 안정적인 60FPS를 유지하기 어려울 수 있다.
이번 실험에서 GSS의 전체 Hero는 Iris Xe 환경에서 30.9~39.1ms를 사용했다.
반면 Shadow를 제거하면 17.9~26.4ms로 감소했다.
따라서 실제 GPU 비용에서 Shadow가 큰 비중을 차지했다.
또한 Dark Mode에서 Bloom을 사용하면서 렌더링 비용이 증가했다.
이러한 결과는 실제 성능에 영향을 주는 것은 JavaScript Bundle 크기만이 아니라 Shader와 Material, Shadow 및 후처리의 비용이라는 점을 보여준다.
Adaptive Rendering
GSS는 실제 렌더링 성능에 따라 Pixel Density를 변경할 수 있다.
기본적으로 0.5의 Density에서 시작한다.
예를 들어 가로와 세로의 Resolution을 각각 절반으로 줄이면 실제 Pixel 수는 약 4분의 1이 된다.
따라서 렌더링 작업량을 줄일 수 있다.
하지만 해상도를 낮추면 실제 이미지가 흐릿하게 보일 수 있다.
즉 GPU 비용과 화면 품질 사이에 트레이드오프가 존재한다.
또한 GSS는 실제 성능이 지나치게 낮은 경우 Scene을 일시 중단하고 Poster를 표시할 수 있다.
하지만 이런 Fallback이 존재한다는 사실만으로 모든 기기에서 높은 Frame Rate가 보장되는 것은 아니다.
Three.js가 필요한 상황
저자는 다음 상황에서는 Three.js를 검토하도록 설명한다.
- 외부 glTF 모델 로딩
- 복잡한 Animation
- 실제 Physics 및 Collision
- 다양한 Material과 Rendering Pipeline
- 큰 규모의 Interactive 3D Scene
반면 다음 상황에서는 GSS가 유리할 수 있다.
- 단순한 장식용 Hero
- CSS와 비슷한 방식의 Animation
- 작은 JavaScript 다운로드 크기
- 제한된 종류의 3D 객체
그러나 GSS는 아직 초기 개발 단계이므로 실제 API와 문법이 변경될 수 있다.
이번 Benchmark의 한계
이번 결과는 저자가 구현한 두 Hero를 기준으로 측정했다.
측정 코드는 공개돼 있다.
하지만 실제 렌더링 결과와 Material이 완전히 동일하지 않다.
또한 두 GPU 환경에서 수행한 결과이므로 모든 모바일 기기와 GPU에 동일하게 적용할 수 없다.
특히 GSS가 빌드 시점 컴파일로 더 작은 번들을 생성했다는 사실과 실제 GPU에서 더 빠르게 실행된다는 사실은 다르다.
이번 글은 3D 웹 UI를 평가할 때 JavaScript 다운로드 크기와 실제 GPU Frame Time, 시각적 품질 및 지원 기능을 각각 독립적으로 비교해야 한다는 점을 공개 코드와 측정 결과로 설명한다.
ONNX Runtime Web falls back from WebGPU to WASM silently, and the session won't say which ran
- 저자: hao jia
- 매체: DEV Community
- 게시일: 2026-11-11 — 정확한 게시 시각 미확인
- 기술: JavaScript, WebGPU, WebAssembly, ONNX Runtime Web, Cross-origin Isolation
- 원문: https://dev.to/iterandum/onnx-runtime-web-falls-back-from-webgpu-to-wasm-silently-and-the-session-wont-say-which-ran-12ml
브라우저에서 ONNX Runtime Web을 이용해 AI 모델을 실행할 때 WebGPU를 사용할 수 없는 상황에서 WASM으로 자동 전환되지만 실제로 어떤 실행 환경이 선택됐는지 애플리케이션이 확인하기 어려운 문제를 분석한 글이다.
이번 글의 특징은 실행 속도만 측정하지 않았다는 점이다.
저자는 GPU가 실제로 사용됐는지 확인했다.
또한 모델의 출력이 정상적인지도 비교했다.
그 결과 WebGPU가 WASM보다 빠르게 실행되더라도 실제 출력 결과가 잘못될 수 있다는 사실을 발견했다.
ONNX Runtime Web의 실행 환경
ONNX Runtime Web은 브라우저에서 AI 모델을 실행할 수 있도록 하는 라이브러리다.
대표적으로 다음 실행 환경을 지원할 수 있다.
- WebGPU
- WebAssembly
WebGPU는 브라우저에서 GPU 작업을 수행할 수 있도록 하는 API다.
WebAssembly는 브라우저에서 바이너리 코드를 실행할 수 있도록 한다.
실제 모델과 기기에 따라 두 방식의 성능과 지원 범위가 달라질 수 있다.
예를 들어 동영상의 배경을 제거하는 AI 기능을 생각할 수 있다.
사용자는 동영상을 선택한다.
브라우저는 각 Frame을 모델에 전달한다.
모델은 사람과 배경을 구분하는 Mask를 생성한다.
이러한 작업에서는 처리 속도뿐 아니라 실제 Mask의 정확성이 중요하다.
실행 Provider 목록
ONNX Runtime Web에서는 다음 설정을 사용할 수 있다.
executionProviders: ["webgpu", "wasm"]
이 설정은 WebGPU를 우선 시도한다.
실제로 사용할 수 없다면 WASM을 이용할 수 있다.
겉으로 보기에는 편리한 설정이다.
하지만 저자는 다음 문제가 존재한다고 설명한다.
실제로 어떤 Provider가 선택됐는지 Session 객체에서 확인하기 어려웠다.
따라서 개발자가 WebGPU로 실행되고 있다고 생각하더라도 실제로는 WASM에서 실행될 수 있다.
이러한 상황은 실제 성능 로그를 분석할 때 문제가 된다.
예를 들어 어떤 사용자에게는 추론에 30ms가 필요하다.
다른 사용자에게는 100ms가 필요하다.
하지만 실제 Backend 정보가 없다면 단순히 기기의 성능 차이 때문인지 실행 Provider가 다르기 때문인지 구분하기 어렵다.
실제 테스트 환경
저자는 다음 환경에서 실험했다.
| 항목 | 구성 |
|---|---|
| ONNX Runtime Web | 1.27.0 |
| 기기 | Apple M4 Mac |
| 브라우저 | 데스크톱 Chromium |
| 모델 | RVM MobileNetV3 FP16 |
| 모델 크기 | 7,503,483바이트 |
실험에서는 실제 동영상을 계속 재생하지 않았다.
동일한 하나의 Frame을 여러 번 전달했다.
따라서 이번 결과는 특정 모델의 실행 환경을 비교한 것이다.
다양한 실제 동영상의 처리 성능을 검증한 Benchmark는 아니다.
WebGPU API는 존재하지만 Adapter가 없는 상황
저자는 다음 두 조건을 구분한다.
첫 번째는 브라우저에 navigator.gpu 자체가 없는 경우다.
두 번째는 navigator.gpu는 존재하지만 실제 GPU Adapter를 가져올 수 없는 경우다.
이번 실험에서는 두 번째 상황이 발생했다.
브라우저에는 WebGPU API가 존재했다.
하지만 다음 요청이 실패했다.
navigator.gpu.requestAdapter()
결과는 null이었다.
이 경우 WebGPU API의 존재 여부만 확인하는 기능 감지는 충분하지 않다.
실제로 사용할 수 있는 Adapter가 존재하는지도 확인해야 한다.
WebGPU만 지정했을 때
저자는 다음 Provider만 사용해 Session을 생성했다.
["webgpu"]
실제 GPU Adapter를 가져올 수 없었으므로 Session 생성이 실패했다.
오류가 발생하기까지 걸린 시간은 약 111ms였다.
이 경우 개발자는 실제 실패를 catch에서 확인할 수 있다.
따라서 사용자에게 오류를 표시하거나 다른 실행 방식을 시도할 수 있다.
WebGPU와 WASM을 함께 지정했을 때
이번에는 다음 설정을 사용했다.
["webgpu", "wasm"]
실제 결과는 달랐다.
WebGPU를 사용할 수 없는 환경이었지만 Session 생성은 성공했다.
Session 생성에는 약 283ms가 필요했다.
실제 모델은 WASM으로 실행됐다.
문제는 코드에서 어떤 Provider가 선택됐는지 직접 확인하기 어려웠다는 것이다.
저자는 ONNX Runtime Web 1.27.0의 공개 Session API에서 실제 선택된 Provider를 확인하는 속성을 찾지 못했다고 설명한다.
다만 이것이 모든 버전에서 관련 내부 정보가 전혀 존재하지 않는다는 의미는 아니다.
이번 분석은 실제 사용한 버전의 공개 API와 Session 객체를 기준으로 한다.
명시적인 Fallback 구현
저자는 실제 운영 코드에서 두 Provider를 명시적으로 구분하도록 변경했다.
흐름은 다음과 같다.
- WebGPU API 존재 여부 확인
- GPU Adapter 요청
- Adapter가 존재하면 WebGPU 전용 Session 생성
- 실패하면 WASM 전용 Session 생성
- Session과 함께 실제 Backend 이름 반환
반환하는 값은 다음과 같은 구조다.
{ session, backend }
이렇게 하면 실제 모델을 실행할 때 다음 정보를 함께 기록할 수 있다.
- 사용한 Backend
- Session 생성 시간
- Frame별 추론 시간
- 오류 발생 여부
- Cross-origin Isolation 상태
- WASM Thread 수
따라서 실제 운영 환경에서 어떤 방식이 사용됐는지 추측하지 않아도 된다.
Cross-origin Isolation과 WASM Thread
WebAssembly의 실행 속도는 Thread 수에 영향을 받을 수 있다.
하지만 브라우저에서 여러 Thread를 사용하려면 Cross-origin Isolation이 필요할 수 있다.
대표적인 조건은 다음 HTTP Header와 관련된다.
- Cross-Origin-Opener-Policy
- Cross-Origin-Embedder-Policy
저자는 다음 설정을 사용했다.
self.crossOriginIsolated
이 값이 참이라면 사용할 수 있는 Thread 수를 최대 4개로 설정했다.
반면 거짓이라면 하나의 Thread만 사용했다.
실제 측정 결과는 다음과 같았다.
| 실행 방식 | 1280×720 Frame의 추론 시간 |
|---|---|
| WASM, 1 Thread | 251.2ms |
| WASM, 4 Threads | 87.2ms |
이번 환경에서는 약 2.9배 차이가 발생했다.
따라서 실제 WASM 성능을 비교하려면 Thread 수를 함께 기록해야 한다.
단순히 wasm이라는 Backend 이름만으로 성능을 충분히 설명하기 어렵다.
WebGPU는 더 빨랐지만 실제 결과는 잘못됐다
이번 글에서 가장 중요한 발견은 다음 문제다.
실제로 WebGPU Adapter가 존재하는 환경에서 모델을 실행했다.
Session 생성도 성공했다.
추론 과정에서도 JavaScript 예외가 발생하지 않았다.
실제 실행 시간도 WASM보다 짧았다.
하지만 출력 Mask는 정상적이지 않았다.
저자가 측정한 결과는 다음과 같다.
| 입력 해상도 | WebGPU 추론 시간 | WASM 추론 시간 |
|---|---|---|
| 512×288 | 29.4ms | 89.4ms |
| 1280×720 | 35.0ms | 87.2ms |
| 1920×1080 | 43.6ms | 91.8ms |
실행 시간만 보면 WebGPU가 더 빨랐다.
하지만 실제 Alpha Mask에서는 다른 결과가 나타났다.
13번째 Frame 기준으로 WebGPU의 Foreground Pixel 비율은 다음과 같았다.
0.00%
반면 WASM의 결과는 다음과 같았다.
5.03%
실제 이미지에는 사람이 존재했다.
따라서 Foreground Pixel이 거의 없는 WebGPU의 결과는 정상적인 배경 제거 결과가 아니었다.
즉 모델이 오류 없이 실행됐다는 사실과 실제 결과가 올바르다는 사실은 다르다.
원인을 확정하지 않은 이유
저자는 WebGPU의 출력이 잘못된 원인을 확정하지 않았다.
가능한 원인으로 다음 문제를 추측했다.
- 특정 Operator의 처리
- FP16 정밀도
- Recurrent Network의 상태 처리
하지만 실제 Layer별 출력 결과를 비교하지 않았다.
따라서 이러한 원인을 검증된 사실로 취급해서는 안 된다.
또한 같은 기기에서 다른 모델인 ISNet INT8을 테스트했을 때는 두 Backend의 출력이 거의 일치했다고 설명한다.
따라서 WebGPU를 사용하는 모든 ONNX 모델에서 같은 문제가 발생한다고 일반화할 수 없다.
이번 문제는 특정 모델과 실행 환경에서 관찰된 현상이다.
실제 결과 검증
저자는 Session 생성 이후 정상적인 테스트 Frame을 실행하는 방법을 제안한다.
예를 들어 사람이 포함된 이미지가 있다고 가정해 보자.
실제로 모델이 반환해야 하는 Foreground Pixel의 대략적인 범위를 알고 있다.
이때 WebGPU로 추론을 실행한다.
그리고 결과 Mask에서 실제 Foreground Pixel 비율을 계산한다.
만약 정상 범위와 큰 차이가 존재한다면 WebGPU 결과를 사용하지 않고 WASM으로 전환할 수 있다.
하지만 이러한 검증에도 비용이 존재한다.
모델을 초기화한 직후 추가적인 추론을 실행해야 하기 때문이다.
또한 모든 모델에 적용할 수 있는 하나의 Foreground 비율은 존재하지 않는다.
실제 모델의 목적에 맞는 테스트 입력과 기대 결과가 필요하다.
측정의 한계
이번 결과는 Apple M4 Mac과 Chromium에서 측정했다.
각 조건의 반복 횟수도 제한적이었다.
따라서 다음 환경에서는 결과가 달라질 수 있다.
- Windows
- 별도의 NVIDIA GPU
- Safari
- Firefox
- 다른 ONNX 모델
- 다른 ONNX Runtime Web 버전
또한 WebGPU의 잘못된 출력 원인은 실제로 확인되지 않았다.
이번 글은 브라우저에서 AI 모델을 실행할 때 어떤 Backend가 선택됐는지 명시적으로 관리하고, 실행 속도뿐 아니라 실제 출력 결과의 정확성까지 검증해야 한다는 점을 구체적인 실험으로 보여준다.
Recoloring a Product Photo with an SVG Filter – and Where It Breaks
- 저자: Iurii Rogulia
- 매체: DEV Community / 개인 기술 블로그
- 게시일: 2026-11-11 — 정확한 게시 시각 미확인
- 기술: React, TypeScript, SVG Filter, Canvas API, sRGB, 이미지 처리
- 원문: https://dev.to/iurii_rogulia/recoloring-a-product-photo-with-an-svg-filter-and-where-it-breaks-2mdg
실제 전자상거래 웹사이트에서 하나의 상품 사진을 여러 색상으로 변환하는 기능을 SVG Filter로 구현했다가 결과의 색상이 예상과 달라 Canvas 기반 방식으로 변경한 과정을 분석한 글이다.
저자는 자신이 개발한 온라인 쇼핑몰에서 상품의 색상을 미리 보여주고 싶었다.
문제는 실제 모든 색상의 상품 사진을 보유하지 않았다는 점이다.
기본적으로 검정과 회색, 흰색 상품 사진만 존재했다.
하지만 사용자는 다른 색상으로 제작한 상품을 주문할 수 있었다.
따라서 사용자가 선택한 색상을 실제 상품 이미지에 반영하는 미리보기 기능이 필요했다.
SVG Filter를 선택한 이유
저자는 처음에 SVG Filter를 이용했다.
이 방식은 복잡한 JavaScript 픽셀 처리 코드를 작성하지 않아도 된다.
또한 실제 상품 이미지에 CSS Filter를 적용할 수 있다.
대표적인 표현은 다음과 같다.
filter: url(#tint)
SVG Filter에서는 다음 두 기능을 사용했다.
feColorMatrixfeComponentTransfer
첫 번째는 원본 이미지를 흑백으로 변경한다.
두 번째는 원본의 밝기에 따라 새로운 색상을 적용한다.
이러한 구조는 단순히 상품 전체를 하나의 색상으로 덮어쓰는 방식과 다르다.
원본 이미지에 존재하는 그림자와 하이라이트를 유지할 수 있기 때문이다.
밝기별 색상 변환
저자는 세 개의 색상 기준점을 정의했다.
첫 번째는 그림자 영역이다.
두 번째는 중간 밝기의 영역이다.
세 번째는 하이라이트 영역이다.
예를 들어 사용자가 초록색을 선택했다고 가정해 보자.
원본 사진의 어두운 부분에는 어두운 초록색을 적용한다.
중간 밝기의 부분에는 선택한 초록색을 적용한다.
밝은 부분에는 흰색을 섞은 초록색을 적용한다.
저자는 다음 두 상수를 사용했다.
SHADOW_MUL = 0.28
HIGHLIGHT_MIX = 0.68
실제 RGB Channel의 값이 c라고 가정하면 세 기준점은 다음과 같이 계산된다.
- 그림자:
c × 0.28 - 기본 색상:
c - 하이라이트:
c + (1 - c) × 0.68
이렇게 하면 하나의 색상을 밝기에 따라 여러 값으로 변환할 수 있다.
colorInterpolationFilters="sRGB"
이번 구현에서 중요한 부분은 색상 보간 공간이다.
저자는 다음 속성을 사용했다.
colorInterpolationFilters="sRGB"
SVG Filter의 기본 색상 처리 방식은 일반적인 화면의 sRGB 값과 다를 수 있다.
특히 Linear RGB에서 색상 값을 보간하면 중간 밝기의 결과가 예상과 달라질 수 있다.
저자는 실제 이미지에서 사용하는 sRGB 기반 값을 기준으로 처리하기 위해 해당 설정을 명시했다.
하지만 sRGB를 사용한다고 해서 결과 색상이 항상 정확해지는 것은 아니다.
문제는 원본 사진의 밝기 분포에도 존재했다.
검정색 원본에서의 문제
저자는 검정색 상품 사진을 기준으로 색상 변환을 수행했다.
하지만 실제 결과는 예상보다 어두웠다.
특히 밝은 색상이나 채도가 높은 색상을 적용했을 때 결과가 탁하게 보였다.
저자는 이를 단순한 시각적 판단으로만 남기지 않았다.
실제 Headless Chrome에서 이미지의 Pixel을 분석했다.
측정 대상은 Alpha가 200 이상인 Pixel이었다.
그리고 밝기의 다음 백분위값을 확인했다.
- P2
- P50
- P98
P2는 전체 측정 대상 Pixel 가운데 밝기가 낮은 쪽의 2% 지점이다.
P50은 중앙값이다.
P98은 밝기가 높은 쪽의 98% 지점이다.
실제 밝기 측정 결과
저자가 측정한 값은 다음과 같다.
| 이미지 | P2 | P50 | P98 |
|---|---|---|---|
| 검정색 원본 | 11 | 38 | 106 |
| 검정 원본 → 초록색 | 35 | 51 | 92 |
| 검정 원본 → 갈색 | 27 | 38 | 69 |
| 흰색 원본 | 184 | 227 | 246 |
| 흰색 원본 → 초록색 | 150 | 185 | 200 |
| 흰색 원본 → 갈색 | 132 | 172 | 190 |
실제 선택한 색상의 밝기는 다음과 같았다.
- 초록색: 약 105
- 갈색: 약 79
검정 원본을 초록색으로 변환한 결과의 P98은 92였다.
즉 밝은 부분조차 실제 선택한 초록색의 밝기보다 낮았다.
반면 흰색 원본을 초록색으로 변환한 경우에는 P2가 150이었다.
이는 실제 선택한 초록색의 밝기보다 훨씬 높은 값이다.
따라서 검정 원본과 흰색 원본에서 같은 SVG Filter를 사용해도 실제 결과는 크게 달랐다.
왜 이런 결과가 발생했는가?
원인은 SVG Filter에 사용한 세 기준점의 위치였다.
저자는 중간 밝기의 기준점을 다음 값에 두었다.
0.5
즉 실제 이미지의 밝기가 0.5라면 선택한 원래 색상을 적용한다.
하지만 검정색 원본의 밝기는 대부분 0.5보다 낮았다.
검정색 원본의 P98은 106이다.
이를 255로 나누면 약 0.42다.
따라서 거의 모든 Pixel이 그림자와 기본 색상 사이에서 계산된다.
그 결과 이미지가 어둡게 표현된다.
반면 흰색 원본은 대부분 0.5보다 밝았다.
흰색 원본의 P2는 184다.
이를 255로 나누면 약 0.72다.
따라서 거의 모든 Pixel이 기본 색상과 하이라이트 사이에서 계산된다.
그 결과 이미지가 실제 선택한 색상보다 밝게 표현된다.
즉 SVG Filter 자체가 잘못된 것은 아니다.
원본 사진이 어떤 밝기 분포를 가지고 있는지 고려하지 않고 고정된 색상 변환 기준을 적용한 것이 문제였다.
Canvas 기반으로 변경
저자는 실제 구현을 Canvas 기반으로 변경했다.
새로운 방식에서는 먼저 원본 이미지의 밝기 분포를 분석한다.
그리고 해당 밝기 분포를 정규화한다.
대표적인 과정은 다음과 같다.
- 원본 이미지를 로딩한다.
- Pixel별 밝기를 계산한다.
- 충분한 Alpha를 가진 Pixel의 Histogram을 만든다.
- 밝기의 P2와 P99를 계산한다.
- 해당 범위를 0~1로 정규화한다.
- 정규화한 밝기를 이용해 선택한 색상을 적용한다.
이렇게 하면 원본 이미지가 매우 어둡거나 밝더라도 실제 색상 변환에 사용하는 밝기 범위를 일정하게 조정할 수 있다.
하지만 이 방법이 모든 상품 사진의 결과를 완전히 동일하게 만드는 것은 아니다.
실제 이미지의 밝기 분포는 계속 다르기 때문이다.
Canvas 변환 결과
저자는 Canvas 방식으로 변경한 이후 같은 이미지의 밝기를 다시 측정했다.
| 이미지 | P2 | P50 | P98 |
|---|---|---|---|
| 검정 원본 → 초록색 | 53 | 65 | 121 |
| 검정 원본 → 갈색 | 40 | 49 | 99 |
| 흰색 원본 → 초록색 | 53 | 88 | 164 |
| 흰색 원본 → 갈색 | 40 | 66 | 148 |
새로운 방식에서는 검정 원본과 흰색 원본의 P2가 동일해졌다.
즉 가장 어두운 부분의 밝기를 일정하게 정규화할 수 있었다.
하지만 P50과 P98은 여전히 달랐다.
따라서 실제 색상 변환 결과는 원본 사진의 명암 분포에 영향을 받는다.
저자는 흰색 원본의 결과가 실제 색상이 적용된 플라스틱처럼 더 자연스럽게 보인다고 판단했다.
하지만 이 부분은 저자의 시각적인 평가다.
별도의 사용자 실험이나 색상 정확도에 관한 독립적인 Benchmark는 아니다.
Canvas의 실행 비용
SVG Filter와 Canvas 방식에는 다른 비용이 존재한다.
SVG Filter는 브라우저의 Rendering Pipeline을 이용해 색상을 변경할 수 있다.
반면 Canvas 방식에서는 JavaScript가 실제 Pixel을 읽고 변경한다.
예를 들어 1000×1000 이미지가 있다고 가정해 보자.
총 100만 개의 Pixel이 존재한다.
사용자가 색상을 변경할 때마다 모든 Pixel을 처리한다면 CPU 작업량이 증가할 수 있다.
저자는 이를 줄이기 위해 미리보기 이미지의 긴 변을 420px로 제한했다.
하지만 실제 색상 변경 한 번에 몇 ms가 필요한지에 관한 Benchmark는 제공하지 않았다.
따라서 Canvas가 항상 SVG Filter보다 느리거나 빠르다고 단정할 수 없다.
실제 이미지의 크기와 처리 방식에 따라 달라질 수 있다.
CORS와 Canvas 제한
Canvas 방식에서는 이미지의 실제 Pixel에 접근해야 한다.
하지만 다른 Origin의 이미지를 사용할 때는 보안 정책이 적용된다.
예를 들어 외부 CDN에서 이미지를 가져왔다고 가정해 보자.
해당 서버가 필요한 CORS Header를 제공하지 않는다면 Canvas에서 Pixel 데이터를 읽을 수 없을 수 있다.
저자는 다음 조건을 확인하도록 설명한다.
- 같은 Origin의 이미지 사용
- 적절한 CORS Header 제공
- 필요한 경우
crossOrigin="anonymous"설정
따라서 SVG Filter보다 실제 배포 환경의 보안 정책에 더 많은 영향을 받을 수 있다.
설계상의 한계
이번 변경에서는 두 가지 작업이 동시에 적용됐다.
첫 번째는 검정 원본에서 흰색 원본으로 변경한 것이다.
두 번째는 SVG Filter에서 Canvas 기반 정규화 방식으로 변경한 것이다.
저자는 두 변경을 독립적으로 비교한 실험을 수행하지 않았다.
따라서 실제 개선 가운데 어느 정도가 원본 사진의 변경 때문이고 어느 정도가 알고리즘 변경 때문인지 확정할 수 없다.
또한 이번 테스트는 하나의 상품과 Headless Chrome에서 수행했다.
모든 전자상거래 이미지에서 같은 결과가 발생한다고 일반화할 수 없다.
이번 글은 상품 이미지의 색상을 동적으로 변경할 때 단순한 색상 변환 공식보다 원본 이미지의 밝기 분포가 실제 결과에 큰 영향을 줄 수 있다는 점을 Pixel 단위의 측정으로 설명한다.
What a 404 Costs on app.onepassapps.com: 114 Requests, 1.2 MB, and a Title That Never Changes
- 저자: OnePassApps
- 매체: DEV Community
- 게시일: 2026-11-10
- 기술: HTTP, JavaScript Bundle, React, Cloudflare, Browser Network, 웹 성능
- 원문: https://dev.to/onepassapps/what-a-404-costs-on-apponepassappscom-114-requests-12-mb-and-a-title-that-never-changes-3hlm
실제 웹 애플리케이션에서 존재하지 않는 URL로 접근했을 때 단순한 404 화면을 보여주기 위해 얼마나 많은 네트워크 요청과 JavaScript가 사용되는지 측정한 글이다.
이번 글의 특징은 정상적인 홈페이지가 아니라 오류 페이지의 비용을 측정했다는 점이다.
일반적으로 웹 성능을 최적화할 때는 다음 페이지에 집중한다.
- 홈페이지
- 상품 목록
- 상품 상세
- 사용자 Dashboard
하지만 실제 사용자는 잘못된 URL에도 접근할 수 있다.
예를 들어 오래된 북마크나 잘못된 링크를 클릭할 수 있다.
이때 서버는 404 응답을 반환한다.
그러나 실제 브라우저에서는 해당 오류 화면을 표시하기 위해 많은 JavaScript를 다운로드할 수 있다.
저자는 자신의 서비스에서 이러한 문제를 조사했다.
실제 테스트 환경
측정 대상은 다음 사이트다.
app.onepassapps.com
저자는 존재하지 않는 URL로 접근했다.
그리고 다음 조건에서 실제 네트워크 요청을 기록했다.
- 로그인하지 않은 상태
- 브라우저 Cache 비활성화
- 첫 번째 페이지 로딩
- 하나의 브라우저 실행
- 2026년 11월 10일의 실제 배포 버전
따라서 이번 결과는 특정 환경에서 측정한 Cold Load다.
여러 사용자와 네트워크에서 수집한 평균값은 아니다.
실제 404 응답
저자는 여섯 개의 존재하지 않는 URL을 테스트했다.
대표적으로 다음과 같다.
/no-such-page-xyz/nope2/deep/missing/path/x.png/x.json/not-found
모든 요청은 실제 HTTP 404를 반환했다.
응답의 Content Type은 다음과 같았다.
text/html; charset=utf-8
Cache 정책은 다음과 같았다.
no-store
또한 모든 응답의 HTML 크기는 14,173바이트였다.
즉 서버는 존재하지 않는 URL을 홈페이지로 Redirect하지 않았다.
실제 404 상태와 HTML 문서를 제공했다.
하지만 브라우저가 해당 페이지를 표시하는 과정에서는 다른 비용이 발생했다.
실제 네트워크 요청
저자가 측정한 결과는 다음과 같았다.
총 114개의 요청, 1,209,946바이트의 압축된 응답 데이터
요청 종류별 결과는 다음과 같다.
| 요청 종류 | 요청 수 | 전송 크기 |
|---|---|---|
| HTML Document | 1 | 5,829B |
| Stylesheet | 4 | 51,928B |
| JavaScript | 94 | 1,077,350B |
| Manifest | 1 | 2,068B |
| Font | 1 | 49,944B |
| Image | 1 | 1,911B |
| CORS Preflight | 4 | 0B |
| Fetch | 7 | 12,772B |
| Other | 1 | 8,144B |
가장 큰 비중은 JavaScript였다.
총 94개의 JavaScript 요청에서 약 1.08MB의 데이터를 다운로드했다.
하지만 실제 사용자에게 표시되는 화면은 단순한 오류 안내였다.
대표적인 문구는 다음과 같았다.
That page doesn't exist
따라서 실제 페이지의 기능에 비해 많은 JavaScript가 전달되고 있었다.
세 개의 Host
요청은 다음 세 Host로 전달됐다.
- app.onepassapps.com
- clerk.onepassapps.com
- api.onepassapps.com
실제 전송 크기는 다음과 같았다.
| Host | 전송 크기 |
|---|---|
| app.onepassapps.com | 844,423B |
| clerk.onepassapps.com | 360,385B |
| api.onepassapps.com | 5,138B |
Clerk는 인증 관련 기능을 제공하는 서비스다.
저자는 404 페이지에서도 Clerk의 JavaScript Bundle이 로딩되고 있었다는 점을 확인했다.
이러한 구조는 공통 App Shell과 인증 관련 코드가 오류 페이지에서도 실행되고 있었기 때문일 수 있다.
하지만 실제로 어떤 Module이 반드시 필요한지는 별도의 Dependency 분석이 필요하다.
이번 측정만으로 모든 Clerk 관련 JavaScript가 불필요하다고 단정할 수는 없다.
큰 JavaScript 파일
저자가 확인한 주요 응답은 다음과 같다.
| 구성요소 | 전송 크기 |
|---|---|
| Clerk UI Bundle | 135,038B |
| 공통 App Shell Chunk | 118,241B |
| Entry Chunk | 92,703B |
| clerk.browser.js | 85,677B |
이러한 파일은 정상적인 애플리케이션 화면에서도 사용될 수 있다.
하지만 오류 페이지에서도 공통으로 로딩됐다.
따라서 사용자에게 실제 오류 안내를 제공하기 전에 많은 JavaScript를 처리해야 할 수 있다.
압축되지 않은 파일과 실제 전송 크기
저자는 파일의 원본 크기와 실제 네트워크 전송 크기도 구분했다.
예를 들어 Entry Chunk의 원본 크기는 다음과 같았다.
325,705바이트
하지만 실제 전송 크기는 다음과 같았다.
92,703바이트
CSS 파일의 경우에도 차이가 존재했다.
원본 CSS는 약 255KB였다.
하지만 실제 네트워크에서 네 개의 Stylesheet 요청으로 전송된 데이터는 약 52KB였다.
따라서 원본 Bundle 크기를 그대로 실제 다운로드 비용으로 사용해서는 안 된다.
실제 압축 방식과 HTTP Response를 확인해야 한다.
검색 엔진과 404
저자는 실제 404 응답의 SEO 관련 Metadata도 확인했다.
다음 설정이 존재했다.
noindex, nofollow
따라서 해당 오류 페이지 자체를 검색 엔진에 색인하도록 의도한 것은 아니었다.
또한 서버에서 반환한 응답 상태는 실제 404였다.
이 점은 중요하다.
일부 웹 애플리케이션에서는 존재하지 않는 페이지를 HTTP 200으로 반환할 수 있다.
이 경우 검색 엔진은 정상 페이지와 오류 페이지를 구분하기 어려울 수 있다.
하지만 이번 사이트는 실제 상태 코드 404를 반환했다.
따라서 HTTP 상태 자체는 올바르게 처리하고 있었다.
문제는 실제 페이지를 표시하기 위해 필요한 JavaScript의 양이었다.
서버 HTML과 클라이언트 결과의 차이
이번 글에서는 React가 실행된 이후 DOM이 변경되는 현상도 확인했다.
서버는 검색 엔진을 위한 Fallback 콘텐츠를 HTML에 포함하고 있었다.
하지만 실제 React 애플리케이션이 Hydration된 이후 해당 Fallback은 DOM에서 사라졌다.
이러한 상황은 서버가 반환한 HTML과 실제 사용자가 보는 화면이 다를 수 있다는 점을 보여준다.
따라서 실제 SSR 애플리케이션의 SEO와 사용자 경험을 검증할 때는 다음 두 단계를 확인해야 한다.
첫 번째는 서버가 반환한 HTML이다.
두 번째는 JavaScript가 실행된 이후의 DOM이다.
두 단계에서 표시하는 콘텐츠가 달라질 수 있기 때문이다.
실제 개선을 검토할 수 있는 영역
이번 측정 결과를 바탕으로 생각할 수 있는 개선 방법은 다음과 같다.
첫 번째는 404 페이지에서 공통 App Shell 전체가 필요한지 확인하는 것이다.
두 번째는 인증 관련 JavaScript가 실제로 필요한지 검토하는 것이다.
세 번째는 오류 페이지에 필요한 CSS와 Script만 분리하는 것이다.
네 번째는 서버가 반환한 HTML만으로 충분한 오류 안내를 제공할 수 있는지 확인하는 것이다.
하지만 저자는 이러한 변경을 실제로 모두 적용한 뒤 성능을 다시 측정한 것은 아니다.
따라서 특정 방법으로 JavaScript를 몇 KB 줄일 수 있다고 단정할 수 없다.
측정상의 한계
이번 결과는 하나의 배포 버전과 하나의 네트워크 환경에서 수행한 측정이다.
브라우저 Cache를 비활성화한 Cold Load이므로 실제 사용자가 이전 페이지에서 다운로드한 JavaScript를 Cache로 재사용하는 상황과는 다를 수 있다.
또한 애플리케이션의 실제 Bundle은 이후 배포에서 변경될 수 있다.
따라서 114개 요청과 1.2MB의 전송량이 해당 사이트의 영구적인 값은 아니다.
이번 글은 정상적인 기능 페이지뿐 아니라 오류 페이지에서도 공통 JavaScript와 인증 SDK가 많은 다운로드 비용을 발생시킬 수 있으며, 실제 HTTP 상태와 클라이언트 실행 비용을 별도로 분석해야 한다는 점을 구체적인 네트워크 측정으로 보여준다.