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

기준 시점: 2026-10-22 06:00 KST
조사 범위: 2026-10-21 06:00 ~ 2026-10-22 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
Cloudflare·Commerce.com, 약 10만 개 쇼핑몰에 WebMCP 도입 — AI 에이전트가 브라우저에서 상품 검색과 장바구니 조작
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare, Commerce.com
- 기술: WebMCP, Cloudflare for SaaS
- 안정화 단계: WebMCP Early Preview
- 원문: https://blog.cloudflare.com/webmcp-commerce/
Cloudflare와 Commerce.com이 Commerce.com 플랫폼에서 운영하는 약 10만 개의 쇼핑몰에 WebMCP를 적용하는 구조를 공개했다.
이번 발표는 새로운 웹 프레임워크의 출시가 아니다. 기존 웹사이트를 AI 에이전트가 직접 이해하고 조작할 수 있도록 브라우저에 구조화된 도구 인터페이스를 제공하는 기술의 대규모 적용 사례다.
현재 대부분의 웹사이트는 사람이 사용하는 것을 전제로 설계돼 있다.
사용자는 상품 검색창에 검색어를 입력하고, 필터를 선택하고, 상품 상세 페이지에서 옵션을 고른 뒤 장바구니 버튼을 클릭한다.
하지만 AI 에이전트가 동일한 작업을 수행하려면 문제가 발생한다.
에이전트는 HTML 구조를 분석하거나 화면을 이미지로 확인한 뒤 어떤 요소를 클릭해야 하는지 판단해야 한다.
예를 들어 버튼에 Add to cart라는 텍스트가 있다면 해당 버튼이 장바구니 추가 기능이라고 추측할 수 있다.
그러나 버튼의 이름이 변경되거나 DOM 구조가 달라지면 기존 자동화가 실패할 수 있다.
또한 상품 옵션을 선택하는 컴포넌트가 복잡하면 에이전트가 잘못된 옵션을 선택할 수도 있다.
WebMCP는 이러한 문제를 해결하기 위해 웹페이지가 AI 에이전트에게 사용할 수 있는 기능을 명시적으로 제공하도록 하는 방식이다.
HTML을 분석하는 대신 도구를 직접 호출
WebMCP에서는 웹페이지가 구조화된 도구를 등록할 수 있다.
에이전트는 브라우저에서 다음 인터페이스를 통해 사용 가능한 도구를 발견한다.
document.modelContext
예를 들어 쇼핑몰에서 다음 도구를 제공한다고 가정해 보자.
search_productsadd_to_cart
사용자가 AI 에이전트에게 140달러 이하의 방수 트레일 러닝화를 찾아 장바구니에 넣어 달라고 요청할 수 있다.
기존 방식에서는 에이전트가 검색창과 가격 필터, 사이즈 선택 UI를 찾아야 한다.
반면 WebMCP에서는 다음과 같은 구조화된 요청을 전달할 수 있다.
search_products({ query: "trail running shoe", max_price: 140, size: 10 })
검색 결과에서 상품을 선택하면 다음 도구를 호출한다.
add_to_cart({ product_id: "shoe_123" })
이러한 방식에서는 웹페이지의 시각적 구조를 해석하는 과정이 줄어든다.
또한 각 도구가 요구하는 입력값을 명확하게 정의할 수 있다.
예를 들어 가격은 숫자로 전달하고, 상품 ID는 문자열로 전달하도록 구성할 수 있다.
이는 단순히 버튼 클릭을 자동화하는 방식과 다르다.
에이전트가 수행할 수 있는 작업의 의미와 입력 구조를 웹 애플리케이션이 직접 제공한다는 점이 중요하다.
브라우저 세션과 쇼핑몰 상태를 공유
이번 구현의 핵심은 WebMCP 도구가 사용자가 현재 열어 둔 쇼핑몰의 브라우저 세션 안에서 실행된다는 점이다.
일반적인 외부 AI 에이전트는 사용자와 별도의 실행 환경에서 쇼핑몰 API를 호출할 수 있다.
이 경우 로그인 상태와 장바구니, 지역별 가격 등이 사용자 브라우저와 달라질 수 있다.
반면 이번 WebMCP 통합은 쇼핑몰과 같은 Origin의 활성 브라우저 세션을 사용한다.
Origin은 웹에서 프로토콜, 호스트, 포트의 조합으로 구분되는 보안 경계다.
같은 세션을 사용하면 에이전트가 확인하는 상품 가격과 장바구니가 사용자의 실제 쇼핑 환경과 일치하도록 구성할 수 있다.
Cloudflare는 이 구조에서 인증 정보를 제3자에게 별도로 전달하지 않는다고 설명한다.
또한 최종 결제는 에이전트가 자동으로 완료하는 방식이 아니다.
상품 검색과 장바구니 조작을 도와주더라도 구매자는 직접 Checkout을 실행한다.
따라서 결제 정보와 최종 구매 결정은 기존 쇼핑몰의 사용자 흐름에 남는다.
다만 브라우저 세션을 사용한다는 사실만으로 에이전트의 모든 동작이 안전해지는 것은 아니다.
실제 서버에서는 사용자의 인증과 권한, 상품 가격, 재고 및 주문 금액을 계속 검증해야 한다.
서버 MCP와 브라우저 WebMCP의 차이
Commerce.com은 기존에도 서버 측 MCP를 제공하고 있었다.
MCP(Model Context Protocol)는 AI 시스템이 외부 도구와 데이터를 사용할 수 있도록 연결하는 프로토콜이다.
서버 측 MCP에서는 에이전트가 쇼핑몰의 MCP 서버에 직접 연결한다.
반면 WebMCP에서는 사용자가 열어 둔 브라우저 페이지를 통해 도구를 사용한다.
| 구분 | 서버 측 MCP | WebMCP |
|---|---|---|
| 실행 위치 | 외부 에이전트와 MCP 서버 | 사용자의 브라우저 세션 |
| 연결 방식 | 서버와 직접 통신 | 페이지에 등록된 도구 사용 |
| 브라우저 UI | 필수 아님 | 사용자가 열어 둔 페이지와 연결 |
| 장바구니 상태 | 별도 세션 관리가 필요할 수 있음 | 현재 브라우저 세션과 연결 가능 |
| 대표 사례 | 여러 쇼핑몰을 검색하는 AI 비서 | 현재 방문 중인 쇼핑몰을 조작하는 에이전트 |
Commerce.com은 두 방식을 모두 지원하는 방향을 선택했다.
같은 쇼핑 기능을 서버 MCP와 WebMCP 양쪽에서 제공하면 쇼핑몰 운영자는 별도로 두 개의 비즈니스 로직을 관리할 필요를 줄일 수 있다.
다만 실제 세션 처리와 인증 방식은 두 실행 환경에 맞게 구성해야 한다.
Cloudflare Edge에서 WebMCP 스크립트 주입
Cloudflare는 2026년 8월 WebMCP의 Edge 기반 구현을 Beta로 공개했다.
이번 Commerce.com 통합은 해당 기능을 많은 쇼핑몰에 적용하는 과정에서 발견한 운영 문제를 해결한 사례다.
일반적인 웹 애플리케이션에서는 WebMCP를 사용하려면 페이지에 관련 JavaScript를 추가해야 할 수 있다.
하지만 Commerce.com은 약 10만 개의 도메인을 운영한다.
각 쇼핑몰의 테마나 프론트엔드 코드를 개별적으로 변경하는 방식은 운영 부담이 크다.
Cloudflare는 이를 해결하기 위해 Edge에서 필요한 JavaScript를 페이지에 삽입하는 방식을 사용한다.
대표적인 스크립트 경로는 다음과 같다.
/.webmcp/bridge.js
이 스크립트는 브라우저의 WebMCP 도구와 기존 MCP 서버를 연결한다.
따라서 개별 쇼핑몰 운영자가 직접 프론트엔드 코드를 수정하지 않아도 된다.
공유 Zone과 쇼핑몰별 Opt-out
이번 통합에서 가장 복잡했던 문제는 멀티테넌트 구조였다.
멀티테넌트는 하나의 플랫폼이나 인프라를 여러 고객이 공유하는 구조다.
Commerce.com은 Cloudflare for SaaS를 사용해 많은 쇼핑몰 도메인을 공유 Zone에서 운영한다.
기존 WebMCP 설정은 Zone 전체에 활성화하는 방식이었다.
하지만 모든 쇼핑몰 운영자가 WebMCP를 사용하고 싶어 하는 것은 아닐 수 있다.
따라서 플랫폼 전체에 기능을 활성화하면서도 개별 쇼핑몰은 기능을 끌 수 있어야 했다.
Cloudflare는 이를 위해 Custom Hostname Metadata와 Configuration Rules를 연결했다.
핵심 조건은 다음과 같다.
cf.hostname.metadata.webmcp_opt_out eq "true"
특정 쇼핑몰의 메타데이터에서 Opt-out이 활성화돼 있으면 해당 도메인에서는 WebMCP를 실행하지 않는다.
이 규칙은 Edge에서 페이지를 전달하기 전에 평가된다.
따라서 Opt-out된 쇼핑몰에는 WebMCP 스크립트가 삽입되지 않는다.
Cloudflare는 해당 요청에 별도의 원격 조회나 추가적인 WebMCP 처리 지연도 발생하지 않는다고 설명한다.
플랫폼 전체 설정과 고객별 설정을 분리한 구성이라는 점에서 멀티테넌트 웹서비스 설계와도 관련된다.
커스텀 MCP 엔드포인트 지원
기존 WebMCP Bridge는 MCP 서버가 /mcp 경로에 존재한다고 가정했다.
하지만 Commerce.com의 MCP 서버는 다음 경로를 사용한다.
/api/mcp
이번 통합에서는 MCP 서버 경로를 설정으로 변경할 수 있도록 개선했다.
주입되는 스크립트에는 다음 속성을 사용할 수 있다.
data-mcp-url="/api/mcp"
이를 통해 실제 MCP 서버의 경로가 기본값과 달라도 Bridge를 연결할 수 있다.
또한 MCP 서버가 사용할 수 없거나 브라우저가 WebMCP를 지원하지 않는 경우에는 Bridge가 실행을 중단한다.
이때 기존 쇼핑몰 페이지는 일반적인 웹페이지로 계속 동작한다.
따라서 WebMCP 지원 여부가 사용자의 기본적인 쇼핑 경험을 막지 않도록 설계했다.
아직 정식 웹 표준은 아니다
이번 발표에서 반드시 구분해야 할 부분이 있다.
WebMCP는 아직 실험적인 브라우저 기술이다.
현재 일부 Chrome 실험 기능과 Origin Trial, AI 에이전트 브라우저 및 개발 환경에서 사용할 수 있다.
Chrome에서는 다음 실험 플래그를 이용할 수 있다.
chrome://flags/#enable-webmcp-testing
그러나 모든 Chrome 사용자에게 기본적으로 제공되는 안정적인 웹 API는 아니다.
Firefox와 Safari를 포함한 모든 브라우저에서 동일하게 지원된다고 볼 수도 없다.
따라서 약 10만 개의 쇼핑몰에 적용하는 구조가 마련됐다는 사실과 약 10만 개 쇼핑몰의 모든 방문자가 즉시 WebMCP를 사용할 수 있다는 사실은 구분해야 한다.
이번 발표는 기존 웹사이트의 UI를 변경하지 않고 Edge에서 에이전트용 도구 인터페이스를 제공하면서, 멀티테넌트 플랫폼의 개별 고객 설정까지 관리할 수 있도록 만든 구현 사례다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome Developers·Chromium, Mozilla·Firefox, WebKit·Safari와 JavaScript·TypeScript·CSS, 웹 성능 및 접근성 관련 공식 발표를 검토했다.
이번 조사 기간에 해당하는 주요 웹 표준의 정식 채택이나 새로운 브라우저 API의 안정화 발표는 확인하지 못했다.
앞서 소개한 WebMCP는 브라우저와 AI 에이전트의 상호작용에 직접적인 영향을 주는 기술이다. 그러나 10월 21일 발표는 WebMCP 규격의 정식 출시가 아니라 Commerce.com과 Cloudflare의 실제 적용 사례이므로 프론트엔드 섹션에서 다뤘다.
실험적 제안이 공개돼 있다는 사실과 브라우저에서 기본적으로 사용할 수 있는 표준 기능이라는 사실은 구분해야 한다.
3️⃣ 백엔드·인프라
Vercel Routing Middleware, Python 지원 — FastAPI·Flask·Django 요청을 애플리케이션 진입 전에 처리
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 적용 대상: Vercel Routing Middleware
- 지원 프레임워크: FastAPI, Flask, Django 등 Python 웹 프레임워크
- 원문: https://vercel.com/changelog/routing-middleware-now-supports-python
Vercel이 Routing Middleware를 Python으로 구현할 수 있도록 지원 범위를 확장했다.
Routing Middleware는 클라이언트의 HTTP 요청이 실제 애플리케이션에 전달되기 전에 실행되는 처리 계층이다.
예를 들어 사용자가 특정 URL에 접근했을 때 다음과 같은 작업을 수행할 수 있다.
- 이전 URL을 새로운 URL로 Redirect
- 요청 경로를 다른 경로로 Rewrite
- 요청 Header 추가
- 특정 조건에서 직접 응답 반환
- 이후 애플리케이션으로 요청 전달
기존 Python 웹 애플리케이션에서도 이러한 기능을 구현할 수 있었다.
예를 들어 Django Middleware를 이용하면 Django가 처리하는 요청에 공통 로직을 적용할 수 있다.
그러나 이번 Vercel Routing Middleware는 실행 위치가 다르다.
Python 웹 애플리케이션의 내부 Middleware가 아니라 Vercel의 요청 라우팅 단계에서 먼저 실행된다.
애플리케이션과 분리된 실행 구조
Vercel은 Routing Middleware를 별도의 ASGI 또는 WSGI 애플리케이션으로 실행한다.
ASGI는 Python 비동기 웹 애플리케이션과 서버 사이의 인터페이스다.
WSGI는 기존의 동기식 Python 웹 애플리케이션에서 널리 사용되는 인터페이스다.
FastAPI는 일반적으로 ASGI를 사용한다.
Flask는 전통적으로 WSGI 기반이다.
이번 기능에서는 두 종류의 인터페이스를 이용하는 Middleware를 구성할 수 있다.
중요한 부분은 실제 서비스를 처리하는 애플리케이션과 Routing Middleware의 실행 환경을 분리할 수 있다는 점이다.
예를 들어 FastAPI 애플리케이션이 여러 데이터베이스와 ML 라이브러리를 사용한다고 가정해 보자.
단순한 URL Redirect를 위해 이러한 의존성을 모두 로딩할 필요는 없다.
별도의 Middleware에서는 해당 작업에 필요한 패키지만 설치하도록 구성할 수 있다.
vercel.toml 설정
Python Routing Middleware의 진입점은 vercel.toml에서 지정한다.
대표적인 설정은 다음과 같다.
[proxy]
entrypoint = "proxy:proxy"
이 설정은 proxy.py 모듈의 proxy 객체를 Routing Middleware의 진입점으로 사용한다는 의미다.
Vercel은 Middleware 전용 의존성 그룹도 제공한다.
해당 그룹의 이름은 다음과 같다.
vercel.proxy
예를 들어 vercel-proxy 패키지를 해당 그룹에 설치하면 Routing Middleware가 사용하는 의존성을 일반 애플리케이션의 의존성과 분리할 수 있다.
패키지 관리에는 다음과 같은 명령을 사용할 수 있다.
uv add --group vercel.proxy vercel-proxy
이러한 구조에서는 실제 FastAPI 애플리케이션과 Routing Middleware가 서로 다른 패키지 집합을 사용할 수 있다.
경로 기반 Redirect
Vercel은 vercel-proxy 패키지에 Proxy 객체를 제공한다.
개발자는 해당 객체에 요청 경로별 Handler를 등록할 수 있다.
예를 들어 이전 블로그 주소가 다음과 같다고 가정해 보자.
/old-blog/my-post
새로운 주소는 다음과 같다.
/blog/my-post
Routing Middleware는 요청 경로에서 my-post를 추출한 뒤 새로운 주소로 Redirect할 수 있다.
공식 예제에서는 HTTP 308 Permanent Redirect를 사용한다.
308은 영구적인 Redirect를 나타내며, 요청 Method와 Body를 유지하는 동작을 정의한다.
일반적인 GET 요청에서는 새로운 주소로 이동하는 형태로 나타난다.
그러나 POST 요청처럼 Body를 가진 요청에서도 Method를 유지한다는 점이 중요하다.
따라서 301·302·307·308을 동일한 방식으로 취급해서는 안 된다.
어떤 Redirect 상태 코드를 선택할지는 실제 서비스의 요청 방식과 이전 URL의 의미에 따라 달라진다.
요청을 계속 전달하는 ContinueResponse
Routing Middleware는 Redirect만 수행하는 것은 아니다.
요청을 실제 애플리케이션으로 계속 전달할 수도 있다.
Vercel은 이를 위해 ContinueResponse를 제공한다.
공식 예제에서는 API 경로에 다음 Header를 추가한다.
x-api-version: 2
예를 들어 클라이언트가 /api/orders에 요청했다고 가정해 보자.
Routing Middleware는 해당 요청에 버전 정보를 추가한 뒤 원래 애플리케이션으로 전달한다.
실제 FastAPI나 Django 애플리케이션은 변경된 Header를 받아 요청을 처리할 수 있다.
다만 Header를 추가했다는 사실만으로 API의 버전 전환이 자동으로 이루어지는 것은 아니다.
애플리케이션이 해당 Header를 해석하고 필요한 로직을 실행하도록 구성돼 있어야 한다.
또한 사용자가 임의로 전달할 수 있는 Header를 인증이나 권한의 근거로 사용할 때는 주의해야 한다.
신뢰할 수 있는 프록시 계층에서 값을 생성하고 검증하는 구조가 필요하다.
일치하지 않는 경로의 처리
개발자가 등록한 Middleware 경로와 일치하지 않는 요청은 기존 애플리케이션으로 그대로 전달된다.
예를 들어 Middleware가 /old-blog/* 경로만 처리한다면 일반적인 /products 요청에는 별도의 처리를 수행하지 않을 수 있다.
이는 모든 요청을 반드시 직접 반환하거나 Redirect하도록 강제하지 않는다는 의미다.
따라서 특정 경로에만 공통 처리 규칙을 적용할 수 있다.
이번 발표는 Python 웹 프레임워크 자체의 Middleware API를 변경하는 것이 아니다.
Vercel의 라우팅 계층에서 Python 코드를 실행할 수 있도록 언어 지원 범위를 확대한 업데이트다.
Cloudflare Agents SDK, createAI로 Workers AI·AI Gateway Provider 통합 — 기존 Provider 패키지는 Deprecated
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare Agents SDK, Workers AI, AI Gateway
- 변경 유형: 신규 통합 API 및 기존 Provider Deprecated
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-21-createai/
Cloudflare가 Agents SDK에 새로운 createAI 함수를 추가하고 기존의 Workers AI 및 AI Gateway Provider 기능을 통합했다.
AI 애플리케이션에서는 하나의 모델 제공업체만 사용하는 경우도 있지만, 여러 모델을 연결하는 경우도 많다.
예를 들어 간단한 요청에는 작은 모델을 사용하고, 복잡한 분석에는 다른 제공업체의 모델을 사용할 수 있다.
또한 장애나 사용량 제한이 발생했을 때 대체 모델을 사용하도록 구성할 수도 있다.
문제는 모델을 호출하는 경로와 실행 환경에 따라 서로 다른 Provider를 관리해야 할 수 있다는 점이다.
Cloudflare에서는 기존에 다음과 같은 Provider를 사용했다.
workers-ai-providerai-gateway-provider
이번 업데이트는 이러한 기능을 createAI라는 하나의 인터페이스로 통합한다.
createAI의 역할
새로운 함수는 다음 경로에서 가져올 수 있다.
agents/models/ai-sdk
이후 createAI()에 사용할 AI Binding과 Gateway ID를 전달한다.
대표적인 형태는 다음과 같다.
createAI({ binding: env.AI, id: "my-gateway" })
이렇게 생성한 Provider로 여러 종류의 모델을 호출할 수 있다.
Cloudflare가 지원하는 주요 모델 경로는 다음과 같다.
| 모델 경로 | 의미 |
|---|---|
@cf/로 시작하는 ID | Workers AI 모델 |
| AI Gateway Catalog 모델 ID | Gateway에서 제공하는 모델 |
dynamic/<route> | AI Gateway Dynamic Route |
| Provider 자체 모델 객체 | 외부 모델 제공업체의 모델 객체 |
예를 들어 하나의 애플리케이션에서 Workers AI 모델과 다른 기업의 모델을 함께 사용하도록 구성할 수 있다.
중요한 점은 서로 다른 모델 경로를 호출하기 위해 매번 별도의 Provider 초기화 구조를 만들 필요가 줄어든다는 것이다.
AI SDK와 Pi AI 지원
createAI는 AI SDK와 Pi AI에서 사용할 수 있다.
AI SDK는 애플리케이션에서 모델 호출과 스트리밍 등을 처리하는 데 사용할 수 있는 라이브러리다.
Cloudflare는 새로운 Provider가 AI SDK의 다양한 모델 유형을 지원한다고 설명한다.
대표적으로 다음과 같다.
- 텍스트 생성
- 임베딩
- 이미지 생성
- 음성 생성
- 음성 인식
- Reranking
임베딩(Embedding)은 텍스트나 이미지 등의 데이터를 수치 벡터로 표현하는 방식이다.
검색 시스템에서는 이러한 벡터를 이용해 의미적으로 비슷한 문서를 찾을 수 있다.
Reranking은 검색된 후보를 다시 분석해 관련성이 높은 순서로 정렬하는 작업이다.
따라서 이번 Provider는 일반적인 챗봇의 텍스트 생성 외에도 여러 AI 기능을 같은 모델 연결 구조에서 사용할 수 있도록 한다.
다만 모든 모델이 모든 종류의 작업을 지원하는 것은 아니다.
실제 사용할 수 있는 기능은 선택한 모델과 제공업체의 지원 범위에 따라 달라진다.
Gateway의 Fallback과 BYOK
새로운 API는 AI Gateway의 기존 운영 기능도 지원한다.
대표적인 기능은 다음과 같다.
- Fallback 모델 설정
- 요청 캐싱
- 로깅
- 요청 메타데이터
- 재시도
- Bring Your Own Key(BYOK)
- Zero Data Retention 관련 설정
Fallback은 기본 모델을 사용할 수 없을 때 대체 모델을 사용하는 방식이다.
예를 들어 기본 모델의 API가 일시적으로 사용할 수 없는 상태라면 Workers AI의 다른 모델로 요청을 전달하도록 구성할 수 있다.
이러한 구조는 외부 모델 제공업체의 장애에 대한 복원력을 높이는 데 도움이 될 수 있다.
그러나 대체 모델을 설정했다고 해서 동일한 출력 품질과 데이터 구조가 항상 보장되는 것은 아니다.
모델마다 입력 문맥과 도구 호출, 구조화된 응답에 대한 지원 범위가 다를 수 있기 때문이다.
따라서 실제 애플리케이션에서는 모델이 변경됐을 때도 필요한 데이터 형식과 업무 규칙을 충족하는지 검증해야 한다.
중단된 스트림 복구
이번 Provider는 스트림 복구 기능도 지원한다.
AI 모델이 긴 답변을 생성하는 동안 네트워크 연결이 끊길 수 있다.
이때 애플리케이션이 요청을 처음부터 다시 시작하면 불필요한 토큰 사용이 발생할 수 있다.
또한 이미 생성한 응답과 새로운 응답의 내용이 달라질 수 있다.
Cloudflare는 Binding을 사용하는 Catalog 또는 Dynamic Route 스트림이 중간에 끊어졌을 때 이어서 수신할 수 있도록 지원한다고 설명한다.
다만 이 기능이 모든 모델 제공업체와 모든 통신 방식에서 동일하게 동작한다는 의미는 아니다.
공식 문서에서 안내하는 지원 경로와 제한사항을 확인해야 한다.
기존 Provider의 Deprecated 처리
이번 업데이트에서는 기존 Provider가 Deprecated 상태로 변경됐다.
| 기존 방식 | 새로운 방식 |
|---|---|
createWorkersAI({ binding }) | createAI({ binding }) |
createWorkersAI({ accountId, apiKey }) | createAI({ accountId, apiToken }) |
createAiGateway(...) | createAI(...) |
createAISearch | createAISearchNamespace |
여기서 마지막 항목은 별도로 주의해야 한다.
AI Search는 일반적인 모델 Provider와 분리됐다.
새로운 Provider에서는 다음 패키지를 사용한다.
ai-search-provider
그리고 createAISearchNamespace를 이용한다.
따라서 기존 Workers AI Provider에서 AI Search 기능을 사용하고 있었다면 단순히 함수 이름만 바꾸는 것으로 마이그레이션이 끝나지 않을 수 있다.
Import 경로와 필요한 설정을 함께 변경해야 한다.
Cloudflare는 기존 workers-ai-provider와 ai-gateway-provider를 기능 추가가 중단된 상태로 표시했다.
하지만 Deprecated가 즉시 삭제됐다는 의미는 아니다.
기존 API는 당장 사용할 수 있지만 앞으로 새로운 기능이 추가되지 않으므로 점진적으로 이전하도록 안내하고 있다.
이번 업데이트는 여러 AI 모델의 호출과 운영 기능을 하나의 Provider 인터페이스로 통합하면서 기존 패키지의 유지보수 방향을 명확히 한 변경이다.
4️⃣ 개발 도구 및 보안
PHP 8.5.12·8.4.27·8.3.36·8.2.35 긴급 보안 업데이트 — 인증 없는 원격 코드 실행 취약점 2건 수정
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 심각도: Critical
- CVSS v4.0: 각 9.2점
- 취약점: CVE-2026-109618, CVE-2026-109619
- 관련 명칭: php2shell
- PHP 공식 원문: https://www.php.net/archive/2026.php
- Cloudflare 분석: https://blog.cloudflare.com/php-vulnerabilities-waf-protection/
PHP 프로젝트가 인증되지 않은 외부 요청으로 원격 코드 실행이 가능한 두 가지 심각한 취약점을 수정한 보안 릴리스를 공개했다.
이번 발표는 10월 20일 예고됐던 긴급 보안 업데이트의 실제 공개다.
따라서 이전 브리핑에서 다룬 사전 예고와 달리 수정 버전과 구체적인 취약점의 영향 범위가 확인된 새로운 소식이다.
이번 취약점은 특정 WordPress 플러그인이나 개별 PHP 프레임워크에만 존재하는 문제가 아니다.
PHP 런타임의 내부 요청 처리 과정에 존재하는 문제다.
따라서 웹 요청을 처리하는 다양한 PHP 실행 환경이 영향을 받을 수 있다.
PHP 프로젝트는 다음 환경을 명시했다.
- PHP-FPM
- Apache mod_php
- php-cgi
- FrankenPHP
- LiteSpeed
특히 Apache mod_php에서는 애플리케이션 코드가 실행되기 전에도 공격이 성립하는 경로가 입증됐다고 설명했다.
CVE-2026-109618: 메모리 부족 처리 과정의 손상
첫 번째 취약점은 Out-of-Memory(OOM) 처리 과정에서 발생하는 메모리 손상 문제다.
프로그램이 메모리를 할당하려고 했지만 필요한 메모리를 확보하지 못하면 OOM 상황이 발생한다.
일반적으로 프로그램은 이러한 오류가 발생하면 일부 작업을 중단하고 사용 중이던 자원을 정리해야 한다.
하지만 자원 정리 과정 역시 정확하게 구현돼 있어야 한다.
이번 취약점에서는 PHP가 요청을 처리하는 동안 내부 데이터 구조가 완전히 초기화되지 않은 상태에서 OOM 오류가 발생할 수 있었다.
이후 정리 과정에서 해당 데이터를 정상적인 데이터로 취급하면서 메모리 손상이 발생할 수 있었다.
공격자는 특정 HTTP 요청을 구성해 이러한 오류 처리 경로에 진입하도록 시도할 수 있다.
취약점의 결과는 단순한 요청 실패에 그치지 않을 수 있다.
PHP 보안팀은 해당 문제가 원격 코드 실행으로 이어질 수 있다고 설명했다.
CVE-2026-109619: 이전 요청의 상태가 다음 요청에 남는 문제
두 번째 취약점은 이미 해제된 메모리를 참조하는 Use-after-free 문제와 관련된다.
웹 서버는 하나의 요청을 처리한 뒤 프로세스를 즉시 종료하지 않는 경우가 많다.
동일한 Worker가 여러 HTTP 요청을 순차적으로 처리할 수 있다.
따라서 요청이 종료되면 해당 요청에 속한 상태를 올바르게 정리해야 한다.
하지만 이번 취약점에서는 이전 요청의 내부 상태가 메모리 해제 이후에도 남을 수 있었다.
이후 같은 Worker가 새로운 요청을 처리하면서 오래된 상태를 다시 참조할 수 있었다.
이러한 상황을 Stale Global State라고 한다.
잘못된 메모리 참조는 프로그램 충돌을 일으킬 수 있다.
또한 공격자가 메모리 상태를 조작할 수 있다면 더 심각한 취약점으로 이어질 수 있다.
PHP 프로젝트는 이 문제 역시 인증 없는 원격 코드 실행으로 이어질 수 있다고 설명했다.
수정 버전
PHP 보안팀이 공개한 수정 버전은 다음과 같다.
| PHP 버전 라인 | 수정 버전 |
|---|---|
| PHP 8.5 | 8.5.12 |
| PHP 8.4 | 8.4.27 |
| PHP 8.3 | 8.3.36 |
| PHP 8.2 | 8.2.35 |
이번 두 취약점에 대한 완전한 수정은 해당 보안 패치가 적용된 PHP 런타임을 사용하는 것이다.
PHP 8.1 이하 버전은 PHP 프로젝트의 일반적인 보안 지원 대상이 아니다.
다만 일부 Linux 배포판과 장기 지원 업체는 이전 버전에도 보안 수정 사항을 별도로 Backport할 수 있다.
Backport는 새로운 버전에서 수정한 내용을 오래된 버전의 코드에 맞게 다시 적용하는 방식이다.
따라서 Linux 배포판이 제공하는 PHP 패키지에서는 버전 번호가 위 표와 달라도 관련 패치가 적용돼 있을 수 있다.
반대로 단순히 오래된 PHP 버전을 사용하고 있다면 해당 취약점에 대한 수정이 존재하지 않을 가능성이 있다.
이 경우 배포판이나 패키지 제공업체의 보안 공지를 확인해야 한다.
오래된 PHP 버전을 위한 긴급 패치
PHP 보안팀은 취약점의 심각도를 고려해 다음 오래된 버전에 대한 최소한의 소스 코드 패치도 제공했다.
- PHP 7.4
- PHP 8.0
- PHP 8.1
이는 지원이 종료된 버전의 일반적인 보안 지원이 재개됐다는 의미가 아니다.
이번 두 취약점에 대응하기 위한 예외적인 조치다.
따라서 해당 패치가 존재하더라도 장기적으로는 지원 중인 PHP 버전으로 이전해야 한다.
PHP 8.2 역시 2026년 12월 31일 지원이 종료될 예정이다.
Cloudflare의 실제 공격 관찰
Cloudflare는 이번 취약점이 공개되는 과정에서 공격자가 실제로 악용을 시도하는 트래픽을 관찰했다고 발표했다.
또한 공격자가 기존 차단 규칙을 우회하려고 입력 데이터를 변형하는 행동도 확인했다.
Cloudflare는 공식 공개 전에 WAF 보호 규칙을 준비하고 있었다.
이후 2026년 10월 21일 03:02 UTC, 한국 시간으로 12:02부터 관련 보호 기능을 무료 및 유료 고객에게 기본 적용했다고 설명했다.
하지만 WAF 보호가 적용됐다는 사실과 PHP 런타임의 취약점이 수정됐다는 사실은 다르다.
WAF는 특정 공격 요청을 차단하는 방어 계층이다.
취약한 PHP 코드를 직접 수정하지는 않는다.
따라서 보안팀은 Cloudflare의 WAF를 사용하고 있더라도 PHP 자체를 업데이트하도록 권고했다.
이번 사건은 특정 프레임워크나 애플리케이션의 코드가 안전하더라도 그 아래에서 실행되는 런타임에 문제가 있다면 서비스 전체가 영향을 받을 수 있다는 사례다.
Cloudflare WAF, 요청과 응답을 함께 분석하는 Full Transaction Detection 정식 출시 — php2shell 방어에 적용
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 안정화 단계: Generally Available
- 적용 대상: Cloudflare WAF
- 지원 요금제: Free 포함 전체
- 원문: https://blog.cloudflare.com/full-transaction-waf/
- 관련 보안 규칙: https://developers.cloudflare.com/changelog/post/2026-10-21-emergency-waf-release-free/
Cloudflare가 HTTP 요청뿐 아니라 Origin 서버가 반환한 응답까지 함께 분석하는 WAF 기능을 정식 출시했다.
이번 기능은 앞서 소개한 PHP 원격 코드 실행 취약점에 대응하는 과정에서 실제로 사용됐다.
기존 WAF는 주로 서버에 들어오는 HTTP 요청을 검사한다.
예를 들어 특정 URL에 SQL Injection으로 의심되는 문자열이 포함돼 있다면 요청을 차단할 수 있다.
하지만 HTTP 요청만 분석하는 방식에는 한계가 있다.
같은 문자열이 포함된 요청이라도 실제 서버가 해당 문자열을 어떻게 처리하는지에 따라 결과가 달라질 수 있기 때문이다.
예를 들어 사용자가 다음과 비슷한 경로를 요청했다고 가정해 보자.
/download/../../config
이 요청은 Path Traversal 공격 시도로 의심할 수 있다.
그러나 실제 서버가 요청을 거부했다면 민감한 파일은 노출되지 않는다.
반대로 서버가 잘못된 경로 처리를 수행해 설정 파일을 반환했다면 정보 노출이 발생한 것이다.
기존 요청 기반 탐지만으로는 두 상황을 완전히 구분하기 어렵다.
이번 Full Transaction Detection은 하나의 HTTP 요청과 해당 요청에 대한 응답을 연결해 분석한다.
요청과 응답의 상관관계 분석
Cloudflare는 다음 두 종류의 정보를 함께 사용한다.
첫 번째는 요청 정보다.
- URL
- Query Parameter
- HTTP Header
- Request Body
- 알려진 공격 패턴
두 번째는 응답 정보다.
- HTTP 상태 코드
- Response Header
- 공격 성공을 나타낼 수 있는 응답 신호
- 민감한 정보 노출과 관련된 패턴
예를 들어 의심스러운 파일 접근 요청이 발생했더라도 서버가 HTTP 403을 반환했다면 실제 파일 노출은 확인되지 않는다.
반면 같은 요청에 대해 HTTP 200 응답과 설정 파일처럼 보이는 내용이 반환됐다면 공격이 성공했을 가능성이 커진다.
이러한 방식은 단순한 공격 시도와 실제 영향이 발생한 것으로 의심되는 상황을 구분하는 데 도움을 준다.
Cloudflare는 요청과 응답을 함께 사용하는 방식이 오탐(False Positive)을 줄이는 데도 도움이 될 수 있다고 설명한다.
하지만 실제로 어떤 요청을 차단할지는 적용한 탐지 규칙과 정책에 따라 달라진다.
Origin Response를 차단
새로운 기능의 핵심은 서버가 이미 응답을 생성했더라도 해당 응답을 사용자에게 전달하기 전에 차단할 수 있다는 점이다.
예를 들어 공격자가 요청한 민감한 설정 파일이 Origin 서버에서 반환됐다고 가정해 보자.
Cloudflare는 해당 응답을 분석한 뒤 정책에 따라 원래 응답을 차단 페이지로 교체할 수 있다.
이 경우 공격자는 Origin 서버가 생성한 민감한 응답을 그대로 받지 못한다.
하지만 여기에는 중요한 제한이 있다.
Origin 서버에서 이미 실행된 작업을 취소할 수는 없다.
예를 들어 공격 요청이 데이터베이스를 변경했다면 응답을 차단한다고 해서 해당 변경이 자동으로 되돌려지는 것은 아니다.
또한 서버에서 임의의 명령이 이미 실행됐다면 응답 차단만으로 해당 실행을 무효화할 수 없다.
따라서 이번 기능은 정보 유출을 제한하거나 공격 결과를 차단하는 추가 보호 계층이지, 애플리케이션 취약점 수정의 대체 수단은 아니다.
새롭게 제공되는 탐지 필드
Cloudflare는 다음 필드를 추가했다.
| 필드 | 의미 |
|---|---|
cf.waf.signature.response.confidence | 응답 기반 탐지 결과의 신뢰도 |
cf.waf.signature.response.categories | 탐지된 공격 유형 |
cf.waf.signature.response.ref | 일치한 Signature의 참조 ID |
response.ref에는 최대 10개의 Signature 참조 ID가 포함될 수 있다.
개발자는 이러한 필드를 다른 요청 정보와 조합해 보안 규칙을 구성할 수 있다.
예를 들어 특정 관리 페이지에서 인증되지 않은 요청이 발생했고, 서버가 정상적인 HTML 관리 화면을 반환한 경우에만 차단하는 규칙을 만들 수 있다.
이 방식은 단순히 /admin 경로에 접근한 모든 요청을 차단하는 것보다 세밀하다.
Managed Rule과 Custom Rule
Cloudflare는 새로운 응답 기반 보호 기능을 Managed Rule과 Custom Rule 두 가지 방식으로 제공한다.
Managed Rule은 Cloudflare가 관리하는 규칙이다.
Cloudflare는 php2shell 취약점에 대응하는 새로운 규칙을 기본 적용했다.
관련 보호 기능은 Free 요금제에도 제공된다.
Custom Rule에서는 개발자가 요청과 응답 정보를 조합해 정책을 정의할 수 있다.
현재 모든 요금제에서 최대 3개의 응답 기반 Custom Rule을 생성할 수 있다.
지원하는 동작은 다음과 같다.
- Log
- Block
다만 모든 요금제에서 사용할 수 있는 탐지 결과 필드가 동일한 것은 아니다.
유료 요금제에서는 더 풍부한 공격 탐지 정보를 활용할 수 있다.
또한 현재 Custom Rule에서 원본 Response Body의 내용을 자유롭게 직접 검사하는 기능은 제공되지 않는다.
관리형 탐지 엔진이 응답의 신호를 분석하는 기능과 개발자가 Custom Rule에서 Response Body를 직접 읽는 기능은 구분해야 한다.
이번 발표는 WAF가 요청을 검사하는 전통적인 역할에서 더 나아가 요청과 응답의 관계를 분석하고 응답 단계에서도 보안 정책을 실행하는 방향으로 확장된 변화다.
Node.js 22·24·26 긴급 보안 업데이트 — Brotli 원격 코드 실행과 TLS 인증서 검증 취약점 수정
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 최고 심각도: High
- 대상 버전: Node.js 22.x, 24.x, 26.x
- 공식 보안 공지: https://nodejs.org/en/blog/vulnerability/october-2026-security-releases
- Node.js 22 릴리스: https://nodejs.org/en/blog/release/v22.23.4
- Node.js 24 릴리스: https://nodejs.org/en/blog/release/v24.22.1
- Node.js 26 릴리스: https://nodejs.org/en/blog/release/v26.12.1
Node.js 프로젝트가 여러 고위험 보안 취약점을 수정한 새 버전을 공개했다.
앞선 브리핑에서는 10월 21일 보안 업데이트가 예정돼 있다는 사전 공지를 다뤘다.
이번에는 실제 수정 버전과 CVE 번호, 영향을 받는 기능이 공개됐다.
새로운 버전은 다음과 같다.
| 릴리스 라인 | 보안 수정 버전 |
|---|---|
| Node.js 22 LTS | 22.23.4 |
| Node.js 24 LTS | 24.22.1 |
| Node.js 26 Current | 26.12.1 |
이번 업데이트에는 DNS, TLS 인증서 검증, Brotli 압축 해제, HTTP 연결 처리, 암호학적 난수, 파일시스템 권한 모델과 관련된 수정이 포함됐다.
CVE-2026-111497: Brotli Decoder의 원격 코드 실행 위험
이번 보안 공지에서 특히 주목할 문제는 Brotli Decoder의 메모리 범위 초과 쓰기(Out-of-bounds Write) 취약점이다.
Brotli는 데이터를 압축하는 알고리즘이다.
웹에서 HTML과 CSS, JavaScript 등의 데이터를 전송할 때 사용할 수 있다.
압축된 데이터를 사용하려면 수신 측에서 이를 해제해야 한다.
하지만 압축 해제 과정에서 메모리의 유효 범위를 벗어나는 위치에 데이터를 쓰면 메모리 손상이 발생할 수 있다.
Node.js 보안팀은 이번 Brotli Decoder 취약점이 원격 코드 실행으로 이어질 가능성이 있다고 설명했다.
영향받는 버전은 다음과 같다.
- Node.js 22.x
- Node.js 24.x
- Node.js 26.x
다만 Node.js를 사용하는 모든 서버가 동일한 조건에서 원격 코드 실행에 노출된다는 의미는 아니다.
실제 위험은 애플리케이션이 어떤 압축 데이터를 처리하는지, 공격자가 해당 데이터에 영향을 줄 수 있는지 등에 따라 달라진다.
그러나 취약점의 최고 심각도가 High이며 메모리 손상과 관련돼 있으므로 수정 버전으로 업데이트하는 것이 중요하다.
TLS 인증서의 Hostname 검증 우회
이번 릴리스에서는 TLS 인증서 검증과 관련된 문제도 수정됐다.
TLS는 네트워크 통신을 암호화하고 서버의 신원을 검증하는 데 사용된다.
예를 들어 HTTPS로 외부 API에 연결할 때 클라이언트는 서버가 제시한 인증서가 실제 접속하려는 대상과 일치하는지 확인해야 한다.
이 과정에서 Hostname 검증이 중요하다.
인증서가 신뢰할 수 있는 발급자에 의해 생성됐더라도 다른 사이트의 인증서라면 정상적인 연결로 인정해서는 안 된다.
이번 릴리스에서는 특정 형식의 IP 주소와 Hostname을 처리하는 과정에서 잘못된 인증서가 허용될 수 있는 문제가 수정됐다.
대표적인 취약점은 다음과 같다.
| CVE | 심각도 | 영향받는 버전 |
|---|---|---|
| CVE-2026-105483 | High | 24.x, 26.x |
| CVE-2026-106069 | High | 22.x, 24.x, 26.x |
| CVE-2026-106072 | Medium | 22.x, 24.x, 26.x |
CVE-2026-105483은 IP 주소가 전각 숫자(Fullwidth Digit)로 표현된 상황과 관련된다.
전각 숫자는 일반적인 ASCII 숫자와 다른 Unicode 문자다.
예를 들어 일반적인 숫자 1과 전각 숫자 1은 서로 다른 문자다.
Hostname 또는 IP 주소를 정규화하는 과정에서 두 표현을 잘못 처리하면 인증서 검증에 문제가 발생할 수 있다.
CVE-2026-106069 역시 Hostname이 IP 주소로 해석되는 방식과 관련된다.
CVE-2026-106072는 비표준적인 IPv4 표기와 인증서의 DNS Subject Alternative Name 검증에 관한 문제다.
이번 수정은 문자열을 IP 주소로 변환하는 과정과 인증서가 실제 접속 대상을 증명하는 과정이 서로 일관되게 동작하도록 하는 것에 초점이 맞춰져 있다.
DNS 이름의 NUL 문자 처리
CVE-2026-105482는 dns.resolve()에서 발생한 문제다.
문자열에는 NUL 문자(\0)가 포함될 수 있다.
일부 시스템에서는 NUL 문자를 문자열의 끝으로 처리한다.
반면 JavaScript 문자열은 NUL 문자가 포함된 이후에도 다른 문자를 가질 수 있다.
이러한 차이가 정확하게 처리되지 않으면 애플리케이션이 의도한 도메인 이름과 실제 DNS 조회에 사용되는 도메인 이름이 달라질 수 있다.
이번 취약점은 이러한 문자열 처리 불일치와 관련된다.
영향받는 버전은 22.x, 24.x, 26.x 전체다.
일반적인 웹 애플리케이션에서 사용자가 전달한 도메인을 DNS로 조회하는 기능이 있다면 해당 입력이 올바르게 처리되는지 중요하다.
예를 들어 URL 검사나 도메인 허용 목록을 사용하는 서비스에서는 문자열의 정규화와 실제 조회 대상이 일치해야 한다.
다만 이번 수정으로 모든 DNS 관련 보안 문제가 해결되는 것은 아니다.
애플리케이션 차원의 URL 검증과 DNS Rebinding 방어 등은 별도로 필요할 수 있다.
crypto.randomUUID()와 randomInt()의 난수 재사용
CVE-2026-106074는 암호학적 난수와 관련된 문제다.
Node.js는 다음 API를 제공한다.
crypto.randomUUID()crypto.randomInt()
randomUUID()는 UUID를 생성하는 데 사용된다.
randomInt()는 지정된 범위의 정수를 암호학적으로 안전한 방식으로 생성하는 데 사용된다.
하지만 이번 취약점에서는 특정 Startup Snapshot 또는 SEA(Single Executable Application) 구성에서 난수 생성에 사용하는 Entropy Cache가 복제될 수 있었다.
Startup Snapshot은 애플리케이션의 초기 실행 상태를 저장하고 다음 실행에서 재사용하는 기능이다.
SEA는 Node.js 애플리케이션을 단일 실행 파일 형태로 패키징하는 방식이다.
문제는 난수 생성기의 내부 상태까지 복제되면 서로 다른 프로세스가 같은 값을 생성할 수 있다는 점이다.
예를 들어 서로 다른 프로세스가 독립적으로 생성해야 하는 식별자가 반복된다면 충돌이나 보안 문제가 발생할 수 있다.
이번 수정은 이러한 환경에서 난수 상태가 안전하게 처리되도록 하는 내용이다.
다만 일반적인 모든 Node.js 실행 환경에서 randomUUID()가 중복된 값을 반환했다는 의미는 아니다.
공식 공지에서 설명하는 특정 Snapshot 및 SEA 사용 조건과 관련된 취약점이다.
HTTP Keep-alive와 파일시스템 권한 문제
그 밖에도 다음 문제가 수정됐다.
- CVE-2026-105481: HTTP Keep-alive 연결에서 요청보다 앞서 작성된 응답을 잘못 처리할 수 있는 문제
- CVE-2026-106070: ZIP Header 검증 실패 이후 잘못된 데이터가 반환될 수 있는 문제
- CVE-2026-106071: Permission Model의 파일시스템 Wildcard 처리 문제
- CVE-2026-106073:
fs.realpathSync()를 통한 Permission Model 우회 문제
CVE-2026-106070과 CVE-2026-106071은 Node.js 26.x에 영향을 준다.
반면 CVE-2026-106073은 22.x, 24.x, 26.x 전체에 영향을 준다.
Permission Model은 애플리케이션이 접근할 수 있는 파일과 리소스를 제한하는 기능이다.
따라서 이 기능을 보안 경계로 사용하는 애플리케이션에서는 특히 관련 수정 사항을 확인할 필요가 있다.
내부 의존성 업데이트
이번 릴리스에는 Node.js가 내부적으로 사용하는 여러 구성요소의 보안 업데이트도 포함됐다.
대표적으로 다음과 같다.
- c-ares
- CA 인증서 목록
- OpenSSL
- SQLite
- Undici
이 가운데 c-ares와 Undici, CA 인증서 관련 업데이트는 22.x, 24.x, 26.x에 적용됐다.
OpenSSL 업데이트는 22.x와 24.x에 포함됐다.
SQLite 관련 업데이트는 22.x에 포함됐다.
CA 인증서 변경에는 eMudhra Intermediate CA의 보안 문제 대응도 포함됐다.
따라서 이번 업데이트는 Node.js 자체 코드의 취약점 수정뿐 아니라 네트워크와 TLS, HTTP 처리에 사용하는 내부 의존성의 보안 상태를 함께 갱신한 릴리스다.
적용 범위
이번 보안 수정은 새로운 JavaScript 문법이나 Node.js 개발 패턴을 도입한 것이 아니다.
기존 API의 보안 문제를 수정하는 릴리스다.
Node.js 22와 24를 사용하는 운영 서버, Node.js 26을 사용하는 개발 및 운영 환경에서 각각 해당 버전으로 업데이트할 수 있다.
다만 Node.js 버전을 변경한 이후에는 애플리케이션이 사용하는 Native Module과 빌드 도구, 배포 환경의 호환성도 확인해야 한다.
또한 실행 중인 애플리케이션은 새 Node.js 바이너리를 설치했다고 해서 자동으로 새로운 런타임을 사용하지 않을 수 있다.
실제로 서비스를 실행하는 프로세스나 컨테이너가 수정 버전을 사용하고 있는지 확인해야 한다.
이번 릴리스는 Node.js가 제공하는 네트워크·압축·암호화 기능의 보안 문제가 실제 서비스의 안전성에 영향을 줄 수 있다는 점을 보여준다.
GitHub CLI 2.103.0, 자동 갱신 OAuth Token 공개 Preview — 장기 유효 인증 정보 대신 만료되는 토큰 사용
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 버전: GitHub CLI 2.103.0 이상
- 안정화 단계: Public Preview
- 적용 대상: GitHub CLI
- 원문: https://github.blog/changelog/2026-10-21-github-cli-opt-in-to-expiring-auto-refreshing-oauth-tokens/
GitHub가 CLI에서 일정 시간이 지나면 만료되는 OAuth Access Token과 자동 갱신 기능을 사용할 수 있도록 지원했다.
GitHub CLI는 터미널에서 GitHub의 Repository, PR, Issue 등을 관리하는 도구다.
일반적으로 gh 명령을 처음 사용하려면 GitHub 계정으로 인증해야 한다.
이 과정에서 CLI는 인증 정보를 저장하고 이후 API 요청에 사용한다.
문제는 장기간 유효한 Access Token을 사용하는 경우다.
해당 Token이 외부에 노출되면 공격자가 오랫동안 사용할 가능성이 있다.
Token을 폐기하거나 권한을 변경하지 않는 한 유효한 상태가 계속 유지될 수 있기 때문이다.
이번 업데이트는 짧은 수명의 Access Token과 이를 갱신하는 Refresh Token을 조합한 방식을 제공한다.
Access Token과 Refresh Token 구분
Access Token은 실제 GitHub API를 호출할 때 사용하는 인증 정보다.
Refresh Token은 Access Token이 만료됐을 때 새로운 Access Token을 발급받기 위한 인증 정보다.
이번 구조에서는 Access Token이 일정 시간이 지나면 만료된다.
따라서 외부에 노출된 Access Token을 무기한 사용할 수 없다.
GitHub CLI는 필요한 경우 Refresh Token을 이용해 새로운 Access Token을 발급받는다.
또한 Refresh Token은 갱신 과정에서 교체된다.
이를 Rotating Refresh Token이라고 한다.
이러한 구조는 장기 유효 Access Token의 위험을 줄이는 데 도움이 된다.
하지만 Refresh Token까지 노출된다면 별도의 보안 문제가 발생할 수 있다.
따라서 CLI가 저장하는 인증 정보를 보호하는 것은 여전히 중요하다.
현재는 Opt-in
이번 기능은 자동으로 모든 사용자에게 적용되지 않는다.
GitHub CLI 2.103.0 이상에서 명시적으로 활성화해야 한다.
로그인 시에는 다음 옵션을 사용할 수 있다.
gh auth login --expiring
또는 기본 설정으로 활성화할 수 있다.
gh config set expiring_credentials enabled
이후 새로운 로그인 과정에서 만료되는 인증 정보를 요청하도록 설정할 수 있다.
기존에 로그인한 계정의 인증 정보가 자동으로 모두 변경되는 것은 아니다.
또한 GitHub는 향후 새로운 로그인에서 이 방식을 기본값으로 사용하는 계획을 언급했지만, 현재는 Public Preview다.
따라서 이미 기존 방식으로 로그인한 사용자는 별도의 변경 없이 계속 사용할 수 있다.
Git Credential Helper와의 연결
GitHub CLI는 일반적인 Git 명령의 인증에도 사용할 수 있다.
예를 들어 HTTPS를 이용해 Repository를 Clone하거나 Push하는 상황이다.
이때 다음 명령을 이용할 수 있다.
gh auth setup-git
이 설정은 Git이 인증 정보를 필요로 할 때 GitHub CLI를 Credential Helper로 사용하도록 한다.
Credential Helper는 Git이 필요한 인증 정보를 저장하거나 가져오도록 돕는 구성요소다.
이번 만료형 Token과 연결하면 GitHub CLI가 필요한 시점에 유효한 Access Token을 제공할 수 있다.
따라서 사용자가 직접 Token을 갱신하거나 새로운 Token을 Git 설정에 복사할 필요가 줄어든다.
GitHub는 Git 2.46 이상 사용을 권장한다.
해당 버전부터는 만료되는 인증 정보가 유효 기간을 초과해 캐싱되지 않도록 처리하는 기능을 활용할 수 있기 때문이다.
오래된 Git 버전에서도 사용 자체는 가능하지만 만료형 인증 정보의 처리 방식에 제한이 있을 수 있다.
자동화 스크립트의 호환성 문제
이번 업데이트에서 중요한 부분은 Token을 한 번 가져와 오래 저장하는 자동화 스크립트가 실패할 수 있다는 점이다.
예를 들어 다음 명령으로 Access Token을 가져올 수 있다.
gh auth token
이후 반환된 값을 환경 변수에 저장하고 여러 시간 동안 재사용하는 스크립트가 있다고 가정해 보자.
기존 장기 유효 Token에서는 문제가 발생하지 않았을 수 있다.
하지만 만료되는 Access Token을 사용하면 일정 시간이 지난 뒤 해당 값은 더 이상 유효하지 않다.
따라서 GitHub는 외부 스크립트에서 인증 정보가 필요할 때마다 gh auth token을 다시 호출하도록 안내한다.
해당 명령은 필요한 경우 Token을 갱신한다.
다만 장시간 실행되는 모든 프로그램이 이 방식으로 쉽게 변경될 수 있는 것은 아니다.
예를 들어 인증 정보를 프로세스 시작 시점에 한 번만 읽는 외부 프로그램이라면 추가적인 인증 처리 구조가 필요할 수 있다.
이러한 시스템에서는 만료형 Token으로 변경하기 전에 호환성을 확인해야 한다.
GitHub Enterprise 지원 범위
현재 지원 범위는 다음과 같다.
- github.com
- ghe.com
- GitHub Enterprise Server 3.23 이상
이전 버전의 GitHub Enterprise Server에서는 만료형 Token 요청이 지원되지 않을 수 있다.
이 경우 GitHub CLI는 기존의 만료되지 않는 Token 방식으로 대체할 수 있다.
따라서 --expiring 옵션을 사용했다고 해서 모든 서버 환경에서 반드시 만료형 Token이 발급된다고 단정해서는 안 된다.
이번 업데이트는 CLI 사용자와 자동화 도구의 인증 정보 노출 위험을 줄이기 위해 짧은 수명의 Access Token을 도입하면서 기존 Git 사용 경험을 유지하도록 만든 보안 기능이다.
Vercel, Monorepo에서 자동 건너뛴 배포는 배포 횟수 제한에서 제외 — Turborepo 의존성 분석 결과 반영
- 발표일: 2026-10-21 — 정확한 게시 시각 미확인
- 적용 대상: Vercel Monorepo Deployment
- 원문: https://vercel.com/changelog/skipped-monorepo-deployments-no-longer-count-against-limits
Vercel이 Monorepo에서 변경 사항이 없어 자동으로 건너뛴 배포를 프로젝트의 배포 횟수 제한에 포함하지 않도록 변경했다.
Monorepo는 여러 애플리케이션과 패키지를 하나의 Repository에서 관리하는 구조다.
예를 들어 다음 프로젝트가 하나의 Repository에 존재할 수 있다.
- 웹 관리자 페이지
- 사용자용 웹 애플리케이션
- API 서버
- 공통 UI 패키지
- 공통 TypeScript 설정
이러한 구조에서는 하나의 Commit이 모든 프로젝트에 영향을 주는 것은 아니다.
예를 들어 관리자 페이지의 컴포넌트 하나를 변경했다고 가정해 보자.
해당 변경이 사용자용 웹 애플리케이션이나 API 서버에 영향을 주지 않는다면 모든 프로젝트를 다시 배포할 필요는 없다.
Vercel은 이러한 상황에서 영향받지 않은 프로젝트의 배포를 자동으로 건너뛰는 기능을 제공한다.
Turborepo의 Package Graph 분석
Vercel은 어떤 프로젝트를 다시 배포해야 하는지 판단할 때 Turborepo의 정보를 사용한다.
Turborepo는 Monorepo에서 여러 패키지와 작업 사이의 의존성을 분석하고 빌드 작업을 관리하는 도구다.
예를 들어 다음과 같은 의존성 구조를 생각할 수 있다.
admin-web → ui-package
user-web → ui-package
관리자 페이지의 개별 파일만 변경됐다면 관리자 페이지의 배포가 필요할 수 있다.
하지만 공통 UI 패키지가 변경됐다면 해당 패키지를 사용하는 두 애플리케이션이 모두 영향을 받을 수 있다.
Vercel은 다음 정보를 분석한다.
- 프로젝트의 소스코드 변경
- 내부 패키지 의존성
- Lockfile 변경
- Commit이 영향을 주는 프로젝트
이러한 정보를 이용해 실제 변경의 영향을 받지 않는 프로젝트는 배포를 건너뛴다.
이번 변경의 내용
기존에는 Vercel이 배포를 자동으로 건너뛰더라도 해당 배포가 프로젝트의 배포 Rate Limit에 영향을 줄 수 있었다.
이번 업데이트 이후에는 Vercel이 자동으로 Skip한 Monorepo 배포가 배포 횟수 제한에 포함되지 않는다.
즉 기존과 동일한 제한 안에서 실제로 필요한 배포를 더 많이 수행할 수 있다.
예를 들어 하나의 Monorepo에 프로젝트가 10개 존재한다고 가정해 보자.
Commit 하나가 프로젝트 하나에만 영향을 준다면 나머지 아홉 개 프로젝트의 배포를 건너뛸 수 있다.
이번 변경으로 자동 건너뛴 아홉 개 배포는 각 프로젝트의 배포 Rate Limit을 소비하지 않는다.
하지만 이것이 모든 빌드와 배포 비용이 무료가 된다는 의미는 아니다.
실제로 실행되는 배포와 관련 리소스에는 기존 정책이 적용될 수 있다.
이번 변경은 배포 횟수 제한을 계산하는 방식에 관한 것이다.
기본 활성화 조건
새롭게 생성하는 Monorepo 프로젝트에서는 자동 Skip 기능이 기본적으로 활성화된다.
기존 프로젝트에서는 설정을 확인해야 한다.
관련 메뉴는 다음과 같다.
Project Settings > Build and Deployment
기존 프로젝트에서도 해당 기능을 활성화하면 변경 사항이 없는 프로젝트의 배포를 자동으로 건너뛸 수 있다.
그러나 Monorepo의 의존성 정보가 부정확하다면 실제로 영향을 받는 프로젝트가 잘못 분류될 수 있다.
예를 들어 패키지의 의존 관계가 설정 파일에 정확하게 반영되지 않은 경우다.
따라서 Monorepo에서는 공통 패키지와 애플리케이션 사이의 의존성 구조를 명확하게 관리해야 한다.
이번 발표는 새로운 Turborepo 빌드 엔진의 출시가 아니다.
기존 Monorepo의 자동 배포 생략 기능을 배포 횟수 제한 정책과 연결해 운영상의 불필요한 제한을 줄인 개선이다.
📚 추천 글
Comparing the top React toast libraries [2026 update]
- 저자: Nefe Emadamerho-Atori
- 2026년 업데이트: Chizaram Ken / LogRocket
- 게시일: 2026-10-21
- 원문: https://blog.logrocket.com/react-toast-libraries-compared-2026/
React에서 사용하는 다섯 가지 Toast 라이브러리를 실제 번들 크기, 접근성, 렌더링 성능, TypeScript 호환성 기준으로 측정해 비교한 글이다.
Toast는 사용자가 특정 작업을 수행했을 때 화면에 짧게 표시되는 알림 UI다.
예를 들어 다음과 같은 상황에서 사용한다.
- 저장 성공
- 파일 업로드 완료
- 서버 요청 실패
- 작업 진행 중
- 네트워크 연결 오류
Toast는 작은 컴포넌트처럼 보이지만 실제 구현에는 여러 문제가 존재한다.
예를 들어 알림을 화면 어디에 표시할지 결정해야 한다.
여러 알림이 동시에 발생했을 때 순서를 관리해야 한다.
알림이 사라지는 시간도 설정해야 한다.
또한 사용자가 키보드와 스크린 리더를 사용하는 경우 해당 알림에 접근할 수 있어야 한다.
저자는 이러한 요구사항을 기준으로 여러 라이브러리를 비교했다.
비교한 다섯 가지 라이브러리
이번 비교 대상은 다음과 같다.
- React Hot Toast
- Sonner
- React-Toastify
- Notistack
- Base UI Toast
기존에 널리 사용됐던 React Toast Notifications는 비교 대상에서 제외됐다.
이 라이브러리는 오랫동안 유지보수되지 않았고 React 19에서 호환성 문제가 발생했기 때문이다.
저자는 React 19 환경에서 실제로 사용할 수 있는 라이브러리를 대상으로 비교했다.
측정 환경
이번 글의 특징은 단순히 각 라이브러리의 공식 문서에 적힌 기능만 비교하지 않았다는 점이다.
저자는 실제 테스트 프로젝트를 구성했다.
측정 환경은 다음과 같다.
| 항목 | 환경 |
|---|---|
| 컴퓨터 | Apple M4 MacBook Air |
| Node.js | 22.14.0 |
| React | 19.3.1 |
| 빌드 도구 | Vite |
| 타입 검사 | TypeScript |
| 브라우저 테스트 | Playwright |
| 접근성 검사 | axe |
| 성능 테스트 | Toast 50개 연속 생성 |
라이브러리별 번들 크기는 독립적인 Production Build를 생성해 비교했다.
기본 React 애플리케이션의 번들 크기를 제외하고 Toast 라이브러리가 추가하는 크기를 계산했다.
따라서 단순한 패키지 설치 크기보다 실제 배포 결과물의 크기에 가까운 값이다.
그러나 이 결과는 저자의 특정 빌드 구성에서 측정한 수치다.
실제 프로젝트에서는 Tree Shaking과 다른 의존성, 빌드 설정에 따라 결과가 달라질 수 있다.
번들 크기 비교
측정 결과는 다음과 같다.
| 라이브러리 | 테스트 버전 | 추가 Gzip 크기 |
|---|---|---|
| React Hot Toast | 2.6.1 | 4.7kB |
| Notistack | 3.0.2 | 8.3kB |
| Sonner | 2.0.8 | 9.2kB |
| React-Toastify | 11.1.0 | 9.5kB |
| Base UI Toast | 1.9.0 | 12.1kB |
가장 작은 라이브러리는 React Hot Toast였다.
추가 Gzip 크기는 약 4.7kB였다.
반면 Base UI Toast는 약 12.1kB로 다섯 라이브러리 가운데 가장 컸다.
하지만 번들 크기가 작다고 해서 모든 프로젝트에서 가장 적합한 것은 아니다.
예를 들어 디자인 시스템이 복잡한 서비스에서는 기본 스타일이 없는 컴포넌트가 오히려 유리할 수 있다.
또한 접근성을 위한 키보드 조작 기능이 필요하다면 단순한 번들 크기 차이보다 실제 UI의 사용성이 중요할 수 있다.
저자는 이러한 이유로 기능과 접근성을 함께 비교했다.
React Hot Toast의 특징
React Hot Toast는 작은 번들과 간결한 API가 장점이다.
대표적으로 다음 기능을 제공한다.
- 성공 및 오류 알림
- 로딩 상태 알림
- Promise 기반 알림
- 여러 Toaster 인스턴스
- Headless Hook
- 커스텀 스타일
특히 toast.promise()는 비동기 작업의 상태를 알림과 연결하는 데 사용할 수 있다.
예를 들어 API 요청이 시작됐을 때 로딩 Toast를 표시하고, 요청이 성공하면 성공 메시지로 변경할 수 있다.
반대로 요청이 실패하면 오류 메시지로 변경할 수 있다.
이러한 구조에서는 비동기 작업 상태마다 별도의 Toast 생성과 삭제 로직을 직접 작성할 필요가 줄어든다.
하지만 접근성 테스트에서는 제한사항이 확인됐다.
React Hot Toast는 기본적으로 role="status"와 aria-live="polite"를 사용해 스크린 리더에 알림을 전달할 수 있다.
그러나 저자의 테스트에서는 키보드의 Tab 키만으로 Toast에 접근할 수 없었다.
따라서 알림에 사용자가 직접 조작해야 하는 버튼이 포함되는 경우에는 추가적인 접근성 설계가 필요할 수 있다.
Sonner의 특징
Sonner는 비교적 적은 설정으로 완성도 높은 Toast UI를 제공하는 라이브러리다.
여러 Toast를 쌓아 표시하는 디자인이 특징이다.
저자는 Sonner의 다음 기능을 소개했다.
- Promise 기반 상태 처리
- Action Button
- 테마 설정
- 여러 Toaster
- 화면 위치 설정
- Swipe 기반 닫기
접근성 측면에서는 Alt+T 단축키를 이용해 Toast 영역으로 이동할 수 있다.
다만 실제 접근성 테스트에서는 색상 대비와 관련된 문제가 발견됐다.
이러한 문제는 테마와 색상 설정에 따라 달라질 수 있으므로 서비스에 적용한 이후 직접 검사해야 한다.
React-Toastify의 특징
React-Toastify는 다양한 기능을 기본적으로 제공한다.
예를 들어 다음 기능이 포함된다.
- 여러 종류의 Toast
- 자동 종료 시간
- Hover 시 종료 일시정지
- Drag로 닫기
- RTL 지원
- 진행 상태 표시
- 커스텀 Transition
React-Toastify 11부터는 CSS를 자동으로 삽입하는 구조가 도입됐다.
따라서 별도의 CSS Import가 필요하지 않은 구성이 가능하다.
저자의 접근성 테스트에서는 Tab 키로 Toast 영역에 접근할 수 있었다.
다만 닫기 버튼의 키보드 접근성은 별도로 확인해야 할 수 있다.
Notistack의 특징
Notistack은 여러 알림을 Queue로 관리하는 기능이 특징이다.
예를 들어 짧은 시간에 Toast 50개가 생성됐다고 가정해 보자.
모든 Toast를 동시에 화면에 표시하면 사용자에게 혼란을 줄 수 있다.
Notistack은 기본적으로 표시할 알림의 개수를 제한하고 나머지는 Queue에서 관리한다.
이러한 구조는 동시에 여러 알림이 발생하는 애플리케이션에 적합할 수 있다.
하지만 저자는 유지보수와 접근성 측면에서 몇 가지 문제를 지적했다.
이번 테스트에서 사용한 Notistack 3.0.2의 마지막 릴리스는 2025년 1월이었다.
또한 접근성 검사에서 색상 대비 문제가 발견됐다.
키보드로 Toast에 직접 접근하는 기능도 제한적이었다.
따라서 기존 프로젝트에서 이미 사용하고 있다면 계속 사용할 수 있지만 새로운 프로젝트에서는 유지보수 상태를 함께 고려해야 한다.
Base UI Toast의 특징
Base UI Toast는 기본 디자인이 없는 Headless UI에 가까운 접근을 사용한다.
개발자는 Toast의 구조와 스타일을 직접 정의할 수 있다.
이 방식은 자체 디자인 시스템을 구축한 프로젝트에서 유리하다.
예를 들어 회사 전체에서 동일한 버튼과 색상, 애니메이션 정책을 사용해야 하는 경우다.
Base UI Toast는 다음 기능을 제공한다.
- Toast Manager
- 중복 Toast 처리
- 특정 요소와 연결되는 Anchored Toast
- 키보드 접근성
- 커스텀 스타일
다만 기본 스타일이 거의 없기 때문에 완성된 Toast UI를 사용하려면 CSS를 직접 작성해야 한다.
또한 이번 비교에서 번들 크기가 가장 컸다.
따라서 구현 편의성과 디자인 제어, 접근성 요구사항을 함께 고려해야 한다.
접근성 테스트 결과
저자는 Playwright와 axe를 이용해 실제 Toast의 접근성을 검사했다.
주요 결과는 다음과 같다.
| 라이브러리 | 키보드로 Toast 접근 | axe에서 발견한 Toast 관련 문제 |
|---|---|---|
| React Hot Toast | 불가 | 0 |
| Sonner | Alt+T | 1 |
| React-Toastify | Tab | 0 |
| Notistack | 불가 | 1 |
| Base UI Toast | F6 이후 Tab | 0 |
흥미로운 점은 axe 검사에서 오류가 0개라고 해서 키보드 사용성이 완전하다는 의미는 아니라는 사실이다.
예를 들어 React Hot Toast는 자동화된 접근성 검사에서 오류가 발견되지 않았지만 키보드로 Toast에 직접 접근할 수 없었다.
반면 Base UI Toast는 자동화 검사와 키보드 테스트에서 모두 좋은 결과를 보였다.
따라서 접근성 검사를 하나의 자동화 도구에만 의존해서는 안 된다.
실제 키보드 조작과 스크린 리더 사용성을 함께 확인해야 한다.
Toast 50개 연속 생성 성능 테스트
저자는 다섯 라이브러리에 대해 Toast를 50개 연속 생성하는 테스트도 수행했다.
각 테스트를 다섯 번 실행한 뒤 중앙값을 계산했다.
결과는 다음과 같다.
| 라이브러리 | 50개 생성 중앙값 | Long Task |
|---|---|---|
| Notistack | 518ms | 0 |
| React Hot Toast | 530ms | 0 |
| React-Toastify | 541ms | 0 |
| Base UI Toast | 564ms | 2개 |
| Sonner | 787ms | 0 |
숫자만 보면 Notistack이 가장 빠르다.
하지만 이는 Notistack이 기본적으로 세 개의 Toast만 표시하고 나머지는 Queue에서 관리하기 때문이다.
따라서 실제 DOM에 렌더링하는 Toast 수가 다른 라이브러리와 다르다.
또한 Base UI Toast에서는 두 개의 Long Task가 발생했으며, 전체 약 223ms에 해당했다.
Long Task는 브라우저 메인 스레드에서 일정 시간 이상 지속돼 다른 작업의 처리를 지연할 수 있는 작업이다.
하지만 일반적인 웹 애플리케이션에서 Toast 50개를 동시에 생성하는 상황은 흔하지 않다.
저자 역시 일반적인 1~5개 Toast 사용 환경에서는 대부분의 라이브러리가 충분히 빠르다고 설명한다.
따라서 이 테스트 결과만으로 실제 사용자 경험의 순위를 결정하는 것은 적절하지 않다.
테스트에서 드러난 트레이드오프
이 글에서 확인할 수 있는 중요한 차이는 다음과 같다.
React Hot Toast는 번들 크기가 작고 사용법이 간단하다.
Sonner는 기본 디자인과 개발 경험이 편리하다.
React-Toastify는 다양한 기능을 제공한다.
Notistack은 많은 알림을 Queue로 관리하는 데 적합하다.
Base UI Toast는 디자인 시스템과 접근성 중심 구현에 유리하다.
이 가운데 어느 하나가 모든 프로젝트에서 가장 적합한 선택은 아니다.
특히 번들 크기, 접근성, 디자인 제어, 알림 개수 관리라는 서로 다른 요구사항을 분리해서 비교했다는 점이 이 글의 장점이다.
The bridge to agent-scale Git infrastructure
- 저자: Eleanor White, Victoria Dye / GitHub Engineering
- 게시일: 2026-10-21
- 원문: https://github.blog/engineering/architecture-optimization/the-bridge-to-agent-scale-git-infrastructure/
GitHub가 급격히 증가하는 Git 요청을 처리하기 위해 Repository 저장 구조와 복제본 사이의 쓰기 동기화 방식을 변경하는 과정을 설명한 기술 글이다.
GitHub는 전 세계 개발자가 소스코드를 저장하고 공유하는 플랫폼이다.
Repository에는 파일뿐 아니라 Commit과 Branch, Tag 등의 정보가 저장된다.
사용자가 Git Push를 실행하면 GitHub는 해당 데이터를 안전하게 저장해야 한다.
또한 여러 사용자가 같은 Repository에 동시에 Push하더라도 Git의 일관성이 유지돼야 한다.
AI 코딩 에이전트의 사용이 증가하면서 이러한 요구사항은 더욱 복잡해졌다.
이전에는 개발자가 직접 작업하는 속도에 맞춰 Git 요청이 발생하는 경우가 많았다.
하지만 에이전트는 여러 작업을 병렬로 수행하며 새로운 Branch를 생성하고 Commit을 Push할 수 있다.
따라서 하나의 Repository에도 짧은 시간 동안 많은 쓰기 요청이 발생할 수 있다.
GitHub는 이러한 환경을 고려해 저장소의 확장성을 기존보다 더 높이는 아키텍처를 개발하고 있다.
GitHub Repository의 Azure 이전
GitHub는 기존 자체 데이터센터에서 실행하던 Git 인프라를 Azure로 확장하고 있다.
공식 발표에 따르면 현재 전체 Repository의 99.99999%가 자체 데이터센터와 Azure 양쪽에 복제본을 가지고 있다.
대부분의 Repository는 다섯 개의 복제본을 사용한다.
그 가운데 두 개가 Azure에 존재한다.
또한 전체 Git 트래픽의 약 절반을 Azure에서 처리하고 있다.
이는 GitHub가 한 번에 모든 데이터를 새로운 인프라로 이전하지 않았다는 의미다.
먼저 기존 데이터센터와 새로운 클라우드 환경에 데이터를 함께 배치한다.
이후 트래픽을 점진적으로 이동시키는 방식이다.
이러한 구조에서는 기존 서비스의 안정성을 유지하면서 새로운 인프라의 처리 능력을 검증할 수 있다.
하지만 일부 매우 바쁜 Repository는 여전히 이전이 어려웠다.
복제본 사이의 네트워크 지연
GitHub가 발견한 문제는 지역 사이의 약 25ms 네트워크 왕복 지연이었다.
분산 시스템에서는 여러 복제본이 동일한 데이터를 유지해야 한다.
예를 들어 사용자가 새로운 Commit을 Push하면 해당 변경 사항을 여러 복제본에 저장해야 한다.
GitHub는 Quorum 기반 방식을 사용한다.
Quorum은 여러 복제본 가운데 일정 수 이상이 작업을 승인하면 해당 작업을 확정하는 구조다.
일반적인 다수결 방식에서는 복제본 다섯 개 가운데 세 개가 응답하면 쓰기를 확정할 수 있다.
하지만 GitHub의 기존 구현에서는 Quorum에 필요한 다수의 응답을 받았더라도 다른 정상 복제본의 응답을 추가로 기다리는 구조가 존재했다.
따라서 가장 느리게 응답하는 정상 복제본의 지연이 전체 쓰기 처리 시간에 영향을 줄 수 있었다.
이 문제가 특히 심각했던 이유는 Repository 전체에 하나의 쓰기 Lock이 존재했기 때문이다.
Repository 단위 Lock의 한계
기존 구조에서는 하나의 Repository에서 Branch를 변경할 때 Repository 전체의 쓰기 Lock을 사용할 수 있었다.
예를 들어 사용자 A가 feature/login Branch에 Push한다고 가정해 보자.
동시에 사용자 B는 feature/payment Branch에 Push한다.
두 Branch는 서로 다른 변경 사항을 가질 수 있다.
하지만 Repository 전체에 하나의 Lock을 사용하는 구조에서는 두 Push가 서로 기다려야 할 수 있다.
첫 번째 Push가 완료될 때까지 두 번째 Push를 처리하지 못하는 것이다.
문제는 두 작업이 반드시 서로 충돌하는 것은 아니라는 점이다.
feature/login과 feature/payment는 독립적인 Branch다.
따라서 해당 Branch의 참조값을 변경하는 작업을 모두 순차적으로 처리할 필요는 없다.
GitHub는 이 점에 주목했다.
Per-reference Serialization
새로운 구조에서는 Repository 전체가 아니라 개별 Git Reference 단위로 쓰기를 직렬화한다.
Git Reference는 Branch나 Tag 등이 가리키는 Commit을 나타내는 참조값이다.
예를 들어 다음 두 Reference는 서로 다르다.
refs/heads/feature/loginrefs/heads/feature/payment
새로운 구조에서는 같은 Reference를 변경하는 요청만 서로 기다리도록 한다.
반면 서로 다른 Reference를 변경하는 작업은 병렬로 처리할 수 있다.
예를 들어 세 개의 에이전트가 서로 다른 Branch에 Push한다면 세 요청이 동시에 진행될 수 있다.
이때 각 요청은 여전히 복제본 사이의 네트워크 지연을 경험한다.
하지만 하나의 느린 요청이 Repository 전체의 다른 쓰기 작업을 막지 않는다.
이 구조는 단순히 Lock의 범위를 줄이는 최적화가 아니다.
어떤 데이터가 실제로 일관성을 공유해야 하는지 분석해 필요한 범위에서만 동기화하는 설계다.
실제 처리량 개선
GitHub는 내부 테스트에서 새로운 구조를 검증했다.
실제 서비스와 비슷한 트래픽을 사용한 테스트에서 서로 다른 Reference에 대한 쓰기 처리량이 기존보다 3~4배 증가했다고 설명한다.
또한 쓰기 요청의 지연 시간도 감소했다.
하지만 이 수치는 GitHub의 자체 내부 테스트 결과다.
모든 Repository에서 동일한 비율로 처리량이 증가한다는 의미는 아니다.
예를 들어 모든 쓰기 요청이 하나의 main Branch에 집중된다면 같은 Reference에 대한 동기화가 여전히 필요하다.
따라서 Per-reference Serialization의 효과는 여러 Branch에 쓰기 요청이 얼마나 분산되는지에 영향을 받는다.
AI 코딩 에이전트가 서로 다른 Branch에서 병렬로 작업하는 환경에서는 이러한 구조가 유리할 수 있다.
읽기와 쓰기의 비율
GitHub는 일반적인 Repository에서 읽기 요청이 쓰기 요청보다 훨씬 많다고 설명한다.
공식 글에서 제시한 비율은 다음과 같다.
쓰기 요청 1개당 읽기 요청 약 100~1,000개
이러한 구조에서는 읽기와 쓰기를 같은 서버에서 처리하는 것이 항상 효율적이지 않을 수 있다.
쓰기 작업은 복제본 사이의 일관성을 유지해야 한다.
반면 읽기 작업은 가능한 한 빠르게 데이터를 반환하는 것이 중요하다.
기존 구조에서는 읽기와 쓰기가 동일한 복제본의 CPU와 메모리를 공유했다.
예를 들어 Repository 데이터를 캐싱하기 위해 메모리를 더 사용하고 싶어도 데이터 정리 작업에 필요한 메모리를 함께 고려해야 했다.
GitHub는 이러한 문제를 해결하기 위해 읽기 처리와 쓰기 처리를 분리하는 구조를 개발하고 있다.
Azure Blob Storage와 Virtual Replica
새로운 아키텍처에서는 Repository 데이터를 Azure Blob Storage에 저장하는 방식을 활용한다.
Blob Storage는 객체 단위로 데이터를 저장하는 서비스다.
GitHub는 이를 단순한 백업 저장소로만 사용하지 않는다.
쓰기 작업의 내구성을 보장하는 구조에 연결하려고 한다.
이를 위해 Virtual Replica라는 개념을 도입했다.
Virtual Replica는 일반적인 읽기 복제본과 다르다.
데이터를 저장하고 읽기 요청을 처리하는 서버가 아니라 쓰기 데이터를 Object Storage에 전달하는 역할을 한다.
GitHub는 Virtual Replica를 Quorum의 구성원으로 포함한다.
따라서 해당 복제본이 Object Storage에 데이터를 안전하게 저장했다는 사실이 확인돼야 쓰기 작업을 확정할 수 있다.
이러한 방식은 데이터를 안정적으로 저장하면서도 읽기 요청을 처리하는 실행 환경을 별도로 확장할 수 있도록 한다.
다만 Object Storage에 데이터를 쓰는 과정에서도 네트워크 지연은 발생한다.
GitHub는 여러 Reference에 대한 쓰기를 병렬로 처리하고 서로 독립적인 변경을 묶어 전달해 해당 비용을 완화하려 한다.
Ephemeral Read Replica
GitHub는 향후 읽기 복제본이 영속적인 로컬 디스크 상태를 반드시 유지하지 않아도 되는 구조를 목표로 한다.
이를 Ephemeral Read Replica라고 할 수 있다.
Ephemeral은 일시적인 실행 환경을 의미한다.
예를 들어 읽기 요청이 급격하게 증가하면 새로운 읽기 복제본을 생성한다.
해당 복제본은 Object Storage에서 필요한 데이터를 가져와 요청을 처리한다.
트래픽이 감소하면 해당 복제본을 제거할 수 있다.
이러한 구조는 읽기 처리량을 쓰기 처리량과 독립적으로 확장하는 데 도움이 된다.
하지만 실제로는 데이터의 최신성을 유지하는 문제가 발생한다.
예를 들어 Object Storage에서 데이터를 가져온 읽기 복제본이 오래된 상태를 가지고 있다면 사용자가 최신 Commit을 확인하지 못할 수 있다.
따라서 새로운 읽기 구조에서는 변경 사항을 어떻게 반영하고 최신 상태를 보장할지 별도로 설계해야 한다.
GitHub는 이러한 구조를 점진적으로 개발하고 있다.
이번 글에서 설명한 모든 구성요소가 이미 전체 GitHub 트래픽에 완전히 적용됐다고 해석해서는 안 된다.
AI 에이전트가 Git 인프라에 미치는 영향
이번 글은 AI 코딩 에이전트가 개발자의 생산성뿐 아니라 개발 플랫폼이 처리해야 하는 요청의 형태 자체를 변경할 수 있다는 점을 보여준다.
기존에는 개발자가 하나의 Branch에서 순차적으로 작업하는 경우가 많았다.
반면 여러 AI 에이전트가 같은 Repository의 서로 다른 Branch에서 병렬로 작업하면 Git Push와 Fetch 요청이 증가한다.
이러한 변화에서는 단순히 서버를 더 추가하는 방식만으로 확장성 문제를 해결하기 어려울 수 있다.
GitHub는 다음 두 원칙을 제시한다.
- 불필요한 동기화를 줄인다.
- 저장소와 실제 실행 자원을 분리한다.
Per-reference Serialization은 첫 번째 원칙에 해당한다.
Object Storage와 Ephemeral Replica는 두 번째 원칙에 해당한다.
이번 글은 분산 시스템에서 일관성을 보장해야 하는 최소 범위를 어떻게 결정하고, 읽기와 쓰기의 요구사항을 분리해 처리량을 높일 수 있는지를 실제 Git 인프라 사례로 설명한다.
Making every harness durable
- 저자: Pranay Prakash, Eric Dodds / Vercel Engineering
- 게시일: 2026-10-19
- 원문: https://vercel.com/blog/making-every-harness-durable
Vercel이 AI 코딩 에이전트의 실행 상태를 지속적으로 저장해 프로세스가 종료되거나 네트워크 연결이 끊어져도 작업을 이어서 수행할 수 있도록 만든 durable-harness의 구조를 설명한 기술 글이다.
AI 코딩 에이전트는 여러 단계에 걸쳐 작업을 수행한다.
예를 들어 사용자가 새로운 결제 페이지를 구현해 달라고 요청했다고 가정해 보자.
에이전트는 다음 작업을 수행할 수 있다.
- 기존 프로젝트 구조 분석
- 관련 소스코드 읽기
- 새로운 페이지 구현
- 테스트 실행
- 오류 수정
- Git Commit 생성
- PR 생성
- CI 결과 확인
이러한 작업에는 몇 분에서 몇 시간까지 걸릴 수 있다.
문제는 작업 도중 실행 환경이 종료될 수 있다는 것이다.
예를 들어 서버리스 함수의 최대 실행 시간에 도달하거나 네트워크 연결이 끊어질 수 있다.
이때 실행 상태를 메모리에만 저장했다면 에이전트는 어떤 작업을 완료했는지 알지 못할 수 있다.
그 결과 이미 수행한 모델 호출과 도구 호출을 다시 실행할 수 있다.
이는 비용 증가뿐 아니라 실제 서비스의 데이터 변경 문제로 이어질 수 있다.
기존 Agent Harness의 구조
Agent Harness는 AI 모델을 이용해 실제 작업을 수행하는 실행 프로그램이다.
대표적으로 다음 도구가 있다.
- Claude Code
- Codex
- OpenCode
- Pi
이러한 도구는 모델과 통신하고 파일을 읽고 수정하며 외부 명령을 실행한다.
일반적으로 개발자의 컴퓨터에서는 이러한 작업이 비교적 단순하게 이루어진다.
에이전트는 로컬 파일시스템에 접근할 수 있다.
세션 기록도 로컬 디스크에 저장할 수 있다.
프로세스가 종료되더라도 필요한 파일이 남아 있다면 이후 작업을 이어서 수행할 수 있다.
하지만 클라우드에서는 다르다.
서버리스 함수는 종료될 수 있다.
다른 실행 인스턴스에서 작업이 다시 시작될 수 있다.
따라서 이전 인스턴스의 메모리에 저장된 상태를 그대로 사용할 수 없다.
Vercel의 durable-harness는 이러한 환경에서 에이전트의 실행 기록을 외부에 저장하고 새로운 실행 인스턴스에서 복구하는 구조를 제공한다.
Durable Log의 역할
이번 구현의 핵심은 Durable Log다.
Durable Log는 프로그램이 수행한 작업의 기록을 영속적으로 저장하는 방식이다.
예를 들어 에이전트가 특정 모델에 요청을 보내려고 한다고 가정해 보자.
먼저 해당 요청을 실행할 예정이라는 정보를 기록한다.
이를 Intent라고 한다.
모델 호출이 완료되면 결과를 기록한다.
이를 Outcome이라고 한다.
도구 호출에서도 같은 방식이 사용된다.
따라서 작업 기록에는 다음 정보가 포함될 수 있다.
- 실행을 시작하려던 작업
- 이미 완료된 작업
- 완료된 작업의 결과
- 실행 중에 중단된 작업
- 아직 시작하지 않은 작업
프로세스가 종료된 이후 새로운 인스턴스가 실행되면 이 기록을 읽는다.
그러면 어떤 작업을 다시 수행해야 하고 어떤 작업은 이미 완료됐는지 판단할 수 있다.
Queue 기반 실행 복구
Vercel은 Durable Log와 함께 Queue도 사용한다.
Queue는 나중에 처리할 작업을 저장하는 구조다.
에이전트가 하나의 작업을 수행하는 동안 해당 실행은 Queue 메시지와 연결된다.
프로세스가 비정상적으로 종료되면 Queue가 새로운 실행 인스턴스에 작업을 다시 전달할 수 있다.
새로운 인스턴스는 Durable Log를 읽는다.
이후 완료된 작업은 재사용하고 중단된 부분부터 실행을 이어간다.
이 구조는 단순한 자동 재시도와 다르다.
일반적인 재시도는 실패한 요청을 처음부터 다시 수행할 수 있다.
반면 Durable Harness는 이미 완료된 단계의 결과를 기록하고 해당 부분을 반복하지 않도록 한다.
모델 호출의 중복 실행 방지
AI 모델 호출에는 비용이 발생한다.
예를 들어 에이전트가 긴 코드베이스를 분석하기 위해 모델을 호출했다고 가정해 보자.
모델이 정상적으로 응답했고 결과도 저장됐다.
그런데 바로 다음 단계에서 프로세스가 종료됐다.
이때 실행 상태를 기억하지 못하는 시스템은 동일한 모델 요청을 다시 보낼 수 있다.
그러면 같은 작업에 대해 추가적인 토큰 비용이 발생한다.
durable-harness는 완료된 모델 호출의 결과를 로그에서 재사용한다.
따라서 이미 완료되고 결과가 저장된 모델 호출을 다시 실행하지 않는다.
다만 네트워크 연결이 끊어진 정확한 시점에 따라 문제가 복잡해질 수 있다.
예를 들어 모델 제공업체에서는 요청을 처리했지만 애플리케이션이 결과를 로그에 저장하기 전에 중단됐다면 해당 요청이 실제로 완료됐는지 확인하기 어려울 수 있다.
따라서 영속적인 실행 기록이 존재한다고 해서 모든 외부 API 호출에서 Exactly-once 실행이 자동으로 보장되는 것은 아니다.
Tool Replay 정책
외부 도구는 모델 호출보다 더 복잡하다.
예를 들어 파일을 읽는 도구는 여러 번 실행해도 큰 문제가 발생하지 않을 수 있다.
하지만 결제 취소나 환불을 실행하는 도구는 다르다.
같은 환불 요청을 두 번 실행하면 실제로 두 번의 환불이 발생할 수 있다.
따라서 도구마다 재실행 정책을 구분해야 한다.
Vercel은 다음 두 가지 정책을 설명한다.
| 정책 | 의미 | 대표적인 작업 |
|---|---|---|
replay: 'safe' | 중단된 경우 다시 실행할 수 있음 | 파일 읽기, CI 상태 조회 |
replay: 'never' | 자동으로 다시 실행하지 않음 | 환불, PR 댓글 작성 |
safe는 재실행해도 문제가 없는 작업에 사용한다.
never는 중복 실행을 피해야 하는 작업에 사용한다.
예를 들어 에이전트가 PR에 댓글을 작성하는 도구를 실행했다고 가정해 보자.
댓글 작성 API가 성공했지만 실행 결과를 저장하기 전에 프로세스가 종료될 수 있다.
이때 무조건 다시 실행하면 같은 댓글이 두 번 등록될 수 있다.
never로 설정된 도구는 이러한 상황에서 자동으로 다시 실행되지 않는다.
대신 모델에 해당 작업이 이미 수행됐을 가능성이 있다는 정보를 전달한다.
모델은 필요한 경우 실제 GitHub PR에 댓글이 존재하는지 확인한 뒤 다음 작업을 결정할 수 있다.
이는 외부 시스템에 영향을 주는 작업과 단순 조회 작업의 재시도 정책을 분리한 설계다.
기존 Harness의 세션 기록 복원
Claude Code와 Codex, OpenCode 등은 자체적인 세션 기록 형식을 사용한다.
예를 들어 모델의 대화 기록과 도구 실행 결과를 파일에 저장할 수 있다.
durable-harness는 이러한 세션 기록을 Durable Log에 복제한다.
이후 새로운 실행 인스턴스에서 해당 기록을 다시 생성할 수 있다.
따라서 에이전트의 원래 Harness 구현을 완전히 변경하지 않고도 영속성을 추가할 수 있다.
다만 각 Harness가 사용하는 내부 기록 형식에 따라 Adapter가 필요할 수 있다.
또한 작업 공간의 모든 파일을 Durable Log에 그대로 저장하는 것이 항상 효율적인 것은 아니다.
Workspace와 Sandbox의 구분
AI 코딩 에이전트는 파일을 많이 생성할 수 있다.
작은 설정 파일이나 대화 기록은 Durable Log에 저장하기 적합할 수 있다.
하지만 대규모 프로젝트의 전체 파일과 빌드 결과물을 모두 같은 로그에 저장하는 것은 비효율적일 수 있다.
Vercel은 이러한 문제를 해결하기 위해 실제 작업 공간과 실행 기록을 분리한다.
예를 들어 다음과 같은 구성이다.
- Durable Log: 모델과 도구의 실행 기록
- Sandbox: 실제 코드 실행 환경
- Drive: 지속적으로 보관해야 하는 파일
- Queue: 중단된 작업의 재실행
- Streamer: 사용자에게 실행 상태 전달
이렇게 하면 작업 실행과 파일 저장, 실행 기록을 서로 다른 시스템이 담당한다.
예를 들어 에이전트가 새로운 프로젝트를 생성했다고 가정해 보자.
소스코드는 Drive에 보관할 수 있다.
빌드와 테스트는 Sandbox에서 실행한다.
어떤 명령을 실행했고 결과가 무엇이었는지는 Durable Log에 저장한다.
사용자는 Streamer를 통해 현재 진행 상태를 확인한다.
이러한 구조는 작업이 장시간 실행되는 동안에도 서버리스 실행 환경의 수명주기와 작업 상태를 분리할 수 있도록 한다.
World 인터페이스
Vercel은 이러한 구성요소를 하나의 인터페이스로 묶는다.
이 인터페이스의 이름은 World다.
World는 다음 세 가지 역할을 결합한다.
- Storage
- Queue
- Streamer
durable-harness는 World를 통해 상태를 저장하고 실행을 이어간다.
중요한 점은 World 구현을 변경하면 같은 Harness 코드를 다른 인프라에서 실행할 수 있다는 것이다.
Vercel은 다음 환경을 지원한다고 설명한다.
- Vercel World
- 로컬 파일 기반 World
- PostgreSQL
- MongoDB
- Redis
- Turso
예를 들어 로컬 개발에서는 파일 기반 저장소를 사용할 수 있다.
운영 환경에서는 PostgreSQL을 사용할 수 있다.
이러한 구조에서는 애플리케이션의 모델 호출 로직과 실제 상태 저장소를 분리할 수 있다.
다만 저장소가 다르면 실행 성능과 일관성, 장애 복구 특성이 달라질 수 있다.
따라서 모든 World 구현이 완전히 동일한 운영 특성을 제공한다고 가정해서는 안 된다.
서버리스 환경의 장시간 작업
일반적인 서버리스 함수는 요청을 처리한 뒤 종료되는 방식으로 사용된다.
하지만 AI 코딩 에이전트는 장시간 실행될 수 있다.
예를 들어 PR을 생성한 뒤 CI 테스트가 완료될 때까지 기다리는 작업이다.
이때 하나의 Function을 계속 실행 상태로 유지할 필요는 없다.
Durable Harness에서는 중간 실행 상태를 저장하고, 나중에 다시 작업을 이어서 처리할 수 있다.
예를 들어 CI 테스트가 완료되지 않았다면 현재 상태를 기록한다.
이후 새로운 실행에서 CI 결과를 확인할 수 있다.
이러한 구조는 장시간 작업을 하나의 프로세스에 묶어 두지 않도록 한다.
그러나 장시간 작업의 모든 문제가 자동으로 해결되는 것은 아니다.
외부 API의 인증 만료, 파일 충돌, 여러 에이전트의 동시 수정, 중복 이벤트 처리 등은 여전히 별도의 설계가 필요하다.
운영상의 한계
durable-harness는 실행 중단과 상태 복구를 돕지만 모든 외부 작업의 정확성을 자동으로 보장하는 것은 아니다.
특히 외부 서비스에서 이미 수행된 작업을 확인할 수 없는 경우에는 중복 실행과 누락 사이에서 선택해야 할 수 있다.
예를 들어 환불 API를 호출했지만 응답을 받지 못한 상황이다.
이때 요청을 다시 보내면 중복 환불 위험이 존재한다.
반대로 요청을 보내지 않으면 실제 환불이 처리되지 않았을 수 있다.
따라서 중요한 외부 작업에는 Idempotency Key나 상태 조회 API를 함께 사용하는 구조가 필요할 수 있다.
또한 Durable Log에 민감한 데이터가 저장될 수 있으므로 접근 권한과 데이터 보존 정책도 고려해야 한다.
이번 글은 AI 코딩 에이전트의 안정성을 모델의 성능과 별개로 실행 상태 관리, 작업 재시도, 외부 도구의 중복 실행 방지 관점에서 설계해야 한다는 점을 보여준다.
특히 서버리스 환경에서 장시간 실행되는 에이전트를 운영하려는 개발자에게 참고할 만한 아키텍처 사례다.