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

기준 시점: 2026-10-20 06:00 KST
조사 범위: 2026-10-19 06:00 ~ 2026-10-20 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
React·Next.js·Vercel, Vue·Nuxt, Angular, Svelte·SvelteKit, Astro, Remix의 공식 발표와 릴리스 채널을 확인했지만, 이번 24시간에 해당하는 주요 프레임워크 기능이나 렌더링 API 변경은 확인하지 못했다.
Vite, TypeScript, JavaScript 개발 도구와 관련해서도 기존 개발 방식에 직접적인 변화를 주는 주요 신규 릴리스는 확인하지 못했다.
Astro 7.4와 Vercel의 최근 플랫폼 업데이트처럼 이전 브리핑에서 이미 다룬 발표는 반복하지 않았다.
다만 이번 추천 글에서는 TanStack AI의 구조화된 응답 처리와 Next.js에서 WebGPU를 사용하는 실제 구현 사례를 다룬다. 두 글 모두 새로운 프레임워크 출시 공지는 아니지만 React 애플리케이션의 데이터 처리와 렌더링 구조를 살펴볼 수 있는 기술 자료다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome Developers·Chromium, Mozilla·Firefox, WebKit·Safari의 공식 발표를 확인했지만 이번 조사 기간에 공개된 주요 웹 API의 정식 출시나 CSS 및 JavaScript 표준의 중요한 변경은 확인하지 못했다.
개발 버전의 개별 변경과 실험적인 기능 구현은 안정적인 웹 플랫폼 기능의 정식 출시와 구분했다.
WebGPU와 관련된 기술 글은 확인했지만, 새로운 브라우저 표준 발표가 아니라 기존 WebGPU를 활용하는 라이브러리의 구현 사례에 해당하므로 마지막 추천 글 섹션에서 소개한다.
3️⃣ 백엔드·인프라
Cloudflare Workers AI, 구조화된 데이터 추출 모델 Clairic Preview 공개 — 자유로운 텍스트 생성 대신 타입이 지정된 값 반환
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 안정화 단계: Preview
- 모델:
@cf/cloudflare/clairic - 적용 대상: Cloudflare Workers AI, AI Gateway
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-19-clairic-workers-ai/
Cloudflare가 문장이나 문서에서 특정 값을 추출하고 계산해 정해진 타입으로 반환하는 모델인 Clairic을 공개했다.
일반적인 대규모 언어 모델(LLM)은 사용자의 입력을 받아 다음에 나올 토큰을 순차적으로 생성한다.
예를 들어 사용자가 청구서의 총금액을 묻는다면 모델은 청구서를 읽고 자연어 답변을 생성할 수 있다.
하지만 실제 웹서비스에서는 자연어 답변보다 구조화된 데이터가 필요한 경우가 많다.
예를 들어 주문 관리 시스템은 다음과 같은 정보를 필요로 할 수 있다.
- 주문번호
- 주문일
- 결제 금액
- 배송 예정일
- 고객 이름
- 주문 상품 목록
이러한 값을 서버에서 사용하려면 정해진 데이터 구조로 변환해야 한다.
기존에는 LLM에 JSON Schema를 전달하고 해당 구조에 맞는 응답을 생성하도록 요청하는 방식이 사용됐다.
하지만 모델이 자유로운 텍스트를 생성하는 방식에서는 응답 형식을 검증하고 잘못된 결과를 다시 처리하는 과정이 필요할 수 있다.
Clairic은 이 문제에 다른 방식으로 접근한다.
자유로운 텍스트를 생성하는 대신 처음부터 지정된 타입의 값을 예측하도록 설계됐다.
Clairic의 실행 방식
Cloudflare는 Clairic을 Extraction Model이라고 부른다.
Extraction Model은 입력에서 필요한 정보를 추출하는 데 초점을 맞춘 모델이다.
Clairic은 일반적인 텍스트 생성 모델처럼 응답을 토큰 단위로 이어서 생성하지 않는다.
입력 내용을 한 번 읽고 지정된 필드의 값을 타입에 맞춰 반환한다.
요청에는 크게 두 가지 정보가 포함된다.
첫 번째는 state다.
state는 모델이 읽고 분석할 입력 데이터다.
예를 들어 청구서의 내용이나 고객 메시지, 주문 정보를 전달할 수 있다.
두 번째는 fields다.
fields에는 추출할 값의 이름과 타입을 정의한다.
예를 들어 다음과 같은 정보를 요구할 수 있다.
| 필드 | 타입 |
|---|---|
| 청구서 번호 | text |
| 발행일 | date |
| 결제 예정일 | date |
| 총금액 | number |
| 담당자 목록 | list |
Clairic은 입력에서 각 필드에 해당하는 값을 찾는다.
필요한 경우 간단한 계산도 수행한다.
예를 들어 청구서에 발행일이 9월 2일이고 결제 조건이 30일이라고 적혀 있다면 결제 예정일을 계산하도록 요청할 수 있다.
하지만 입력에서 값을 결정할 수 없다면 무조건 답을 만들어내는 대신 null을 반환할 수 있다.
이러한 구조는 값이 없는 상황과 값이 존재하는 상황을 데이터 형태로 구분할 수 있도록 한다.
지원하는 데이터 타입
현재 Clairic은 다음 타입을 지원한다.
| 타입 | 반환하는 데이터 |
|---|---|
text | 입력에서 찾은 정확한 문자열과 문자 위치 |
number | 숫자 또는 입력을 바탕으로 계산한 값 |
date | ISO 형식의 날짜 |
time | 유효한 시간 |
datetime | 날짜와 시간 |
duration | 지정된 단위의 시간 길이 |
list | 입력에서 일치하는 항목들의 목록 |
estimate | 가능한 값의 분포와 추정 구간 |
forecast | 시계열의 미래 값 예측 |
text 타입은 단순히 문자열만 반환하지 않는다.
입력에서 해당 문자열이 존재하는 위치도 함께 제공한다.
예를 들어 주문번호를 추출했다면 원본 문서의 어느 부분에서 해당 값을 가져왔는지 확인할 수 있다.
이는 문서 분석 시스템에서 유용하다.
사용자는 모델이 반환한 값뿐 아니라 해당 값의 원본 위치를 이용해 결과를 검토할 수 있기 때문이다.
number 타입은 정수 여부와 소수점 자릿수 등을 지정할 수 있다.
estimate는 특정 값의 단일한 추정치뿐 아니라 가능한 값의 범위도 제공한다.
forecast는 시계열 데이터를 입력받아 최대 64개 미래 단계의 값을 예측할 수 있다.
다만 이러한 예측 기능의 정확도는 입력 데이터와 문제의 특성에 따라 달라진다.
Confidence와 Null Probability
Clairic은 반환한 값의 신뢰도를 판단하는 데 사용할 수 있는 정보도 제공한다.
대표적인 값은 다음과 같다.
confidencep_null
confidence는 반환한 값에 대한 모델의 신뢰도를 나타낸다.
p_null은 입력으로부터 해당 값을 결정할 수 없을 확률을 나타낸다.
예를 들어 고객 메시지에서 배송 예정일을 추출한다고 가정해 보자.
메시지에 정확한 날짜가 적혀 있다면 모델은 해당 날짜를 높은 신뢰도로 반환할 수 있다.
반면 메시지에 배송 예정일에 관한 정보가 없다면 null일 가능성이 높다고 판단할 수 있다.
개발자는 이러한 값을 이용해 자동 처리할 데이터를 구분할 수 있다.
예를 들어 신뢰도가 높은 주문번호는 자동으로 저장하고, 불확실성이 높은 배송 예정일은 사용자에게 다시 확인하도록 구성할 수 있다.
하지만 Confidence가 높다는 사실이 실제 정답이라는 보장은 아니다.
모델은 잘못된 정보를 높은 신뢰도로 반환할 수 있다.
따라서 중요한 데이터에서는 원본 문서와 실제 업무 규칙을 이용한 추가 검증이 필요하다.
Estimate와 Forecast의 불확실성 표현
일반적인 예측 모델은 하나의 숫자만 반환하는 경우가 많다.
예를 들어 수리 비용을 50달러로 예상했다고 가정해 보자.
하지만 실제 비용은 부품과 작업 시간에 따라 달라질 수 있다.
Clairic은 estimate에서 중앙값과 추정 구간을 제공한다.
기본적으로 다음 백분위수를 반환한다.
- P10
- P50
- P90
P50은 추정 분포의 중앙값이다.
P10과 P90은 가운데 약 80%에 해당하는 구간의 경계를 나타낸다.
예를 들어 수리 비용의 중앙값이 50달러이고 P10과 P90이 각각 30달러와 90달러라면 모델은 상당한 불확실성이 존재한다고 표현할 수 있다.
이러한 구간은 추정 결과를 검토하는 데 도움이 된다.
하지만 모델이 계산한 구간이 실제 데이터에서 항상 정확한 비율로 정답을 포함한다는 보장은 없다.
Cloudflare는 모델이 익숙하지 않은 데이터에서는 추정 구간이 실제보다 좁아질 수 있다고 명시했다.
따라서 통계적으로 엄격한 확률 보장이 필요한 시스템에서는 별도의 검증과 보정 과정이 필요하다.
이미지 입력 지원
Clairic은 텍스트뿐 아니라 이미지도 입력으로 받을 수 있다.
현재 요청에는 최대 4개의 이미지를 포함할 수 있다.
예를 들어 청구서 사진이나 배송 문서 이미지를 전달하고 필요한 필드를 추출하도록 구성할 수 있다.
다만 이미지 입력을 지원한다는 사실이 모든 종류의 문서를 완벽하게 인식한다는 의미는 아니다.
이미지의 해상도, 글자의 형태, 표 구조와 내용의 복잡도에 따라 결과가 달라질 수 있다.
특히 이미지에서 읽은 숫자와 날짜를 실제 업무 데이터로 저장할 때는 추가적인 검증을 고려해야 한다.
Preview 단계의 제한사항
Cloudflare는 현재 모델이 초기 Preview 단계라고 명시했다.
특히 계산과 예측에서 여러 제한이 존재한다.
첫 번째는 복잡한 산술 연산이다.
Clairic은 한두 단계의 간단한 계산에는 비교적 안정적인 결과를 제공하지만, 여러 계산을 연쇄적으로 수행하거나 큰 표의 많은 행을 집계하는 작업에서는 정확도가 떨어질 수 있다.
예를 들어 청구서의 항목이 수백 개라면 모든 금액을 모델에서 직접 더하도록 하는 것보다 항목별 금액을 추출한 뒤 애플리케이션 코드에서 합계를 계산하는 편이 적합할 수 있다.
두 번째는 Forecast다.
현재 Forecast는 시계열의 최근 패턴을 바탕으로 다음 값을 예측하는 수준이다.
예측 시점이 멀어질수록 평균값에 가까워지는 경향이 있다.
또한 입력에 존재하지 않는 공휴일이나 특별 이벤트를 자동으로 예측하지는 못한다.
따라서 자체 데이터로 학습한 전문적인 시계열 예측 모델을 대체한다고 볼 수 없다.
세 번째는 입력 문맥이다.
입력에 연도가 없는 날짜가 있다면 주변 문맥을 이용해 연도를 결정한다.
현재 날짜가 필요한 작업에서는 reference_date를 명시하거나 입력에 기준일을 포함해야 한다.
그렇지 않으면 개발자가 의도한 날짜와 다른 결과가 생성될 수 있다.
Workers AI와 API 연결
Clairic은 Workers AI를 통해 사용할 수 있다.
Workers에서는 다음 모델 식별자를 지정한다.
@cf/cloudflare/clairic
호출은 기존 Workers AI의 env.AI.run() 인터페이스를 이용한다.
REST API의 /ai/run 엔드포인트로도 호출할 수 있다.
Cloudflare AI Gateway와 연결하는 방식도 지원한다.
따라서 완전히 새로운 API 통신 구조를 구현하지 않고 기존 Workers AI 애플리케이션에 연결할 수 있다.
요금과 공개 범위
현재 가격은 다음과 같다.
입력 토큰 100만 개당 0.10달러
Clairic은 일반적인 텍스트 생성 모델처럼 출력 토큰을 순차적으로 생성하지 않기 때문에 출력 토큰 요금이 별도로 부과되지 않는다.
다만 실제 사용 비용을 비교할 때는 요청 처리량, 입력 데이터 크기, 필요한 추가 검증 작업 등을 함께 고려해야 한다.
Cloudflare는 앞으로 모델의 가중치를 공개할 계획이라고 밝혔다.
하지만 현재 공개된 것은 Workers AI의 Preview 서비스다.
모델 가중치가 이미 오픈소스로 공개된 것은 아니다.
이번 업데이트는 일반적인 챗봇 모델을 추가한 것보다 문서 추출, 업무 데이터 구조화, 간단한 계산과 불확실성 표현에 특화된 새로운 모델 인터페이스를 제공한 변화에 가깝다.
Cloudflare API MCP 서버, OAuth Step-up Authorization 지원 — 에이전트가 필요한 순간에만 추가 권한 요청
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare API MCP Server
- 지원 범위: Step-up Authorization을 지원하는 MCP 클라이언트
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-19-cloudflare-mcp-step-up/
Cloudflare가 API MCP 서버에 Step-up Authorization을 추가했다.
MCP(Model Context Protocol)는 AI 애플리케이션이 외부 도구와 데이터에 접근할 수 있도록 하는 프로토콜이다.
예를 들어 코딩 에이전트가 Cloudflare의 Worker 목록을 조회하거나 DNS 설정을 확인하고, 특정 설정을 변경하는 작업을 수행할 수 있다.
문제는 이러한 작업마다 필요한 권한이 다르다는 점이다.
Worker 목록을 읽는 작업에는 조회 권한만 필요할 수 있다.
반면 실제 설정을 변경하거나 리소스를 삭제하는 작업에는 더 높은 권한이 필요하다.
기존의 외부 서비스 연결에서는 에이전트가 앞으로 사용할 수 있는 권한을 연결 시점에 미리 요청하는 방식이 사용될 수 있었다.
하지만 사용자가 모든 권한을 처음부터 승인하면 에이전트가 실제로 필요하지 않은 권한까지 보유하게 된다.
이번 기능은 이러한 문제를 줄이기 위해 최소 권한으로 시작하고, 특정 작업을 실행할 때만 추가 권한을 승인받는 구조를 제공한다.
최소 권한으로 연결
Cloudflare API MCP 서버의 주소는 다음과 같다.
https://mcp.cloudflare.com/mcp
사용자는 MCP 클라이언트에서 이 서버에 연결할 수 있다.
이번 업데이트 이후 연결 과정의 동의 화면에서 Minimal을 선택할 수 있다.
Minimal은 처음부터 광범위한 API 접근 권한을 승인하지 않고 최소한의 Scope만 허용하는 방식이다.
Scope는 OAuth에서 클라이언트가 어떤 작업을 수행할 수 있는지 나타내는 권한 범위다.
예를 들어 읽기 전용 작업과 변경 작업에 서로 다른 Scope가 필요할 수 있다.
Minimal로 연결한 이후에는 현재 승인한 권한 안에서 작업을 수행할 수 있다.
추가 권한이 필요한 작업을 요청하면 그때 새로운 승인 과정을 시작한다.
insufficient_scope 응답
이번 기능의 핵심은 insufficient_scope 인증 Challenge다.
예를 들어 에이전트가 특정 Cloudflare 리소스를 변경하려고 한다고 가정해 보자.
하지만 현재 연결된 OAuth Token에는 해당 작업에 필요한 Scope가 포함돼 있지 않다.
이 경우 서버는 무조건 모든 권한을 요청하는 대신 어떤 Scope가 부족한지 알려주는 Challenge를 반환한다.
MCP 클라이언트는 이를 확인해 사용자에게 추가 권한 승인을 요청한다.
사용자가 승인하면 필요한 권한이 추가된다.
기존에 승인한 Scope는 유지된다.
따라서 새 권한이 필요할 때마다 처음부터 전체 OAuth 연결을 다시 구성할 필요가 없다.
기존 방식과의 차이
| 구분 | 초기 일괄 권한 승인 | Step-up Authorization |
|---|---|---|
| 최초 연결 | 여러 권한을 미리 요청할 수 있음 | 최소 권한으로 시작 |
| 추가 작업 | 이미 승인한 권한 사용 | 부족한 Scope만 추가 요청 |
| 사용자 승인 | 연결 초기에 집중 | 필요한 작업 시점에 승인 |
| 기존 권한 | 초기 승인 범위 유지 | 기존 Scope 유지하면서 확장 |
| 보안 설계 | 사전 권한 범위가 넓어질 수 있음 | 최소 권한 원칙 적용에 유리 |
이러한 구조는 특히 AI 코딩 에이전트와 관련이 있다.
일반적인 개발자는 도구를 이용해 어떤 작업을 할지 직접 결정한다.
반면 AI 에이전트는 사용자의 목표를 달성하기 위해 여러 API를 연속해서 호출할 수 있다.
이때 에이전트에게 처음부터 광범위한 관리 권한을 부여하면 예상하지 못한 작업이 실행될 위험이 커질 수 있다.
Step-up Authorization은 이러한 상황에서 추가 권한이 필요한 시점을 사용자에게 명시적으로 드러내는 수단이다.
지원 조건과 제한사항
이번 기능은 모든 MCP 클라이언트에서 자동으로 작동하지 않는다.
Step-up Authorization을 지원하는 클라이언트에서만 사용할 수 있다.
클라이언트가 insufficient_scope Challenge를 처리하지 못한다면 추가 권한 승인 흐름이 정상적으로 진행되지 않을 수 있다.
또한 추가 권한을 승인받았다는 사실이 해당 작업의 결과가 안전하다는 의미는 아니다.
예를 들어 리소스 변경 권한이 있는 에이전트가 잘못된 설정을 적용할 수 있다.
따라서 중요한 리소스의 변경이나 삭제에는 애플리케이션 차원의 별도 검증과 승인 절차가 필요할 수 있다.
이번 발표는 MCP의 전체 인증 표준을 새로 만든 것이 아니다.
Cloudflare API MCP 서버가 기존 OAuth 기반 권한 모델 안에서 점진적인 권한 확대를 지원하도록 변경한 업데이트다.
4️⃣ 개발 도구 및 보안
GitHub, 공개 저장소의 HTTPS Clone·Fetch에 일시적인 인증 요구 가능 — 비정상 트래픽 방어 정책 변경
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 적용 대상: github.com의 공개 저장소
- 영향받는 작업: 인증하지 않은 HTTPS 기반 Clone 및 Fetch
- 원문: https://github.blog/changelog/2026-10-19-some-git-over-https-may-temporarily-require-authentication/
GitHub가 비정상적인 트래픽이 발생하는 상황에서 일부 공개 저장소의 HTTPS Clone과 Fetch에 일시적으로 인증을 요구할 수 있다고 발표했다.
일반적으로 GitHub의 공개 저장소는 로그인하지 않아도 코드를 내려받을 수 있다.
예를 들어 누구나 공개 저장소의 HTTPS 주소를 이용해 Git Clone을 수행할 수 있다.
이는 오픈소스 생태계의 기본적인 사용 방식 가운데 하나다.
하지만 공개 저장소의 Git 데이터를 가져오는 요청 역시 GitHub 서버의 네트워크와 처리 자원을 사용한다.
정상적인 개발자와 CI 시스템뿐 아니라 자동화된 수집 프로그램도 같은 기능을 사용할 수 있다.
짧은 시간에 비정상적으로 많은 요청이 발생하면 GitHub 서비스의 가용성에 영향을 줄 수 있다.
이번 정책은 이러한 상황에서 일부 비정상적인 트래픽을 제한하기 위한 보호 장치다.
어떤 상황에서 인증이 필요한가
GitHub는 모든 공개 저장소에 인증을 필수로 적용하지 않는다.
비정상적인 트래픽을 나타내는 신호가 확인된 경우에만 일부 요청에 일시적으로 적용한다.
영향을 받는 작업은 다음 두 가지다.
- HTTPS 기반
git clone - HTTPS 기반
git fetch
해당 요청이 보호 정책의 영향을 받으면 인증이 필요하다는 오류 메시지와 함께 실패할 수 있다.
이 경우 사용자는 인증 후 다시 시도할 수 있다.
또는 GitHub의 일시적인 보호 조치가 해제된 이후 인증 없이 다시 시도할 수도 있다.
여기서 중요한 점은 공개 저장소가 비공개 저장소로 변경되는 것이 아니라는 사실이다.
GitHub가 특정한 트래픽 조건에서 데이터 다운로드 요청을 제한하는 것이다.
영향받지 않는 작업
GitHub는 다음 작업은 영향을 받지 않는다고 명시했다.
- 이미 인증된 HTTPS Git 작업
- SSH를 이용하는 Git 작업
- Git Push
- GitHub 웹사이트의 일반적인 사용
- 관련 없는 GitHub API 요청
따라서 이번 발표를 GitHub 전체가 로그인 없이 사용할 수 없게 됐다는 의미로 해석해서는 안 된다.
또한 HTTPS 방식의 모든 Git 요청이 항상 실패하는 것도 아니다.
비정상 트래픽에 대응하는 보호 정책이 특정 요청에 적용됐을 때만 문제가 발생할 수 있다.
CI/CD 환경에서 발생할 수 있는 문제
자동화 시스템에서는 공개 저장소를 인증 없이 내려받는 경우가 있다.
예를 들어 빌드 서버가 외부 공개 저장소의 코드를 다운로드하거나, 테스트 과정에서 Git Repository를 가져오는 상황이다.
기존에는 공개 저장소이기 때문에 별도의 인증 정보를 설정하지 않아도 정상적으로 동작했을 수 있다.
하지만 이번 정책이 적용되는 상황에서는 같은 명령이 일시적으로 실패할 가능성이 생겼다.
문제는 CI 시스템이 이러한 오류를 일반적인 Git 실패로 처리할 수 있다는 점이다.
실제로는 저장소가 삭제되거나 권한이 변경된 것이 아닌데 빌드 파이프라인이 중단될 수 있다.
따라서 자동화 시스템에서는 인증된 Git 요청을 사용하는 방식이 도움이 될 수 있다.
GitHub는 다음 방법을 안내했다.
| 실행 환경 | 권장 인증 방식 |
|---|---|
| GitHub Actions | 기본 제공 GITHUB_TOKEN |
| 다른 CI 시스템 | GitHub App Installation Access Token |
| 별도 GitHub App 사용이 어려운 환경 | Personal Access Token |
| 인증을 사용할 수 없는 작업 | 보호 조치 해제 후 재시도 |
특히 GitHub Actions에서는 별도의 장기적인 인증 정보를 직접 저장하기보다 기본 제공되는 GITHUB_TOKEN을 활용할 수 있다.
다만 Token에 필요한 권한을 최소한으로 부여하는 것이 좋다.
공개 저장소를 읽기 위해 불필요한 쓰기 권한까지 제공할 필요는 없기 때문이다.
일반적인 네트워크 오류와의 구분
기존 Git Clone 오류에서는 여러 원인이 존재할 수 있다.
예를 들어 네트워크 연결 실패, 저장소 주소 오류, 접근 권한 부족, 서버 장애 등이 있다.
이번 정책의 영향을 받는 경우에는 인증이 필요하다는 메시지가 반환된다.
따라서 오류 메시지를 확인하면 다른 Git 실패와 구분하는 데 도움이 된다.
다만 GitHub는 정상적인 사용자와 비정상적인 트래픽이 일부 신호를 공유할 수 있다고 설명했다.
따라서 실제로 문제가 없는 정상 요청도 일시적인 제한의 영향을 받을 수 있다.
이러한 경우에는 인증된 방식으로 재시도하거나 보호 정책이 해제된 이후 다시 요청할 수 있다.
적용 범위
이번 정책은 github.com의 모든 요금제에 적용될 수 있다.
하지만 사용자의 요금제에 따라 공개 저장소의 인증 여부가 일괄적으로 변경되는 것은 아니다.
트래픽 상태에 따라 제한적으로 적용되는 보호 기능이다.
이번 발표는 Git 프로토콜의 동작이나 GitHub Repository의 공개 여부를 변경하지 않는다.
공개 저장소의 코드 다운로드 경로에서도 비정상 트래픽에 대응하기 위한 인증 요구가 일시적으로 발생할 수 있도록 한 운영 정책 변경이다.
GitHub CodeQL, macOS 27·Xcode 27에서 Build Tracing 제한 — Swift 6.4 분석도 미지원
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 적용 대상: GitHub CodeQL, macOS 기반 코드 스캐닝
- 관련 환경: macOS 27, Xcode 27, Swift 6.4
- 원문: https://github.blog/changelog/2026-10-19-codeql-changes-for-macos-27-xcode-27-and-swift-6-4/
GitHub가 macOS 27과 Xcode 27에서 CodeQL의 일부 코드 분석 방식이 지원되지 않으며, Swift 6.4는 현재 CodeQL에서 분석할 수 없다고 발표했다.
CodeQL은 소스코드를 분석해 보안 취약점과 잠재적인 오류를 찾는 정적 분석 도구다.
코드를 실제로 실행하지 않고 프로그램의 구조와 데이터 흐름을 분석한다.
일반적인 정적 분석에서는 소스코드만 읽어도 일부 문제를 발견할 수 있다.
하지만 컴파일 과정에서 코드의 실제 구조가 결정되는 언어에서는 빌드 정보를 함께 확인해야 더 정확한 분석이 가능할 수 있다.
이러한 분석에 사용되는 방식 가운데 하나가 Build Tracing이다.
Build Tracing은 실제 빌드 과정에서 어떤 컴파일 명령과 설정이 사용되는지 추적하는 방식이다.
문제는 macOS와 Xcode의 내부 구조가 변경되면서 기존 CodeQL 분석 방식과의 호환성 문제가 발생했다는 점이다.
macOS와 Xcode의 변경
GitHub에 따르면 Apple은 macOS 27과 Xcode 27에서 일부 시스템 바이너리와 라이브러리에 포함돼 있던 Intel(x86_64) 코드를 제거했다.
CodeQL의 Build Tracing 기능은 이러한 구성요소에 의존하고 있었다.
따라서 기존 방식으로 빌드 과정을 추적할 수 없는 상황이 발생한다.
특히 Xcode 27이 설치된 환경에서는 다음 언어의 Traced Extraction을 사용할 수 없다.
- C/C++
- C#
- Go
- Java
- Kotlin
- Swift
Traced Extraction은 실제 빌드 과정을 추적하면서 CodeQL이 분석할 정보를 추출하는 방식이다.
macOS 27에서도 동일한 언어의 Traced Extraction이 지원되지 않을 수 있다.
다만 macOS 27과 Xcode 27의 영향 범위는 정확히 동일하지 않다.
GitHub는 Xcode 27에 대해서는 해당 방식이 사용할 수 없다고 명시했다.
반면 macOS 27에서는 사용 불가능할 수 있다고 설명했다.
따라서 실제 실패 여부는 설치된 도구와 실행 환경에 따라 달라질 수 있다.
Swift 6.4는 플랫폼과 관계없이 미지원
이번 발표에서 가장 명확한 제한은 Swift 6.4의 CodeQL 분석 미지원이다.
GitHub는 Swift 6.4가 모든 플랫폼에서 CodeQL의 지원 대상이 아니라고 밝혔다.
따라서 Xcode만 이전 버전으로 변경한다고 해서 Swift 6.4를 정상적으로 분석할 수 있는 것은 아니다.
반대로 Swift 6.4를 사용하지 않더라도 Xcode 27에서 다른 언어의 Traced Extraction이 실패할 수 있다.
이 두 제한사항은 구분해야 한다.
하나는 분석 대상 언어 버전의 문제다.
다른 하나는 분석을 수행하는 운영체제와 빌드 도구의 호환성 문제다.
GitHub Actions에서 발생할 수 있는 문제
GitHub Actions를 이용해 CodeQL을 실행하는 저장소에서는 Runner의 운영체제와 Xcode 버전이 중요하다.
예를 들어 Workflow가 최신 macOS Runner를 사용하도록 설정돼 있다고 가정해 보자.
Runner 이미지가 업데이트되면 개발자가 Workflow 파일을 변경하지 않았더라도 실제 실행 환경의 macOS 버전이 달라질 수 있다.
또한 Xcode 버전이 변경되면서 이전에 정상적으로 실행되던 CodeQL 분석이 실패할 수 있다.
GitHub는 이를 방지하기 위해 분석에 사용하는 Runner 이미지와 Xcode 버전을 명시적으로 고정하도록 안내했다.
Build Tracing이 필요한 경우 다음 환경을 사용하도록 권장한다.
- macOS 26 이하
- Xcode 26 이하
단순히 최신 버전을 사용하는 대신 CodeQL이 지원하는 버전을 명시적으로 선택하는 것이다.
Build Mode None 활용
일부 언어에서는 Build Tracing을 사용하지 않는 분석 모드를 사용할 수 있다.
GitHub는 다음 언어에 대해 build mode none을 대안으로 제시했다.
- C/C++
- C#
- Java
이 방식은 실제 빌드를 추적하지 않고 CodeQL 분석을 수행한다.
따라서 Build Tracing을 사용할 수 없는 환경에서도 분석을 계속할 수 있다.
하지만 두 방식의 분석 조건이 완전히 동일한 것은 아니다.
실제 빌드 과정에서 생성되는 파일이나 컴파일 설정이 중요한 프로젝트에서는 분석 범위에 차이가 발생할 수 있다.
따라서 build mode none을 사용한다면 기존 Traced Extraction과 동일한 분석 결과가 나오는지 확인해야 한다.
또한 GitHub가 이번 발표에서 모든 언어에 대해 이 대안을 제공한 것은 아니다.
Go, Kotlin, Swift까지 동일한 방식으로 해결할 수 있다고 가정해서는 안 된다.
Intel 기반 Mac 지원 종료 예정
GitHub는 Intel 기반 Mac에 대한 CodeQL 지원도 종료할 계획이라고 발표했다.
예정 시점은 2026년 12월 중순이다.
이는 현재 즉시 지원이 종료됐다는 의미는 아니다.
하지만 Intel 기반 Mac에서 CodeQL을 실행하는 조직은 지원 종료 일정에 맞춰 분석 환경을 변경해야 할 수 있다.
특히 자체 호스팅 Runner를 사용하는 경우 실제 하드웨어 구성을 확인할 필요가 있다.
적용 범위와 한계
이번 발표는 CodeQL의 모든 언어 분석 기능이 중단된다는 의미가 아니다.
문제의 핵심은 특정 macOS·Xcode 환경에서 Build Tracing이 제한된다는 것이다.
또한 Swift 6.4는 현재 CodeQL에서 지원되지 않는다.
JavaScript와 TypeScript를 분석하는 모든 CodeQL Workflow가 동일한 제한을 받는다고 해석해서는 안 된다.
이번 발표는 새로운 보안 취약점이나 분석 규칙을 추가한 것이 아니다.
운영체제와 컴파일 도구의 변경으로 인해 기존 정적 분석 파이프라인의 호환성이 달라졌다는 점을 알리고 대체 실행 환경을 안내한 업데이트다.
Cloudflare Cloud Workload Security Beta 공개 — 클라우드 API 요청이 실행되기 전에 정책으로 차단
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 안정화 단계: Beta
- 적용 대상: Cloudflare Cloud Workload Security
- 사용 조건: Cloudflare Zero Trust 구독. Free 요금제 사용 가능
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-19-cloud-workload-security-beta/
- 설정 문서: https://developers.cloudflare.com/cloudflare-one/cloud-workload-security/get-started/
Cloudflare가 클라우드 API 요청을 실제로 실행하기 전에 조직의 보안 정책을 검사하고 차단할 수 있는 Cloud Workload Security(CWS)를 Beta로 공개했다.
클라우드 환경에서는 개발자가 API와 CLI, Infrastructure as Code 도구를 이용해 다양한 리소스를 생성하고 변경한다.
예를 들어 다음과 같은 작업을 수행할 수 있다.
- 새로운 스토리지 버킷 생성
- 데이터베이스 설정 변경
- 네트워크 접근 범위 변경
- 클라우드 리소스의 공개 여부 수정
- 서비스에 필요한 권한 부여
문제는 이러한 작업이 잘못 실행됐을 때의 영향이 크다는 점이다.
예를 들어 원래 비공개여야 하는 스토리지 버킷을 실수로 공개하도록 설정할 수 있다.
일반적인 보안 모니터링에서는 리소스가 변경된 이후 해당 설정이 안전한지 검사하는 방식이 사용된다.
하지만 이 경우 이미 잘못된 설정이 적용된 상태일 수 있다.
Cloudflare CWS는 이 문제에 접근하기 위해 클라우드 API 요청이 실제 제공업체에 도달하기 전에 정책을 적용하는 방식을 사용한다.
Proxy 기반 실행 구조
Cloudflare CWS의 핵심은 Proxy다.
Proxy는 클라이언트와 실제 서버 사이에서 요청을 전달하는 중간 구성요소다.
CWS에서는 개발자가 사용하는 CLI나 애플리케이션의 API 요청을 Proxy를 통해 전달하도록 구성한다.
전체 흐름은 다음과 같다.
- 개발 도구가 클라우드 API 요청을 생성한다.
- 요청이 CWS Proxy로 전달된다.
- Proxy가 요청의 대상과 내용을 확인한다.
- 조직의 보안 정책을 평가한다.
- 허용된 요청만 실제 클라우드 제공업체로 전달한다.
- 정책에 위반되는 요청은 차단한다.
여기서 중요한 점은 CWS Proxy가 고객의 실행 환경 안에서 동작한다는 것이다.
Cloudflare가 모든 요청을 자동으로 가로채는 구조가 아니다.
조직이 Proxy를 실행하고 클라이언트 요청이 해당 Proxy를 통과하도록 구성해야 한다.
따라서 특정 애플리케이션이나 CLI가 Proxy를 우회하도록 설정돼 있다면 해당 요청에는 CWS 정책이 적용되지 않을 수 있다.
Rego를 이용한 정책 정의
CWS는 요청을 검사하기 위해 두 가지 방식을 조합한다.
첫 번째는 Request Filter다.
Request Filter는 어떤 요청에 정책을 적용할지 결정한다.
예를 들어 특정 클라우드 서비스의 특정 API 작업에만 정책을 적용할 수 있다.
두 번째는 Rego 정책이다.
Rego는 Open Policy Agent(OPA)에서 사용하는 정책 언어다.
OPA는 애플리케이션이나 인프라의 요청이 특정 정책을 만족하는지 판단하는 데 사용된다.
Rego에서는 입력 데이터에 대해 참 또는 거짓으로 평가되는 조건을 정의할 수 있다.
예를 들어 스토리지 버킷의 공개 여부를 변경하는 요청에 대해 다음 정책을 구성할 수 있다.
- 공개 접근을 비활성화하는 요청은 허용한다.
- 공개 접근을 활성화하는 요청은 차단한다.
Cloudflare의 공식 예제는 R2 버킷을 공개 상태로 변경하는 요청을 차단하는 정책을 구현한다.
이 과정에서 API 요청을 식별한 뒤 요청 본문의 enabled 값이 false인지 확인한다.
조건을 만족하면 요청을 허용한다.
조건을 만족하지 않으면 차단한다.
이렇게 하면 개발자가 실수로 버킷을 공개하는 명령을 실행하더라도 실제 API가 변경을 처리하기 전에 요청을 차단할 수 있다.
Policy와 Policy Set
CWS는 개별 정책뿐 아니라 여러 정책을 묶어 관리할 수 있도록 한다.
공식 문서에서는 다음 개념을 사용한다.
| 구성요소 | 역할 |
|---|---|
| Policy | 특정 API 요청을 검사하는 규칙 |
| Policy Set | 여러 Policy를 묶은 단위 |
| Workload | 정책을 적용할 대상 작업 환경 |
| Deployment | 실제 적용할 정책과 실행 환경의 구성 |
예를 들어 운영 환경과 개발 환경에 서로 다른 정책을 적용할 수 있다.
운영 환경에서는 외부 공개를 엄격하게 차단하고, 별도의 테스트 환경에서는 일부 작업을 허용하도록 구성할 수 있다.
다만 이러한 정책 차이는 조직이 직접 정의해야 한다.
CWS가 환경의 목적을 자동으로 판단해 모든 권한을 설정하는 것은 아니다.
요청의 기본 동작
CWS에는 정책에 일치하지 않는 요청을 어떻게 처리할지 결정하는 기본 동작도 존재한다.
공식 예제에서는 Default Allow를 사용한다.
이 경우 정의된 정책에 위반되는 요청은 차단하지만, 정책의 검사 범위에 포함되지 않는 요청은 허용한다.
예를 들어 R2 버킷 공개 여부만 검사하는 정책을 만들었다면 다른 종류의 API 요청은 기본적으로 허용할 수 있다.
이는 초기 도입 시 예상하지 못한 서비스 중단을 줄이는 데 도움이 될 수 있다.
반면 조직이 더욱 엄격한 정책을 요구한다면 허용할 API 범위를 명시적으로 제한하는 구조를 고려해야 한다.
어떤 기본 동작을 선택할지는 조직의 보안 요구사항과 실제 업무 흐름에 따라 달라진다.
HTTPS 요청을 검사하기 위한 인증서 구성
CWS Proxy는 HTTPS API 요청의 내용을 검사할 수 있도록 TLS Interception을 사용한다.
이를 위해 Proxy에는 CA 인증서와 개인키를 설정해야 한다.
클라이언트는 해당 CA 인증서를 신뢰하도록 구성한다.
이 과정에는 보안상 중요한 제한사항이 존재한다.
CA 개인키가 노출되면 해당 Proxy의 신뢰 체계에 영향을 줄 수 있다.
따라서 개인키는 Proxy가 실행되는 환경에서 안전하게 관리해야 한다.
또한 HTTPS 트래픽을 중간에서 검사하는 방식은 조직의 개인정보 처리 정책과 보안 규정에 따라 신중하게 운영해야 한다.
CWS의 공식 가이드에서도 테스트용 CA를 생성하는 방법과 클라이언트의 인증서 신뢰 설정을 구분해서 설명한다.
운영 환경의 적용 범위
CWS는 현재 Beta 단계다.
또한 Zero Trust 구독이 필요하다.
다만 유료 Zero Trust 요금제가 필수인 것은 아니다.
Zero Trust Free 요금제로도 사용할 수 있다.
공식 시작 가이드는 Linux 환경에서 Proxy를 실행하고 Cloudflare CLI의 R2 요청을 검사하는 예제를 제공한다.
하지만 하나의 테스트 예제가 존재한다는 사실이 모든 클라우드 서비스와 API를 동일한 수준으로 지원한다는 의미는 아니다.
실제로 사용할 클라우드 제공업체와 API 작업의 지원 범위를 확인해야 한다.
또한 Proxy를 경유하지 않는 요청에는 해당 정책이 적용되지 않을 수 있다.
이번 발표는 클라우드 리소스의 설정을 나중에 검사하는 방식과 달리 API 요청이 실행되기 전에 정책 위반을 차단하는 예방적 보안 계층을 제공한 변화다.
Cloudflare Precursor, Monitor 모드 추가 — 실제 사용자 차단 없이 봇 탐지 정책 검증
- 발표일: 2026-10-19 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare Precursor
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-19-precursor-monitor-mode/
Cloudflare가 Precursor에 Monitor 모드를 추가했다.
Precursor는 웹사이트에 접근하는 자동화 트래픽을 식별하고, 필요에 따라 추가적인 보안 검사를 적용하는 기능이다.
웹 애플리케이션에서 봇 탐지 정책을 변경할 때는 중요한 문제가 발생할 수 있다.
보안 정책을 강하게 설정하면 악의적인 자동화 요청을 차단하는 데 도움이 될 수 있다.
하지만 정상적인 사용자까지 봇으로 잘못 판단하면 서비스 이용에 문제가 생길 수 있다.
특히 로그인, 결제, 주문처럼 사용자가 중요한 작업을 수행하는 화면에서 불필요한 Challenge가 발생하면 정상적인 사용자 흐름이 중단될 수 있다.
따라서 새로운 봇 탐지 정책을 적용하기 전에 실제 트래픽을 대상으로 어떤 결과가 나오는지 확인하는 과정이 중요하다.
이번 업데이트는 탐지 결과를 관찰하면서 실제 보안 판정에는 영향을 주지 않는 모드를 제공한다.
세 가지 실행 모드
Precursor의 설정에는 이제 다음 세 가지 상태가 존재한다.
| 모드 | 동작 |
|---|---|
Off | Precursor 비활성화 |
Monitor | 탐지만 수행하고 Bot Score와 Challenge Clearance에 영향 없음 |
Arm | 탐지 결과가 Bot Score와 Challenge Clearance에 영향을 줄 수 있음 |
Monitor에서는 탐지가 이루어지더라도 해당 결과가 Bot Score를 변경하지 않는다.
또한 Challenge Clearance에도 영향을 주지 않는다.
따라서 Precursor의 탐지 결과가 실제 사용자 요청에 어떤 영향을 줄지 관찰할 수 있다.
반면 Arm에서는 Precursor의 탐지 결과가 실제 보안 판정에 영향을 줄 수 있다.
이 구분은 운영 중인 서비스에서 새로운 보안 정책을 점진적으로 적용하는 데 활용할 수 있다.
다만 Monitor 모드가 Cloudflare의 모든 보안 기능을 비활성화한다는 의미는 아니다.
다른 WAF 규칙이나 보안 정책은 별도로 적용될 수 있다.
Interstitial 설정 분리
이번 변경에서는 Precursor Interstitial의 설정을 실행 모드와 분리했다.
Interstitial은 사용자가 특정 작업을 수행하기 전에 추가적인 화면이나 검증 과정을 표시하는 방식이다.
기존에는 탐지 모드와 Interstitial 설정이 서로 밀접하게 연결될 수 있었다.
새로운 구조에서는 사이트 전체에 대한 기본 설정과 특정 요청에 적용할 규칙을 분리한다.
사이트 전체의 기본 동작은 Show Interstitial로 설정한다.
그리고 Interstitial Rules를 이용해 특정 요청에 별도의 조건을 적용할 수 있다.
예를 들어 웹사이트 대부분의 경로에서는 Interstitial을 표시하지 않고 /checkout에 대해서만 추가 검증을 요구할 수 있다.
이러한 구조는 사이트 전체에 동일한 보안 절차를 적용하는 것보다 세밀한 정책을 구성할 수 있도록 한다.
결제 화면의 별도 검증
예를 들어 전자상거래 서비스를 생각해 보자.
상품 목록을 조회하는 요청과 최종 결제를 진행하는 요청은 보안 위험이 다르다.
상품 목록을 조회할 때마다 강한 Challenge를 요구하면 일반적인 탐색 경험이 불편해질 수 있다.
반면 결제 요청에서는 자동화된 대량 요청이나 비정상적인 세션을 더 엄격하게 검사해야 할 수 있다.
Precursor의 새로운 Interstitial Rules를 이용하면 이러한 차이를 정책으로 표현할 수 있다.
다만 /checkout에 Interstitial을 설정한다고 해서 결제 시스템의 모든 보안 문제가 해결되는 것은 아니다.
결제 API의 인증, CSRF 방어, 주문 금액 검증과 결제 승인 처리 등은 애플리케이션에서 별도로 구현해야 한다.
Precursor는 이러한 기능을 대체하지 않는 추가적인 보안 계층이다.
이번 변경은 새로운 봇 탐지 알고리즘의 성능 향상을 발표한 것이 아니다.
기존 탐지 기능을 실제 차단 없이 관찰할 수 있는 모드를 추가하고, 사용자에게 표시하는 검증 화면의 적용 범위를 더욱 세밀하게 제어하도록 한 운영 기능이다.
📚 추천 글
Structured Output with TanStack AI
- 저자: Adam Rackis / Master.dev
- 게시일: 2026-10-19
- 원문: https://blog.master.dev/structured-output-with-tanstack-ai/
React 애플리케이션에서 AI 모델이 생성하는 구조화된 JSON 데이터를 TypeScript 타입과 연결하고, 완성되지 않은 JSON을 스트리밍하는 과정을 설명한 글이다.
저자는 앞선 글에서 TanStack AI를 이용한 기본적인 채팅 기능과 대화 저장, 스트리밍, 연결 복구를 다뤘다.
이번 글은 그다음 단계인 Structured Output에 집중한다.
일반적인 채팅 애플리케이션에서는 모델이 생성한 문자열을 그대로 화면에 표시할 수 있다.
하지만 실제 웹서비스에서는 AI가 생성한 데이터를 기존 컴포넌트나 비즈니스 로직에서 사용해야 하는 경우가 있다.
예를 들어 사용자가 AI에게 운동 계획을 생성해 달라고 요청했다고 가정해 보자.
모델이 자연어로 다음과 같이 답할 수 있다.
스쿼트 3세트, 벤치프레스 3세트, 러닝 20분을 수행하라는 식이다.
하지만 피트니스 애플리케이션에서는 이러한 문자열을 그대로 저장하기 어렵다.
운동 종류와 세트 수, 반복 횟수, 무게, 시간 등을 개별 필드로 분리해야 하기 때문이다.
저자는 이를 해결하기 위해 모델의 출력 구조를 사전에 정의하고 React의 타입 시스템까지 연결한다.
Zod를 이용한 데이터 구조 정의
저자는 Zod를 사용한다.
Zod는 TypeScript 애플리케이션에서 데이터의 구조와 유효성을 정의하고 검증하는 라이브러리다.
예제에서는 운동 계획을 여러 계층으로 구성한다.
가장 작은 단위는 운동의 측정값이다.
예를 들어 다음 정보가 포함된다.
- 세트 순서
- 반복 횟수
- 사용한 무게
- 운동 시간
- 이동 거리
하나의 운동에는 여러 세트가 포함된다.
하나의 운동 세그먼트에는 여러 운동이 포함될 수 있다.
그리고 하나의 운동 계획에는 여러 세그먼트가 포함된다.
따라서 데이터 구조는 다음과 같은 계층을 가진다.
운동 계획 → 세그먼트 → 운동 → 세트별 측정값
저자는 이러한 구조를 중첩된 Zod Schema로 정의했다.
최종 출력에는 운동 계획 목록뿐 아니라 모델이 어떤 운동을 선택했는지 설명하는 commentary 필드도 포함된다.
이렇게 하면 AI가 생성한 결과를 기존 React 컴포넌트에서 사용하는 데이터 구조와 연결할 수 있다.
서버의 outputSchema
TanStack AI에서는 모델 호출 시 outputSchema를 지정할 수 있다.
저자는 서버의 chat() 호출에 앞서 정의한 Zod Schema를 전달한다.
이렇게 하면 모델이 생성할 응답의 구조를 제한할 수 있다.
예를 들어 모델이 운동 계획을 자유로운 Markdown 문장으로 반환하는 대신 정해진 필드를 가진 객체 형태로 생성하도록 한다.
일반적인 비스트리밍 요청에서는 toJsonResponse()를 이용해 결과를 반환한다.
프론트엔드에서는 useChat()을 사용한다.
연결에는 fetchJson()을 지정한다.
그리고 클라이언트에도 동일한 outputSchema를 전달한다.
이렇게 하면 서버와 클라이언트가 같은 데이터 구조를 사용한다.
useChat()과 구조화된 메시지
일반적인 AI 채팅 메시지는 텍스트 부분을 포함한다.
하지만 구조화된 응답에서는 메시지의 Part가 다르게 표현된다.
저자의 예제에서는 다음 타입을 검사한다.
structured-output
이 Part에는 상태 정보가 포함된다.
응답이 완성됐다면 다음 상태를 확인할 수 있다.
status === "complete"
이때 완성된 데이터는 part.data에서 읽을 수 있다.
예를 들어 part.data.workouts에 접근하면 생성된 운동 계획 목록을 가져올 수 있다.
이러한 방식에서는 JSON 문자열을 수동으로 파싱하고 여러 곳에서 타입 단언을 반복하는 코드를 줄일 수 있다.
하지만 여기까지는 응답 전체가 완성된 이후의 처리 방식이다.
문제는 구조화된 JSON을 생성하는 도중에도 사용자에게 결과를 보여주려는 경우에 발생한다.
완성되지 않은 JSON을 어떻게 렌더링할까
일반적인 텍스트 스트리밍에서는 모델이 생성한 문자열을 차례대로 화면에 추가하면 된다.
하지만 JSON은 다르다.
예를 들어 모델이 다음과 같은 객체를 생성하고 있다고 가정해 보자.
처음에는 객체의 이름만 생성될 수 있다.
이후 배열이 생성되고, 배열의 첫 번째 항목이 만들어지고, 다시 그 안의 속성들이 채워진다.
중간 단계의 JSON은 문법적으로 완성되지 않았을 수 있다.
예를 들어 문자열을 여는 따옴표가 존재하지만 닫는 따옴표는 아직 생성되지 않았을 수 있다.
배열이나 객체의 닫는 괄호가 아직 나타나지 않았을 수도 있다.
일반적인 JSON Parser는 이러한 데이터를 처리할 수 없다.
완성되지 않은 JSON은 유효한 JSON 문서가 아니기 때문이다.
TanStack AI는 이 문제를 해결하기 위해 부분적으로 생성된 JSON을 보완해 현재까지 만들어진 데이터 구조로 표현하는 기능을 제공한다.
예를 들어 아직 닫히지 않은 문자열이나 배열을 처리하고, 지금까지 생성된 필드에 접근할 수 있도록 한다.
그러나 이 과정에서 아직 생성되지 않은 값이 존재할 수 있다.
따라서 완성된 데이터와 부분 데이터의 타입을 동일하게 취급해서는 안 된다.
Streaming 설정
저자는 서버에서 stream: true를 지정한다.
이후 일반적인 JSON 응답 대신 다음 함수를 사용한다.
toServerSentEventsResponse()
SSE(Server-Sent Events)는 서버가 클라이언트에 데이터를 지속적으로 전달하는 방식이다.
클라이언트에서는 fetchServerSentEvents()를 사용한다.
그러면 AI 모델이 구조화된 데이터를 생성하는 동안 변경 사항을 조금씩 받을 수 있다.
하지만 모델이 생성하는 모든 부분 데이터를 즉시 완성된 운동 계획으로 처리하면 오류가 발생할 수 있다.
예를 들어 운동 계획 이름은 존재하지만 실제 운동 목록은 아직 생성되지 않았을 수 있기 때문이다.
따라서 UI 컴포넌트는 필드가 존재하지 않는 상황을 처리할 수 있어야 한다.
DeepPartial의 역할
저자는 이 문제를 해결하기 위해 TanStack AI의 DeepPartial 타입을 사용한다.
TypeScript의 기본 Partial은 객체의 최상위 속성을 선택 사항으로 만든다.
하지만 깊게 중첩된 객체에서는 내부 속성까지 모두 선택 사항으로 변경해야 하는 경우가 있다.
예를 들어 운동 계획의 이름만 없는 것이 아니라 운동 목록의 특정 항목이나 세트별 측정값도 아직 존재하지 않을 수 있다.
DeepPartial은 이러한 중첩 구조의 모든 속성이 존재하지 않을 가능성을 타입에 반영한다.
따라서 컴포넌트는 값을 사용하기 전에 존재 여부를 확인해야 한다.
예를 들어 다음과 같은 상황을 구분할 수 있다.
- 운동 계획 자체가 아직 생성되지 않음
- 운동 계획 이름만 생성됨
- 첫 번째 운동이 생성됨
- 운동의 반복 횟수가 생성됨
- 모든 필드가 완성됨
이러한 단계에서 가능한 범위만 화면에 표시할 수 있다.
완성된 데이터와 부분 데이터 분리
저자는 구조화된 응답 Part의 상태를 기준으로 렌더링을 분리했다.
완성된 경우에는 part.data를 사용한다.
완성되지 않은 경우에는 part.partial을 사용한다.
이 두 데이터는 TypeScript 관점에서 서로 다르게 취급해야 한다.
완성된 데이터는 정의한 Schema의 필드가 모두 존재한다고 기대할 수 있다.
반면 부분 데이터에서는 어떤 필드도 아직 생성되지 않았을 수 있다.
따라서 스트리밍 전용 컴포넌트에서는 모든 중첩 속성을 선택적으로 처리한다.
이 방식은 단순히 TypeScript 오류를 제거하기 위한 우회가 아니다.
생성 중인 데이터와 완성된 데이터가 서로 다른 상태라는 사실을 애플리케이션의 타입과 UI 구조에 반영한 설계다.
타입 안정성과 데이터의 정확성은 다르다
구조화된 출력은 모델이 반환하는 데이터를 다루기 쉽게 만든다.
하지만 Schema에 맞는 데이터가 반환됐다는 사실만으로 해당 내용이 올바른 것은 아니다.
예를 들어 운동 계획의 반복 횟수가 숫자 타입이라고 해서 실제 사용자에게 적합한 반복 횟수라는 의미는 아니다.
또한 데이터베이스의 기존 값과 중복되지 않아야 하는 UUID를 생성하도록 요청했다고 해도 모델이 생성한 UUID가 실제로 유일하다는 보장은 없다.
따라서 구조화된 출력 이후에도 비즈니스 규칙을 검증해야 한다.
특히 데이터베이스에 저장할 값이라면 다음과 같은 검사를 고려할 수 있다.
- 실제 데이터 범위에 포함되는지
- 필요한 필드가 모두 존재하는지
- 중복 데이터가 아닌지
- 사용자 권한과 일치하는지
- 애플리케이션의 업무 규칙을 충족하는지
이러한 검증은 AI 모델에 Schema를 전달하는 과정과 별개다.
구현상의 한계
저자는 실제 코드를 공개하고 있지만 이 구현의 성능을 다른 AI SDK와 정량적으로 비교하지는 않았다.
따라서 TanStack AI가 모든 환경에서 다른 라이브러리보다 빠르다고 단정할 수는 없다.
또한 스트리밍 중간에 필드가 생성되는 순서는 모델과 Schema의 영향을 받을 수 있다.
UI가 특정 순서를 강하게 가정하면 모델의 응답 방식이 달라졌을 때 문제가 발생할 수 있다.
따라서 부분 데이터에 대응하는 UI는 가능한 한 누락된 필드를 자연스럽게 처리하도록 설계하는 것이 적합하다.
이번 글은 AI 모델의 구조화된 출력을 React의 컴포넌트와 TypeScript 타입에 연결할 때 발생하는 문제를 실제 구현으로 설명한다.
특히 JSON 스트리밍에서 부분적으로 완성된 데이터와 최종 데이터의 차이를 이해하는 데 참고할 만하다.
Vercel's vgpu: A WebGPU library built for AI agents
- 저자: Emmanuel John / LogRocket
- 게시일: 2026-10-19
- 원문: https://blog.logrocket.com/vercel-vgpu-webgpu-ai-agents/
WebGPU 기반의 그래픽 효과를 구현할 때 필요한 복잡한 설정을 줄이고, Next.js의 React 컴포넌트에서 GPU Shader를 실행한 뒤 Node.js에서 렌더링 결과를 검증하는 방법을 설명한 글이다.
WebGPU는 웹 애플리케이션에서 GPU의 그래픽 및 계산 능력을 사용할 수 있도록 하는 API다.
일반적인 웹페이지에서는 HTML과 CSS, JavaScript로 UI를 구현한다.
하지만 복잡한 시각 효과나 대규모 병렬 계산이 필요한 경우 GPU를 직접 활용하는 편이 적합할 수 있다.
예를 들어 화면 전체에 실시간으로 변화하는 입자 효과를 표시하거나, 복잡한 Shader를 이용해 이미지를 처리하는 상황이다.
문제는 WebGPU의 API가 상대적으로 저수준이라는 점이다.
GPU에서 간단한 Shader 하나를 실행하려고 해도 여러 구성요소를 설정해야 한다.
대표적으로 다음과 같은 작업이 필요하다.
- GPU Adapter 선택
- GPU Device 생성
- Shader Module 구성
- Pipeline 생성
- Buffer 할당
- Bind Group 설정
- Render Pass 구성
- GPU 자원 정리
이러한 과정은 GPU 프로그래밍에 익숙하지 않은 프론트엔드 개발자에게 진입 장벽이 될 수 있다.
저자는 이 문제를 해결하기 위한 라이브러리로 vgpu를 소개한다.
vgpu가 제공하는 추상화
vgpu는 WebGPU의 저수준 API를 직접 관리해야 하는 작업을 줄이기 위한 라이브러리다.
예를 들어 일반적인 WebGPU에서는 Shader에서 사용하는 Uniform Buffer의 메모리 구조와 바인딩을 개발자가 직접 관리해야 할 수 있다.
Uniform은 여러 Shader 실행에서 공통으로 사용하는 값이다.
예를 들어 현재 시간, 화면 크기, 마우스 위치 등이 있다.
vgpu는 Shader의 필드 이름을 기반으로 필요한 값을 연결할 수 있도록 한다.
개발자는 TypeScript 객체를 이용해 Shader의 값을 설정할 수 있다.
이를 통해 수동으로 Buffer의 Offset과 Layout을 계산하는 코드를 줄일 수 있다.
또한 다음 기능을 제공한다.
| 기능 | 역할 |
|---|---|
init() | GPU 실행 문맥 초기화 |
surface() | Canvas와 GPU 출력 연결 |
effect() | Shader 기반 시각 효과 구성 |
clock() | 경과 시간과 Frame 정보 관리 |
frameLoop() | 반복 렌더링 처리 |
renderBundle() | 반복 실행할 GPU 명령 재사용 |
vgpu doctor | 실행 환경과 WebGPU 구성 점검 |
이러한 API는 WebGPU의 모든 개념을 없애는 것이 아니다.
개발자가 자주 반복하는 설정 작업을 더 높은 수준의 인터페이스로 제공하는 것이다.
WGSL Shader 작성
저자는 실제 예제로 우주 성운처럼 움직이는 배경 효과를 구현했다.
사용한 언어는 WGSL(WebGPU Shading Language)이다.
WGSL은 WebGPU에서 Shader를 작성하기 위한 언어다.
일반적인 JavaScript가 CPU에서 실행되는 로직을 작성하는 데 사용된다면 WGSL은 GPU에서 실행할 계산과 렌더링 작업을 정의한다.
예제에서는 nebula.wgsl 파일을 만든다.
그리고 Fractal Brownian Motion 기반의 Noise 함수를 사용한다.
Noise는 자연스러운 불규칙성을 표현하기 위한 수학적 함수다.
구름이나 연기, 물결 같은 효과를 구현할 때 활용할 수 있다.
저자는 fbmPerlin3d를 이용해 두 종류의 Noise를 생성한다.
첫 번째 Noise는 성운의 밀도를 표현한다.
두 번째 Noise는 빛나는 영역의 분포를 표현한다.
이후 두 값을 이용해 색상을 섞는다.
기본 배경색과 보라색, 따뜻한 색상을 혼합해 성운이 움직이는 효과를 만든다.
중요한 점은 실시간으로 변화하는 값을 JavaScript에서 Shader로 전달한다는 사실이다.
예를 들어 현재 경과 시간을 Shader에 전달하면 시간이 흐르면서 Noise의 위치가 달라진다.
그 결과 매 Frame마다 조금씩 다른 이미지가 생성된다.
Next.js와 GPU 실행 문맥 연결
저자는 Shader 실행 로직을 별도의 TypeScript 파일에 작성했다.
이 파일에서는 GPU의 생성과 렌더링, 종료를 관리한다.
주요 흐름은 다음과 같다.
init()으로 GPU 실행 문맥을 생성한다.surface()를 이용해 Canvas를 연결한다.effect()로 WGSL Shader를 구성한다.- Shader에 시간과 화면 크기를 전달한다.
frameLoop()에서 반복적으로 화면을 렌더링한다.- 작업이 종료되면 GPU 자원을 해제한다.
이 구조에서는 실제 React 컴포넌트와 GPU 처리 로직을 분리한다.
React 컴포넌트는 Canvas 요소를 렌더링한다.
GPU Controller는 해당 Canvas에 실제 그래픽을 그린다.
이러한 분리는 React의 선언적인 UI 관리와 WebGPU의 명령형 실행 구조를 연결할 때 유용하다.
React는 DOM의 구조와 상태를 관리한다.
반면 WebGPU는 GPU 자원의 생성과 실행 순서를 직접 관리해야 한다.
따라서 두 역할을 완전히 동일한 방식으로 처리하기보다 경계를 명확하게 두는 것이 적합하다.
useRef와 useEffect
Next.js에서는 Canvas를 사용하는 컴포넌트를 Client Component로 구성한다.
브라우저의 DOM과 GPU API에 접근해야 하기 때문이다.
저자는 useRef를 이용해 실제 Canvas 요소를 참조한다.
이후 useEffect에서 GPU 초기화 함수를 호출한다.
컴포넌트가 제거될 때는 Cleanup 함수를 실행한다.
Cleanup에서는 다음 작업을 수행한다.
- Frame Loop 중단
- GPU 자원 해제
- 관련 실행 상태 정리
이 과정이 중요한 이유는 WebGPU가 브라우저의 DOM 요소만 관리하는 API가 아니기 때문이다.
GPU의 메모리와 실행 자원은 별도로 관리해야 한다.
React 컴포넌트가 사라졌다고 해서 모든 GPU 작업이 자동으로 올바르게 종료된다고 가정해서는 안 된다.
예를 들어 페이지를 이동했는데 기존 Frame Loop가 계속 실행된다면 불필요한 GPU 작업과 메모리 사용이 발생할 수 있다.
저자는 이를 방지하기 위해 gpu.dispose()를 사용한다.
비동기 초기화 중 컴포넌트가 제거되는 문제
GPU 초기화는 비동기 작업이다.
따라서 초기화가 완료되기 전에 사용자가 페이지를 이동할 수 있다.
예를 들어 컴포넌트가 마운트된 직후 GPU 초기화를 시작했다고 가정해 보자.
초기화가 진행되는 동안 사용자가 다른 페이지로 이동한다.
이때 컴포넌트는 이미 제거됐지만 비동기 초기화는 나중에 완료될 수 있다.
초기화 완료 후 무조건 렌더링을 시작하면 더 이상 존재하지 않는 UI를 위한 GPU 작업이 실행될 수 있다.
저자는 이를 방지하기 위해 disposed 상태를 확인한다.
컴포넌트가 이미 제거됐다면 초기화된 GPU 자원을 정리하고 실행을 중단한다.
이러한 패턴은 WebGPU에만 적용되는 것이 아니다.
비동기적으로 생성하는 외부 리소스를 React 컴포넌트와 연결할 때도 비슷한 문제가 발생할 수 있다.
화면 크기와 Pixel Ratio 처리
WebGPU 렌더링에서는 Canvas의 실제 픽셀 크기와 CSS로 표시되는 크기를 구분해야 한다.
예를 들어 CSS에서는 Canvas의 너비가 500px일 수 있다.
하지만 디스플레이의 Device Pixel Ratio가 2라면 실제 렌더링 해상도는 더 높게 설정될 수 있다.
이러한 차이를 고려하지 않으면 렌더링 결과가 흐릿하게 표시되거나 불필요하게 많은 GPU 자원을 사용할 수 있다.
저자는 surface()의 DPR 설정을 이용한다.
예제에서는 DPR을 1에서 2 사이로 제한한다.
이렇게 하면 지나치게 높은 픽셀 밀도에서 렌더링 해상도가 무제한으로 증가하는 문제를 줄일 수 있다.
다만 DPR을 제한하면 매우 높은 해상도의 디스플레이에서 완전한 픽셀 밀도를 사용하지 않을 수 있다.
따라서 화질과 GPU 처리 비용 사이의 트레이드오프가 존재한다.
또한 창 크기가 변경됐을 때 Shader에 전달하는 화면 관련 값도 업데이트한다.
저자는 surface.onResize()를 이용해 이러한 변경을 처리했다.
브라우저 없이 Shader 테스트
이 글의 흥미로운 부분은 Node.js에서 GPU 렌더링 결과를 테스트하는 과정이다.
일반적인 프론트엔드 테스트에서는 JavaScript 함수의 반환값을 검사하거나 브라우저에서 DOM을 확인한다.
하지만 Shader는 최종적으로 이미지의 픽셀을 생성한다.
따라서 함수 호출이 성공했다고 해서 화면에 올바른 이미지가 표시됐다고 볼 수 없다.
예를 들어 Shader가 정상적으로 실행되더라도 모든 픽셀이 검은색으로 출력될 수 있다.
이러한 문제를 확인하려면 실제 렌더링 결과를 검사해야 한다.
vgpu는 Node.js 환경에서 Headless 실행을 지원한다.
Headless 실행은 일반적인 브라우저 창이나 화면을 표시하지 않고 프로그램을 실행하는 방식이다.
저자는 vgpu/node를 이용해 GPU 문맥을 생성한다.
이후 Offscreen Target에 Shader를 실행한다.
렌더링이 완료되면 color.read()로 픽셀 데이터를 읽는다.
픽셀 데이터로 렌더링 결과 검증
저자는 렌더링 결과의 RGBA 픽셀 값을 읽는다.
RGBA는 Red, Green, Blue, Alpha 값을 이용해 픽셀 색상을 표현하는 방식이다.
이후 각 픽셀의 RGB 값을 이용해 밝기를 계산한다.
모든 픽셀의 밝기를 조사한 뒤 가장 밝은 값과 가장 어두운 값을 비교한다.
예제에서는 밝기 차이가 10 미만이면 화면이 거의 일정한 색으로 출력됐다고 판단한다.
그리고 테스트를 실패 처리한다.
이 방식은 간단하지만 Shader가 완전히 비어 있거나 예상하지 못한 단색 이미지를 생성하는 문제를 발견하는 데 도움이 될 수 있다.
그러나 이 테스트가 렌더링 결과의 시각적 정확성을 완전히 보장하는 것은 아니다.
예를 들어 색상의 분포가 완전히 잘못됐더라도 밝기 차이가 충분히 크다면 테스트를 통과할 수 있다.
따라서 복잡한 시각 효과를 검증하려면 Snapshot 비교나 특정 픽셀 영역의 검사 등 추가적인 테스트 방법이 필요할 수 있다.
CI 환경에서의 WebGPU 테스트
저자는 Headless 렌더링을 CI/CD 시스템에서도 사용할 수 있다고 설명한다.
예를 들어 코드가 변경될 때마다 Shader를 컴파일하고 렌더링 결과를 검사하는 작업을 구성할 수 있다.
하지만 GPU 실행에는 환경별 제약이 존재한다.
모든 CI Runner가 GPU를 제공하는 것은 아니다.
또한 GPU 드라이버와 Adapter 구성이 올바르게 설치돼 있어야 한다.
vgpu는 이러한 문제를 확인하기 위한 진단 명령을 제공한다.
npx vgpu doctor
이를 통해 현재 실행 환경이 WebGPU 코드를 실행할 수 있는지 확인할 수 있다.
다만 진단에 성공했다고 해서 모든 Shader가 실제 운영 환경에서 동일하게 실행된다는 의미는 아니다.
GPU 종류와 드라이버, 브라우저 구현에 따라 결과나 성능이 달라질 수 있기 때문이다.
Raw WebGPU·Three.js·vgpu 비교
저자는 세 가지 접근 방식을 비교한다.
| 구분 | Raw WebGPU | Three.js | vgpu |
|---|---|---|---|
| 추상화 수준 | 낮음 | 높음 | 중간 |
| 주요 관심사 | GPU 리소스 직접 관리 | 3D Scene과 Object 관리 | Shader와 GPU 작업 구성 |
| 초기 설정 | 직접 구성할 항목이 많음 | Scene·Camera 등의 구조 필요 | 간결한 GPU 초기화 API |
| 주요 활용 | 커스텀 렌더링 엔진 | 복잡한 3D 그래픽 | Shader 효과·GPU 작업 |
| Headless 테스트 | 별도 환경 구성 필요 | 실행 환경별 추가 설정 가능 | Node.js Adapter 제공 |
이 비교는 라이브러리의 설계 방향을 설명한 것이다.
동일한 조건에서 렌더링 성능을 측정한 독립적인 Benchmark는 아니다.
따라서 vgpu가 Three.js보다 항상 빠르다거나 메모리 사용량이 적다고 결론 내릴 수는 없다.
또한 vgpu가 Raw WebGPU의 모든 제어 기능을 동일한 수준으로 제공한다고 가정해서도 안 된다.
복잡한 3D 엔진을 개발해야 한다면 저수준 WebGPU API를 직접 다루는 편이 적합할 수 있다.
반면 웹페이지의 배경 효과나 비교적 작은 GPU 작업을 구성한다면 vgpu의 추상화가 개발 코드를 줄이는 데 도움이 될 수 있다.
이번 글은 React의 컴포넌트 수명주기와 GPU 자원 관리, Shader 작성, Headless 테스트를 하나의 실제 구현 사례로 연결한다.
특히 브라우저 화면에서만 확인하던 GPU 렌더링 결과를 자동화된 테스트의 대상으로 다룬다는 점이 특징이다.
Cutting our inference bill by 68% by being obsessive about optimization
- 저자: Ryan Skidmore / Cloudflare
- 게시일: 2026-10-19
- 원문: https://blog.cloudflare.com/obsessive-inference-optimization/
Cloudflare가 사내 AI 코드 리뷰 시스템의 평균 실행 비용을 68% 줄이고, 리뷰 시간을 크게 단축한 최적화 과정을 설명한 기술 글이다.
이번 글은 새로운 AI 코딩 제품의 출시 공지가 아니다.
이미 운영 중인 AI 코드 리뷰 시스템에서 비용이 증가한 원인을 분석하고, 모델 호출 방식과 캐시 구조를 개선한 운영 회고다.
Cloudflare는 내부에서 AI 에이전트를 이용해 코드 변경 사항을 리뷰하고 있다.
코드 품질, 보안, 성능, 내부 개발 규칙 등을 서로 다른 Reviewer가 검사하는 구조다.
하나의 Coordinator가 코드 변경 사항을 분석한 뒤 필요한 Reviewer에게 작업을 전달한다.
이러한 구조에서는 하나의 PR을 검사하기 위해 여러 번의 모델 호출이 발생한다.
처음에는 비용이 크게 부담되지 않았다.
하지만 시스템을 계속 운영하면서 평균 비용이 증가했다.
리뷰당 비용의 증가
Cloudflare가 공개한 자체 운영 데이터에 따르면 2026년 4월에는 리뷰 한 건당 평균 1.19달러가 사용됐다.
8월 말에는 평균 비용이 약 2.46달러까지 증가했다.
이는 리뷰 시스템의 구조를 크게 변경하지 않았는데도 발생한 변화였다.
주요 원인은 모델 변경과 리뷰 작업의 복잡도 증가였다.
Cloudflare는 코드 리뷰의 정확도를 높이기 위해 더 강한 모델을 사용하도록 변경했다.
또한 개발자가 작성하는 코드 변경 사항의 크기와 복잡도도 증가했다.
그 결과 한 번의 리뷰에서 더 많은 토큰이 사용됐다.
하지만 문제는 단순히 모델의 토큰 가격이 비싸졌다는 것만이 아니었다.
리뷰 시스템 자체가 불필요하게 많은 모델 호출과 반복적인 문맥 전달을 수행하고 있었다.
최적화 이후 결과
Cloudflare가 최적화 전후의 특정 측정 구간을 비교한 결과는 다음과 같다.
| 항목 | 최적화 전 | 최적화 후 |
|---|---|---|
| 리뷰당 평균 비용 | 2.43달러 | 0.77달러 |
| 리뷰 소요 시간 중앙값 | 4분 9초 | 1분 32초 |
| 평균 비용 감소율 | - | 약 68% |
비용은 약 68% 감소했다.
리뷰 소요 시간의 중앙값도 크게 감소했다.
하지만 이 수치를 모든 AI 코딩 에이전트에서 재현할 수 있는 일반적인 성능 개선율로 해석해서는 안 된다.
Cloudflare가 자체 시스템에서 여러 최적화를 순차적으로 적용한 결과다.
동일한 조건의 통제 실험만으로 전체 68% 감소를 측정한 것은 아니다.
Cloudflare는 비용 절감 원인을 크게 두 가지로 구분했다.
약 40%는 리뷰 시스템의 코드와 실행 구조 개선에서 발생했다.
나머지는 모델 선택과 추론 강도 조정 등의 변경에서 발생했다.
Cache Hit Rate가 높아도 비용이 증가한 이유
이번 글에서 가장 흥미로운 부분은 캐시 적중률이 높아졌는데도 전체 비용이 증가했다는 점이다.
Cloudflare의 캐시 적중률은 다음과 같이 변화했다.
| 항목 | 2026년 4월 | 2026년 9월 |
|---|---|---|
| Cache Hit Rate | 85.7% | 97% |
| 리뷰당 토큰 수 | 91만 개 | 299만 개 |
캐시 적중률은 개선됐다.
하지만 한 번의 리뷰에서 처리하는 토큰 수가 세 배 이상 증가했다.
따라서 캐시를 많이 재사용하더라도 전체 토큰 처리 비용은 증가할 수 있었다.
이 사례는 캐시 적중률 하나만으로 AI 시스템의 비용 효율성을 판단하기 어렵다는 점을 보여준다.
예를 들어 이전보다 훨씬 많은 데이터를 전달한다면 캐시 사용 비율이 높더라도 절대적인 비용이 증가할 수 있다.
또한 모델 제공업체에 따라 캐시 읽기와 캐시 쓰기의 요금이 다르다.
따라서 실제 비용을 분석하려면 다음 항목을 구분해야 한다.
- 새로운 입력 토큰
- 캐시에 새로 저장한 토큰
- 캐시에서 읽은 토큰
- 출력 토큰
- 모델 호출 횟수
Cloudflare는 최적화 직전의 비용 구성을 다음과 같이 분석했다.
| 비용 항목 | 전체 비용 비중 |
|---|---|
| 출력 토큰 | 약 23% |
| 캐시 쓰기 | 약 43% |
| 캐시 읽기 | 약 34% |
즉 전체 비용의 상당 부분이 이미 사용했던 문맥을 다시 처리하는 데 소비되고 있었다.
Prompt Cache의 Prefix 구조
Cloudflare가 발견한 첫 번째 문제는 Prompt Cache의 구조였다.
일부 모델 제공업체의 Prompt Cache는 입력의 앞부분, 즉 Prefix를 기준으로 동작한다.
예를 들어 여러 요청이 동일한 시스템 지침과 도구 정의를 사용한다면 해당 부분을 캐시에서 재사용할 수 있다.
하지만 공통된 내용 뒤에 요청마다 다른 정보가 포함되고, 캐시를 저장하는 경계가 잘못 설정돼 있다면 기대한 만큼 캐시가 재사용되지 않을 수 있다.
Cloudflare의 기존 시스템에서는 에이전트의 공통 지침 뒤에 작업 디렉터리와 날짜 같은 정보가 포함돼 있었다.
이러한 정보는 Repository와 실행 시점마다 달라졌다.
문제는 공통 지침과 요청별 정보를 하나의 블록으로 구성한 뒤 해당 블록의 끝에 캐시 경계를 설정했다는 점이었다.
따라서 서로 다른 Repository의 리뷰가 동일한 시스템 지침을 사용하더라도 캐시가 충분히 재사용되지 않았다.
공통 Prefix 분리
Cloudflare는 이를 해결하기 위해 Prompt를 분리했다.
먼저 모든 리뷰에서 동일하게 사용하는 정보를 앞부분에 배치했다.
대표적으로 다음 정보다.
- 도구 정의
- 공통 시스템 지침
- 리뷰 규칙
이 부분은 Repository와 관계없이 동일한 바이트로 유지하도록 했다.
이후 Repository별로 다른 정보를 추가했다.
또한 공통 Prompt의 끝에 별도의 Cache Breakpoint를 설정했다.
이렇게 하면 서로 다른 Repository를 리뷰하더라도 동일한 Prefix를 재사용할 수 있다.
Cloudflare가 공개한 결과에 따르면 첫 번째 모델 호출에서 캐시를 읽는 비율이 크게 개선됐다.
| 항목 | 변경 전 | 변경 후 |
|---|---|---|
| 첫 요청의 공통 Prefix Cache Hit Rate | 약 14~26% | 약 91~99.5% |
이는 Cloudflare의 자체 운영 측정 결과다.
하지만 공통 Prefix를 유지하기 위해 일부 설계상의 제약을 받아들여야 했다.
예를 들어 Repository마다 제공되는 도구 설명이 달라지면 공통 Prefix도 달라질 수 있다.
따라서 Cloudflare는 일부 도구와 Skill 정보를 공통 Prompt에서 제외하거나 동일한 형태로 유지하도록 변경했다.
즉 캐시 재사용을 높이기 위해 Prompt의 구성 방식 자체를 제한한 것이다.
불필요한 도구 호출 제거
두 번째 최적화는 모델 호출 횟수를 줄이는 것이었다.
Cloudflare는 일부 Reviewer가 실제로 사용할 수 없는 도구까지 모델에 제공하고 있다는 사실을 발견했다.
예를 들어 최종 리뷰 댓글을 작성하는 도구는 Coordinator만 사용할 수 있었다.
하지만 하위 Reviewer에게도 해당 도구의 정의가 전달됐다.
그 결과 일부 Reviewer는 사용할 수 없는 도구를 호출했다.
서버는 해당 호출을 거부했다.
이후 모델은 오류를 확인하고 다른 방식으로 작업을 다시 수행했다.
Cloudflare에 따르면 이러한 잘못된 도구 호출은 해당 Reviewer 실행의 57~76%에서 발생했다.
사용할 수 없는 도구를 제거하자 불필요한 모델 호출이 줄었다.
또한 매 요청에서 전달하는 도구 정의도 약 2,900토큰 감소했다.
이 사례는 에이전트가 사용할 수 있는 도구를 많이 제공하는 것이 항상 유리하지는 않다는 점을 보여준다.
도구가 많아질수록 모델이 판단해야 하는 선택지도 증가한다.
또한 실제로 사용할 수 없는 도구를 제공하면 불필요한 오류와 재시도가 발생할 수 있다.
파일 읽기 작업 병렬화
Cloudflare는 코드 품질 Reviewer의 도구 호출 패턴도 분석했다.
그 결과 상당수의 호출에서 모델이 파일 하나만 읽고 다시 모델을 호출하는 방식으로 동작하고 있었다.
예를 들어 Reviewer가 다음 순서로 작업할 수 있다.
- 파일 A를 읽는다.
- 모델이 내용을 분석한다.
- 파일 B를 읽는다.
- 모델이 내용을 분석한다.
- 파일 C를 읽는다.
- 모델이 내용을 분석한다.
이 구조에서는 파일을 하나 읽을 때마다 모델 호출이 발생한다.
하지만 코드 리뷰를 시작할 때 이미 필요한 파일 목록을 알고 있다면 여러 파일을 한 번에 읽을 수 있다.
Cloudflare는 Reviewer가 공통 문맥과 변경 파일들을 초기 단계에서 함께 읽도록 변경했다.
이를 통해 반복적인 도구 호출을 줄였다.
다만 모든 파일을 무조건 한 번에 읽는 것이 최적이라는 의미는 아니다.
입력 문맥이 지나치게 커지면 토큰 비용이 증가할 수 있다.
따라서 실제 변경 파일의 크기와 필요한 문맥 범위를 고려해야 한다.
재리뷰에서 불필요한 Reviewer 생략
PR이 한 번에 병합되지 않는 경우도 많다.
개발자가 리뷰 결과를 반영해 새로운 Commit을 Push하면 동일한 PR을 다시 검토해야 한다.
하지만 이전 리뷰 이후 변경되지 않은 영역까지 매번 모든 Reviewer가 다시 검사할 필요는 없을 수 있다.
Cloudflare는 이를 해결하기 위해 재리뷰에서 일부 Reviewer를 생략하도록 했다.
예를 들어 문서 변경이 없다면 문서 Reviewer를 생략할 수 있다.
내부 개발 규칙과 관련된 변경이 없다면 해당 Reviewer도 생략할 수 있다.
Cloudflare는 재리뷰에서 이러한 작업을 선택적으로 건너뛰도록 구성했다.
이를 통해 중복된 코드 분석과 모델 호출을 줄였다.
하지만 변경 범위를 판단하는 로직이 잘못되면 실제로 필요한 리뷰가 생략될 수 있다.
따라서 어떤 Reviewer를 생략할지 결정하는 기준이 중요하다.
취소된 작업의 실행 중단
또 다른 문제는 취소된 리뷰 작업이 실제로는 계속 실행된다는 것이었다.
예를 들어 개발자가 PR에 새로운 Commit을 Push하면서 이전 리뷰 작업을 취소할 수 있다.
하지만 하위 실행 환경이 취소 신호를 정상적으로 전달하지 못하면 기존 AI 리뷰는 계속 실행된다.
사용자는 결과를 더 이상 필요로 하지 않는데 모델 호출 비용은 계속 발생하는 것이다.
Cloudflare는 취소된 작업 446개를 분석했다.
그중 358개가 작업을 취소한 이후에도 리뷰를 끝까지 실행하고 있었다.
이를 해결하기 위해 실행 중인 작업이 자신의 상태를 주기적으로 확인하도록 변경했다.
현재 작업이 취소된 상태라면 실행을 중단한다.
이 방식은 불필요한 AI 요청과 실행 시간을 줄이는 데 도움이 됐다.
다만 모든 외부 모델 요청을 언제나 즉시 취소할 수 있다는 의미는 아니다.
실제 취소 가능 여부는 모델 제공업체의 API와 실행 환경에 따라 달라질 수 있다.
여러 Reviewer가 동일한 Cache Prefix 공유
Cloudflare는 코드 품질, 보안, 성능 Reviewer가 동일한 코드 변경 사항을 읽는다는 점에도 주목했다.
이전에는 각 Reviewer가 별도의 Prompt를 구성했다.
따라서 같은 코드 변경 내용을 여러 번 캐시에 저장할 수 있었다.
새로운 구조에서는 세 Reviewer가 동일한 공통 Prefix를 사용한다.
공통 Prefix에는 다음 정보가 포함된다.
- 동일한 도구 정의
- 공통 규칙
- PR의 변경 내용
이후 각 Reviewer의 개별 지침을 추가한다.
하나의 Reviewer가 먼저 공통 Prefix를 캐시에 저장하면 나머지 Reviewer가 이를 읽도록 구성했다.
하지만 여기에도 트레이드오프가 있다.
캐시에 기록한 응답이 실제로 스트리밍되기 시작하기 전에는 다른 요청이 해당 캐시를 읽지 못할 수 있다.
따라서 나머지 Reviewer는 첫 번째 Reviewer의 응답이 시작될 때까지 잠시 대기해야 한다.
Cloudflare는 최대 20초까지 기다리도록 구성했다.
이 방식은 일부 작업의 시작을 늦추지만 전체 토큰 비용을 줄이는 데 도움이 됐다.
즉 최소 지연 시간과 최소 비용 사이에서 일부 대기 시간을 허용한 설계다.
모델과 추론 수준의 변경
Cloudflare는 코드 최적화 외에도 모델과 추론 수준을 변경했다.
하지만 무조건 저렴한 모델을 선택하지는 않았다.
Reviewer마다 필요한 추론 능력이 다르기 때문이다.
예를 들어 단순한 문서 변경은 상대적으로 낮은 추론 수준으로도 충분할 수 있다.
반면 복잡한 코드 변경에서 발생할 수 있는 보안 문제를 발견하려면 더 깊은 분석이 필요할 수 있다.
Cloudflare는 여러 모델과 추론 수준을 실제 리뷰 결과와 비교했다.
특히 단순히 평균 비용이 낮아졌는지만 확인하지 않았다.
다음 항목도 함께 검토했다.
- Critical Finding 수
- Warning Finding 수
- 리뷰 승인 결정의 일치율
- 모델 호출 횟수
- 리뷰 소요 시간
- 비용
일부 Reviewer에서는 낮은 추론 수준을 사용해도 주요 결과가 크게 달라지지 않았다.
반면 코드 품질 Reviewer에서는 높은 추론 수준을 사용했을 때 더 많은 중요한 문제를 발견했다.
따라서 Cloudflare는 코드 품질 Reviewer에 높은 추론 수준을 유지하도록 결정했다.
이러한 접근은 AI 모델 사용량을 줄이는 과정에서 중요한 문제를 보여준다.
비용이 낮아졌더라도 실제 코드 리뷰의 품질까지 떨어졌다면 성공적인 최적화라고 보기 어렵다.
최종 비용 감소의 원인
Cloudflare가 공개한 분석에 따르면 비용 감소는 크게 두 부분에서 발생했다.
첫 번째는 코드 및 실행 구조 개선이다.
공통 Prompt Prefix, 불필요한 도구 제거, 병렬 파일 조회, 중복 리뷰 생략 등의 변경이 여기에 포함된다.
두 번째는 모델 선택과 추론 수준 조정이다.
일부 Reviewer에서는 비슷한 품질을 유지하면서 더 적은 토큰을 사용하는 모델 조합을 선택했다.
다만 여러 변경이 순차적으로 적용됐고 서로 영향을 줄 수 있으므로 각 변경의 효과를 완전히 독립적으로 분리하기는 어렵다.
Cloudflare도 이 비교가 엄격한 통제 실험은 아니라고 명시했다.
그럼에도 실제 운영 환경의 로그와 비용 데이터를 기반으로 각 변경 이후 비용이 어떻게 달라졌는지 기록했다는 점에서 참고할 가치가 있다.
성능 수치의 한계
이번 글에서 제시한 68% 비용 감소와 리뷰 시간 단축은 Cloudflare의 실제 운영 환경에서 관찰한 결과다.
다만 모든 AI 리뷰 시스템에서 동일한 비율의 개선을 기대할 수는 없다.
최적화 이전의 Prompt 구조, 사용 중인 모델, 캐시 정책, 코드 리뷰의 크기와 복잡도에 따라 결과가 달라질 수 있기 때문이다.
또한 모델 제공업체의 가격과 Prompt Cache 정책이 변경되면 비용 구조도 달라질 수 있다.
특정 모델의 캐시 읽기 비용이 낮다고 해서 다른 모델에서도 같은 요금 비율을 적용하는 것은 아니다.
이번 글의 핵심은 특정 모델을 사용하는 것이 아니라 실제 실행 기록을 바탕으로 토큰이 어디에 소비되는지 분석하고, 호출 횟수와 공유 문맥의 구조를 변경한 과정이다.
특히 여러 AI 에이전트를 병렬로 실행하는 시스템에서는 모델 성능뿐 아니라 Prompt Prefix, 도구 정의, 캐시 경계, 취소 처리와 재시도 정책이 전체 비용과 지연 시간에 큰 영향을 줄 수 있다는 점을 구체적인 운영 데이터로 보여준다.