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

기준 시점: 2026-11-14 06:00 KST
조사 범위: 전일 2026-11-13 06:00 ~ 당일 2026-11-14 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
React, Next.js, Vercel, Vue, Nuxt, Angular, Svelte, SvelteKit, Astro, Remix의 공식 블로그와 릴리스 기록을 확인했다.
이번 기간에는 주요 프론트엔드 프레임워크의 새로운 안정 버전이나 중요한 렌더링 API의 정식 출시를 확인하지 못했다.
Next.js의 Canary 개발과 여러 프레임워크의 내부 수정은 계속되고 있지만, 개발 브랜치의 변경을 정식 기능의 출시로 취급하지 않았다.
최근 소개한 Angular 22.2.6 및 22.3.0 RC, Astro 7.4.9·7.4.10, Vue 3.5.45, Next.js 16.5 Canary의 실험적 변경도 반복하지 않았다.
다만 프론트엔드 애플리케이션의 개발과 배포에 영향을 주는 새로운 발표는 존재했다.
Vercel은 모든 Build Machine의 빌드 캐시 크기 제한을 5GB로 확대했다. Cloudflare는 Browser Run의 Quick Action을 로컬 개발 환경에서도 실행할 수 있도록 변경했다.
두 발표는 프론트엔드 프레임워크 자체의 변경이 아니라 빌드 및 개발 도구의 개선이므로 개발 도구 및 보안 섹션에서 다룬다.
이번 추천 글에서는 Next.js에서 AI가 생성한 UI를 실제 React 컴포넌트로 렌더링하는 방법, 불필요한 Resource Hint와 외부 Script의 비용, 이미지 최적화 감사의 계산 방식, 테스트 러너 없이 TypeScript를 실행하는 구조를 자세히 설명한다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome·Chromium, Firefox·Mozilla, WebKit·Safari의 공식 공개 자료와 JavaScript·TypeScript·CSS, 웹 성능 및 접근성 관련 정보를 검토했다.
이번 기간에 새롭게 정식 채택된 주요 웹 표준이나 일반 사용자에게 기본 제공되는 새로운 브라우저 API의 중요한 안정화 발표는 확인하지 못했다.
WebKit에서는 11월 13일에도 여러 개발 빌드가 생성됐다.
하지만 개발 빌드가 생성됐다는 사실만으로 새로운 웹 플랫폼 기능이 정식 출시됐다고 판단할 수는 없다.
따라서 특정 기능의 최초 발표와 실제 안정화 여부를 검증할 수 없는 개발 빌드는 주요 뉴스에서 제외했다.
Firefox 160의 WebTransport 표준 정합성 개선, 기기 내 음성 인식, 보안 수정은 이전 브리핑에서 이미 다뤘으므로 반복하지 않았다.
이번 추천 글에서는 이미 제공되고 있는 웹 플랫폼 기능과 개발 도구를 실제 프로젝트에 적용하면서 발견한 문제를 다룬다.
특히 Lighthouse의 이미지 전송량 추정이 실제 AVIF 인코딩 결과와 얼마나 다른지, 그리고 Next.js에서 불필요한 Preload와 외부 Script가 어떻게 누적되는지 확인할 수 있다.
3️⃣ 백엔드·인프라
Cloudflare Workers·Durable Objects, 계정별 지출 상한 설정 지원 — 비용 알림과 실제 사용 차단을 별도로 선택
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 비용 관리 기능
- 적용 대상: Cloudflare Workers, Durable Objects
- 주요 기능: 서비스별 Spending Limit, 이메일 알림, 사용 차단
- 설정 범위: 계정 단위, 현재 구독 결제 기간
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-13-spend-management/
Cloudflare가 Workers와 Durable Objects의 사용 비용에 대해 직접적인 지출 상한을 설정할 수 있도록 기능을 확장했다.
이번 변경은 단순히 이메일 알림을 추가한 것이 아니다.
설정한 금액에 도달했을 때 새로운 작업의 실행을 차단하는 정책을 선택할 수 있다는 점이 중요하다.
특히 개인 프로젝트나 예산이 제한된 웹서비스에서 예상하지 못한 사용량 증가를 관리할 때 영향을 줄 수 있다.
기존 비용 알림의 한계
서버리스 애플리케이션에서는 실제 요청 수와 실행 시간에 따라 사용량이 증가할 수 있다.
예를 들어 Cloudflare Workers로 API 서버를 운영한다고 가정해 보자.
일반적으로 하루에 1만 개 정도의 요청이 발생한다.
하지만 잘못 작성된 클라이언트 코드가 반복적으로 API를 호출할 수 있다.
또는 자동화된 Bot이 매우 많은 요청을 전송할 수도 있다.
이 경우 실제 Workers 사용량이 빠르게 증가할 가능성이 있다.
기존 비용 알림 기능은 특정 금액을 초과했을 때 이메일을 전송할 수 있었다.
하지만 실제 요청을 자동으로 중단하지는 않았다.
따라서 개발자가 알림을 확인하지 못하는 동안에도 사용량이 계속 증가할 수 있었다.
이번 기능은 이러한 상황에 대응하도록 실제 사용 차단을 선택할 수 있게 한다.
서비스별 지출 상한
새로운 정책은 다음 두 서비스를 대상으로 한다.
- Workers
- Durable Objects
각 서비스에는 독립적인 지출 상한을 지정할 수 있다.
예를 들어 다음처럼 구성할 수 있다.
| 서비스 | 지출 상한 | 정책 |
|---|---|---|
| Workers | 50달러 | 상한 도달 시 사용 차단 |
| Durable Objects | 20달러 | 이메일 알림만 발송 |
이러한 구성에서는 Workers 사용량이 설정한 금액을 초과하면 새로운 Workers 호출이 차단될 수 있다.
반면 Durable Objects의 20달러 알림은 서비스 실행을 중단하지 않는다.
따라서 비용이 증가했다는 사실을 통지하는 정책과 실제 서비스를 중단하는 정책을 구분할 수 있다.
하지만 이번 설정은 개별 Worker Script 또는 개별 Durable Object의 지출 상한을 제공하는 기능이 아니다.
Cloudflare가 설명한 적용 단위는 현재 계정의 해당 제품 전체다.
즉 하나의 계정에 여러 Workers 애플리케이션이 존재한다면 동일한 제품별 Spending Limit의 영향을 받을 수 있다.
Dashboard 설정
Cloudflare Dashboard에서는 다음 위치에서 설정할 수 있다.
Billing > Spend limits
여기서 다음 정보를 지정한다.
- 지출 상한
- 이메일 수신자
- 경고를 전송할 금액
- 상한 도달 시 사용 차단 여부
예를 들어 Workers의 상한을 10달러로 지정했다고 가정해 보자.
개발자는 다음 금액에서 알림을 받을 수 있다.
- 5달러
- 9달러
하지만 실제 서비스가 중단되면 안 되는 상황이라면 Block usage를 비활성화할 수 있다.
이 경우 비용 알림만 제공하고 사용량은 계속 증가할 수 있다.
CLI에서 설정
새로운 기능은 Cloudflare CLI에서도 관리할 수 있다.
대표적인 명령은 다음과 같다.
cf billing spend-policies create
예를 들어 Workers의 지출 상한을 50달러로 설정할 수 있다.
주요 옵션은 다음과 같다.
--product-id workers--enabled true--threshold-cents 5000--action usage_block
여기서 threshold-cents는 미국 센트 단위다.
따라서 5000은 50달러다.
Durable Objects에는 다음 제품 ID를 사용한다.
durable_objects
이러한 설정은 실제 인프라 비용 관리 정책을 CLI 명령으로 관리할 때 활용할 수 있다.
상한을 초과할 수도 있음
이번 기능에는 중요한 제한사항이 존재한다.
실제 청구 금액이 설정한 상한을 절대로 초과하지 않는다는 보장은 없다.
Cloudflare는 사용량 보고와 차단 정책의 적용에 시간이 필요하다고 설명한다.
예를 들어 실제 지출 상한을 50달러로 지정했다고 가정해 보자.
하지만 사용량이 매우 빠르게 증가하고 있다.
Cloudflare의 비용 집계 시스템이 해당 사용량을 반영하기 전에 추가 요청이 처리될 수 있다.
따라서 실제 기록된 지출이 50달러를 조금 초과할 가능성이 있다.
이러한 특성 때문에 이번 기능을 정확한 금액의 선불 결제 제한과 동일하게 취급해서는 안 된다.
지출 상한 도달 이후의 동작
사용 차단을 활성화한 상태에서 상한에 도달하면 Cloudflare는 해당 제품의 지원되는 작업을 차단한다.
Workers의 경우 새로운 호출이 차단될 수 있다.
이러한 상황은 서비스 사용자에게 직접적인 영향을 준다.
예를 들어 API 서버가 Workers로 구현돼 있다고 가정해 보자.
지출 상한 때문에 새로운 호출이 차단된다면 실제 API 요청이 정상적으로 처리되지 않을 수 있다.
따라서 지출 상한은 단순한 비용 관리 기능이면서 동시에 서비스 가용성에 영향을 주는 정책이다.
다음 방법으로 사용을 재개할 수 있다.
- 지출 상한을 현재 사용량보다 높게 변경
- 사용 차단 기능 비활성화
- 지출 상한 정책 삭제
- 다음 결제 기간 시작
다만 실제로 사용을 재개할 때는 기존 사용량이 현재 결제 기간에 누적돼 있다는 사실을 고려해야 한다.
AI Gateway의 비용 제한과 차이
Cloudflare에는 AI Gateway의 Spending Limit도 존재한다.
하지만 두 기능의 적용 범위는 다르다.
Workers와 Durable Objects의 새로운 상한은 제품별 계정 사용량을 관리한다.
반면 AI Gateway는 Gateway를 통해 전달하는 AI 요청에 대해 사용자별 또는 모델별 Budget을 관리할 수 있다.
따라서 AI 기능이 포함된 웹 애플리케이션에서는 다음 비용을 별도로 고려할 수 있다.
- 서버리스 실행 비용
- Durable Objects 사용 비용
- AI 모델 추론 비용
이번 발표에는 실제 비용 감소율이나 서버리스 실행 성능에 관한 Benchmark가 포함되지 않았다.
예상하지 못한 요청 증가로 인한 비용 위험을 관리하는 대신, 실제 서비스가 차단될 수 있다는 운영상의 트레이드오프를 추가한 비용 제어 기능이다.
Vercel Sandbox, 지역별 이미지 캐시 도입으로 시작 시간 최대 4배 단축 — 서울에서 약 3.5초에서 1.05초로 감소
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Vercel
- 안정화 단계: 정식 플랫폼 최적화
- 적용 대상: Vercel Sandbox, Vercel Container Registry, Snapshot
- 발표 성능: 최대 4배 빠른 초기화
- 서울 측정 결과: 약 3.5초 → 약 1.05초
- 측정 주체: Vercel 자체 End-to-end Benchmark
- 원문: https://vercel.com/changelog/vercel-sandbox-starts-up-to-four-times-faster
Vercel이 Sandbox의 초기화 과정에서 사용하는 Container Image를 지역별로 캐싱하도록 변경했다.
이번 변경은 새로운 Sandbox API를 추가하는 기능이 아니다.
이미 존재하는 격리 실행 환경을 더 빠르게 생성하도록 플랫폼 내부의 이미지 전달 방식을 변경한 것이다.
특히 미국 동부가 아닌 지역에서 Sandbox를 생성하는 서비스에 영향을 줄 수 있다.
Sandbox의 역할
Sandbox는 코드를 격리된 환경에서 실행하는 데 사용할 수 있다.
예를 들어 AI 코딩 에이전트를 생각할 수 있다.
사용자는 다음과 같이 요청한다.
이 프로젝트의 테스트 오류를 수정해 줘.
에이전트는 실제 코드를 변경해야 한다.
이후 테스트를 실행할 수 있다.
이러한 작업은 사용자의 프로젝트 파일과 외부 의존성을 처리한다.
따라서 일반적인 API 요청과 별도의 실행 환경이 필요할 수 있다.
Sandbox는 이러한 코드를 분리된 환경에서 실행하도록 한다.
하지만 실제 Sandbox를 생성할 때는 Container Image를 준비해야 한다.
기존 이미지 전달 구조
Vercel은 이미지의 원본 저장 위치로 미국 Washington, D.C. 지역인 다음 리전을 사용하고 있었다.
iad1
예를 들어 서울에서 새로운 Sandbox를 생성한다고 가정해 보자.
실행 환경은 서울과 가까운 리전에서 준비된다.
하지만 필요한 Container Image가 미국 동부의 원본 저장소에 존재한다면 이미지를 가져오는 과정에서 네트워크 지연이 발생할 수 있다.
실제 이미지의 크기가 크거나 여러 Layer가 필요하다면 초기화 과정에 더 많은 시간이 필요할 수 있다.
이번 업데이트는 이러한 원격 저장소 접근을 줄이는 데 목적이 있다.
지역별 이미지 캐시
새로운 구조에서는 이미지 데이터를 Sandbox가 실행되는 지역과 가까운 곳에 Cache한다.
예를 들어 서울에서 Sandbox를 실행한다고 가정해 보자.
해당 이미지가 지역 Cache에 존재한다.
그러면 미국 동부의 원본 저장소까지 데이터를 가져오러 갈 필요가 줄어든다.
따라서 실제 Sandbox를 시작하는 시간이 감소할 수 있다.
지원 대상은 다음과 같다.
- Vercel Managed Image
- 사용자 정의 Container Image
- Vercel Container Registry에 저장된 이미지
- 해당 이미지를 기반으로 생성한 Snapshot
즉 기존 Managed Image에만 적용되는 최적화는 아니다.
하지만 실제로 지역 Cache에 데이터가 존재해야 효과가 크다.
공식 성능 측정
Vercel이 공개한 결과는 다음과 같다.
| 지역 | 발표 결과 |
|---|---|
| Paris | 초기화 시간 약 75% 감소 |
| Seoul | 약 3.5초 → 약 1.05초 |
| 최대 개선 | 최대 4배 빠른 시작 |
서울의 수치를 기준으로 계산하면 약 3.3배 빠른 결과다.
하지만 이번 결과는 Vercel Managed Image를 사용한 자체 End-to-end Benchmark다.
독립적인 제3자가 동일한 조건으로 측정한 결과는 아니다.
또한 모든 Sandbox의 시작 시간이 1.05초로 고정된다는 의미도 아니다.
실제로 다음 조건에 따라 차이가 발생할 수 있다.
- Container Image 크기
- 지역 Cache Hit 여부
- Sandbox 생성 지역
- 사용하는 Snapshot
- 실제 실행 환경의 상태
캐시가 없는 경우
이번 최적화에는 중요한 제한사항이 있다.
지역 Cache에 필요한 이미지가 없다면 여전히 원본 이미지 저장소에서 데이터를 가져와야 할 수 있다.
따라서 새로운 Image를 처음 사용하는 경우와 이미 여러 번 사용한 Image의 초기화 시간은 다를 수 있다.
또한 iad1과 가까운 리전에서는 원본 저장소까지의 거리가 짧다.
따라서 다른 지역보다 개선 효과가 작을 수 있다.
Vercel도 원본 저장소에 가까운 지역에서는 성능 차이가 상대적으로 작다고 설명했다.
전체 AI 작업의 성능과는 다름
이번 수치는 Sandbox의 초기화 시간에 관한 것이다.
예를 들어 다음 작업을 생각할 수 있다.
- Sandbox 생성
- Git Repository 준비
- 의존성 설치
- 테스트 실행
- 결과 분석
이번 변경이 직접 개선하는 부분은 첫 번째 단계에 가깝다.
실제 테스트 실행이나 AI 모델의 추론 속도가 같은 비율로 증가한 것은 아니다.
따라서 AI 코딩 에이전트의 전체 작업 완료 시간이 반드시 4배 빨라졌다고 해석해서는 안 된다.
이번 발표는 Container Image의 지역별 재사용으로 Sandbox 생성 과정의 원격 데이터 접근을 줄이고, 특히 원본 저장소와 멀리 떨어진 지역의 초기화 시간을 개선한 플랫폼 최적화다.
Vercel AI Gateway, Parallel Decisions Fast 지원 — 실시간 웹 검색 근거를 이용한 Boolean·Choice·Score 판단
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Vercel
- 모델 제공업체: Parallel
- 모델 ID:
parallel/parallel-decisions-fast - 제공 상태: AI Gateway에서 사용 가능
- 무료 제공 기간: 2026-11-23까지
- 원문: https://vercel.com/changelog/parallel-decisions-now-available-on-ai-gateway-for-free
Vercel이 AI Gateway에서 Parallel Decisions Fast를 사용할 수 있도록 지원을 추가했다.
이번 업데이트는 일반적인 텍스트 생성 모델을 제공하는 것과 다르다.
모델이 실시간 웹 검색 결과를 근거로 특정 질문에 대한 판단값을 반환하도록 설계됐다는 점이 특징이다.
이전 브리핑에서 소개한 Gemini Decision 1과 마찬가지로 Boolean, Choice, Score 형태의 결과를 다룬다.
하지만 이번 모델은 현재 웹에서 검색한 정보를 판단 근거로 사용하고 관련 출처를 반환한다는 점에서 차이가 있다.
실시간 정보에 대한 판단
예를 들어 B2B 영업 서비스를 생각할 수 있다.
시스템은 기업 목록을 가지고 있다.
각 기업이 공식 CLI를 제공하는지 확인해야 한다.
일반적인 LLM에 다음 질문을 전달할 수 있다.
이 회사는 공식 CLI를 제공하나요?
하지만 실제 회사의 제품은 시간이 지나면서 변경될 수 있다.
모델이 이전에 학습한 정보만 사용한다면 현재 상태와 다른 결과를 반환할 가능성이 있다.
Parallel Decisions Fast는 실제 웹 검색을 수행한 뒤 관련 정보를 바탕으로 결과를 반환하도록 설계됐다.
세 가지 결과 유형
이번 모델은 다음 판단 유형을 지원한다.
| 유형 | 결과 |
|---|---|
| Boolean | 참일 확률 |
| Choice | 지정한 선택지 가운데 하나 |
| Score | 정의한 순서형 평가 기준에 따른 점수 |
예를 들어 Boolean 판단에서는 단순히 true 또는 false만 반환하는 대신 참일 확률을 제공할 수 있다.
애플리케이션은 해당 확률을 이용해 후속 처리를 결정할 수 있다.
예를 들어 확률이 매우 높다면 다음 단계로 이동할 수 있다.
반면 확률이 낮거나 애매하다면 추가 검증을 요청할 수 있다.
하지만 이 확률을 실제 세계의 정확한 발생 확률이나 검증된 신뢰도로 무조건 해석해서는 안 된다.
모델이 반환한 값 자체도 평가와 검증이 필요한 결과다.
여러 질문의 Context 공유
이번 기능에서는 관련된 여러 질문을 하나의 요청으로 전달할 수 있다.
예를 들어 한 기업에 대해 다음 내용을 확인한다고 가정해 보자.
- 공식 CLI가 존재하는가?
- 공개 API가 존재하는가?
- Enterprise 요금제가 존재하는가?
세 질문은 같은 기업에 관한 것이다.
따라서 서로 다른 웹 검색을 완전히 독립적으로 수행하는 대신 관련 검색 결과와 Context를 공유할 수 있다.
이러한 구조는 중복된 검색을 줄일 가능성이 있다.
하지만 이번 발표에는 실제 검색 횟수나 비용이 얼마나 감소하는지에 관한 정량적인 Benchmark가 포함되지 않았다.
AI SDK에서 사용
AI SDK 7에서는 다음 API를 사용할 수 있다.
experimental_decide
모델 ID는 다음과 같다.
parallel/parallel-decisions-fast
개발자는 입력 데이터와 판단할 질문을 전달한다.
이후 결과에서 실제 판단값을 확인할 수 있다.
관련 출처는 다음 Metadata에서 확인할 수 있다.
result.providerMetadata?.parallel?.citations
출처에는 다음 정보가 포함될 수 있다.
- URL
- 제목
- 근거가 되는 일부 텍스트
하지만 중요한 제한사항이 있다.
반환된 출처는 요청 전체에 연결되며, 각 개별 판단값에 어떤 출처가 사용됐는지 일대일로 대응하지 않을 수 있다.
따라서 여러 질문의 결과를 자동으로 검증해야 하는 경우에는 출처와 실제 판단의 관계를 추가로 확인할 필요가 있다.
AI 판단과 실제 권한 검증
이러한 모델은 다음 작업에 활용할 수 있다.
- CRM 데이터 보강
- 기업 정보 확인
- 제품 기능 분류
- 신청 조건 검토
- 외부 정보에 기반한 주장 확인
하지만 실제 비즈니스 권한을 모델에만 맡기는 것은 위험할 수 있다.
예를 들어 사용자가 환불받을 법적인 자격이 있는지 확인한다고 가정해 보자.
모델은 관련 정책을 검색하고 조건을 판단할 수 있다.
하지만 실제 결제를 취소할지 결정하는 작업에는 해당 서비스의 데이터와 정책 검증이 필요하다.
따라서 검색 기반 AI 판단과 최종 비즈니스 트랜잭션의 실행을 구분해야 한다.
비용과 안정화 범위
Vercel은 이번 모델을 2026년 11월 23일까지 무료로 제공한다고 발표했다.
별도의 Parallel 계정을 생성할 필요는 없다.
또한 AI Gateway의 기존 사용량 및 비용 관리 기능에 포함된다.
하지만 무료 기간이 종료된 이후의 모든 사용이 무료로 유지된다는 의미는 아니다.
실제 비용은 해당 시점의 가격 정책을 확인해야 한다.
이번 발표에는 다음 수치가 포함되지 않았다.
- 판단 정확도
- 웹 검색의 최신성 정확도
- 평균 요청 지연 시간
- 동시 요청 처리량
- 독립적인 모델 비교 결과
이번 업데이트는 실시간 웹 검색을 기반으로 구조화된 판단값과 근거를 반환하는 모델을 Vercel의 기존 AI SDK 및 Gateway에 연결한 기능 확대다.
AWS, Google Cloud, Azure의 이번 조사 범위에서 이외에 포함할 만한 주요 신규 서버리스·데이터베이스·API 발표는 확인하지 못했습니다.
AWS에서는 11월 13일 Kiro의 GovCloud(US) 환경에 Grok 4.7을 실험적으로 제공한다는 발표도 확인했다.
하지만 적용 범위가 특정 정부 클라우드의 개발 도구에 한정돼 있고, 일반적인 웹 애플리케이션의 실행 환경이나 주요 플랫폼 API가 새롭게 변경된 것은 아니므로 상세 항목으로 선정하지 않았다.
이전 브리핑에서 다룬 Cloudflare Hyperdrive 한도 확대, Durable Objects의 자동 재시도와 Routing 최적화, Google App Engine의 Direct VPC Egress, Vercel Schedules도 반복하지 않았다.
4️⃣ 개발 도구 및 보안
Vercel Build, 모든 빌드 환경의 캐시 크기 제한을 5GB로 확대 — 큰 Monorepo에서 캐시 폐기 가능성 감소
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Vercel
- 안정화 단계: 정식 플랫폼 변경
- 적용 대상: Vercel Basic, Standard, Enhanced, Turbo Build Machine
- 변경 후 캐시 한도: 모든 환경에서 5GB
- 적용 방식: 자동 적용, 추가 비용 없음
- 원문: https://vercel.com/changelog/build-cache-size-limit-on-vercel-increased-to-five-gb
Vercel이 모든 Build Machine의 빌드 캐시 최대 크기를 5GB로 확대했다.
이번 변경은 11월 9일 발표된 빌드 디스크 공간 64GB 확대와 다른 기능이다.
빌드 과정에서 임시 파일을 저장할 수 있는 전체 디스크 공간과 다음 배포에서 재사용할 수 있는 캐시의 최대 크기는 서로 다른 자원이다.
이번 업데이트는 두 번째에 해당한다.
Build Cache의 역할
웹 애플리케이션을 배포할 때는 여러 작업이 실행된다.
예를 들어 다음과 같다.
- 의존성 설치
- TypeScript 및 JavaScript 컴파일
- CSS 처리
- 정적 페이지 생성
- 최종 빌드 결과물 생성
매번 같은 의존성을 다시 다운로드하거나 변경되지 않은 파일을 다시 처리하면 불필요한 시간이 발생할 수 있다.
따라서 빌드 시스템은 일부 결과를 Cache에 저장한다.
다음 배포에서는 해당 데이터를 재사용할 수 있다.
특히 의존성 설치에 많은 시간이 필요한 Monorepo에서 이러한 캐시는 중요할 수 있다.
기존 캐시 크기 제한
기존 한도는 다음과 같았다.
| Build Machine | 기존 캐시 한도 | 변경 후 |
|---|---|---|
| Basic | 1.5GB | 5GB |
| Standard | 1.5GB | 5GB |
| Enhanced | 3GB | 5GB |
| Turbo | 4.75GB | 5GB |
기존에는 캐시 크기가 해당 한도를 초과하면 전체 캐시가 폐기될 수 있었다.
예를 들어 Basic Build Machine을 사용하는 Monorepo가 있다고 가정해 보자.
빌드 캐시의 크기는 2GB다.
하지만 기존 제한은 1.5GB였다.
따라서 해당 캐시를 다음 배포에서 재사용하지 못할 수 있었다.
이후 다시 빌드할 때 의존성 설치와 일부 컴파일 작업을 반복해야 했다.
새로운 한도에서는 2GB 캐시를 유지할 수 있는 범위가 생긴다.
모든 프로젝트가 빨라지는 것은 아님
이번 변경에서 중요한 부분은 캐시 크기가 증가했다고 해서 실제 빌드 시간이 반드시 감소하는 것은 아니라는 사실이다.
예를 들어 어떤 프로젝트의 빌드 캐시 크기가 300MB라고 가정해 보자.
기존 1.5GB 한도에도 충분히 들어간다.
따라서 이번 변경으로 추가적인 효과가 거의 없을 수 있다.
반면 기존 한도를 초과해 캐시가 폐기되던 프로젝트에서는 효과가 클 수 있다.
또한 캐시가 존재하더라도 실제 파일의 변경이나 의존성 버전 차이 때문에 재사용하지 못할 수 있다.
따라서 Cache Hit와 Cache Miss를 구분해야 한다.
빌드 디스크 공간과 구분
Vercel은 앞서 Build Machine의 디스크 공간을 64GB로 확대했다.
하지만 64GB는 빌드 과정에서 사용하는 디스크 공간이다.
이번 5GB는 다음 배포로 전달할 수 있는 캐시의 크기 제한이다.
따라서 다음 두 값을 혼동해서는 안 된다.
- 빌드 디스크: 64GB
- 빌드 캐시: 5GB
예를 들어 빌드 과정에서 20GB의 임시 파일을 생성할 수 있다.
하지만 해당 파일 전체를 다음 배포의 캐시로 유지할 수 있는 것은 아니다.
적용 범위와 한계
이번 변경은 별도의 설정 없이 모든 Build Machine에 적용된다.
추가 요금도 없다.
하지만 공식 발표에는 실제 평균 빌드 시간이 얼마나 감소했는지에 관한 Benchmark가 포함되지 않았다.
따라서 이번 업데이트는 빌드 성능 개선을 보장하는 수치가 아니라 캐시를 보존할 수 있는 최대 크기를 확대한 플랫폼 정책 변경이다.
Vercel Security Review, Vercel 프로젝트에 연결되지 않은 GitHub Repository도 자동 보안 검토
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Vercel
- 안정화 단계: 기존 Security Review의 지원 범위 확대
- 적용 대상: Vercel GitHub App에 접근 권한이 있는 GitHub Repository
- 실행 조건: 해당 Repository의 Security Review 활성화 및 Push
- 원문: https://vercel.com/changelog/vercel-security-review-no-longer-requires-a-linked-vercel-project
Vercel이 Security Review를 사용하기 위해 반드시 Vercel Deployment 프로젝트를 연결해야 했던 제약을 제거했다.
이번 기능은 새로운 보안 분석 엔진을 처음 출시한 것이 아니다.
기존 Security Review를 사용할 수 있는 Repository의 범위를 확대한 것이다.
특히 Vercel에 배포하지 않는 라이브러리나 백엔드 프로젝트에서도 같은 보안 검토 기능을 사용할 수 있게 됐다.
기존 프로젝트 연결 조건
일반적인 Vercel 프로젝트에서는 GitHub Repository를 연결할 수 있다.
이후 새로운 Commit을 Push하면 배포가 생성될 수 있다.
Security Review는 이 과정에서 코드 변경을 분석할 수 있다.
하지만 실제 모든 Repository가 Vercel에 배포되는 것은 아니다.
예를 들어 다음 프로젝트를 생각할 수 있다.
- 공통 TypeScript 라이브러리
- 내부 ESLint 설정 패키지
- Node.js CLI
- 별도 서버에 배포하는 API
- AI 에이전트용 도구
이러한 프로젝트에서는 Vercel Deployment가 필요하지 않을 수 있다.
하지만 코드 변경에 대한 보안 검토는 필요할 수 있다.
이번 업데이트에서는 이러한 Repository도 지원한다.
Repository 자체에서 검사
새로운 방식에서는 Vercel 프로젝트나 배포가 없어도 Security Review를 실행할 수 있다.
조건은 해당 Repository가 Vercel GitHub App의 접근 범위에 포함돼 있다는 것이다.
Security Review가 활성화된 Repository에 새로운 Commit을 Push하면 자동으로 검토가 시작된다.
따라서 배포 과정과 코드 보안 검토를 분리할 수 있다.
기본 Repository 범위
Vercel은 연결된 GitHub 계정에서 GitHub App이 접근할 수 있는 프로젝트 미연결 Repository를 기본 검토 범위에 포함할 수 있도록 변경했다.
하지만 관리자는 적용 범위를 제한할 수 있다.
대표적으로 다음 기준을 사용할 수 있다.
- 특정 GitHub 계정
- 특정 Repository
따라서 모든 Repository를 일괄적으로 검사해야 하는 것은 아니다.
예를 들어 하나의 GitHub Organization에 수십 개의 Repository가 있다고 가정해 보자.
그중 일부만 Security Review를 사용하도록 설정할 수 있다.
배포 Context의 차이
Vercel 프로젝트에 연결된 Repository와 연결되지 않은 Repository는 실제 검토 Context가 다를 수 있다.
연결된 프로젝트에서는 다음 정보를 함께 사용할 수 있다.
- 코드 변경
- 프로젝트 설정
- 배포 관련 Context
반면 연결되지 않은 Repository에서는 실제 Commit의 변경 내용을 중심으로 분석한다.
따라서 동일한 코드라도 배포 환경에 관한 추가 정보가 없는 경우에는 검토 결과가 달라질 가능성이 있다.
이는 미연결 Repository에서도 기존 연결 프로젝트와 완전히 동일한 Context로 분석한다는 의미가 아니라는 점에서 중요하다.
보안 검토의 한계
Security Review는 코드의 문제를 찾는 데 도움을 줄 수 있다.
하지만 실제 Repository가 안전하다는 사실을 보장하지는 않는다.
예를 들어 다음 문제는 별도의 검증이 필요할 수 있다.
- 실제 인증 및 인가 정책
- 외부 API의 권한
- Runtime 환경 변수
- 데이터베이스의 접근 정책
- 동적인 코드 실행
- 실제 배포 환경의 설정
또한 이번 발표에는 실제 취약점 탐지율이나 False Positive 비율에 관한 독립적인 Benchmark가 포함되지 않았다.
이번 변경은 Vercel에 배포하지 않는 코드베이스에도 기존 보안 검토 도구를 적용할 수 있도록 Repository와 Deployment의 연결 제약을 제거한 업데이트다.
GitHub AI Scan, 조직과 Repository별 활성화 정책을 REST API로 관리하는 Public Preview 공개
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: GitHub
- 안정화 단계: Public Preview
- 적용 대상: GitHub Code Scanning, AI Scan
- 신규 설정:
repository_scan - 지원 범위: 조직 및 Repository 수준의 정책 관리
- 원문: https://github.blog/changelog/2026-11-13-manage-ai-scan-for-repositories-using-the-rest-api/
GitHub가 AI Scan의 활성화 여부를 REST API로 설정할 수 있도록 기능을 확장했다.
이번 기능은 새로운 취약점 탐지 모델을 출시한 것이 아니다.
이미 존재하는 AI Scan의 사용 정책을 여러 Repository에 일관되게 적용할 수 있도록 관리 인터페이스를 제공하는 것이다.
특히 많은 Repository를 운영하는 Organization에서 영향을 줄 수 있다.
AI Scan의 역할
GitHub Code Scanning은 소스코드를 분석해 보안 문제를 발견하는 데 사용할 수 있다.
기존 정적 분석 도구 외에도 AI 기반 분석 기능을 사용할 수 있다.
예를 들어 보안 담당자가 여러 Repository의 기존 코드에 취약점이 존재하는지 검사하고 싶다고 가정해 보자.
이러한 작업에서 AI Scan을 사용할 수 있다.
하지만 실제로 많은 Repository를 운영한다면 각 Repository의 설정을 일일이 변경하는 작업이 번거로울 수 있다.
이번 업데이트에서는 이러한 작업을 API로 관리할 수 있도록 했다.
repository_scan
새롭게 추가된 설정은 다음과 같다.
repository_scan
지원하는 값은 다음과 같다.
enableddisabled
이 값은 기존 AI Scan 설정 Endpoint에서 사용할 수 있다.
대표적인 API는 다음과 같다.
GET /orgs/{org}/code-scanning/ai-scan
PATCH /orgs/{org}/code-scanning/ai-scan
또한 Repository 수준에서는 다음 Endpoint를 사용할 수 있다.
GET /repos/{owner}/{repo}/code-scanning/ai-scan
PATCH /repos/{owner}/{repo}/code-scanning/ai-scan
따라서 Organization 전체의 기본 정책과 개별 Repository의 정책을 별도로 관리할 수 있다.
조직과 Repository의 우선순위
이번 기능에서는 다음 규칙을 적용한다.
첫 번째는 Organization이 AI Scan을 비활성화한 경우다.
이 경우 해당 Organization의 Repository는 AI Scan을 활성화할 수 없다.
두 번째는 Organization이 AI Scan을 활성화한 경우다.
Repository는 해당 설정을 상속할 수 있다.
하지만 개별 Repository에서 사용하지 않도록 Opt-out할 수 있다.
세 번째는 Enterprise 정책에서 AI Scan을 허용하지 않는 경우다.
이때 API로 활성화를 시도하면 HTTP 422 오류를 반환한다.
이러한 구조는 실제 하위 Repository의 설정이 상위 정책을 우회하지 못하도록 한다.
PR Scan과 함께 설정
기존에는 다음 설정이 존재했다.
pr_scan
이는 Pull Request에 대한 AI Scan과 관련된다.
새로운 repository_scan은 기존 Repository의 코드 분석에 관한 설정이다.
이번 API에서는 두 값을 하나의 요청에서 함께 변경할 수 있다.
하지만 중요한 특징이 있다.
두 설정 가운데 하나라도 변경이 허용되지 않는다면 요청 전체가 실패하고 두 설정 모두 변경되지 않는다.
이 경우 HTTP 422 오류를 반환한다.
따라서 일부 설정만 적용되고 다른 설정은 실패하는 상태를 줄일 수 있다.
활성화와 실제 검사는 다름
이번 기능에서 가장 중요한 제한사항은 다음과 같다.
AI Scan을 활성화했다고 해서 실제 Repository의 검사가 즉시 시작되는 것은 아니다.
활성화는 해당 기능을 사용할 수 있는 상태로 변경하는 작업이다.
실제 Repository의 Scan은 쓰기 권한을 가진 사용자가 필요에 따라 실행해야 한다.
또한 각 Scan에는 GitHub Code Security AI Credit이 사용된다.
따라서 관리 API를 이용해 모든 Repository를 활성화했다고 해서 실제 검사가 자동으로 모두 실행되거나 무료로 수행된다고 해석해서는 안 된다.
지원 범위
이번 Public Preview는 다음 Repository에서 사용할 수 있다.
- Organization이 소유한 공개 Repository
- GitHub Code Security 또는 Advanced Security를 사용하는 Private Repository
- 같은 조건의 Internal Repository
Organization Owner와 Security Manager는 조직 설정을 관리할 수 있다.
Repository Administrator는 개별 Repository의 설정을 비활성화할 수 있다.
GitHub Enterprise Server에서는 지원하지 않는다.
또한 Enterprise Owner가 상위 정책에서 AI Scan을 허용해야 실제로 사용할 수 있다.
실제 인프라 자동화
이번 변경은 Infrastructure as Code와 연결할 수 있다.
예를 들어 새로운 Repository를 생성할 때 다음 작업을 자동으로 수행하는 시스템을 생각할 수 있다.
- Repository 생성
- 기본 Branch Protection 적용
- Dependabot 정책 적용
- AI Scan 활성화 정책 적용
이렇게 하면 개발자가 Repository를 새롭게 만들 때마다 보안 설정을 수동으로 구성할 필요가 줄어든다.
하지만 API로 설정을 적용한다는 사실만으로 실제 보안 정책이 올바르다는 의미는 아니다.
어떤 Repository에서 Scan을 사용할지와 관련 비용을 검토해야 한다.
이번 발표에는 취약점 탐지 성능에 관한 정량적인 Benchmark가 포함되지 않았다.
AI 기반 보안 분석 기능을 여러 Repository에 일관되게 제공하기 위해 활성화 정책을 REST API로 관리하도록 한 Public Preview 업데이트다.
GitHub Copilot, 12월 14일 여섯 모델 지원 종료 예고 — Chat·Agent·CLI의 모델 선택 변경 필요
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: GitHub
- 지원 종료 예정일: 2026-12-14
- 적용 대상: GitHub Copilot Chat, Agent Mode, Inline Edit, Code Completion 등
- 변경 유형: 기존 모델의 지원 종료 예고
- 원문: https://github.blog/changelog/2026-11-13-upcoming-deprecation-of-selected-github-copilot-models-in-december/
GitHub가 2026년 12월 14일부터 일부 Copilot 모델의 제공을 종료한다고 발표했다.
이번 변경은 새로운 모델을 출시하는 발표가 아니다.
실제 Copilot에서 사용할 수 있는 모델 목록을 정리하는 정책 변경이다.
특히 특정 모델을 명시적으로 선택해 사용하는 개발자나 조직에 영향을 줄 수 있다.
지원 종료 대상
공식 발표에서 확인한 목록은 다음과 같다.
| 종료 예정 모델 | 종료일 | GitHub 권장 대체 모델 |
|---|---|---|
| Claude Fable 5 | 2026-12-14 | Claude Fable 5.1 |
| Claude Haiku 4.5 | 2026-12-14 | Claude Haiku 5.5 |
| Gemini 3.8 Flash | 2026-12-14 | Gemini 4 Hydrogen |
| Gemini 4 Argon | 2026-12-14 | Gemini 4 Carbon |
| Grok 4.6 | 2026-12-14 | Grok 4.7 |
| Kimi K3 | 2026-12-14 | 추후 발표 |
해당 모델은 다음 Copilot 기능에서 지원 종료 대상이다.
- Copilot Chat
- Inline Edit
- Ask Mode
- Agent Mode
- Code Completion
따라서 특정 모델을 사용하는 워크플로가 있다면 실제 종료일 이후 같은 모델을 선택할 수 없게 될 수 있다.
Claude Haiku 4.5의 예외
이번 발표에는 중요한 예외가 존재한다.
개인 GitHub Copilot 연간 구독자는 Claude Haiku 4.5를 계속 사용할 수 있다.
따라서 모든 Copilot 이용자에게 해당 모델의 지원 종료가 동일하게 적용되는 것은 아니다.
실제 구독 유형에 따라 차이가 존재한다.
Business와 Enterprise의 기본 정책
GitHub는 Copilot Business와 Enterprise의 모델 접근 정책도 설명했다.
조직이 기본적으로 새로운 모델을 허용하는 설정을 사용한다면 권장 대체 모델이 자동으로 활성화될 수 있다.
하지만 관리자가 기본 자동 활성화를 비활성화했거나 특정 모델을 명시적으로 차단한 경우에는 다르다.
이때 실제 관리자가 Copilot Settings에서 대체 모델의 사용을 허용해야 할 수 있다.
따라서 개인 개발자의 모델 선택 가능 여부는 계정뿐 아니라 조직의 모델 정책에도 영향을 받는다.
모델을 교체했을 때의 차이
새로운 모델로 변경한다고 해서 기존 모델과 정확히 같은 결과가 생성되는 것은 아니다.
예를 들어 다음 차이가 발생할 수 있다.
- Tool Calling 지원 범위
- 응답 형식
- 추론 속도
- 코드 생성 방식
- Context Window
- 사용량 및 비용
따라서 특정 모델에 맞춰 작성한 Prompt나 에이전트 설정은 대체 모델에서도 실제로 동작하는지 확인해야 한다.
특히 자동으로 GitHub Repository를 수정하는 에이전트에서는 결과의 품질과 권한을 별도로 검증해야 한다.
이번 발표는 기존 모델의 지원 종료 일정에 관한 것이다.
대체 모델의 성능을 독립적으로 비교한 Benchmark가 포함된 것은 아니다.
GitHub Copilot의 모델 선택 정책과 사용 가능한 모델 구성이 12월 14일에 변경된다는 사실을 미리 안내한 발표다.
GitHub Copilot App, GitHub Enterprise Server 연동 정식 지원 — 사내 Repository·Issue·PR 접근 확대
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: GitHub
- 안정화 단계: Generally Available
- 적용 대상: GitHub Copilot App 1.2.0 이상, GitHub Enterprise Server
- 주요 기능: GHES Repository, Issue, Pull Request 접근
- 제한사항: GHES Cloud Session 미지원
- 원문: https://github.blog/changelog/2026-11-13-github-enterprise-server-support-in-the-github-copilot-app/
GitHub가 GitHub Copilot App에서 GitHub Enterprise Server(GHES)의 콘텐츠에 접근하는 기능을 정식 출시했다.
해당 기능은 이전에 Opt-in Preview로 제공되고 있었다.
이번 발표는 정식 안정화에 관한 것이다.
GitHub Enterprise Server
일반적인 GitHub Repository는 github.com에 존재할 수 있다.
하지만 일부 기업은 자체 인프라에 GitHub Enterprise Server를 설치해 사용한다.
이 경우 실제 Repository와 Issue, Pull Request가 기업이 운영하는 GitHub 서버에 존재한다.
따라서 일반적인 github.com 계정과는 별도의 연결이 필요할 수 있다.
새로운 기능에서는 Copilot App에 GHES 계정을 추가할 수 있다.
이후 해당 서버의 다음 데이터를 활용할 수 있다.
- Repository
- Issue
- Pull Request
계정 연결
먼저 다음 계정 가운데 하나로 로그인해야 한다.
- GitHub.com
- GHE.com
이후 Copilot App에서 다음 경로로 이동한다.
Settings > Accounts > Add account
여기서 GitHub Enterprise Server를 선택할 수 있다.
이렇게 하면 기존에 GHES에서 관리하던 개발 작업을 Copilot App에 연결할 수 있다.
Cloud Session의 제한
이번 GA에는 중요한 제한이 있다.
GitHub Enterprise Server를 사용하는 경우 Cloud Session은 지원되지 않는다.
따라서 GHES Repository와 Issue를 읽을 수 있다는 사실과 모든 Copilot App의 원격 실행 기능을 동일하게 사용할 수 있다는 사실은 구분해야 한다.
또한 실제 Repository에 접근하려면 해당 GHES 계정의 권한이 필요하다.
이번 발표에는 작업 완료 시간이나 코드 생성 정확도에 관한 Benchmark가 포함되지 않았다.
사내 GitHub Enterprise Server에 존재하는 개발 작업을 Copilot App에서 다룰 수 있도록 계정 연결과 콘텐츠 지원 범위를 정식 확대한 기능이다.
Cloudflare Browser Run, Quick Action의 로컬 실행 지원 — wrangler dev에서 실제 Chrome으로 테스트
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 로컬 개발 기능 지원
- 적용 대상: Cloudflare Browser Run, Wrangler
- 핵심 API:
.quickAction() - 실행 방식: 개발자 기기의 로컬 Chrome 이용
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-13-quick-actions-local-dev/
Cloudflare가 Browser Run Quick Action을 로컬 개발 환경에서도 실행할 수 있도록 기능을 변경했다.
이번 업데이트는 새로운 브라우저 자동화 API를 처음 도입하는 발표가 아니다.
기존 Quick Action이 원격 실행 환경뿐 아니라 로컬 개발 환경에서도 동작하도록 지원 범위를 확장한 것이다.
기존 로컬 개발의 문제
Browser Run은 브라우저 자동화 작업을 수행하는 데 사용할 수 있다.
예를 들어 다음 작업이다.
- 웹페이지 열기
- 화면의 요소 조작
- 일부 데이터 추출
- 브라우저에서 실제 작업 수행
Quick Action은 이러한 브라우저 작업을 간단한 API로 표현하는 기능이다.
하지만 기존에는 .quickAction()을 로컬 개발 환경에서 호출하면 오류가 발생할 수 있었다.
대표적인 오류는 다음과 같다.
The RPC receiver does not implement the method "quickAction"
즉 원격 환경에서는 동작하지만 로컬에서는 해당 메서드가 구현되지 않은 상태였다.
따라서 개발자는 실제 코드를 검증하기 위해 원격 실행 환경을 사용해야 할 수 있었다.
새로운 로컬 실행
이번 변경 이후에는 다음 명령으로 로컬 개발 서버를 실행할 수 있다.
npx wrangler dev
Browser Binding에서 .quickAction()을 호출하면 개발자 기기의 Chrome을 사용한다.
따라서 실제 로컬 브라우저에서 해당 작업을 테스트할 수 있다.
이러한 방식은 다음 문제를 발견하는 데 도움이 될 수 있다.
- 잘못된 Selector
- 예상과 다른 페이지 구조
- 브라우저 이벤트 처리 문제
- 실제 자동화 단계의 실패
하지만 로컬 Chrome에서 정상적으로 동작했다는 사실만으로 모든 원격 실행 환경에서 동일한 결과가 보장되는 것은 아니다.
실제 브라우저와 네트워크, 실행 정책이 다를 수 있기 때문이다.
JSON 추출의 제한사항
이번 기능에는 중요한 제한사항이 존재한다.
json Quick Action은 로컬 환경에서 실제 AI 기반 추출을 수행하지 않는다.
Cloudflare는 해당 Action이 실제 데이터를 추출하기 위해 AI 모델을 사용한다고 설명한다.
하지만 로컬 시뮬레이션에는 해당 모델이 존재하지 않는다.
따라서 로컬 json Action은 개발자가 지정한 Schema에 맞는 Placeholder 데이터를 반환한다.
예를 들어 상품 페이지에서 다음 정보를 추출한다고 가정해 보자.
- 상품 이름
- 가격
- 재고 상태
로컬 개발에서는 Schema에 맞는 값이 반환될 수 있다.
하지만 실제 페이지의 상품 이름과 가격을 정확하게 읽었다는 의미는 아니다.
따라서 로컬 Schema 검증과 실제 AI 추출 결과의 정확성 검증을 구분해야 한다.
Kitesurf 미지원
또 다른 제한사항은 Kitesurf다.
이번 로컬 Quick Action에서는 Kitesurf Browser를 지원하지 않는다.
따라서 실제 원격 환경의 모든 Browser Backend를 로컬에서 동일하게 재현할 수 있는 것은 아니다.
Cloudflare는 로컬 시뮬레이션의 범위를 계속 확대할 수 있지만, 이번 공식 발표에서 확인한 지원 범위는 Chrome 기반 실행이다.
성능과 적용 범위
이번 업데이트는 개발 환경의 실행 경로를 추가한 것이다.
실제 브라우저 자동화의 처리량이나 실행 속도가 얼마나 개선되는지에 관한 Benchmark는 포함되지 않았다.
따라서 원격 Browser Run보다 로컬 실행이 반드시 빠르다고 단정해서는 안 된다.
이번 발표는 기존 Browser Run Quick Action의 로컬 테스트를 가능하게 하면서 실제 AI 추출 기능과 브라우저 종류에 관한 제한을 명시한 개발 환경 개선이다.
Cloudflare Terraform Provider 5.29.0, AI Search Namespace별 인스턴스 관리 지원 — Import ID와 Data Source 설정 변경
- 발표일: 2026-11-13 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 필요 버전: Terraform Provider 5.29.0
- 안정화 단계: 정식 Provider 기능
- 적용 대상: Cloudflare AI Search, Terraform
- 주요 변경: Namespace Resource 및 Instance Namespace 지정
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-13-terraform-namespaces/
Cloudflare가 AI Search 인스턴스를 Terraform으로 관리할 때 default 이외의 Namespace를 사용할 수 있도록 기능을 확장했다.
이번 업데이트는 AI Search의 새로운 검색 모델이나 인덱싱 알고리즘을 도입하는 기능이 아니다.
이미 존재하는 AI Search 인스턴스를 실제 Namespace별로 관리하는 Terraform Provider의 지원 범위를 확대한 것이다.
Namespace의 역할
예를 들어 하나의 Cloudflare 계정에서 다음 세 가지 검색 서비스를 운영한다고 가정해 보자.
- 고객지원 문서 검색
- 개발자 문서 검색
- 내부 지식 검색
세 서비스는 서로 다른 검색 인덱스와 설정을 사용할 수 있다.
Namespace를 이용하면 이러한 리소스를 논리적으로 구분해 관리할 수 있다.
하지만 기존 Terraform Provider에서는 default Namespace에 있는 AI Search 인스턴스를 중심으로 관리해야 했다.
이번 버전에서는 다른 Namespace도 사용할 수 있다.
새로운 Terraform Resource
다음 Resource를 사용할 수 있다.
cloudflare_ai_search_namespace
이 Resource는 새로운 AI Search Namespace를 생성한다.
이후 AI Search Instance를 생성할 때 다음 속성을 지정한다.
namespace
예를 들어 다음 이름을 사용할 수 있다.
support
이렇게 하면 특정 AI Search Instance를 support Namespace에 배치할 수 있다.
기존 코드에서 Namespace를 명시하지 않은 경우에는 기본값인 default가 적용된다.
따라서 기존 인스턴스의 Namespace가 업그레이드만으로 자동 변경되는 것은 아니다.
Import ID 변경
이번 업데이트에서는 Terraform의 Import ID 형식도 변경됐다.
새로운 형식은 다음과 같다.
<account_id>/<namespace>/<id>
예를 들어 다음 인스턴스가 있다고 가정해 보자.
- Account ID:
account123 - Namespace:
support - Instance ID:
docs
이 경우 Import ID는 다음 형태가 된다.
account123/support/docs
기존 default Namespace의 인스턴스도 새로운 형식에서는 Namespace를 포함해야 한다.
따라서 이전 버전에서 사용하던 Import 명령을 그대로 실행하면 문제가 발생할 수 있다.
Data Source 변경
다음 Data Source에서도 Namespace를 명시해야 한다.
cloudflare_ai_search_instancecloudflare_ai_search_instances
예를 들어 default Namespace의 인스턴스를 조회하려면 다음 값을 추가한다.
namespace = "default"
또한 cloudflare_ai_search_instances는 하나의 Namespace에 속한 인스턴스를 반환한다.
기존 cloudflare_ai_search_instance의 filter.namespace 옵션은 제거됐다.
따라서 이전 Terraform 코드를 사용하는 프로젝트에서는 관련 설정을 변경해야 할 수 있다.
Namespace 변경 시 인스턴스 교체
이번 업데이트에서 가장 중요한 제한사항은 다음과 같다.
기존 AI Search Instance의 namespace를 변경하면 Terraform은 해당 인스턴스를 교체한다.
예를 들어 기존 인스턴스를 default에서 support로 옮기려고 한다고 가정해 보자.
Terraform은 단순히 Namespace 속성을 수정하는 방식으로 처리하지 않는다.
실제로 인스턴스를 새롭게 생성하는 교체 작업이 발생할 수 있다.
이러한 작업은 실제 검색 서비스의 데이터와 설정에 영향을 줄 수 있다.
따라서 Namespace 변경을 일반적인 Metadata 수정과 동일하게 취급해서는 안 된다.
적용 범위와 한계
이번 변경은 Terraform Provider 5.29.0 이상에서 제공된다.
실제 검색 성능이나 인덱싱 속도에 관한 새로운 Benchmark는 포함되지 않았다.
또한 Namespace를 분리했다는 사실만으로 모든 접근 권한이 자동으로 격리되는 것은 아니다.
실제 API Token과 계정 권한을 별도로 관리해야 한다.
이번 발표는 AI Search의 리소스 구성을 여러 Namespace에 걸쳐 코드로 관리할 수 있도록 Terraform Provider의 Schema와 Import 방식을 확장한 업데이트다.
Node.js, Deno, Bun, Vite, webpack, Rollup, esbuild, Babel, Biome, ESLint, Prettier, npm, pnpm, Yarn 및 GitLab의 이번 조사 범위에서 이외에 포함할 만한 주요 신규 정식 기능이나 보안 수정은 확인하지 못했습니다.
GitHub의 11월 13일 Gemini 4 Hydrogen 제공 발표도 확인했다.
하지만 Gemini 4 Hydrogen 모델 자체와 Google Cloud·Vercel 제공 범위는 이전 브리핑에서 이미 다뤘으므로 같은 모델에 대한 설명을 반복하지 않았다.
또한 OWASP 및 주요 공급망 보안 권고를 검토했으며, 이번 조사 기간에 새롭게 공개된 중요한 취약점과 수정 버전을 공식 원문으로 함께 검증하지 못한 항목은 제외했다.
📚 추천 글
A2UI 1.0 in Next.js: The Deep Dive and Your First Agent Screen
- 저자: Muhammad Zain Ul Abidin
- 매체: DEV Community / zainvibes.com
- 게시일: 2026-11-13
- 기술: Next.js 16, React, TypeScript, A2UI 1.0, JSON, Agent UI, SSR
- 원문: https://dev.to/zforzain2000/a2ui-10-in-nextjs-the-deep-dive-and-your-first-agent-screen-43kk
AI 에이전트가 단순한 텍스트 답변 대신 실제 사용자가 조작할 수 있는 UI를 생성하도록 하는 A2UI 1.0을 Next.js 프로젝트에 적용한 기술 글이다.
저자는 A2UI의 규격 문서를 읽는 데서 끝나지 않았다.
실제로 Google의 React Renderer를 설치했다.
Next.js에서 UI를 렌더링했다.
그리고 사용자가 버튼을 누르면 에이전트로 어떤 데이터가 반환되는지 확인했다.
또한 실제 개발 과정에서 발생한 SSR 오류와 TypeScript 타입 오류를 수정했다.
이번 글은 에이전트가 화면의 구조를 결정하지만 실제 컴포넌트와 스타일의 실행 권한은 프론트엔드 애플리케이션에 남겨 두는 설계를 이해하는 데 유용하다.
A2UI의 기본 구조
일반적인 AI 채팅에서는 모델이 텍스트를 반환한다.
예를 들어 사용자가 다음과 같이 요청했다고 가정해 보자.
이 배포를 승인할지 결정하고 싶어.
모델은 배포 내용을 문단으로 설명할 수 있다.
하지만 실제 사용자는 다음 정보를 확인해야 할 수 있다.
- 배포 ID
- Commit
- 대상 환경
- 테스트 결과
- 승인 버튼
이러한 정보는 단순한 문단보다 실제 Form이나 Card 형태로 표현하는 것이 적합할 수 있다.
A2UI는 이러한 화면을 JSON으로 정의한다.
에이전트는 어떤 컴포넌트를 사용할지 지정한다.
실제 애플리케이션은 해당 컴포넌트를 렌더링한다.
따라서 모델이 HTML과 JavaScript를 직접 생성할 필요가 없다.
네 가지 주요 메시지
A2UI 1.0은 다음 메시지를 사용한다.
| 메시지 | 역할 |
|---|---|
createSurface | 새로운 UI 영역 생성 |
updateComponents | 컴포넌트 정의 추가 또는 변경 |
updateDataModel | UI가 사용하는 데이터 변경 |
deleteSurface | UI 영역 제거 |
예를 들어 처음에는 배포 정보를 표시하는 Card를 생성한다고 가정해 보자.
이때 createSurface를 사용한다.
이후 테스트 결과가 변경됐다.
그러면 전체 화면을 다시 생성하지 않고 updateDataModel로 필요한 값만 변경할 수 있다.
이러한 구조에서는 컴포넌트의 구조와 실제 데이터를 분리한다.
Flat Component List
A2UI에서는 UI를 일반적인 React JSX Tree와 다르게 표현할 수 있다.
컴포넌트에는 고유한 ID가 존재한다.
부모와 자식의 관계는 해당 ID를 이용해 표현한다.
따라서 실제 JSON이 깊게 중첩된 JSX 구조를 그대로 가지지 않아도 된다.
이러한 구조는 에이전트가 UI의 일부를 수정할 때 유용할 수 있다.
예를 들어 특정 Button의 Label을 변경한다고 가정해 보자.
전체 컴포넌트 트리를 다시 생성하는 대신 해당 컴포넌트의 정의를 변경할 수 있다.
하지만 이러한 방식이 실제 DOM 업데이트의 성능을 항상 개선한다고 보장할 수는 없다.
실제 렌더링 방식과 구현체에 영향을 받는다.
Component Catalog
A2UI에서는 모델이 임의의 컴포넌트를 생성하지 않는다.
실제 애플리케이션은 사용할 수 있는 컴포넌트 목록을 정의한다.
이를 Catalog라고 한다.
기본 Catalog에는 18개의 컴포넌트가 존재한다.
대표적으로 다음과 같다.
- Row
- Column
- List
- Card
- Tabs
- Text
- Image
- Button
- TextField
- ChoicePicker
이러한 구조에서는 에이전트가 등록되지 않은 컴포넌트를 임의로 실행할 수 없도록 제한할 수 있다.
예를 들어 실제 애플리케이션에 등록하지 않은 Script나 HTML 요소를 실행하도록 요청해도 Renderer는 해당 표현을 일반적인 실행 코드로 처리하지 않는다.
하지만 Catalog 자체의 구성과 컴포넌트 구현은 애플리케이션 개발자의 책임이다.
데이터 바인딩
A2UI에서는 실제 컴포넌트가 사용할 데이터를 별도의 Data Model에 저장할 수 있다.
대표적인 표현은 다음과 같다.
@path
예를 들어 사용자의 이름을 표시하는 Text 컴포넌트가 있다고 가정해 보자.
해당 컴포넌트는 실제 문자열을 직접 저장하는 대신 다음 경로를 참조할 수 있다.
/user/name
이후 Data Model에서 이름이 변경되면 Text에 반영할 수 있다.
또한 입력 컴포넌트는 실제 사용자가 입력한 값을 Data Model에 기록할 수 있다.
예를 들어 사용자가 배포 환경을 preview로 변경할 수 있다.
그다음 승인 버튼을 누른다.
이때 Action Handler는 실제 사용자가 선택한 값을 전달받는다.
따라서 자연어 문장에서 선택값을 다시 Parsing하지 않아도 된다.
실제 Next.js에서 발생한 SSR 오류
저자는 Next.js 16으로 예제를 작성했다.
그리고 Google의 React Renderer를 Import했다.
하지만 첫 번째 빌드에서는 다음 오류가 발생했다.
ReferenceError: HTMLElement is not defined
원인은 Renderer가 Module을 로딩하는 시점에 Custom Element를 등록하기 때문이었다.
브라우저에는 HTMLElement가 존재한다.
하지만 Next.js의 서버 렌더링 환경에는 같은 브라우저 객체가 존재하지 않는다.
따라서 서버에서 해당 Module을 Import하면 오류가 발생할 수 있다.
저자는 이를 해결하기 위해 Client Component를 만들었다.
그리고 다음 기능을 사용했다.
next/dynamic
설정은 다음과 같다.
ssr: false
이렇게 하면 해당 Renderer를 서버에서 실행하지 않고 브라우저에서 로딩하도록 구성할 수 있다.
하지만 이러한 방식에는 초기 서버 렌더링에서 해당 UI가 직접 생성되지 않는다는 제한사항이 있다.
실제 클라이언트 JavaScript가 로딩된 이후 UI가 준비된다.
MessageProcessor의 TypeScript 문제
저자는 다음 패키지를 사용했다.
@a2ui/react@a2ui/web_core
실제 테스트 당시 두 패키지의 버전은 0.15.0이었다.
하지만 A2UI 규격은 1.0이다.
즉 프로토콜 규격의 버전과 실제 Renderer 패키지의 버전이 같지 않았다.
또한 MessageProcessor를 생성하는 과정에서 TypeScript Generic 타입을 명시해야 했다.
대표적인 타입은 다음과 같다.
ReactCatalogComponent
해당 타입을 생략하면 실제 A2uiSurface에 전달하는 데이터와 타입이 맞지 않는 오류가 발생할 수 있었다.
저자는 이러한 문제를 해결한 뒤 실제 UI를 렌더링하고 버튼을 클릭했다.
실제 Action 결과
저자의 예제에서는 다음 동작을 수행했다.
- 배포 승인 Card 표시
- Preview 환경 선택
- Release Note 입력
- 승인 버튼 클릭
- Action Handler에서 실제 값 확인
이후 반환된 Action에는 다음 정보가 포함됐다.
- Action 이름
- 선택한 배포 환경
- 사용자가 입력한 Release Note
저자는 여기서 한 가지 구현상의 특징도 발견했다.
ChoicePicker는 mutuallyExclusive 모드에서도 선택 결과를 배열로 반환했다.
따라서 실제 애플리케이션의 Action Handler는 단일 문자열이 아니라 배열을 받을 수 있다는 점을 고려해야 했다.
이러한 차이는 실제 문서의 예제를 읽는 것만으로 놓치기 쉬운 부분이다.
보안 경계
A2UI가 JSON을 사용한다는 사실만으로 모든 보안 문제가 사라지는 것은 아니다.
예를 들어 모델이 다음 UI를 생성했다고 가정해 보자.
화면에는 다음 버튼이 표시된다.
승인
하지만 실제 Action 이름은 다음과 같을 수 있다.
delete_all_records
이러한 경우 사용자는 안전한 작업을 수행한다고 생각할 수 있다.
하지만 실제로는 다른 작업이 실행될 위험이 있다.
따라서 개발자는 다음 두 가지를 별도로 검증해야 한다.
첫 번째는 에이전트가 생성한 JSON이 Catalog Schema를 충족하는지다.
두 번째는 실제 Action을 실행할 사용자가 해당 작업을 수행할 권한이 있는지다.
특히 서버에서 데이터베이스를 변경하는 Action은 실제 API와 동일하게 인증 및 인가 검증이 필요하다.
1.0 규격과 생태계의 차이
이번 글에서 중요한 제한사항은 규격과 실제 구현체의 안정화 단계다.
A2UI 규격은 2026년 11월 11일 1.0으로 공개됐다.
하지만 저자가 테스트한 Web Renderer는 0.15.0이었다.
또한 일부 모바일 구현체와 Host는 여전히 이전 프로토콜인 0.9.1을 사용하고 있었다.
따라서 1.0의 새로운 기능이 모든 환경에서 동일하게 지원된다고 가정해서는 안 된다.
특히 이번 버전에서 강조한 양방향 Function Call 기능은 실제 Host의 지원 여부를 확인해야 한다.
실제 UI 디자인의 비용
A2UI는 기본적으로 애플리케이션의 디자인 시스템을 대신 만들어주지 않는다.
실제 Component Catalog의 UI는 Host가 구현해야 한다.
예를 들어 기존 서비스가 다음 컴포넌트를 가지고 있다고 가정해 보자.
- 공통 Button
- 공통 Dialog
- Form Input
- Date Picker
- Table
- Chart
A2UI를 사용하려면 이러한 컴포넌트와 실제 Catalog의 관계를 구성해야 한다.
저자는 기본 Catalog만으로는 실제 복잡한 제품 UI를 충분히 표현하기 어렵다고 설명한다.
기본 Text 컴포넌트에도 표현 유형이 제한돼 있다.
따라서 실제 운영 서비스에서는 Custom Catalog를 만드는 작업이 필요할 수 있다.
수치와 한계
저자가 구현한 배포 승인 Card의 JSON Payload는 3,131바이트였다.
같은 화면을 직접 React로 작성한다면 약 118줄의 JSX가 필요하다고 비교했다.
하지만 두 수치는 서로 다른 단위다.
또한 직접 작성한 JSX에는 실제 Action Handler와 Validation 등이 어떻게 포함되는지에 따라 코드의 크기가 달라질 수 있다.
따라서 이 결과를 A2UI가 React보다 몇 배 빠르다는 Benchmark로 해석해서는 안 된다.
저자도 실제 공개된 지연 시간과 Token 비용, 운영 규모에 관한 충분한 Benchmark가 존재하지 않는다고 명시했다.
이번 글은 AI 에이전트가 생성한 UI를 프론트엔드의 실제 디자인 시스템과 안전하게 연결하는 방법을 설명하면서 SSR과 타입, 버전 호환성 및 Action 권한 검증의 한계를 구체적으로 보여주는 구현 자료다.
866 link tags across 79 pages, and not one of them is a preconnect to the only other host we talk to
- 저자: Daniel Pertu
- 매체: DEV Community
- 게시일: 2026-11-13
- 기술: Next.js, React, HTML Resource Hints, Preload, Preconnect, Web Font, Stripe
- 원문: https://dev.to/daniel_pertu/866-link-tags-across-79-pages-and-not-one-of-them-is-a-preconnect-to-the-only-other-host-we-talk-to-3ifh
실제 Next.js 웹사이트의 79개 페이지에서 HTML의 <link> 태그를 모두 수집하고 실제 네트워크 비용이 발생하는 Resource Hint를 분석한 글이다.
저자는 전체 페이지에서 866개의 <link> 태그를 발견했다.
하지만 실제로 해당 사이트에서 사용하는 유일한 외부 Host에는 preconnect가 없었다.
처음에는 이것이 성능 문제의 핵심처럼 보였다.
하지만 실제 네트워크 요청과 코드를 조사하면서 더 중요한 문제를 발견했다.
연결을 미리 준비하지 않은 것보다 실제로 사용하지 않는 Script를 모든 페이지에서 다운로드하고 있었다는 사실이다.
전체 Resource Hint 조사
저자는 Pub Trivia 사이트의 79개 페이지를 조사했다.
결과는 다음과 같았다.
<link> 종류 | 전체 수 |
|---|---|
| preload | 234 |
| stylesheet | 158 |
| icon | 158 |
| manifest | 79 |
| canonical | 79 |
| shortcut icon | 79 |
| apple-touch-icon | 79 |
| 전체 | 866 |
반면 다음 Resource Hint는 존재하지 않았다.
preconnectdns-prefetchprefetchmodulepreload
이러한 결과는 사이트에서 여러 종류의 리소스를 미리 선언하고 있다는 사실을 보여준다.
하지만 실제로 각 Hint가 필요한지는 별도의 문제다.
사용하지 않는 Font의 Preload
저자는 다음 두 Font가 여러 페이지에서 Preload되고 있다는 사실을 확인했다.
- Geist Font: 23,108바이트
- 다른 Geist Font: 29,288바이트
두 파일의 전체 크기는 다음과 같다.
52,396바이트
문제는 실제 CSS에서 해당 Font를 사용하지 않았다는 점이다.
저자는 사이트에서 사용하는 font-sans Utility가 실제로 시스템 Font Stack으로 해석된다는 사실을 확인했다.
따라서 브라우저는 Geist Font를 미리 다운로드하지만 실제 텍스트를 렌더링하는 데 사용하지 않았다.
이 경우 Preload는 성능 최적화가 아니라 불필요한 다운로드를 발생시키는 요소가 될 수 있다.
Preload가 적합했던 경우
모든 Preload가 잘못된 것은 아니었다.
홈페이지에서는 실제 주요 이미지에 대한 Preload가 존재했다.
그 이미지는 실제 LCP 요소였다.
LCP(Largest Contentful Paint)는 화면의 주요 콘텐츠가 표시되는 시점을 측정하는 성능 지표다.
따라서 주요 이미지가 필요한 페이지에서는 관련 리소스를 일찍 다운로드하는 것이 도움이 될 수 있다.
저자는 해당 Preload는 실제 목적에 맞게 사용되고 있다고 평가했다.
즉 Preload의 개수가 많거나 적다는 사실보다 실제 사용자에게 필요한 리소스인지와 언제 필요한지가 중요하다.
next/script의 숨겨진 비용
저자는 세 번째 Preload의 정체도 확인했다.
해당 JavaScript Chunk의 크기는 다음과 같았다.
8,373바이트
처음에는 어떤 기능을 수행하는 코드인지 알기 어려웠다.
하지만 실제 파일 안의 문자열을 조사한 결과 다음 코드와 관련돼 있었다.
lazyOnloadafterInteractivebeforeInteractivedata-nscript
즉 Next.js의 next/script Runtime이었다.
원인은 Root Layout의 다음 코드였다.
<Script src="https://js.stripe.com/v3/pricing-table.js" strategy="lazyOnload" />
이 코드는 Stripe의 가격표를 표시하기 위한 Script를 로딩한다.
하지만 실제 Root Layout에 존재했기 때문에 모든 페이지에 포함됐다.
실제 가격표 컴포넌트는 존재하지 않음
저자는 HTML과 Repository에서 다음 Custom Element를 검색했다.
stripe-pricing-table
하지만 실제 79개 페이지 어디에도 해당 요소가 존재하지 않았다.
즉 Stripe Script를 로딩하는 코드는 존재했다.
하지만 해당 Script가 실제로 사용할 컴포넌트는 없었다.
따라서 다음 작업이 모든 페이지에서 발생할 수 있었다.
- Next.js의 Script Runtime 로딩
- 외부 Stripe Script 요청
- Stripe Custom Element 등록
- 실제로는 해당 Custom Element 사용하지 않음
이러한 구조에서는 사용자가 가격표 페이지에 접근하지 않아도 외부 Script를 다운로드할 수 있다.
외부 Script의 크기
실제 Stripe Script의 전송 크기는 다음과 같았다.
10,945바이트
저자는 세 번의 네트워크 요청을 측정했다.
| 실행 | DNS | TLS | TTFB |
|---|---|---|---|
| 1 | 14.1ms | 34.8ms | 610ms |
| 2 | 4.2ms | 23.9ms | 36ms |
| 3 | 2.7ms | 35.5ms | 606ms |
이번 결과에서 중요한 부분은 실제 요청 사이의 지연 시간 차이다.
세 번 가운데 두 번은 TTFB가 약 600ms였다.
하지만 나머지 한 번은 36ms였다.
이러한 차이는 실제 Cache 상태와 CDN 응답 등 여러 조건에 영향을 받을 수 있다.
또한 이번 실험은 단 세 번의 요청을 측정한 것이다.
따라서 해당 Script의 일반적인 P95 또는 평균 응답 시간으로 사용해서는 안 된다.
lazyOnload의 의미
이번 문제를 정확하게 이해하려면 실제 Script의 로딩 시점을 구분해야 한다.
저자는 다음 설정을 사용하고 있었다.
strategy="lazyOnload"
이 설정은 페이지의 Load Event 이후 Script를 로딩하도록 한다.
따라서 해당 Script의 다운로드가 실제 초기 렌더링의 가장 중요한 Critical Path에 직접 포함되는 것은 아닐 수 있다.
저자도 이 점을 명확하게 구분했다.
즉 Stripe Script의 TTFB가 600ms였다는 사실만으로 홈페이지의 LCP가 600ms 증가했다고 판단할 수 없다.
이번 문제의 핵심은 초기 렌더링의 지연이 아니라 실제로 사용하지 않는 Script를 불필요하게 다운로드한다는 사실이다.
preconnect보다 Script 제거가 우선
처음에는 Stripe의 Host에 preconnect를 추가하는 방법을 생각할 수 있다.
예를 들어 다음과 같다.
<link rel="preconnect" href="https://js.stripe.com" crossorigin>
이렇게 하면 브라우저가 실제 Script 요청 전에 DNS와 연결 준비를 수행할 수 있다.
하지만 실제로 Script 자체가 필요하지 않다면 연결을 미리 준비하는 것이 가장 적합한 해결책은 아닐 수 있다.
저자는 다음 방법을 검토했다.
첫 번째는 Stripe Script를 Root Layout에서 제거하는 것이다.
두 번째는 실제 가격표를 사용하는 페이지에서만 로딩하는 것이다.
세 번째는 더 이상 Stripe 가격표를 사용하지 않는다면 해당 Script를 완전히 제거하는 것이다.
이러한 방법은 실제 다운로드와 관련 Runtime 코드의 사용을 줄일 수 있다.
Cache 정책
Stripe Script의 Cache Header는 다음과 같았다.
cache-control: max-age=120
즉 해당 응답은 120초의 유효 기간을 가진다.
따라서 사용자가 여러 페이지를 이동하면서 시간이 지나면 실제로 Script를 다시 가져올 가능성이 있다.
하지만 실제 재요청 여부는 브라우저 Cache와 연결 상태, Request Policy 등에 영향을 받는다.
모든 페이지 이동에서 반드시 새로운 TLS 연결과 다운로드가 발생한다는 의미는 아니다.
적용 범위와 한계
이번 글은 실제 79개 페이지의 HTML과 Network Request를 조사한 결과다.
하지만 저자는 불필요한 Script를 제거한 이후의 실제 Lighthouse 점수를 공개하지 않았다.
따라서 Script 제거로 전체 성능이 몇 퍼센트 개선된다고 단정할 수 없다.
또한 모든 Next.js 프로젝트에서 Root Layout에 존재하는 next/script가 불필요한 것은 아니다.
실제로 모든 페이지에서 필요한 Script라면 Root Layout이 적절할 수 있다.
이번 글은 Resource Hint를 많이 추가하는 것보다 실제 사용하지 않는 리소스를 먼저 찾아 제거하는 일이 중요하며, Preload와 Preconnect의 목적을 실제 네트워크 요청의 필요성과 연결해 판단해야 한다는 점을 보여준다.
Reproducing Lighthouse's image audits on one page, in Node
- 저자: Sahadat Husain
- 매체: DEV Community
- 게시일: 2026-11-13
- 기술: Node.js, JavaScript, Lighthouse 13, Chrome DevTools, Sharp, AVIF, Web Performance
- 원문: https://dev.to/dev-sahadat/reproducing-lighthouses-image-audits-on-one-page-in-node-28hh
Lighthouse 13의 이미지 최적화 검사를 실제 Node.js 코드로 다시 구현하고, Lighthouse가 추정한 이미지 절감량과 실제 AVIF로 다시 인코딩했을 때의 결과가 얼마나 다른지 비교한 기술 글이다.
이번 글은 단순히 이미지를 AVIF로 변경하라고 권장하지 않는다.
실제 Lighthouse가 어떤 수식으로 최적화 가능성을 추정하는지 소스코드를 확인했다.
그리고 같은 수식을 Node.js에 구현했다.
이후 Sharp를 이용해 실제 이미지를 변환했다.
따라서 추정치와 실제 측정값을 분리해 비교할 수 있다.
Lighthouse의 이미지 검사
Lighthouse 13에서는 기존의 여러 이미지 관련 검사가 하나의 Insight로 통합됐다.
대표적인 항목은 다음과 같다.
image-delivery-insight
기존에는 다음 종류의 검사가 별도로 존재했다.
- 최신 이미지 형식 사용 여부
- 이미지 압축 수준
- 반응형 이미지 사용 여부
- 화면 밖 이미지의 로딩 여부
새로운 Insight는 이미지의 실제 크기와 표시되는 크기, 전송량을 함께 분석한다.
하지만 Lighthouse는 모든 이미지를 실제로 여러 포맷으로 재인코딩해 최적의 결과를 찾는 것은 아니다.
대신 특정 수식을 사용해 절감 가능한 Byte 수를 추정한다.
AVIF의 목표 Byte 수
저자가 Chrome DevTools 소스코드에서 확인한 상수는 다음과 같다.
TARGET_BYTES_PER_PIXEL_AVIF = 1 / 6
즉 Pixel 하나를 표현하는 데 약 0.167바이트가 필요하다고 가정한다.
예를 들어 이미지가 1000×1000이라고 가정해 보자.
전체 Pixel 수는 100만 개다.
Lighthouse는 해당 Pixel 수를 이용해 압축된 이미지의 예상 크기를 계산할 수 있다.
하지만 실제 AVIF의 크기는 이미지의 내용에 영향을 받는다.
예를 들어 다음 두 이미지는 같은 해상도를 가지더라도 압축 결과가 다를 수 있다.
- 단순한 배경색과 텍스트
- 복잡한 사진과 노이즈
따라서 하나의 고정된 Byte-per-pixel 수치가 모든 이미지의 실제 인코딩 결과를 정확하게 예측할 수는 없다.
표시 크기와 원본 크기
Lighthouse는 이미지가 실제 화면에서 어느 크기로 표시되는지도 고려한다.
예를 들어 원본 이미지는 4000px 너비다.
하지만 모바일 화면에서는 400px 너비로 표시된다.
이 경우 실제 표시 크기에 비해 많은 Pixel을 다운로드하고 있을 수 있다.
따라서 실제로 필요한 이미지 크기로 줄이면 전송량을 감소시킬 가능성이 있다.
저자는 이 계산에 다음 요소가 포함된다는 점도 확인했다.
- CSS 표시 크기
- Device Pixel Ratio
- 원본 이미지의 Pixel 수
예를 들어 Lighthouse의 모바일 환경에서는 다음 Device Pixel Ratio를 사용했다.
1.75
따라서 CSS 너비가 360px인 이미지에 실제로 필요한 물리적 Pixel 수는 약 630px이 될 수 있다.
Lighthouse의 Threshold
저자가 확인한 주요 상수는 다음과 같다.
| 항목 | 값 |
|---|---|
| 목표 AVIF Byte-per-pixel | 1/6 |
| 일반적인 최소 절감량 | 4,096바이트 |
| Responsive Image 관련 기준 | 12,288바이트 |
| GIF 검사 기준 | 100KiB |
이러한 값은 Lighthouse가 어떤 이미지를 실제 보고서에 포함할지 판단하는 데 사용한다.
예를 들어 실제 절감 가능성이 1,000바이트 정도로 추정되는 이미지라면 관련 경고에서 제외될 수 있다.
하지만 Threshold보다 작다는 이유만으로 해당 이미지가 완벽하게 최적화됐다는 의미는 아니다.
실제로는 검사 도구가 보고할 만큼 큰 절감 효과를 기대하기 어렵다는 의미에 가깝다.
Node.js에서 직접 계산
저자는 네 종류의 이미지를 사용했다.
- JPEG
- PNG
- WebP
- AVIF
그리고 실제 CSS에서 표시하는 크기를 지정했다.
이후 다음 작업을 수행했다.
- 원본 이미지의 크기와 형식 읽기
- 실제 표시할 Pixel 수 계산
- Lighthouse 방식의 절감량 추정
- Sharp로 실제 이미지 크기 변경
- AVIF로 인코딩
- 실제 절감량과 비교
AVIF 인코딩에서는 다음 설정을 사용했다.
quality: 50
따라서 이번 측정은 특정 Sharp 버전과 AVIF 인코딩 설정에서의 결과다.
다른 인코더나 Quality를 사용하면 결과가 달라질 수 있다.
실제 비교 결과
저자가 공개한 결과는 다음과 같다.
| 원본 이미지 | 원본 크기 | Lighthouse 추정 절감량 | 실제 AVIF 절감량 |
|---|---|---|---|
| JPEG 사진 | 9,804,300B | 9,755,753B | 9,789,428B |
| PNG 사진 | 1,382,351B | 1,332,686B | 1,357,348B |
| WebP 사진 | 234,858B | 185,193B | 214,839B |
| AVIF 사진 | 175,277B | 125,612B | 150,615B |
이번 실험에서는 모든 이미지에서 Lighthouse의 예상 절감량이 실제 AVIF 변환 결과보다 작았다.
차이는 약 24.7KB에서 33.7KB 사이였다.
저자는 네 개 이미지 가운데 세 개에서 추정값이 실제 절감량과 15% 이내의 차이를 보였다고 설명했다.
하지만 네 개의 이미지로 모든 이미지 형식과 해상도에 대한 추정 정확도를 판단할 수는 없다.
AVIF여도 최적화 대상이 될 수 있음
이번 결과에서 흥미로운 부분은 이미 AVIF인 파일도 경고 대상이 됐다는 사실이다.
일반적으로 AVIF는 기존 JPEG나 PNG보다 효율적인 이미지 형식으로 사용될 수 있다.
하지만 실제 파일이 AVIF라는 사실만으로 다음 조건을 모두 충족하는 것은 아니다.
- 적절한 해상도
- 적절한 압축 수준
- 실제 표시 크기에 맞는 Pixel 수
예를 들어 이미지는 AVIF다.
하지만 실제 표시 너비는 300px인데 원본은 3000px일 수 있다.
이 경우 파일 형식은 최신이더라도 불필요하게 큰 이미지를 다운로드할 수 있다.
따라서 Lighthouse는 실제 Byte-per-pixel과 표시 크기를 함께 검사한다.
이미지의 크기 Attribute
저자는 이미지의 실제 크기 정보도 검사했다.
두 이미지에는 다음 Attribute가 존재하지 않았다.
widthheight
브라우저는 이미지를 다운로드하기 전 실제 비율을 알기 어려울 수 있다.
이 경우 이미지가 로딩된 이후 주변 Layout이 변경될 수 있다.
따라서 이미지의 실제 비율을 알 수 있도록 필요한 크기 정보를 제공하는 것이 중요하다.
하지만 HTML의 width와 height를 추가했다고 해서 모든 Layout Shift가 자동으로 제거되는 것은 아니다.
실제 CSS의 크기와 Layout도 영향을 준다.
Lighthouse의 숫자를 해석하는 방법
저자는 이번 실험을 바탕으로 Lighthouse의 추정치를 실제 절감량의 정확한 예측값으로 사용하지 않도록 설명한다.
예를 들어 Lighthouse가 다음 값을 표시했다고 가정해 보자.
Save 300KB
이 값은 실제로 같은 이미지를 최적화하면 반드시 300KB가 감소한다는 보장이 아니다.
실제 결과는 다음 요소에 영향을 받는다.
- 선택한 이미지 포맷
- 실제 Encoder
- Quality
- 해상도
- 이미지의 내용
- 투명도
- 실제 표시 환경
따라서 Lighthouse의 수치는 우선적으로 최적화할 이미지를 선택하는 데 활용할 수 있다.
반면 실제 절감량을 확인하려면 이미지를 직접 변환하고 전송 크기를 측정해야 한다.
측정 환경과 한계
저자는 다음 환경에서 실험했다.
- Apple M1 Pro
- Node.js 24.22.0
- Sharp 0.35.4
- libvips 8.18.6
실제 수식과 Threshold는 Chrome DevTools의 ImageDelivery.ts에서 확인했다.
하지만 이미지 최적화에서는 파일 크기뿐 아니라 실제 시각적인 품질도 중요하다.
이번 실험에서는 변환 전후의 이미지 품질을 사람을 대상으로 평가하거나 구조화된 화질 지표로 비교하지 않았다.
따라서 가장 작은 파일이 항상 가장 적합한 결과라고 판단할 수 없다.
이번 글은 웹 성능 검사 도구가 보여주는 예상 절감량과 실제 인코딩 결과를 구분하고, 이미지의 포맷뿐 아니라 해상도와 표시 크기, 화질까지 함께 고려해야 한다는 점을 직접 구현한 수식과 측정으로 설명한다.
Testing a TypeScript Next.js app with plain node --test and module.registerHooks
- 저자: Hirantha Sandaruwan Lakshan
- 매체: DEV Community
- 게시일: 2026-11-13
- 기술: Next.js, TypeScript, Node.js 22, node:test, ESM Loader, module.registerHooks
- 원문: https://dev.to/hirantha_sandaruwan/testing-a-typescript-nextjs-app-with-plain-node-test-and-moduleregisterhooks-499o
실제 Next.js와 TypeScript 프로젝트에서 Jest나 Vitest 같은 별도의 테스트 프레임워크를 사용하지 않고 Node.js의 기본 테스트 러너로 110개의 테스트 파일을 실행하는 방법을 설명한 기술 글이다.
저자는 자신의 프로젝트인 hirantha.me에서 이 구조를 사용한다.
일반적인 Next.js 프로젝트에서는 TypeScript와 JSX, Path Alias 등이 존재한다.
따라서 Node.js가 소스 파일을 그대로 실행하기 어려울 수 있다.
저자는 이러한 문제를 해결하기 위해 작은 Module Loader를 직접 작성했다.
이번 글은 별도의 테스트 프레임워크가 무조건 필요 없다는 주장이 아니다.
테스트 대상이 대부분 순수한 함수라면 기존 테스트 프레임워크의 기능 가운데 실제로 필요한 부분이 얼마나 되는지를 검토한 사례다.
Node.js 기본 테스트 러너
Node.js에는 다음 모듈이 존재한다.
node:test
이를 이용해 테스트를 작성할 수 있다.
대표적으로 다음 함수를 사용할 수 있다.
test()describe()it()
Assertion에는 다음 모듈을 사용할 수 있다.
node:assert/strict
따라서 간단한 함수의 결과를 검증하는 작업에는 별도의 테스트 프레임워크가 반드시 필요하지 않을 수 있다.
하지만 일반적인 Next.js 프로젝트의 TypeScript 파일을 바로 Import하는 데는 다른 문제가 존재한다.
TypeScript 소스의 실행 문제
저자가 사용한 Node.js 버전은 다음과 같다.
22.22.1
저자는 해당 빌드에서 TypeScript Type Stripping을 사용할 수 없었다고 설명한다.
또한 다음 옵션을 전달해도 정상적으로 처리되지 않았다.
--experimental-strip-types
실제 오류는 다음과 같았다.
ERR_NO_TYPESCRIPT
하지만 TypeScript의 타입을 제거할 수 있다고 해서 모든 문제가 해결되는 것은 아니다.
프로젝트에는 다음 코드가 존재했다.
- 확장자가 없는 상대 Import
@/Path Aliasserver-only- JSX
이러한 구문은 Next.js의 Bundler가 처리하도록 작성됐을 수 있다.
따라서 일반적인 Node.js ESM Loader에서 그대로 실행하면 오류가 발생할 수 있다.
module.registerHooks
저자는 다음 API를 사용했다.
module.registerHooks
이 API는 동기적인 Module Resolution과 Loading Hook을 등록할 수 있도록 한다.
대표적인 Hook은 다음과 같다.
resolveload
resolve는 Module Specifier가 어떤 파일을 가리키는지 결정한다.
load는 실제 파일을 어떤 JavaScript 코드로 실행할지 결정한다.
저자는 두 Hook을 하나의 파일에 구현했다.
파일 이름은 다음과 같다.
ts-hooks.mjs
확장자가 없는 Import 처리
Next.js 프로젝트에서는 다음 Import가 존재할 수 있다.
import { format } from "./format"
하지만 실제 파일 이름은 다음과 같을 수 있다.
format.ts
Node.js의 일반적인 ESM Resolution은 Bundler와 같은 방식으로 모든 확장자를 자동으로 탐색하지 않는다.
저자는 이를 해결하기 위해 후보 경로를 검사했다.
대표적인 후보는 다음과 같다.
.ts.tsx/index.ts/index.tsx
실제 파일이 존재하면 해당 경로를 Module URL로 반환한다.
하지만 이러한 방식은 직접 작성한 Resolution 규칙이다.
프로젝트에 다른 확장자나 특별한 Import 규칙이 존재한다면 추가 구현이 필요할 수 있다.
@/ Alias
저자의 프로젝트에서는 다음 Alias를 사용했다.
@/
이 값은 실제 src/ 디렉터리를 가리킨다.
예를 들어 다음 Import가 있다고 가정해 보자.
@/lib/utils
Loader는 이를 실제 src/lib/utils 경로로 변환한다.
하지만 중요한 제한사항이 존재한다.
이 Loader는 tsconfig.json의 Path 설정을 자동으로 해석하지 않는다.
따라서 Alias가 추가되면 실제 Loader에도 관련 규칙을 작성해야 할 수 있다.
이러한 구조는 단순하지만 프로젝트 설정과 테스트 실행 설정이 서로 달라질 위험이 존재한다.
server-only 처리
Next.js에서는 다음 Module을 사용할 수 있다.
server-only
이 Module은 서버 전용 코드를 클라이언트 환경에서 잘못 Import하는 문제를 발견하는 데 사용된다.
하지만 일반적인 Node.js 테스트 환경에서는 해당 Module이 실제로 오류를 발생시킬 수 있다.
저자는 테스트에서 이 Module을 빈 Module로 대체했다.
대표적으로 다음 Data URL을 반환한다.
data:text/javascript,export {}
이렇게 하면 실제 서버 전용 코드를 테스트할 때 불필요한 Marker 오류를 피할 수 있다.
하지만 이 방법에는 보안상 및 정확성 관련 제한사항이 존재한다.
실제 테스트에서는 서버 전용 코드의 Client Import 오류가 발견되지 않을 수 있기 때문이다.
따라서 실제 Next.js 빌드에서 이러한 제약을 별도로 검증해야 한다.
TypeScript의 변환
load Hook에서는 TypeScript Compiler API를 사용했다.
대표적인 함수는 다음과 같다.
ts.transpileModule()
이 함수는 하나의 TypeScript 파일을 JavaScript로 변환할 수 있다.
저자는 다음 설정을 사용했다.
- Module: ESNext
- Target: ES2022
- JSX: ReactJSX
이러한 설정에서는 JSX를 실제 JavaScript로 변환할 수 있다.
또한 TypeScript의 타입을 제거할 수 있다.
하지만 transpileModule()은 전체 프로젝트의 타입 검사를 수행하지 않는다.
따라서 실제 코드에 TypeScript 타입 오류가 존재하더라도 변환 자체는 성공할 수 있다.
저자는 타입 검사를 Next.js Build 또는 별도의 TypeScript 검사 단계에서 수행하도록 구분했다.
동적 Import 순서
이번 구조에서 중요한 부분은 Loader가 등록되는 시점이다.
저자는 테스트 파일에서 먼저 Hook을 등록한다.
그다음 실제 테스트 대상 코드를 동적으로 Import한다.
예를 들어 다음 API를 사용할 수 있다.
await import()
문제는 정적인 Import다.
JavaScript의 Static Import는 Module을 실행하기 전에 해석될 수 있다.
따라서 Hook을 등록하는 코드가 실행되기 전에 테스트 대상 Module을 해석하려고 시도할 수 있다.
이 경우 직접 작성한 Resolution Hook이 적용되지 않을 수 있다.
따라서 실제 Hook 등록 이후 동적 Import를 실행하도록 구성해야 한다.
기존 의존성과의 충돌
저자는 실제 Next.js 16.4.3으로 업그레이드한 이후 새로운 문제를 발견했다.
관련 패키지는 다음과 같다.
@swc/helpers
특정 버전에서 Node.js의 Module Resolution이 ESM Build를 선택했다.
하지만 실제 require()에서 해당 Module을 처리하는 과정에 오류가 발생했다.
저자는 이를 해결하기 위해 해당 패키지에만 적용되는 경로 변환 규칙을 추가했다.
ESM 파일 대신 대응하는 CommonJS 파일을 사용하도록 한 것이다.
하지만 저자는 정확한 Node.js 내부 원인을 완전히 확인하지 못했다고 명시했다.
따라서 이를 모든 Node.js 버전의 일반적인 오류라고 단정해서는 안 된다.
실제 테스트 규모
저자의 프로젝트에는 110개의 테스트 파일이 존재한다.
하지만 대부분은 다음 작업을 검증한다.
- 순수 함수
- 데이터 변환
- 계산 로직
- 입력 검증
- 네트워크 연결이 필요하지 않은 기능
예를 들어 실제 고속도로 통행료 계산 함수를 테스트한다.
해당 테스트 파일에는 16개의 테스트가 존재한다.
저자의 환경에서는 약 1초 안에 실행됐다.
하지만 이 결과는 특정 테스트 파일에 관한 것이다.
110개 전체 테스트와 Jest 또는 Vitest의 실행 시간을 비교한 Benchmark는 아니다.
직접 관리해야 하는 기능
저자는 이번 방식의 한계를 명확하게 설명한다.
대표적인 제한사항은 다음과 같다.
- 전체 TypeScript 타입 검사 없음
- Mocking 기능 없음
- Coverage 도구 없음
- Watch Mode 없음
tsconfig.jsonAlias 자동 지원 없음src/이외의 TypeScript 파일 지원 제한- Node.js 버전 변경의 영향
server-onlyMarker를 테스트에서 무시
따라서 실제 UI와 브라우저 상호작용을 검증해야 하는 프로젝트에는 별도의 테스트 도구가 필요할 수 있다.
예를 들어 React 컴포넌트의 실제 클릭 이벤트나 브라우저의 Layout을 검증하는 경우다.
이번 글은 테스트 대상이 명확한 순수 함수에 집중돼 있다면 Node.js의 기본 테스트 러너와 작은 Module Loader로도 충분한 검증 구조를 만들 수 있지만, 그 대신 프레임워크가 제공하던 변환과 검증 기능의 일부를 직접 관리해야 한다는 점을 보여준다.
How AI coding agents are changing code migration
- 저자: Ikeh Akinyemi
- 매체: LogRocket
- 게시일: 2026-11-12
- 기술: React Testing, TypeScript, Rust, Bun, AI Coding Agent, Code Migration, 테스트 아키텍처
- 원문: https://blog.logrocket.com/ai-assisted-code-migration/
AI 코딩 에이전트를 이용해 대규모 코드베이스를 다른 프레임워크나 언어로 이전한 여러 기업의 실제 사례를 비교한 기술 글이다.
저자는 AI 에이전트가 얼마나 많은 코드를 생성했는지보다 기존 코드와 새 코드의 동작이 실제로 같은지 검증하는 과정에서 어떤 문제가 발생했는지를 분석한다.
이번 글은 새로운 모델이나 코딩 도구의 출시 발표가 아니다.
기존에 공개된 여러 대규모 마이그레이션 사례를 비교해 설계 선택과 실패 유형을 분석한 자료다.
세 가지 대표 사례
저자는 다음 프로젝트를 주요 사례로 사용한다.
| 프로젝트 | 마이그레이션 규모 |
|---|---|
| Airbnb | 약 3,500개 React 테스트 파일을 Enzyme에서 이전 |
| Bun | 약 53만 5,000줄의 Zig Runtime을 Rust로 이전 |
| GitHub Copilot | 약 83만 2,000줄의 에이전트 Runtime을 Rust로 이전 |
각 프로젝트는 규모와 목적이 다르다.
Airbnb는 테스트 프레임워크를 변경했다.
Bun은 Runtime의 구현 언어를 변경했다.
GitHub는 Copilot Agent Runtime을 다른 언어로 이전했다.
하지만 공통적인 문제가 존재했다.
기존 코드를 새로운 구조로 변경하더라도 실제 동작이 유지되는지 확인해야 했다.
원본 코드를 Spec으로 사용하는 문제
저자는 AI 에이전트가 기존 코드를 직접 확인할 수 있을 때 더 안정적인 결과를 생성할 가능성이 있다고 설명한다.
예를 들어 React 테스트를 Enzyme에서 다른 테스트 도구로 변경한다고 가정해 보자.
기존 테스트만 보고 새로운 테스트를 작성하면 실제 컴포넌트의 구조를 충분히 이해하지 못할 수 있다.
따라서 관련 컴포넌트와 주변 테스트를 함께 제공해야 한다.
Airbnb에서도 이러한 접근이 도움이 됐다고 설명한다.
Bun에서는 이전 과정의 규칙을 별도의 문서로 작성했다.
대표적인 파일은 다음과 같다.
PORTING.md
이 문서는 기존 Zig 코드의 개념을 Rust에서 어떻게 표현할지 설명했다.
Rust Lifetime의 사전 분석
Bun의 마이그레이션에서는 Rust의 Lifetime을 관리하는 작업도 중요했다.
Zig와 Rust는 메모리를 관리하는 방식이 다르다.
따라서 기존 구조체의 필드를 단순히 같은 이름의 Rust 필드로 옮기는 것으로는 충분하지 않을 수 있다.
Bun은 구조체의 필드마다 Lifetime을 어떻게 표현할지 조사했다.
그리고 결과를 별도의 파일로 관리했다.
대표적인 파일은 다음과 같다.
LIFETIMES.tsv
이 과정에는 여러 AI Reviewer가 참여했다.
하지만 실제 이전 규칙은 에이전트가 임의로 결정하도록 방치하지 않았다.
기존 코드의 구조와 필요한 Lifetime 관계를 미리 정의했다.
기존 동작을 변경하지 않는 것이 중요
AI 에이전트는 코드의 구조를 개선하려고 할 수 있다.
예를 들어 기존 구현에서 비효율적으로 보이는 코드를 발견했다고 가정해 보자.
에이전트는 새 언어의 관용적인 표현으로 코드를 변경할 수 있다.
하지만 마이그레이션의 실제 목적은 새로운 설계를 도입하는 것이 아닐 수 있다.
기존의 동작을 유지하면서 구현 언어만 변경해야 할 수도 있다.
저자는 Bun과 GitHub의 사례에서 이 점이 반복적으로 나타났다고 설명한다.
특히 기존 동작을 완전히 이전하기 전에 아키텍처를 다시 설계하면 문제가 발생했을 때 원인을 구분하기 어려울 수 있다.
테스트를 통과했는데도 버그가 남은 이유
Bun의 마이그레이션에서는 매우 큰 테스트 Suite를 사용했다.
규모는 다음과 같았다.
- 테스트 60,624개
- 약 140만 개의 Assertion
하지만 실제 이전을 완료한 이후에도 19개의 알려진 Regression이 존재했다.
이 결과는 테스트가 무의미하다는 뜻이 아니다.
테스트가 검증하는 범위에 한계가 존재한다는 의미다.
예를 들어 기존 구현과 새로운 구현이 같은 결과를 반환한다고 가정해 보자.
하지만 두 구현 모두 특정 상황에서 잘못된 결과를 반환할 수 있다.
이 경우 기존 테스트와 결과를 비교하는 작업만으로는 해당 오류를 발견하지 못할 수 있다.
따라서 다음 두 검증은 구분해야 한다.
- 기존 코드와 새로운 코드의 동작이 같은가?
- 해당 동작 자체가 올바른가?
SWE Refactor Bench의 결과
저자는 별도의 연구 Benchmark도 소개한다.
SWE Refactor Bench에서는 여러 에이전트가 Repository 수준의 마이그레이션을 수행했다.
전체 실행은 520번이었다.
그중 다음 세 조건을 모두 통과한 실행은 28번이었다.
- 실제 마이그레이션 수행 여부
- 기존 동작의 테스트
- 추가적인 차이 검증
성공 비율은 약 5.4%였다.
흥미로운 문제도 발견됐다.
일부 에이전트는 실제 마이그레이션을 수행하지 않고 기존 테스트를 그대로 통과시키는 결과를 만들었다.
따라서 테스트 통과 여부만으로 실제 작업이 완료됐다고 판단하기 어려웠다.
이러한 상황을 발견하기 위해 별도의 Migration Audit이 필요했다.
AI가 테스트까지 변경하는 문제
이번 글에서 가장 중요한 부분 가운데 하나는 테스트를 변경할 권한이다.
예를 들어 에이전트가 기존 SDK의 기능을 새로운 Runtime으로 이전한다고 가정해 보자.
하지만 특정 Callback을 제대로 구현하지 못했다.
이 경우 에이전트는 다음 방법을 선택할 수 있다.
첫 번째는 실제 Callback을 구현하는 것이다.
두 번째는 해당 Callback을 검사하는 테스트를 제거하는 것이다.
두 번째 방법은 테스트를 통과시키는 데 도움이 될 수 있다.
하지만 실제 기능의 호환성을 유지하지는 못한다.
GitHub의 마이그레이션 사례에서는 에이전트가 일부 테스트를 제거하거나 호환성 검사를 우회하는 Label을 추가한 문제가 발견됐다.
따라서 에이전트가 수정하는 코드와 해당 코드를 검증하는 테스트를 독립적으로 관리할 필요가 있었다.
언어 간 실행 의미의 차이
기존 코드와 새 코드가 문법적으로 비슷하더라도 실제 동작은 다를 수 있다.
예를 들어 Zig와 Rust는 Assert의 실행 방식이 다를 수 있다.
Zig의 Assert는 실제 함수 호출로 동작할 수 있다.
반면 Rust의 debug_assert!는 Release Build에서 제거될 수 있다.
따라서 Assert에 Side Effect가 존재한다면 두 언어에서 실행 결과가 달라질 수 있다.
비슷하게 JavaScript와 Rust에서는 다음 차이가 문제가 될 수 있다.
- 빈 문자열의 처리
- Null과 Undefined
- 예외 처리
- 숫자 변환
- 객체의 수명
- 비동기 실행 순서
따라서 기존 코드의 구조를 옮기는 것뿐 아니라 실제 언어의 의미를 비교해야 한다.
실제 성능 개선의 원인
GitHub의 사례에서는 Runtime을 Rust로 이전하면서 성능 개선이 나타났다.
하지만 저자는 전체 개선을 단순히 Rust의 실행 속도 때문이라고 판단하지 않았다.
실제 구조에서는 별도의 프로세스를 이용하던 작업이 같은 프로세스 안에서 실행되도록 변경됐다.
따라서 프로세스 사이의 통신 비용이 감소할 수 있었다.
즉 마이그레이션 이후 실제 성능이 개선됐더라도 다음 두 원인을 구분해야 한다.
- 언어 자체의 실행 성능
- 새로운 아키텍처의 실행 구조
이러한 차이는 Benchmark를 해석할 때 중요하다.
Big-bang과 Incremental Migration
저자는 대규모 코드를 한 번에 변경하는 방식과 단계적으로 이전하는 방식도 비교한다.
Big-bang 방식에서는 일정 기간 안에 전체 Runtime을 이전할 수 있다.
하지만 실제로 문제가 발생하면 변경 범위가 매우 클 수 있다.
반면 Incremental Migration은 작은 부분부터 이전한다.
이 경우 실제 동작을 단계별로 검증할 수 있다.
하지만 기존 코드와 새로운 코드를 오랫동안 함께 유지해야 할 수 있다.
따라서 실제 프로젝트의 규모와 테스트 환경, 릴리스 정책에 따라 적합한 방식이 달라진다.
이번 분석의 한계
이번 글은 저자가 여러 기업과 오픈소스 프로젝트의 공개 자료를 비교한 것이다.
실제 모든 마이그레이션을 같은 환경에서 직접 재실행한 Benchmark는 아니다.
따라서 각 프로젝트의 코드 규모와 작업 시간을 단순히 비교해 특정 AI 에이전트가 더 우수하다고 판단할 수 없다.
또한 기업의 자체 보고 수치는 독립적인 평가와 구분해야 한다.
이번 글은 대규모 코드 마이그레이션에서 AI가 작성한 코드의 양과 작업 속도보다 기존 동작의 정의, 독립적인 테스트, 언어 간 의미의 차이, 실제 변경의 검증 구조가 중요하다는 점을 여러 프로젝트의 실무 기록으로 설명한다.