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

기준 시점: 2026-11-13 06:00 KST
조사 범위: 전일 2026-11-12 06:00 ~ 당일 2026-11-13 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
Angular 22.2.6 정식 패치와 22.3.0 RC 공개 — Signal Forms 안정화, Router 오류 처리 개선, HTTP QUERY API 도입 준비
- 발표일: 2026-11-12 — 정확한 게시 시각의 시간대 미확인
- 버전: Angular 22.2.6, Angular 22.3.0-rc.0
- 안정화 단계: 22.2.6은 정식 안정 패치, 22.3.0-rc.0은 Release Candidate
- 적용 대상: Angular Core, Signal Forms, HttpClient, Router, Compiler, Hydration
- 원문: https://github.com/angular/angular/releases/tag/v22.2.6
- RC 원문: https://github.com/angular/angular/releases/tag/v22.3.0-rc.0
Angular가 22.2 계열의 새로운 안정 패치와 22.3의 첫 번째 Release Candidate를 공개했다.
두 릴리스는 같은 날짜에 발표됐지만 실제 적용 단계는 다르다.
Angular 22.2.6은 기존 안정 버전에서 발생하던 메모리 누수와 비동기 상태 관리 문제를 수정하는 패치다.
반면 22.3.0-rc.0은 새로운 HTTP API와 Router 기능을 포함한 출시 후보 버전이다.
따라서 RC에 포함된 기능을 현재 안정 버전에서 정식 지원하는 기능으로 취급해서는 안 된다.
Angular 22.2.6: 메모리 누수 수정
이번 안정 패치에서는 여러 종류의 메모리 누수가 수정됐다.
대표적으로 다음 영역이다.
- Animation Renderer
- Closure 및 객체 Reference 유지
- Signal Forms의 AbstractControl
- HTML Sanitization의 중간 DOM Tree
예를 들어 사용자가 여러 화면을 반복해서 이동하는 Angular 애플리케이션을 생각할 수 있다.
화면에서 벗어난 컴포넌트는 더 이상 필요하지 않은 객체와 이벤트를 정리해야 한다.
하지만 관련 객체의 Reference가 남아 있다면 JavaScript Garbage Collector가 해당 객체를 회수하지 못할 수 있다.
이러한 상황이 반복되면 실제 브라우저의 메모리 사용량이 증가할 가능성이 있다.
이번 수정은 사용이 종료된 객체에 대한 불필요한 Reference를 제거하는 데 초점을 맞췄다.
다만 공식 릴리스에는 메모리 사용량이 몇 MB 감소했는지에 관한 정량적인 Benchmark가 포함되지 않았다.
따라서 모든 Angular 프로젝트에서 같은 수준의 메모리 개선이 발생한다고 판단할 수 없다.
Signal Forms의 상태 처리
이번 패치에서는 Signal Forms 관련 수정도 여러 개 포함됐다.
Signal Forms는 Angular의 반응형 상태 관리와 Form의 필드 상태를 연결하는 방식이다.
대표적인 변경은 다음과 같다.
SignalFormControl에 전달한 Options 유지parseErrors업데이트 시 중복valueChanges이벤트 방지- Validator가
parseErrors를 읽을 때 불필요한 반응형 의존성 추적 방지 - 필드가 Touched 상태가 될 때 지연된 하위 필드 값 반영
- 더 이상 필요하지 않은
AbstractControlReference 해제
예를 들어 사용자가 Form에 값을 입력한다고 가정해 보자.
일부 필드는 입력이 변경될 때마다 즉시 값을 반영한다.
반면 다른 필드는 Debounce를 적용해 일정 시간 뒤 값을 반영할 수 있다.
문제는 사용자가 입력을 마친 직후 해당 필드에서 Focus를 이동하는 상황이다.
아직 반영되지 않은 값이 존재한다면 실제 검증과 화면 상태가 어긋날 수 있다.
이번 변경에서는 필드가 Touched 상태로 변경되는 과정에서 대기 중인 하위 필드 값을 반영하도록 수정했다.
또한 Parser 오류가 변경됐다는 이유만으로 실제 값 변경 이벤트가 중복 발생하는 문제도 수정했다.
이러한 수정은 복잡한 Form에서 Validation과 상태 구독을 사용하는 프로젝트에 영향을 줄 수 있다.
HTTP Interceptor의 Pending Task 정리
HttpClient 관련 수정도 포함됐다.
대표적으로 다음 문제다.
clean up pending tasks when interceptors throw synchronously
Angular의 HTTP Interceptor는 요청을 서버로 보내기 전에 공통 처리를 수행할 수 있다.
예를 들어 다음 작업이다.
- 인증 Header 추가
- 요청 Logging
- 공통 오류 처리
- 요청 URL 변경
하지만 Interceptor 안에서 동기적인 오류가 발생할 수 있다.
기존에는 이러한 경우 일부 Pending Task가 정상적으로 정리되지 않을 가능성이 있었다.
이번 패치는 해당 작업을 정리하도록 수정했다.
또한 httpResource에서 실제로 사용하지 않는 Transfer Cache 관련 코드를 제거하기 쉽도록 Tree Shaking 처리를 개선했다.
Tree Shaking은 실제로 사용하지 않는 코드를 최종 Bundle에서 제거하는 최적화다.
하지만 이번 릴리스에 구체적인 JavaScript Bundle 감소량은 공개되지 않았다.
Router의 불필요한 페이지 Reload 수정
Angular Router에서는 다음 문제가 수정됐다.
- 첫 번째 Navigation이 성공하기 전에 취소됐을 때
history.go()를 호출하지 않도록 변경 - Navigation API를 이용해 URL을 복원하는 과정에서 전체 페이지를 Reload하지 않도록 변경
예를 들어 사용자가 특정 페이지로 이동하려고 했다고 가정해 보자.
하지만 Route Guard 또는 다른 Navigation 조건 때문에 해당 이동이 취소될 수 있다.
이때 Router는 이전 URL과 History 상태를 적절하게 복원해야 한다.
문제는 복원 과정에서 브라우저의 전체 문서 Navigation이 발생할 수 있다는 점이다.
SPA에서는 일반적으로 전체 페이지를 다시 다운로드하지 않고 화면을 변경한다.
따라서 불필요한 Reload는 현재 메모리 상태를 잃거나 초기화 비용을 발생시킬 수 있다.
이번 수정은 URL 복원 과정에서 이러한 동작을 줄이도록 한다.
Angular 22.3.0-rc.0: HttpClient.query()
22.3.0-rc.0에서는 새로운 HTTP 메서드 지원이 추가됐다.
대표적인 API는 다음과 같다.
HttpClient.query()
이는 HTTP QUERY 요청을 처리하기 위한 메서드다.
일반적인 웹 API에서는 다음 메서드를 자주 사용한다.
- GET
- POST
- PUT
- PATCH
- DELETE
QUERY는 조회 요청의 의미를 표현하면서 Request Body를 활용할 수 있도록 하는 HTTP 메서드다.
예를 들어 복잡한 검색 조건을 서버에 전달한다고 가정해 보자.
검색 조건을 모두 URL의 Query String에 포함하기 어려울 수 있다.
이러한 상황에서 QUERY 메서드를 사용하는 API를 구성할 수 있다.
다만 실제 서버와 Proxy, CDN, 보안 장비가 QUERY를 지원하는지는 별도로 확인해야 한다.
Angular의 HttpClient.query()가 추가됐다는 사실만으로 모든 서버와 중간 네트워크 장비에서 해당 메서드를 사용할 수 있다는 의미는 아니다.
또한 이번 API는 아직 Release Candidate에 포함된 기능이다.
Router Error Boundary와 Redirect 처리
22.3.0-rc.0에는 다음 변경도 포함됐다.
introduce route error boundaries and redirect handling
Router Error Boundary는 Route를 준비하거나 실행하는 과정에서 발생하는 오류를 처리하는 구조와 관련된다.
예를 들어 특정 페이지를 열기 전에 데이터가 필요하다고 가정해 보자.
Router는 해당 데이터를 로딩한다.
하지만 API 요청이 실패할 수 있다.
이 경우 전체 애플리케이션이 예외를 처리하지 못한 채 중단되는 대신 Route 수준에서 오류를 처리할 수 있는 구조가 필요하다.
이번 RC는 이러한 실행 경로를 확장한다.
또한 다음 변경도 포함됐다.
run loadComponent in parallel with route resources
기존에는 Route에 필요한 컴포넌트와 데이터 로딩이 순차적으로 실행되는 구간이 존재할 수 있다.
새로운 구현은 일부 작업을 병렬로 수행하도록 변경한다.
이론적으로는 전체 Route가 준비되기까지 걸리는 시간을 줄일 가능성이 있다.
하지만 실제 개선은 컴포넌트의 크기와 네트워크 상태, 데이터 로딩 시간에 영향을 받는다.
공식 발표에는 실제 Navigation 시간이 몇 퍼센트 감소했는지에 관한 Benchmark가 포함되지 않았다.
Compiler와 Custom Form Control
RC에서는 Compiler 관련 변경도 제공한다.
대표적으로 다음과 같다.
- 명시적인
ng-template의 알 수 없는 Property Binding 검사 - 콜론으로 시작하는 잘못된 Attribute 및 Property 이름 차단
- TypeScript의 Ambient Global 탐색 최적화
- 같은 프로세스의
.d.tsParsing 결과 재사용 - AOT Bootstrap에서 불필요한 JIT 의존성 제거
또한 Signal Forms에서 사용자 정의 Form Control에 Validator Directive를 적용하는 기능도 개선됐다.
예를 들어 일반적인 Input 대신 직접 만든 Date Picker나 Select 컴포넌트를 사용하는 상황을 생각할 수 있다.
이러한 컴포넌트에도 일관된 Form Validation을 적용할 수 있어야 한다.
이번 RC는 이러한 사용자 정의 Control과 Validator의 연결을 보완한다.
안정화 단계와 적용 범위
이번 두 릴리스는 다음처럼 구분해야 한다.
| 항목 | Angular 22.2.6 | Angular 22.3.0-rc.0 |
|---|---|---|
| 릴리스 상태 | Stable Patch | Release Candidate |
| 주요 목적 | 버그 수정 및 안정화 | 새로운 기능의 출시 전 검증 |
| Signal Forms 수정 | 포함 | 포함 |
HttpClient.query() | 포함되지 않음 | 포함 |
| Router Error Boundary | 신규 기능 없음 | 도입 준비 |
| 운영 환경 적용 | 일반적인 패치 업그레이드 대상 | 호환성 검증 필요 |
이번 발표에는 렌더링 속도나 메모리 사용량, Route Navigation에 관한 정량적인 성능 비교가 포함되지 않았다.
안정 버전에서는 실제 오류와 메모리 누수를 수정하고, 다음 버전에서는 HTTP API 및 Router의 실행 모델을 확장하려는 변경이 함께 공개된 것이다.
Cloudflare RealtimeKit, 웹용 Noise Cancellation SDK 공개 — React·Angular·Vanilla JavaScript에서 브라우저 측 소음 제거
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 웹 SDK 기능
- 적용 대상: RealtimeKit 기반 웹 애플리케이션
- 패키지:
@cloudflare/realtimekit-noise-cancellation - 지원 브라우저: Chrome, Edge, Firefox, Safari 및 지원 모바일 브라우저
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-realtimekit-noise-cancellation/
Cloudflare가 RealtimeKit을 사용하는 웹 애플리케이션에 Noise Cancellation 기능을 추가할 수 있는 새로운 패키지를 공개했다.
RealtimeKit은 웹과 모바일 애플리케이션에서 실시간 음성 및 영상 통화 기능을 구현하는 데 사용하는 SDK다.
예를 들어 다음 서비스를 만들 수 있다.
- 화상회의
- 온라인 수업
- 원격 면접
- 실시간 상담
이번 변경은 새로운 영상통화 서비스를 출시한 것이 아니다.
이미 RealtimeKit을 사용하는 애플리케이션에서 사용자의 주변 소음을 브라우저 안에서 제거할 수 있도록 기능을 확장한 것이다.
오디오가 서버로 전달되기 전에 처리
이번 기능의 주요 특징은 처리 위치다.
Noise Cancellation은 참가자의 브라우저에서 동작한다.
예를 들어 사용자가 카페에서 화상회의에 참여한다고 가정해 보자.
마이크에는 사용자의 목소리뿐 아니라 다음 소리가 들어갈 수 있다.
- 주변 대화
- 키보드 입력
- 에어컨 소음
- 다른 배경 소리
이번 기능은 해당 오디오를 다른 참가자에게 전달하기 전에 브라우저에서 처리한다.
따라서 회의 서버에서 모든 참가자의 오디오를 다시 처리하는 방식과 다르다.
하지만 브라우저에서 처리한다는 사실만으로 실제 소음 제거 품질이 모든 기기에서 동일하다고 보장할 수는 없다.
실제 마이크와 브라우저, 기기 성능에 따라 결과가 달라질 수 있다.
SDK 초기화
패키지를 설치한 뒤 기존 RealtimeKit Meeting 객체를 전달한다.
대표적인 API는 다음과 같다.
RealtimeKitNoiseCancellation.init({ meeting })
이 함수는 비동기적으로 초기화된다.
Cloudflare는 초기화 때문에 회의 참가 자체를 지연할 필요가 없다고 설명한다.
즉 사용자는 회의에 먼저 참여할 수 있다.
이후 Noise Cancellation이 준비되면 자동으로 동작할 수 있다.
이러한 방식에서는 추가적인 오디오 처리 기능이 준비되지 않았다는 이유로 전체 회의 참가를 차단하지 않아도 된다.
마이크 상태와의 관계
이번 기능은 마이크가 실제로 활성화되면 자동으로 동작한다.
또한 다음 상황에서도 상태를 유지한다.
- 마이크 Mute
- Unmute
- 연결이 끊겼다가 복구되는 경우
회의에서 나가면 관련 리소스를 정리한다.
중요한 제한사항도 존재한다.
Noise Cancellation은 사용자가 Mute한 마이크를 임의로 Unmute하지 않는다.
따라서 실제 사용자의 마이크 권한과 Mute 상태를 유지하면서 오디오 처리 기능만 추가한다.
지원 범위
이번 기능은 다음 프론트엔드 기술에서 사용할 수 있다.
- React
- Angular
- Vanilla JavaScript
또한 Chrome과 Edge, Firefox, Safari 및 지원 모바일 브라우저에서 동작한다.
하지만 다음 SDK에는 아직 제공되지 않는다.
- RealtimeKit Native iOS SDK
- RealtimeKit Native Android SDK
따라서 모바일 브라우저에서 사용할 수 있다는 사실과 Native 모바일 앱의 SDK에서도 동일하게 사용할 수 있다는 사실은 구분해야 한다.
성능과 한계
이번 발표에는 다음 수치가 포함되지 않았다.
- 실제 소음 감소량
- 음성 명료도의 개선 비율
- CPU 사용량
- 배터리 사용량
- 오디오 지연 증가량
- 독립적인 음질 비교 결과
따라서 이번 기능을 활성화하면 모든 환경에서 소음이 특정 비율만큼 감소한다고 단정할 수 없다.
또한 실제로 중요한 통화 서비스를 운영한다면 오디오 처리 실패와 사용자의 기기 성능 차이를 고려해야 한다.
이번 업데이트는 실시간 통화 애플리케이션에서 서버 전송 이전의 오디오를 브라우저가 직접 처리할 수 있도록 RealtimeKit의 웹 SDK를 확장한 기능이다.
이번 조사 범위에서 React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Astro, Remix의 이외에 포함할 만한 주요 신규 안정 기능은 확인하지 못했습니다.
프레임워크의 개발 브랜치와 개별 패치 릴리스는 새로운 주요 API의 안정화와 구분했다.
이전 브리핑에서 이미 다룬 Astro 7.4.9·7.4.10과 Vue 3.5.45, Next.js 16.5 Canary의 실험 기능은 반복하지 않았다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome·Chromium, Firefox·Mozilla, WebKit·Safari의 주요 공식 발표와 JavaScript·TypeScript·CSS, 웹 성능 및 접근성 관련 자료를 확인했다.
이번 기간에 해당하는 중요한 신규 웹 표준의 정식 채택이나 브라우저 안정 버전에서의 주요 API 출시를 확인하지 못했다.
특히 다음 종류의 변경을 구분했다.
- 표준 규격 문서의 수정
- 브라우저 개발 브랜치에서의 구현
- 실험 설정 뒤에서 제공되는 기능
- 일반 사용자에게 기본 제공되는 안정 기능
개발 브랜치에서 특정 기능을 구현했다는 사실만으로 해당 기능이 실제 안정 브라우저에 출시됐다고 판단하지 않았다.
또한 기존 API의 동작을 분석한 기술 글을 새로운 브라우저 기능 출시로 취급하지 않았다.
Firefox 160의 WebTransport 변경과 보안 수정은 11월 11일 브리핑에서 이미 다뤘으므로 반복하지 않았다.
이번 추천 글에서는 Vite의 CSS 컴파일 대상 설정을 다룬 실제 오픈소스 기여 기록을 소개한다.
특히 @media 문법의 변환이 브라우저 지원 대상 설정에 영향을 받을 수 있고, 여러 Vite Plugin이 같은 설정을 수정할 때 실제 적용 순서가 중요하다는 점을 살펴볼 수 있다.
3️⃣ 백엔드·인프라
Google App Engine Standard, Direct VPC Egress 정식 출시 — Serverless VPC Access Connector 없이 사설 네트워크 접근
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Google Cloud
- 안정화 단계: Generally Available
- 적용 대상: App Engine Standard의 Go, Java, Node.js, PHP, Python, Ruby Runtime
- 주요 기능: Direct VPC Egress
- 원문: https://docs.cloud.google.com/release-notes/
- 상세 문서: https://docs.cloud.google.com/appengine/docs/standard/vpc-direct-vpc
Google Cloud가 App Engine Standard Environment에서 Direct VPC Egress를 정식 제공한다고 발표했다.
이번 변경은 App Engine이 외부 또는 내부 네트워크로 요청을 전달하는 방식과 관련된다.
특히 App Engine에서 실행하는 웹 API 서버가 VPC 안에 존재하는 데이터베이스나 내부 서비스에 접근해야 하는 경우에 영향을 줄 수 있다.
기존 Serverless VPC Access Connector
일반적인 웹 애플리케이션에서는 다음과 같은 구조를 사용할 수 있다.
- 사용자의 브라우저
- App Engine의 API 서버
- VPC 내부의 데이터베이스
- 내부 API 또는 다른 서버
문제는 App Engine의 실행 환경과 VPC 내부 네트워크를 연결하는 방식이다.
기존에는 Serverless VPC Access Connector를 구성할 수 있었다.
Connector는 서버리스 실행 환경에서 VPC로 트래픽을 전달하는 데 사용된다.
하지만 실제로는 Connector 자체도 관리해야 한다.
대표적으로 다음 설정이 필요할 수 있다.
- Connector 인스턴스
- 머신 유형
- 확장 설정
- 네트워크 구성
- 관련 비용
이러한 요소는 실제 애플리케이션의 코드와 별개로 운영 부담을 만든다.
이번 Direct VPC Egress는 이러한 Connector를 별도로 구성하지 않아도 VPC로 트래픽을 보낼 수 있도록 한다.
app.yaml에서 네트워크 구성
새로운 방식에서는 App Engine 서비스의 설정 파일에서 네트워크 연결을 구성할 수 있다.
대표적인 파일은 다음과 같다.
app.yaml
개발자는 실제 VPC Network와 Subnet을 지정할 수 있다.
App Engine은 해당 설정을 이용해 VPC 리소스에 접근한다.
예를 들어 PostgreSQL 데이터베이스가 VPC의 사설 주소에 있다고 가정해 보자.
API 서버는 해당 데이터베이스에 연결해야 한다.
Direct VPC Egress를 사용하면 별도의 Connector 인스턴스를 구성하지 않고도 VPC로 트래픽을 전송할 수 있다.
하지만 실제 데이터베이스에 연결할 수 있는지는 다음 조건에도 영향을 받는다.
- VPC Firewall
- Database 인증
- 서비스 계정의 권한
- Subnet 설정
- 실제 Route
Direct VPC Egress를 활성화했다는 사실만으로 모든 내부 리소스에 접근할 수 있는 것은 아니다.
네트워크 비용
Google Cloud는 Direct VPC Egress 자체에 별도의 추가 서비스 요금이 없다고 설명한다.
또한 Connector VM을 유지하기 위한 고정적인 월간 비용을 줄일 수 있다.
하지만 실제 네트워크 트래픽에 관한 다른 비용이 전혀 없다는 의미는 아니다.
예를 들어 Cloud NAT나 실제 데이터 전송에 관한 비용은 별도로 발생할 수 있다.
따라서 Connector가 필요하지 않다는 사실과 전체 네트워크 사용료가 0원이 된다는 사실을 구분해야 한다.
Firewall과 Network Tag
이번 기능에서는 App Engine 서비스 버전에 Network Tag를 직접 적용할 수 있다.
이를 이용하면 서비스별로 다른 Firewall 정책을 지정할 수 있다.
예를 들어 다음 두 서비스가 있다고 가정해 보자.
- 공개 고객용 API
- 내부 관리자 API
두 서비스가 같은 VPC에 접근하더라도 허용해야 하는 목적지는 다를 수 있다.
Network Tag를 이용하면 특정 서비스 버전에 적용할 네트워크 정책을 분리할 수 있다.
또한 VPC Flow Logs를 이용해 관련 Egress 트래픽을 분석할 수 있다.
하지만 실제 로그에서 App Engine의 서비스 버전 정보가 모두 동일한 형태로 제공되는 것은 아니다.
Google Cloud 문서에 따르면 VPC Flow Logs에는 서비스 이름이 표시되지만 버전 정보의 표시에는 제한이 있다.
IP 주소 사용량
이번 기능에서 중요한 운영상의 제한은 Subnet의 IP 주소다.
Direct VPC Egress에서는 실행 중인 App Engine 인스턴스 수에 따라 IP 주소를 사용한다.
Google Cloud는 안정적인 운영을 위해 실제 인스턴스 수보다 많은 IP 주소를 확보하도록 안내한다.
공식 문서에서는 정상 상태에서도 인스턴스 수의 약 두 배에 해당하는 IP 주소를 사용할 수 있다고 설명한다.
예를 들어 100개의 인스턴스가 실행된다고 가정해 보자.
실제로는 약 200개의 IP 주소가 필요할 수 있다.
또한 새로운 버전을 배포하는 과정에서 기존 버전과 새로운 버전이 잠시 동시에 실행될 수 있다.
이 경우 더 많은 주소가 필요할 수 있다.
따라서 단순히 서버 인스턴스 수만큼 IP 주소를 준비해서는 부족할 수 있다.
Cold Start와 인스턴스 제한
공식 문서에는 다음 제한사항도 명시돼 있다.
- 기본적으로 App Engine 버전당 Direct VPC Egress 인스턴스 100개
- 필요한 경우 Quota 상향 요청 가능
- 일부 지역은 초대 기반 제공
- 드문 경우 초기 Cold Start가 최대 1분까지 걸릴 수 있음
- 네트워크 유지보수 중 일시적인 연결 끊김 가능
- Direct VPC Ingress는 지원하지 않음
따라서 이번 기능은 VPC로 나가는 트래픽을 처리하는 기능이다.
외부의 VPC 트래픽을 App Engine에 직접 유입시키는 기능과는 구분해야 한다.
또한 실제 Cold Start의 시간은 지역과 애플리케이션의 구성에 따라 달라질 수 있다.
성능과 한계
Google Cloud는 Connector를 제거하면서 연결의 성능과 안정성이 개선될 수 있다고 설명한다.
하지만 이번 GA 발표에는 다음 정량적인 비교 결과가 포함되지 않았다.
- 기존 Connector와의 지연 시간 차이
- P95 및 P99 연결 시간
- 최대 동시 요청 처리량
- 실제 비용 감소율
따라서 이번 변경으로 모든 애플리케이션의 네트워크 응답 시간이 특정 비율만큼 감소한다고 단정할 수 없다.
이번 발표는 App Engine Standard에서 VPC 내부 리소스에 접근하는 네트워크 구성을 단순화하고 Connector 운영 부담을 줄인 정식 기능이다.
Google App Engine에서 Cloud Run으로 기존 이미지 직접 이전 지원 — 소스코드 없이 검증용 서비스를 생성하는 Preview
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Google Cloud
- 안정화 단계: Preview
- 적용 대상: App Engine Standard의 지원되는 Second-generation Runtime
- 주요 기능: Migration Hub, 기존 빌드 이미지 재사용
- 원문: https://docs.cloud.google.com/release-notes/
- 상세 문서: https://docs.cloud.google.com/appengine/migration-center/run/migrate-app-engine-standard-to-run
Google Cloud가 App Engine Standard에서 실행 중인 애플리케이션을 Cloud Run으로 이전할 수 있는 새로운 경로를 Preview로 공개했다.
이번 기능의 가장 큰 특징은 현재 실행 중인 App Engine 버전의 기존 Container Image를 사용해 Cloud Run 서비스를 생성할 수 있다는 점이다.
따라서 원래 소스코드를 다시 가져오거나 애플리케이션을 새롭게 빌드하지 않아도 되는 경로가 제공된다.
소스코드가 없는 애플리케이션
예를 들어 오래전에 개발한 Node.js API 서버가 있다고 가정해 보자.
현재 App Engine에서 정상적으로 실행되고 있다.
하지만 다음 문제가 존재할 수 있다.
- 원래 개발 Repository에 접근하기 어려움
- 기존 빌드 환경이 남아 있지 않음
- 일부 의존성의 이전 버전이 더 이상 설치되지 않음
- 이전 배포 과정이 문서화돼 있지 않음
이러한 상황에서는 Cloud Run으로 이전하기 위해 전체 애플리케이션을 다시 빌드하는 일이 어려울 수 있다.
새로운 Migration Hub의 기존 이미지 사용 기능은 이러한 문제를 줄인다.
이미 App Engine에서 실행 중인 버전의 이미지를 활용할 수 있기 때문이다.
실제 서비스에 영향을 주지 않는 복제
이번 기능에서는 App Engine의 기존 서비스를 즉시 Cloud Run으로 전환하지 않는다.
대신 별도의 Cloud Run 서비스를 생성한다.
따라서 다음 두 서비스가 동시에 존재할 수 있다.
- 기존 App Engine 서비스
- 새롭게 생성한 Cloud Run 서비스
Google Cloud는 새로운 Cloud Run 서비스의 생성이 기존 App Engine의 실제 트래픽에 영향을 주지 않는다고 설명한다.
따라서 기존 서비스를 계속 운영하면서 새로운 배포 환경을 테스트할 수 있다.
하지만 실제 데이터베이스를 두 서비스가 함께 사용하는 경우에는 별도의 주의가 필요하다.
예를 들어 새로운 Cloud Run 서비스에서 테스트 요청을 실행했는데 실제 운영 데이터베이스를 변경할 수 있다.
따라서 복제된 서비스의 네트워크와 데이터베이스 접근 권한도 확인해야 한다.
Cloud Run의 기본 인증 정책
App Engine과 Cloud Run은 기본 접근 정책이 다를 수 있다.
Google Cloud의 마이그레이션 문서에 따르면 App Engine 서비스는 기본적으로 공개될 수 있다.
반면 Cloud Run 서비스는 기본적으로 Private 상태로 생성된다.
따라서 새롭게 생성한 Cloud Run의 URL에 일반 브라우저로 접근하면 HTTP 403이 반환될 수 있다.
이러한 결과가 반드시 애플리케이션의 오류를 의미하는 것은 아니다.
Cloud Run의 인증 정책 때문에 발생할 수 있다.
실제 검증을 위해서는 필요한 IAM 권한을 부여하거나 적절한 Proxy를 사용할 수 있다.
지원 범위
이번 Preview는 Second-generation Runtime을 대상으로 한다.
또한 App Engine의 Legacy Bundled Service를 사용하는 일부 애플리케이션에는 제한이 존재한다.
따라서 모든 App Engine 애플리케이션을 변경 없이 Cloud Run으로 이전할 수 있다고 해석해서는 안 된다.
실제 기능의 호환성을 검증해야 한다.
마이그레이션 도구는 다음 작업도 지원한다.
- 기존 서비스의 호환성 검사
- Cloud Run 설정 생성
- 새로운 Cloud Run 서비스 배포
- 기존 이미지 또는 새로운 소스 빌드 선택
실제 Preview 기능이므로 지원 범위와 API는 이후 변경될 수 있다.
필요한 권한
기존 App Engine 이미지를 Cloud Run으로 배포하려면 관련 IAM 권한이 필요하다.
대표적으로 다음 역할이 사용된다.
- App Engine Deployer
- Cloud Run Developer
- Artifact Registry Reader
- Service Account User
따라서 기존 서비스에 접근할 수 있다는 사실만으로 해당 이미지를 새로운 Cloud Run 서비스에 배포할 수 있는 것은 아니다.
실제 배포 계정의 권한도 필요하다.
적용상의 한계
이번 기능은 이미 빌드한 애플리케이션을 새로운 환경에서 테스트하기 위한 수단이다.
하지만 실제 클라우드 서비스의 실행 특성은 서로 다를 수 있다.
예를 들어 다음 항목을 확인해야 한다.
- 인증 정책
- 환경 변수
- 네트워크 연결
- 파일시스템의 동작
- 최소 및 최대 인스턴스 수
- 요청 Timeout
- 데이터베이스 연결
- 실제 비용
이번 발표에는 마이그레이션 이후의 비용이나 응답 시간이 얼마나 개선됐는지에 관한 독립적인 Benchmark가 포함되지 않았다.
이미 운영 중인 App Engine 애플리케이션을 기존 서비스에 영향을 주지 않고 Cloud Run에서 검증할 수 있도록 기존 배포 이미지를 재사용하는 Preview 기능이다.
Cloudflare AI Search, D1 데이터베이스를 검색 소스로 연결하는 Beta 공개 — SQL 결과를 문서 단위로 인덱싱
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: Beta
- 적용 대상: Cloudflare D1, AI Search, Workers
- 지원 방식: Read-only SQL Query, 행 단위 인덱싱, 주기적 동기화
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-d1-data-source/
Cloudflare가 AI Search의 데이터 소스로 D1 데이터베이스를 사용할 수 있도록 기능을 확장했다.
이번 변경은 기존 RAG 또는 검색 기능에 새로운 데이터베이스 연결 방식을 추가한 것이다.
D1은 Cloudflare가 제공하는 SQLite 기반 관리형 데이터베이스다.
예를 들어 웹 애플리케이션에서 다음 데이터를 D1에 저장할 수 있다.
- 기술 문서
- 고객지원 FAQ
- 상품 설명
- 게시글
- 서비스 설정 데이터
이번 기능을 사용하면 D1에 저장한 데이터 가운데 필요한 정보를 AI Search의 인덱스로 가져올 수 있다.
관계형 데이터와 문서 검색의 차이
일반적인 관계형 데이터베이스는 구조화된 데이터를 저장하고 SQL로 조회한다.
예를 들어 다음 Table이 존재한다고 가정해 보자.
articles
컬럼은 다음과 같다.
- article_id
- title
- content
- category
- published
개발자는 다음처럼 데이터를 조회할 수 있다.
SELECT title, content FROM articles WHERE published = 1
이러한 SQL Query는 정확한 조건을 기준으로 데이터를 가져오는 데 적합하다.
하지만 사용자가 자연어로 질문하는 검색 서비스에서는 다른 방식이 필요할 수 있다.
예를 들어 사용자가 다음과 같이 질문한다고 가정해 보자.
비밀번호를 잊어버렸을 때 계정을 복구하는 방법이 뭐야?
실제 문서에는 정확히 같은 표현이 존재하지 않을 수 있다.
하지만 의미상 관련된 문서를 찾아야 한다.
AI Search는 이러한 검색 작업에 사용할 수 있다.
이번 기능은 D1에 저장한 데이터를 AI Search의 인덱스로 가져오는 경로를 제공한다.
Read-only Query
개발자는 AI Search가 실행할 SQL Query를 지정한다.
예를 들어 다음과 같은 Query를 사용할 수 있다.
SELECT article_id AS id, title, content, category AS meta_category FROM articles WHERE published = 1
AI Search는 해당 Query를 읽기 전용으로 실행한다.
그리고 결과의 각 행을 별도의 문서로 인덱싱한다.
예를 들어 Query 결과가 1,000개 행이라면 1,000개의 문서가 생성될 수 있다.
이러한 구조에서는 모든 Table의 데이터를 무조건 인덱싱할 필요가 없다.
실제 검색에 필요한 행과 컬럼만 선택할 수 있다.
문서 ID
이번 기능에서는 결과 행을 식별하는 값도 중요하다.
예를 들어 다음 컬럼을 사용할 수 있다.
article_id AS id
이렇게 하면 실제 D1의 각 행을 AI Search의 문서와 연결할 수 있다.
문서 ID를 안정적으로 유지하면 이후 같은 행의 데이터가 변경됐을 때 관련 문서를 갱신하는 데 도움이 된다.
반면 실제 행을 식별할 수 있는 안정적인 ID가 존재하지 않는다면 동기화 과정에서 문서의 변경을 추적하기 어려울 수 있다.
따라서 단순히 SQL Query가 실행된다는 사실뿐 아니라 검색 문서의 식별 방식도 고려해야 한다.
주기적 동기화
이번 기능은 D1과 AI Search의 데이터를 자동으로 동기화할 수 있도록 한다.
Cloudflare는 다음 변경을 반영한다고 설명한다.
- 새로운 행 추가
- 기존 행 변경
- 기존 행 삭제
예를 들어 고객지원 문서가 수정됐다고 가정해 보자.
원본 D1 데이터베이스의 내용이 변경된다.
이후 AI Search의 동기화 작업이 실행되면 해당 변경을 인덱스에 반영할 수 있다.
하지만 중요한 제한사항이 있다.
D1에 데이터를 저장한 순간 AI Search의 결과가 반드시 즉시 변경되는 것은 아니다.
AI Search는 주기적인 동기화 구조를 사용한다.
따라서 실제 검색 결과에는 일정 시간 이전의 데이터가 남아 있을 수 있다.
예를 들어 금융 거래와 같이 데이터의 정확한 최신성이 필요한 작업을 AI Search의 인덱스만으로 처리하는 것은 적절하지 않을 수 있다.
실제 원본 데이터베이스를 다시 조회하는 검증이 필요할 수 있다.
데이터 노출 범위
이번 기능에서는 SQL Query가 실제로 어떤 데이터를 반환하는지 중요하다.
예를 들어 고객지원 문서 Table에 공개 문서와 내부 문서가 함께 저장돼 있다고 가정해 보자.
AI Search에 모든 행을 제공하면 내부 문서도 검색 인덱스에 포함될 수 있다.
따라서 Query에서 필요한 조건을 적용해야 한다.
대표적으로 다음과 같다.
WHERE published = 1
또한 검색에 필요하지 않은 개인정보나 인증 정보는 결과에 포함하지 않는 편이 적절하다.
Read-only Query라는 사실만으로 결과 데이터가 외부에 공개해도 안전하다는 의미는 아니다.
Read-only는 데이터를 변경하지 않는다는 뜻이다.
어떤 데이터를 읽어 검색 인덱스에 포함할지는 별도로 검증해야 한다.
생성 방법
D1을 사용하는 AI Search Instance는 다음 방식으로 생성할 수 있다.
- Cloudflare Dashboard
- REST API
- Workers Binding
따라서 실제 애플리케이션의 구성에 따라 관리 방법을 선택할 수 있다.
예를 들어 인프라 구성을 자동화하는 경우에는 API를 이용할 수 있다.
반면 초기 검증을 수행하는 경우에는 Dashboard에서 직접 연결할 수 있다.
적용 범위와 한계
이번 기능은 Beta 상태다.
따라서 이후 데이터 소스의 지원 범위와 API가 변경될 수 있다.
또한 이번 발표에는 다음 수치가 포함되지 않았다.
- 1만 행 인덱싱에 필요한 시간
- 동기화 지연의 P95
- 검색 품질의 개선 비율
- D1 Query의 실제 비용
- 독립적인 검색 정확도 Benchmark
이번 변경은 검색 모델 자체의 성능을 개선한 것이 아니라 새로운 데이터 소스 연결을 제공한 것이다.
D1에 저장한 구조화 데이터를 SQL Query로 선별해 AI Search의 검색 문서로 활용할 수 있도록 한 Beta 기능이다.
Google Gemini 4 Hydrogen 정식 출시와 Vercel AI Gateway 지원 — 대규모 문맥·긴 출력·Thinking Level 제공
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Google Cloud, Vercel
- Google Cloud 안정화 단계: Generally Available
- Vercel 제공 단계: AI Gateway에서 모델 선택 가능
- Google Cloud 모델 ID:
gemini-4-hydrogen - Vercel 모델 ID:
google/gemini-4-hydrogen - 최대 통합 Context: 1,048,576 Token
- 원문: https://docs.cloud.google.com/release-notes/
- Vercel 원문: https://vercel.com/changelog/gemini-4-hydrogen-now-available-on-ai-gateway
Google Cloud가 Gemini 4 Hydrogen을 정식 제공한다고 발표했다.
같은 날짜에 Vercel도 AI Gateway에서 해당 모델을 사용할 수 있도록 지원을 추가했다.
두 발표는 같은 모델에 관한 것이므로 하나로 묶었다.
이번 변경은 웹 개발 프레임워크 자체의 새로운 기능이 아니다.
AI 기능을 포함한 웹 애플리케이션의 백엔드와 코딩 에이전트에서 사용할 수 있는 모델의 선택 범위를 확대한 것이다.
대규모 Context와 긴 출력
Google Cloud의 공식 문서에 따르면 Gemini 4 Hydrogen은 입력과 출력을 합쳐 다음 Context 범위를 지원한다.
1,048,576 tokens
하지만 하나의 출력 구간에서 무조건 100만 Token을 연속 생성할 수 있다는 의미는 아니다.
하나의 Foreground Segment에서 생성할 수 있는 출력의 상한은 다음과 같다.
65,536 tokens
더 긴 출력을 생성하려면 다음 기능을 사용할 수 있다.
continuation_token
이 기능을 이용해 이후 출력 구간을 이어갈 수 있다.
따라서 100만 Token의 통합 Context 지원과 단일 응답의 출력 길이 제한을 구분해야 한다.
또한 실제 Context에 다음 정보가 포함될 수 있다.
- 사용자 입력
- 시스템 지침
- 도구 호출
- 도구 실행 결과
- 모델의 Thinking
- 최종 출력
따라서 Context Window의 최대 수치만큼 사용자의 문서를 모두 넣을 수 있다고 단순하게 계산해서는 안 된다.
Thinking Level
이번 모델에서는 다음 네 가지 Thinking Level을 지원한다.
- LOW
- MEDIUM
- HIGH
- XHIGH
기본값은 HIGH다.
이러한 설정은 모델이 작업을 처리할 때 사용하는 추론 수준을 제어하는 데 사용된다.
예를 들어 간단한 텍스트 분류와 복잡한 Repository 수정 작업은 필요한 처리 수준이 다를 수 있다.
따라서 실제 작업의 성격에 맞춰 설정을 선택할 수 있다.
하지만 Thinking Level이 높다는 사실이 모든 작업에서 정확도를 높인다는 보장은 없다.
실제 처리 시간과 비용도 달라질 수 있다.
또한 이번 모델에서는 다음 설정을 지원하지 않는다.
thinking_budgetthinking_level="MINIMAL"
Google Cloud는 이러한 값을 사용하면 HTTP 400 Validation Error가 발생한다고 설명한다.
따라서 이전 모델에서 사용하던 설정을 그대로 적용하면 오류가 발생할 수 있다.
Context Caching
Gemini 4 Hydrogen은 Implicit Context Caching을 지원한다.
Google Cloud는 공유하는 Prompt Prefix가 4,096 Token 이상인 경우 자동 캐싱을 활용할 수 있다고 설명한다.
예를 들어 AI 코딩 에이전트가 같은 Repository의 기본 지침과 파일 목록을 반복해서 전달한다고 가정해 보자.
일부 입력이 반복되므로 캐싱을 통해 불필요한 처리 비용을 줄일 가능성이 있다.
하지만 이번 모델에서는 Explicit Context Caching을 지원하지 않는다.
따라서 개발자가 모든 입력을 직접 캐시에 저장하고 참조하는 구조를 이전 모델과 동일하게 사용할 수 있다고 가정해서는 안 된다.
또한 2027년 1월 12일부터 별도의 Cache Control 및 TTL 관련 기능과 과금 정책이 적용될 예정이라고 안내됐다.
예고된 정책에서는 기본 Cache Write에 일반 입력 Token 가격의 1.25배가 적용될 수 있다.
하지만 이 날짜는 이번 조사 시점의 실제 적용일이 아니라 미래의 정책 적용 예정일이다.
문서 형식 지원
이번 모델은 여러 종류의 문서를 입력으로 사용할 수 있다.
대표적으로 다음과 같다.
- DOCX
- DOC
- PPTX
- PPT
- XLSX
- XLS
- ODT
- ODP
- ODS
이러한 입력은 실제 사용자 문서를 분석하는 웹 애플리케이션에서 활용할 수 있다.
예를 들어 사용자가 Excel 파일을 업로드한다고 가정해 보자.
모델은 해당 문서의 내용을 분석하고 요약할 수 있다.
하지만 문서 형식을 지원한다는 사실만으로 복잡한 모든 수식과 Layout을 완벽하게 해석한다고 보장할 수 없다.
실제 결과는 문서의 구성과 모델의 처리 능력에 영향을 받는다.
Vercel AI SDK에서 사용
Vercel AI Gateway에서는 다음 모델 ID를 사용한다.
google/gemini-4-hydrogen
대표적인 AI SDK API는 다음과 같다.
streamText()
개발자는 해당 모델을 이용해 스트리밍 응답을 생성할 수 있다.
다만 실제 사용량과 비용은 모델의 가격 정책에 영향을 받는다.
또한 Gateway에서 사용할 수 있다는 사실과 모든 AI SDK 기능을 동일하게 지원한다는 사실은 구분해야 한다.
실제 Tool Calling과 입력 형식, 출력 제한은 모델 및 제공업체의 지원 범위를 확인해야 한다.
모델 수명주기
이번 모델은 Google Cloud에서 Short-term Release로 제공된다.
Google Cloud는 해당 모델의 사용 종료와 관련해 최소 45일 전 통지 정책을 명시했다.
따라서 장기간 운영하는 웹 애플리케이션에서 특정 모델 ID에 영구적으로 의존하는 구조는 주의가 필요하다.
모델이 교체되면 다음 요소가 달라질 수 있다.
- 응답 형식
- Tool Calling
- 추론 시간
- 입력 및 출력 비용
- 실제 결과의 품질
따라서 모델 교체가 실제 서비스에 미치는 영향을 확인할 수 있어야 한다.
성능 수치와 한계
Google Cloud는 Provisioned Throughput에서 다음 수치를 안내한다.
685 tokens/s per GSU
하지만 이는 특정 용량 단위와 제공 방식에 관한 수치다.
모든 일반적인 API 요청이 해당 속도를 보장받는다는 의미는 아니다.
또한 이번 발표에는 모든 주요 모델과 동일한 데이터셋에서 비교한 독립적인 Benchmark가 제공되지 않았다.
Vercel 역시 코딩과 도구 실행, 문서 생성의 성능 개선을 설명하지만 이를 모든 작업에서 보장되는 결과로 해석해서는 안 된다.
이번 발표는 대규모 Context와 긴 출력, 여러 Thinking Level을 지원하는 모델을 Google Cloud에서 정식 제공하고 Vercel AI Gateway에서도 사용할 수 있도록 확대한 업데이트다.
Cloudflare Workers AI, EmbeddingGemma 2 지원 — 텍스트·이미지·오디오·비디오를 공통 벡터 공간으로 변환
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: Workers AI에서 모델 제공
- 모델 ID:
@cf/google/embeddinggemma-2 - 기본 Embedding 크기: 768차원
- 선택 가능한 축소 차원: 128, 256, 512
- Context Window: 8,192 Token
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-embeddinggemma-2-workers-ai/
Cloudflare가 Workers AI에서 EmbeddingGemma 2를 사용할 수 있도록 지원을 추가했다.
이번 모델은 일반적인 채팅 모델과 목적이 다르다.
사용자의 질문에 긴 텍스트를 생성하는 대신 입력 데이터를 벡터로 변환한다.
이러한 벡터는 의미적으로 비슷한 데이터를 검색하거나 분류하는 데 사용할 수 있다.
Embedding의 역할
예를 들어 상품 검색 서비스를 생각할 수 있다.
사용자는 다음 문장을 입력한다.
비 오는 날 신을 수 있는 러닝화
상품 데이터베이스에는 다음 표현이 존재할 수 있다.
방수 트레일 러닝화
두 문장은 정확히 같은 문자열이 아니다.
하지만 의미상 관련성이 있다.
Embedding은 이러한 문장들을 숫자 벡터로 변환해 비교할 수 있도록 한다.
예를 들어 두 벡터의 거리를 계산하면 의미적으로 가까운 데이터를 찾는 데 활용할 수 있다.
하지만 Embedding이 의미상 가까운 결과를 제공한다는 사실만으로 실제 검색 결과가 항상 정확하다고 보장할 수는 없다.
실제 인덱싱 방식과 검색 기준, 입력 데이터의 품질도 중요하다.
멀티모달 벡터 공간
이번 모델의 특징은 여러 종류의 데이터를 같은 벡터 공간에 표현한다는 점이다.
지원하는 입력에는 다음이 포함된다.
- 텍스트
- 코드
- 이미지
- 오디오
- 비디오
예를 들어 상품의 사진과 설명을 함께 검색해야 한다고 가정해 보자.
일반적인 텍스트 Embedding만 사용하면 사진의 정보를 직접 활용하기 어려울 수 있다.
하지만 멀티모달 Embedding을 이용하면 사진과 텍스트를 같은 벡터 공간에 표현할 수 있다.
이렇게 하면 텍스트 검색어를 이용해 관련 이미지나 다른 미디어 데이터를 찾는 구조를 만들 수 있다.
벡터 차원과 저장 비용
기본 출력은 768차원이다.
하지만 다음 차원도 선택할 수 있다.
- 128
- 256
- 512
예를 들어 100만 개의 상품 데이터를 인덱싱한다고 가정해 보자.
각 상품에 768개의 숫자를 저장하는 경우와 128개의 숫자를 저장하는 경우는 필요한 저장 공간이 다르다.
차원을 줄이면 저장 비용과 일부 검색 연산 비용을 줄일 가능성이 있다.
하지만 실제 벡터가 표현할 수 있는 정보량도 줄어들 수 있다.
Cloudflare는 차원을 줄일 때 품질에 작은 손실이 발생할 수 있다고 설명한다.
다만 이번 발표에는 차원별 검색 정확도와 저장 비용을 독립적으로 측정한 비교 결과가 포함되지 않았다.
Task Prefix
이번 모델에서는 입력에 Task Prefix를 추가할 수 있다.
예를 들어 다음과 같은 표현이다.
task: search result | query: waterproof shoes
이러한 Prefix는 실제 모델이 입력을 어떤 용도로 처리해야 하는지 알려주는 역할을 한다.
대표적인 용도는 다음과 같다.
- 검색
- 분류
- Clustering
- 유사도 비교
하지만 Prefix를 사용한다는 사실만으로 모든 작업에 동일한 품질 개선이 보장되지는 않는다.
실제 인덱싱 데이터와 검색 Query에서 일관된 규칙을 사용하는 것이 중요할 수 있다.
입력 크기와 비용
이번 모델의 Context Window는 8,192 Token이다.
이 제한은 입력에 포함되는 여러 종류의 콘텐츠에 함께 적용된다.
따라서 텍스트와 이미지, 오디오를 함께 전달한다고 해서 각 입력 형식마다 독립적으로 8,192 Token을 사용할 수 있다고 해석해서는 안 된다.
Cloudflare가 공개한 가격은 다음과 같다.
입력 100만 Token당 0.0017달러
하지만 실제 서비스를 운영할 때는 Embedding 생성 비용 외에도 벡터를 저장하고 검색하는 데 필요한 비용이 존재할 수 있다.
이번 발표는 텍스트와 코드를 포함한 여러 종류의 미디어 데이터를 같은 벡터 공간에 표현하는 모델을 Workers AI에서 사용할 수 있도록 확대한 업데이트다.
4️⃣ 개발 도구 및 보안
Cloudflare Tunnel, 세미콜론 경로 파라미터 정규화 확대 — Access 보호 규칙의 우회 가능성 줄이기
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 적용 버전: cloudflared 2026.11.0 이상
- 현재 기본값: 비활성화
- 새 기본값 적용 예정일: 2027-02-08
- 적용 대상: Cloudflare Access 검증을 요구하는 Ingress Rule을 가진 Tunnel
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-cloudflared-matrix-path-parameters/
Cloudflare가 Tunnel의 URL 정규화 범위를 확장했다.
이번 변경은 새로운 Tunnel 서비스를 출시한 것이 아니다.
Ingress Rule이 실제 URL의 경로를 해석하는 방법을 변경해 Access 보호 규칙과 원본 서버의 URL 해석이 서로 달라질 가능성을 줄이는 보안 관련 업데이트다.
Ingress Rule의 역할
Cloudflare Tunnel에서는 외부에서 들어온 요청을 내부 서비스로 전달할 수 있다.
예를 들어 다음 서비스가 있다고 가정해 보자.
- 공개 웹페이지
- 내부 관리자 화면
- 비공개 API
Ingress Rule은 요청의 Hostname과 Path를 기준으로 어떤 내부 서비스에 요청을 전달할지 결정할 수 있다.
또한 특정 Rule에 Cloudflare Access 검증을 요구할 수 있다.
예를 들어 다음 경로는 인증이 필요할 수 있다.
/admin
반면 다음 경로는 공개할 수 있다.
/public
문제는 URL의 Path를 실제로 해석하는 방식이다.
세미콜론 경로 파라미터
일부 URL에서는 세미콜론으로 경로 파라미터를 표현할 수 있다.
예를 들어 다음과 같은 경로다.
/admin;mode=edit
이러한 경로를 어떤 시스템에서는 /admin과 관련된 요청으로 처리할 수 있다.
반면 다른 시스템에서는 전체 문자열을 다른 경로로 취급할 수 있다.
문제는 앞단의 Tunnel과 실제 원본 서버가 서로 다르게 해석하는 경우다.
예를 들어 Tunnel은 특정 요청을 공개 경로로 판단했다.
하지만 실제 원본 서버는 보호해야 하는 관리자 경로로 해석할 수 있다.
이러한 불일치가 존재한다면 접근 정책을 우회하는 문제가 발생할 가능성이 있다.
이번 변경은 이러한 위험을 줄이기 위해 세미콜론 경로 파라미터의 정규화를 확장했다.
실제 원본 URL은 변경하지 않음
이번 기능에서는 다음 두 작업을 구분한다.
첫 번째는 Ingress Rule을 선택하기 위한 URL 정규화다.
두 번째는 실제 원본 서버에 전달하는 URL이다.
Cloudflare는 이번 변경에서 원본 서버에 전달하는 URL의 Path는 변경하지 않는다고 설명한다.
즉 정규화는 Access 검증과 Ingress Rule 매칭에 사용된다.
따라서 실제 애플리케이션이 수신하는 경로까지 자동으로 새롭게 변환하는 기능은 아니다.
하지만 Rule 매칭 결과가 변경되면 실제 요청이 전달되는 내부 서비스나 필요한 인증 조건이 달라질 수 있다.
따라서 경로 기반 Routing을 사용하는 프로젝트에서는 실제 규칙을 테스트해야 한다.
기본값 변경 일정
새로운 정규화는 cloudflared 2026.11.0부터 지원한다.
하지만 현재 기본적으로 활성화되지는 않는다.
Cloudflare는 다음 날짜부터 새롭게 공개하는 cloudflared 버전에서 기본 활성화할 예정이라고 발표했다.
2027-02-08
기존에 안내한 날짜는 다음과 같았다.
2027-01-25
따라서 실제 기본값 변경일이 뒤로 연기됐다.
이번 발표일인 2026년 11월 12일에 모든 Tunnel의 동작이 즉시 변경됐다는 의미는 아니다.
사전 활성화
개발자는 새로운 동작을 미리 활성화할 수 있다.
대표적인 환경 변수는 다음과 같다.
TUNNEL_DISABLE_PATH_NORMALIZATION=false
이 값은 각 cloudflared Replica에 적용해야 한다.
이후 관련 프로세스를 재시작해야 실제 설정이 반영된다.
중요한 점은 변수 이름이다.
DISABLE이라는 이름을 사용하므로 false는 정규화를 비활성화하지 않는다는 의미다.
따라서 실제로는 정규화를 활성화한다.
적용 범위와 제한사항
이번 변경은 Access 검증이 필요한 Ingress Rule을 사용하는 Tunnel에 영향을 준다.
Cloudflare는 Access 검증을 요구하는 Rule이 없는 설정에는 이번 변경의 영향이 없다고 설명한다.
또한 모든 세미콜론이 포함된 URL을 무조건 차단하는 기능이 아니다.
실제로는 경로를 정규화한 뒤 Ingress Rule을 비교한다.
이번 발표에는 요청 처리 성능이나 지연 시간에 관한 Benchmark가 포함되지 않았다.
Cloudflare Tunnel과 원본 애플리케이션이 URL을 다르게 해석해 접근 정책이 어긋날 수 있는 상황을 줄이도록 경로 매칭 방식을 강화한 보안 관련 변경이다.
Cloudflare RealtimeKit, 인증 Token의 시작·만료 시각과 개별 폐기 지원 — 기존 100일 유효 기간을 선택적으로 단축
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 API 기능
- 적용 대상: RealtimeKit Meeting, Participant Authentication
- 신규 설정:
not_before,expires_on - 기존 기본 유효 기간: 100일
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-realtimekit-auth-token-validity/
Cloudflare가 RealtimeKit의 참가자 인증 Token에 유효 기간을 직접 설정하고 개별 Token을 폐기할 수 있도록 기능을 추가했다.
이번 변경은 실시간 회의 서비스의 인증과 관련된다.
특히 일정한 시간에만 참가할 수 있는 온라인 수업이나 면접, 상담 서비스에서 영향을 줄 수 있다.
기존 100일 유효 기간
RealtimeKit에서 사용자를 회의에 추가하면 참가자 인증 Token을 발급받을 수 있다.
사용자의 애플리케이션은 해당 Token으로 회의에 참여한다.
기존에는 모든 Token이 발급 시점부터 100일 동안 유효했다.
예를 들어 한 시간짜리 온라인 면접을 생각해 보자.
면접은 오전 10시에 시작해 오전 11시에 종료된다.
하지만 인증 Token이 100일 동안 유효하다면 실제 일정이 종료된 이후에도 오랫동안 사용할 수 있는 상태로 남을 수 있다.
물론 실제 회의가 종료됐는지와 다른 접근 정책에 따라 참가 가능 여부는 달라질 수 있다.
그러나 인증 Token 자체의 유효 기간은 실제 사용 목적보다 길 수 있었다.
이번 업데이트에서는 이러한 기간을 직접 제한할 수 있다.
not_before와 expires_on
새로운 설정은 두 가지다.
첫 번째는 다음과 같다.
not_before
이는 Token을 사용할 수 있는 시작 시각이다.
두 번째는 다음과 같다.
expires_on
이는 Token이 만료되는 시각이다.
예를 들어 다음과 같이 설정할 수 있다.
- 시작 시각: 2026-12-01T15:00:00Z
- 종료 시각: 2026-12-01T17:00:00Z
이 경우 지정한 기간에만 Token을 사용할 수 있도록 제한한다.
두 필드는 선택 사항이다.
따라서 기존 API 요청에서 값을 생략하면 기존처럼 100일 유효 기간이 적용된다.
즉 이번 변경이 모든 기존 Token의 유효 기간을 자동으로 짧게 변경하는 것은 아니다.
Token의 개별 폐기
이번 업데이트에서는 특정 Token만 폐기할 수도 있다.
예를 들어 온라인 수업에 여러 사용자가 참가한다고 가정해 보자.
사용자 A의 Token이 외부에 노출됐다.
하지만 사용자 B와 C는 정상적으로 수업에 참가하고 있다.
기존 방식에서는 전체 회의를 비활성화하는 방법을 사용할 수 있었다.
하지만 새로운 방식에서는 사용자 A의 Token만 폐기할 수 있다.
따라서 전체 회의를 종료하지 않고 특정 참가자의 접근을 차단할 수 있다.
실제 적용까지 최대 60초
이번 기능에는 중요한 제한사항이 존재한다.
Token을 폐기한 뒤 모든 지역에서 해당 변경이 반영되기까지 최대 60초가 걸릴 수 있다.
따라서 Token 폐기 API가 성공했다고 해서 모든 요청에서 해당 Token이 즉시 거부된다고 보장할 수 없다.
Cloudflare는 즉시 차단해야 하는 경우 해당 참가자를 삭제하도록 안내한다.
이러한 차이는 보안상 중요하다.
예를 들어 특정 사용자의 접근을 긴급하게 차단해야 하는 상황에서는 최대 60초의 지연도 문제가 될 수 있기 때문이다.
회의 중인 참가자
이미 회의에 참가한 사용자의 Token을 폐기하면 RealtimeKit은 해당 참가자를 회의에서 제거한다.
따라서 단순히 이후의 재접속만 차단하는 기능은 아니다.
현재 참가 중인 사용자의 접근 상태에도 영향을 줄 수 있다.
하지만 전체 시스템에서 실제 폐기 상태가 반영되는 시점은 앞서 설명한 전파 지연의 영향을 받을 수 있다.
인증 실패와 과금
RealtimeKit은 다음 Token을 회의에 입장시키기 전에 거부한다.
- 아직 유효 기간이 시작되지 않은 Token
- 만료된 Token
- 폐기된 Token
Cloudflare는 이러한 인증 실패 요청에는 참가자 사용 요금이 청구되지 않는다고 설명한다.
하지만 이는 해당 인증 실패 상황의 과금 정책에 관한 것이다.
실제로 정상 참가한 사용자의 회의 이용 비용은 별도로 적용된다.
적용 범위와 한계
이번 업데이트는 RealtimeKit의 참가자 Token에 관한 것이다.
애플리케이션의 모든 사용자 인증과 권한을 자동으로 관리하는 범용 인증 서비스는 아니다.
예를 들어 사용자가 어떤 회의에 참가할 수 있는지는 애플리케이션의 실제 비즈니스 정책에서도 결정해야 한다.
또한 참가자 Token을 외부에 노출하지 않도록 관리해야 한다.
유효 기간을 짧게 설정한다는 사실만으로 노출된 Token의 위험이 완전히 사라지지는 않는다.
이번 발표에는 Token 검증의 실제 응답 시간이나 폐기 처리량에 관한 Benchmark가 포함되지 않았다.
참가자 인증 정보를 실제 회의 일정과 연결하고, 특정 사용자만 선택적으로 차단할 수 있도록 RealtimeKit의 인증 정책을 세분화한 기능이다.
Google Cloud Service Mesh, 세 계열 보안 패치 공개 — Istio·Envoy 관련 취약점 수정
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Google Cloud
- 안정화 단계: 정식 보안 패치
- 적용 대상: In-cluster Cloud Service Mesh
- 신규 패치 버전: 1.31.3-asm.9, 1.30.7-asm.9, 1.29.10-asm.4
- 관련 보안 권고: GCP-2026-080, ISTIO-SECURITY-2026-009
- 원문: https://docs.cloud.google.com/release-notes/
- 공식 보안 권고: https://docs.cloud.google.com/service-mesh/docs/security-bulletins
Google Cloud가 In-cluster Cloud Service Mesh의 여러 버전에 대한 새로운 보안 패치를 공개했다.
이번 발표는 새로운 서비스 메시 기능의 출시가 아니다.
기존 Istio 및 Envoy 기반 실행 환경에서 발견된 취약점을 수정하기 위한 업데이트다.
Service Mesh의 역할
Service Mesh는 여러 서버와 서비스 사이의 통신을 관리하는 데 사용할 수 있다.
예를 들어 마이크로서비스 기반 웹 애플리케이션을 생각해 보자.
서비스는 다음처럼 구성될 수 있다.
- 사용자 API
- 주문 API
- 결제 API
- 알림 서비스
이러한 서비스는 서로 통신해야 한다.
실제 서비스 간 통신에는 다음 기능이 필요할 수 있다.
- 인증
- TLS
- 접근 제어
- Traffic Routing
- Retry
- Observability
Service Mesh는 이러한 기능을 공통 네트워크 계층에서 제공할 수 있다.
대표적으로 Envoy Proxy가 각 서비스의 통신을 처리한다.
문제는 이러한 Proxy 자체에 보안 취약점이 존재할 수 있다는 점이다.
실제 애플리케이션 코드가 안전하더라도 중간 통신 계층에 문제가 있다면 영향을 받을 수 있다.
새로 공개된 세 패치 버전
이번 발표에서 제공하는 버전은 다음과 같다.
| Cloud Service Mesh | Envoy 버전 |
|---|---|
| 1.31.3-asm.9 | 1.39.5-dev |
| 1.30.7-asm.9 | 1.38.10-dev |
| 1.29.10-asm.4 | 1.37.10-dev |
Google Cloud는 세 버전 모두 관련 보안 권고의 취약점을 수정한다고 설명한다.
다만 모든 버전의 실제 구성요소가 완전히 동일한 것은 아니다.
예를 들어 Envoy의 버전이 서로 다르다.
따라서 단순히 가장 높은 버전 번호만 선택하는 것이 아니라 현재 사용하는 Cloud Service Mesh 계열에 맞는 패치를 확인해야 한다.
공식적으로 나열된 CVE
1.29.10-asm.4의 공식 릴리스 정보에서는 다음 CVE가 나열됐다.
| CVE | 공식 릴리스의 분류 |
|---|---|
| CVE-2026-89161 | Medium |
| CVE-2026-89157 | Medium |
| CVE-2026-89158 | Medium |
| CVE-2026-89160 | Medium |
| CVE-2026-89156 | Medium |
| CVE-2026-103111 | Medium |
| CVE-2026-109518 | High |
| CVE-2026-86145 | Medium |
Google Cloud는 일부 CVE에 대해 비정상적인 점수 표기를 제공하고 있다.
따라서 해당 표의 숫자만으로 각 취약점의 실제 CVSS 점수와 공격 난이도를 확정해서는 안 된다.
이번 확인 범위에서는 각 CVE의 모든 공격 조건과 실제 악용 여부를 독립적으로 확인하지 못했다.
확인된 내용은 Google Cloud가 관련 취약점에 대한 수정 버전을 공식적으로 제공했다는 사실이다.
Proxy와 Control Plane
공식 릴리스 정보에서는 해당 취약점이 다음 구성요소에 영향을 줄 수 있다고 표시한다.
- Proxy
- Control Plane
- CNI
Service Mesh에서는 이러한 구성요소가 서로 다른 역할을 수행한다.
Proxy는 실제 요청의 전달과 처리를 담당한다.
Control Plane은 네트워크 정책과 설정을 배포한다.
CNI는 Kubernetes 네트워크 구성과 관련된다.
따라서 특정 취약점이 어느 구성요소에 영향을 주는지 확인해야 실제 위험 범위를 판단할 수 있다.
예를 들어 실제 Sidecar Proxy만 업데이트했지만 Control Plane은 이전 버전으로 남아 있다면 모든 관련 구성요소가 수정됐다고 판단할 수 없을 수 있다.
실제 운영상의 제한사항
이번 업데이트는 In-cluster Cloud Service Mesh에 관한 것이다.
모든 Istio 설치 방식이나 모든 관리형 Service Mesh 환경에 동일하게 적용되는 것은 아니다.
또한 실제 업그레이드는 현재 Kubernetes Cluster와 Cloud Service Mesh의 호환성에 영향을 받는다.
따라서 기존 설정과 운영 중인 워크로드를 고려해야 한다.
이번 발표에는 패치 적용 전후의 처리량이나 지연 시간에 관한 성능 Benchmark가 포함되지 않았다.
또한 취약점이 실제 공격에서 악용되고 있다는 근거를 이번 공식 릴리스 정보만으로 확인할 수는 없다.
이번 업데이트는 서비스 간 네트워크 통신을 관리하는 Cloud Service Mesh의 여러 지원 계열에 보안 수정 버전을 제공한 정식 패치다.
Cloudflare AI Security for Apps, Terraform Provider 5.29.0에서 설정 관리 지원
- 발표일: 2026-11-12 — 정확한 게시 시각 미확인
- 발표 주체: Cloudflare
- 안정화 단계: 정식 Terraform Provider 기능
- 적용 대상: Cloudflare AI Security for Apps, Terraform
- 필요 버전: Cloudflare Terraform Provider 5.29.0
- 원문: https://developers.cloudflare.com/changelog/post/2026-11-12-ai-security-for-apps-terraform/
Cloudflare가 AI Security for Apps의 설정을 Terraform으로 관리할 수 있도록 기능을 확장했다.
이번 변경은 새로운 AI 보안 탐지 모델을 출시한 것이 아니다.
기존 보안 기능의 활성화와 탐지 기준, 완화 규칙을 Infrastructure as Code 방식으로 관리할 수 있도록 만든 것이다.
기존 Dashboard 설정과 Terraform
Terraform은 클라우드 인프라와 서비스 설정을 코드로 관리하는 도구다.
예를 들어 웹 애플리케이션의 다음 설정을 Terraform으로 관리할 수 있다.
- 네트워크
- DNS
- Firewall
- Load Balancer
- 보안 규칙
이러한 방식은 실제 운영 환경의 설정 변경을 코드 리뷰와 배포 절차에 연결하는 데 도움이 된다.
하지만 일부 보안 기능을 Dashboard에서만 관리해야 한다면 설정이 여러 위치에 분산될 수 있다.
이번 업데이트는 AI Security for Apps의 주요 설정도 Terraform에 포함할 수 있도록 한다.
새롭게 제공하는 Resource
Cloudflare Terraform Provider 5.29.0에서는 다음 두 Resource가 추가됐다.
첫 번째는 다음과 같다.
cloudflare_ai_security
이 Resource는 특정 Zone에서 AI Security for Apps를 활성화하거나 비활성화한다.
두 번째는 다음과 같다.
cloudflare_ai_security_custom_topics
이 Resource는 실제 AI 요청을 분류할 때 사용할 Custom Topic을 정의한다.
예를 들어 기업이 다음 주제에 관한 입력을 별도로 탐지하고 싶다고 가정해 보자.
seeking info on competitors
Custom Topic에 해당 내용을 정의할 수 있다.
이후 AI Security for Apps의 분류 결과를 실제 보안 규칙에 활용할 수 있다.
WAF 규칙과의 연결
Cloudflare는 이러한 설정을 기존 cloudflare_ruleset Resource와 함께 사용할 수 있다고 설명한다.
따라서 다음 세 종류의 설정을 하나의 Terraform 구성으로 관리할 수 있다.
- AI Security for Apps 활성화
- Custom Topic 정의
- 실제 완화 및 접근 제어 규칙
이러한 구조는 개발과 운영 환경의 설정을 일관되게 유지하는 데 도움이 될 수 있다.
하지만 Custom Topic을 정의했다는 사실만으로 실제 위험한 입력을 모두 탐지한다고 보장할 수는 없다.
실제 탐지 모델은 입력의 의미를 잘못 분류할 수 있다.
따라서 탐지 결과를 이용해 어떤 요청을 차단할지는 실제 서비스의 요구사항에 따라 결정해야 한다.
설정의 책임
이번 기능에서는 Terraform의 State와 실제 Cloudflare 설정의 관계도 중요하다.
예를 들어 관리자가 Dashboard에서 직접 보안 설정을 변경했다고 가정해 보자.
하지만 Terraform 코드에는 해당 변경이 반영되지 않았다.
이후 Terraform을 다시 적용하면 코드에 정의된 설정으로 되돌아갈 수 있다.
따라서 Terraform을 사용하려는 프로젝트에서는 실제 설정의 관리 주체를 명확하게 해야 한다.
적용 범위와 한계
이번 업데이트는 기존 보안 기능의 설정 관리 범위를 확장한 것이다.
AI Security for Apps의 탐지 정확도나 보안 위험 감소율이 개선됐다는 Benchmark는 포함되지 않았다.
또한 Terraform을 사용한다는 사실만으로 보안 정책 자체가 올바르게 설계되는 것은 아니다.
이번 발표는 AI 관련 입력과 출력의 보안 정책을 다른 인프라 설정과 함께 코드로 관리할 수 있도록 Terraform Provider를 확장한 변경이다.
이번 조사 범위에서 GitHub, GitLab, Node.js, Deno, Bun, Vite, webpack, Rollup, esbuild, Babel, Biome, ESLint, Prettier, npm, pnpm, Yarn 및 주요 테스트·배포 도구의 이외에 포함할 만한 주요 신규 발표는 확인하지 못했습니다.
특히 다음 항목은 이전 브리핑에서 이미 다뤘으므로 반복하지 않았다.
- Node.js 26.13.1·24.22.3·22.23.5 보안 릴리스
- Vite 8.3.8
- GitHub 개인 계정의 Issue Type 및 Custom Field 지원
- GitHub Secret Scanning의 Workflow Log 검사
- GitHub Dependabot 비활성 Repository 정책
개발 브랜치의 Commit이나 아직 정식으로 공개되지 않은 수정 사항을 새로운 안정 버전으로 취급하지 않았다.
📚 추천 글
初めてのOSS貢献。vinextに出したPRが、数日後の深夜にマージされていた
- 원제: 初めてのOSS貢献。vinextに出したPRが、数日後の深夜にマージされていた
- 저자: A.Haruki
- 매체: Zenn
- 게시일: 2026-11-11
- 기술: Vite, CSS, Browser Target, Next.js, vinext, Open Source Contribution
- 원문: https://zenn.dev/haruzou_tech/articles/vinext-css-target-oss
- 관련 PR: https://github.com/cloudflare/vinext/pull/4415
Next.js 애플리케이션을 Vite 기반의 vinext로 실행하는 과정에서 CSS의 컴파일 대상 브라우저를 명시했는데도 실제로는 이전 브라우저 버전의 설정이 계속 적용되는 문제를 수정한 오픈소스 기여 기록이다.
이번 글의 특징은 처음 작성한 수정 코드가 실제 최종 구현과 크게 달라졌다는 점이다.
저자는 10월 30일에 PR을 제출했다.
이후 Maintainer가 여러 번 코드를 변경했다.
또한 AI Code Review를 반복적으로 실행했다.
저자는 실제로 병합된 결과와 자신이 처음 작성한 코드를 비교하면서 어떤 상황을 놓쳤는지 분석했다.
vinext의 역할
vinext는 Next.js API를 Vite에서 다시 구현하는 오픈소스 프로젝트다.
주요 목적 가운데 하나는 Next.js 애플리케이션을 Cloudflare Workers에서 실행하도록 지원하는 것이다.
다만 vinext가 모든 Next.js 기능을 완전히 동일하게 제공하는 것은 아니다.
실제 지원 범위에는 제한이 존재한다.
이번 문제는 해당 실행 환경에서 CSS를 어떤 브라우저 버전에 맞춰 변환할지 결정하는 설정과 관련된다.
build.cssTarget
Vite에서는 CSS의 컴파일 대상을 지정할 수 있다.
대표적인 설정은 다음과 같다.
build.cssTarget
예를 들어 다음 브라우저를 대상으로 설정할 수 있다.
- Chrome 111
- Edge 111
- Firefox 114
- Safari 15
이러한 정보는 CSS의 문법을 변환하거나 압축하는 과정에 영향을 줄 수 있다.
예를 들어 다음 Media Query가 있다고 가정해 보자.
@media (max-width: 768px)
새로운 CSS 문법에서는 다음처럼 표현할 수도 있다.
@media (width <= 768px)
두 표현은 지원하는 브라우저에서 같은 조건을 나타낸다.
하지만 실제 Target Browser의 지원 범위에 따라 새로운 문법으로 변환할 수 있는지 달라질 수 있다.
또한 CSS 문자열 자체를 검사하는 기존 코드가 존재한다면 두 표현의 차이가 영향을 줄 수 있다.
기존 vinext의 설정
vinext는 기본값으로 다음 배열을 사용하고 있었다.
["chrome111", "edge111", "firefox114", "safari15"]
이 값에는 이유가 있었다.
일부 CSS 문법이 최신 표현으로 변경돼 기존 Next.js 프로젝트의 동작이나 테스트에 영향을 주는 문제를 줄이려는 목적이었다.
하지만 실제 애플리케이션이 CSS Target을 직접 지정한 경우에는 문제가 발생했다.
예를 들어 개발자가 다음 값을 지정했다고 가정해 보자.
["chrome111", "edge111", "firefox121", "safari15.6"]
개발자는 Firefox 121과 Safari 15.6을 대상으로 CSS를 생성하려고 한다.
하지만 Vite는 여러 설정의 배열을 병합하는 과정에서 값을 이어 붙일 수 있다.
그 결과 다음과 같은 배열이 생성될 수 있다.
["chrome111", "edge111", "firefox121", "safari15.6", "chrome111", "edge111", "firefox114", "safari15"]
즉 실제 애플리케이션이 더 새로운 브라우저 버전을 지정했더라도 기존 Firefox 114와 Safari 15가 함께 남는다.
이 경우 CSS를 변환할 때 여전히 이전 브라우저를 고려해야 할 수 있다.
따라서 개발자가 기대한 새로운 Target이 실제 빌드 결과에 완전히 적용되지 않는다.
첫 번째 수정
저자는 이 문제를 해결하기 위해 기본값을 조건부로 제공하도록 변경했다.
핵심 조건은 다음과 같았다.
config.build?.cssTarget === undefined
즉 실제 애플리케이션이 CSS Target을 지정하지 않은 경우에만 기본값을 제공한다.
반면 개발자가 값을 명시했다면 해당 값을 유지한다.
여기서 중요한 부분은 undefined와 false의 구분이다.
cssTarget에는 false도 유효한 설정으로 사용할 수 있다.
따라서 단순히 다음과 같은 Truthy 검사를 사용하면 문제가 발생할 수 있다.
if (!cssTarget)
이 경우 false를 미설정 값으로 잘못 판단한다.
저자는 이러한 문제를 피하기 위해 실제 값이 undefined인지 비교했다.
처음에는 이 수정으로 문제가 해결된 것처럼 보였다.
하지만 실제 Vite의 설정 시스템은 더 복잡했다.
Vite의 여러 설정 계층
Vite에서는 설정이 한 곳에서만 정의되지 않을 수 있다.
예를 들어 다음 위치에서 설정이 제공될 수 있다.
- 애플리케이션의 기본 설정
- Environment별 설정
- Vite Plugin
- 설정을 변경하는 Hook
- 설정이 실제로 해석된 이후의 단계
따라서 최상위 Configuration Object만 확인하면 충분하지 않을 수 있다.
예를 들어 Production 환경에서는 다른 CSS Target을 지정했다고 가정해 보자.
또는 나중에 실행되는 Plugin이 cssTarget을 변경할 수도 있다.
이 경우 처음 설정을 읽는 시점의 값만 보고 기본값을 적용하면 실제 최종 설정과 충돌할 가능성이 있다.
AI Review가 발견한 문제
저자의 PR에서는 Maintainer가 AI Review를 반복적으로 요청했다.
AI Review는 처음 수정에 다음 문제가 존재할 수 있다고 지적했다.
- Environment별
cssTarget누락 - Plugin 실행 순서 문제
false값의 처리- 나중에 변경되는 설정의 우선순위
예를 들어 어떤 환경에서는 cssTarget을 false로 설정했다고 가정해 보자.
하지만 기존 기본값의 배열이 해당 값에 병합된다면 실제 CSS 빌드가 실패할 수 있다.
따라서 최상위 설정만 확인하는 방식은 충분하지 않았다.
Maintainer는 관련 문제를 해결하기 위해 구현을 여러 차례 변경했다.
최종 구현: 전용 Plugin
최종적으로 병합된 코드에서는 CSS Target 설정을 별도의 Plugin으로 분리했다.
이름은 다음과 같다.
css-target
이 Plugin은 설정을 해석하는 과정에서 실제 사용자의 명시적인 설정을 확인한다.
또한 Environment별 설정을 고려한다.
이후 최종 설정을 해석한 뒤 값이 존재하지 않는 곳에만 기본값을 적용한다.
이렇게 하면 실제 사용자가 명시한 값을 기본값으로 덮어쓰는 문제를 줄일 수 있다.
중요한 변화는 기본값을 언제 적용하는가 하는 문제다.
초기 구현에서는 Configuration을 구성하는 비교적 이른 단계에서 값을 넣었다.
반면 최종 구현에서는 설정의 실제 병합과 해석 과정까지 고려한 뒤 적용한다.
즉 기본값의 값 자체보다 해당 기본값을 적용하는 시점과 우선순위가 중요했던 것이다.
테스트의 확장
처음 저자가 작성한 테스트는 단순했다.
사용자가 CSS Target을 직접 지정했을 때 기존 값이 유지되는지 확인했다.
하지만 최종 구현에서는 다음 상황을 추가로 검증했다.
| 테스트 | 목적 |
|---|---|
| 명시적인 설정 없음 | 기본 Target 적용 |
| App Router의 명시적 설정 | 사용자 설정 보존 |
| Pages Router의 명시적 설정 | 사용자 설정 보존 |
| Environment별 설정 | 환경별 값 보존 |
| 나중에 실행되는 Plugin | 후속 설정과의 충돌 방지 |
cssTarget: false | 유효한 비활성화 값 유지 |
| 실제 생성 CSS 검사 | 최종 결과가 Target 설정을 반영하는지 확인 |
마지막 테스트가 특히 중요하다.
설정 Object에 기대한 값이 존재한다는 사실만으로 실제 CSS가 해당 설정을 이용해 컴파일됐다고 보장할 수는 없다.
따라서 실제 생성한 CSS를 확인해야 한다.
이러한 방식은 테스트가 내부 구현의 상태뿐 아니라 실제 사용자에게 전달되는 결과까지 검증하도록 한다.
AI Review의 한계
저자는 모든 AI Review의 지적이 올바르지는 않았다고 설명한다.
특정 Review에서는 Environment별 설정이 실제 빌드에 반영되지 않는 문제를 지적했다.
하지만 Maintainer는 해당 현상이 vinext의 구현 문제가 아니라 Vite의 Shared Configuration Build 동작과 관련된 것이라고 설명했다.
따라서 해당 지적을 그대로 수용하지 않았다.
이는 AI Review가 유용한 문제를 발견할 수 있지만 실제 실행 환경의 모든 조건을 완벽하게 이해한다고 보장할 수는 없다는 사례다.
실제 Maintainer가 원인을 검토하고 수정 여부를 결정해야 한다.
다른 PR의 운영 방식
저자는 자신의 PR만 특별하게 처리됐는지도 조사했다.
비슷한 시기에 외부 기여자가 제출해 병합된 PR 여덟 개를 확인했다.
해당 여덟 개 모두에서 같은 Maintainer가 추가 Commit을 작성했다.
또한 /bigbonk review를 이용한 AI Review를 실행했다.
하지만 저자는 이 결과를 프로젝트 전체의 일반적인 운영 방식이라고 확정하지 않았다.
실제로 조사한 PR은 여덟 개뿐이기 때문이다.
또한 AI Review가 실제 코드의 품질을 얼마나 개선했는지에 관한 정량적인 Benchmark는 존재하지 않는다.
이번 글의 적용 범위
이번 사례는 vinext와 Vite의 CSS 설정을 대상으로 한다.
하지만 문제의 구조는 다른 빌드 도구에서도 발생할 수 있다.
예를 들어 여러 Plugin이 같은 설정을 변경하는 경우다.
실제로는 다음 세 가지를 확인해야 할 수 있다.
- 기본값의 출처
- 사용자가 명시한 설정의 우선순위
- 실제 최종 빌드 결과
이번 글은 오픈소스의 설정 오류를 수정할 때 단순히 Configuration Object 하나를 변경하는 것보다 실제 설정 병합의 순서와 최종 출력 결과를 검증하는 일이 중요하다는 점을 구체적인 PR과 테스트 과정으로 설명한다.
実績ページを作ろうとしたら、作ったものが全部社外秘だった
- 원제: 実績ページを作ろうとしたら、作ったものが全部社外秘だった
- 저자: shinsei99
- 매체: Zenn
- 게시일: 2026-11-11
- 기술: Next.js, TypeScript, Zod, JSON, MDX, 콘텐츠 파이프라인, CI
- 원문: https://zenn.dev/shinsei99/articles/ai-tools-base
실제 사내 업무용 애플리케이션을 개발한 경험을 포트폴리오에 정리하려다가 프로젝트의 화면과 고객 정보를 공개할 수 없다는 문제를 발견하고 콘텐츠 데이터 구조 자체를 다시 설계한 사례다.
이번 글은 디자인이나 포트폴리오 문구 작성 방법보다 실제 콘텐츠의 공개 경계를 코드로 강제하는 방법에 초점을 맞춘다.
특히 중요한 특징은 다음 세 가지다.
첫 번째는 공개할 수 없는 데이터를 애초에 콘텐츠 스키마에 포함하지 않는 것이다.
두 번째는 공개 여부를 반드시 지정하도록 하는 것이다.
세 번째는 콘텐츠에 오류가 발생하면 조용히 무시하지 않고 빌드를 실패시키는 것이다.
기존 스키마의 문제
저자는 처음에 일반적인 포트폴리오 데이터 구조를 만들었다.
대표적인 필드는 다음과 같았다.
- name
- screenshots
- demoUrl
- description
이러한 구조는 공개할 수 있는 개인 프로젝트에 적합할 수 있다.
예를 들어 개발자가 직접 만든 Todo 애플리케이션을 소개한다고 가정해 보자.
실제 Screenshot과 Demo URL을 제공할 수 있다.
하지만 사내 업무용 애플리케이션에서는 문제가 발생한다.
저자가 개발한 업무용 앱에는 다음 정보가 포함될 수 있었다.
- 실제 부동산 정보
- 고객 정보
- 개인 이름
- 내부 업무 절차
- 회사가 외부에 공개하지 않은 기능
따라서 실제 Screenshot이나 Demo URL을 공개할 수 없었다.
저자는 이러한 문제를 해결하기 위해 실제 프로젝트를 공개할 수 있는 방법을 다시 정의했다.
결과물이 아니라 개발 과정을 공개
저자는 포트폴리오에서 실제 업무용 앱의 화면을 보여주는 대신 개발 과정과 설계 선택을 설명하기로 했다.
대표적인 내용은 다음과 같다.
- 어떤 문제를 해결하려고 했는가?
- AI에게 어떤 지시를 전달했는가?
- 어떤 기능을 구현했는가?
- 어떤 순서로 설계를 결정했는가?
- 실제로 어떤 문제가 발생했는가?
- 문제의 원인을 어떻게 분석하고 수정했는가?
이러한 정보를 설명하기 위해 실제 고객 데이터와 내부 화면을 반드시 공개할 필요는 없다.
따라서 기존 스키마를 변경했다.
새로운 콘텐츠 스키마
저자는 Zod를 이용해 다음 필드를 정의했다.
slugnamevisibilitypromptsfeaturesprocessimprovements
실제 핵심은 네 종류의 개발 기록이다.
첫 번째는 Prompt와 결과다.
두 번째는 구현한 기능이다.
세 번째는 실제 설계 과정이다.
네 번째는 문제와 개선 과정이다.
이렇게 하면 포트폴리오의 중심이 실제 고객 데이터에서 개발자의 설계와 문제 해결 과정으로 이동한다.
하지만 개발 과정을 설명한다는 사실만으로 모든 정보가 공개 가능한 것은 아니다.
예를 들어 실제 내부 시스템의 구조나 인증 정보를 그대로 설명한다면 여전히 비공개 정보가 노출될 수 있다.
따라서 콘텐츠 자체의 검토는 필요하다.
visibility 필수화
저자는 각 프로젝트에 공개 여부를 지정하도록 했다.
대표적인 값은 다음과 같다.
publicinternal
Zod에서는 다음과 같이 표현할 수 있다.
z.enum(["public", "internal"])
이 필드는 선택 사항이 아니다.
따라서 새로운 프로젝트 기록을 생성하면서 visibility를 지정하지 않으면 Schema Validation에 실패한다.
이러한 방식의 핵심은 기본적으로 공개하는 구조가 아니라 명시적으로 공개를 허용해야 하는 구조라는 점이다.
예를 들어 AI가 새로운 프로젝트 기록을 생성했다고 가정해 보자.
하지만 공개 여부 필드를 누락했다.
이 경우 해당 기록이 자동으로 공개 페이지에 포함되지 않도록 해야 한다.
저자는 Schema Validation을 실패시켜 이를 강제했다.
화면마다 필터를 작성하지 않음
다음 문제는 콘텐츠를 읽는 위치였다.
예를 들어 다음 두 페이지가 있다고 가정해 보자.
- 프로젝트 목록
- 프로젝트 상세
두 페이지가 각각 JSON 파일을 직접 읽는다면 각 페이지에서 공개 여부를 검사해야 한다.
하지만 개발자가 상세 페이지에서 visibility 검사를 누락할 수 있다.
이 경우 목록에서는 보이지 않는 내부 프로젝트가 실제 상세 URL로는 접근 가능할 수 있다.
저자는 이러한 문제를 방지하기 위해 콘텐츠를 읽는 함수를 하나로 제한했다.
대표적인 함수는 다음과 같다.
getWorks()
이 함수는 실제 콘텐츠를 읽는다.
그리고 다음 조건에 해당하는 항목만 반환한다.
visibility === "public"
실제 페이지와 컴포넌트는 직접 fs를 호출하지 않도록 구성했다.
따라서 공개 여부를 확인하는 로직이 여러 페이지에 흩어지지 않는다.
이러한 방식은 실수로 필터를 빠뜨릴 가능성을 줄인다.
읽기 함수의 단일 진입점
이번 구조에서는 공개 가능한 프로젝트 목록을 얻으려면 반드시 getWorks()를 호출해야 한다.
따라서 내부 프로젝트를 표시하려면 실제 공통 읽기 함수를 변경해야 한다.
이러한 설계는 개발자가 주의해서 작성하도록 요구하는 것과 다르다.
실수하기 어려운 코드 경계를 만드는 것이다.
추가적인 장점도 존재한다.
예를 들어 이후 콘텐츠 저장소를 JSON 파일에서 CMS로 변경한다고 가정해 보자.
페이지마다 파일 읽기 로직을 작성했다면 여러 파일을 수정해야 할 수 있다.
반면 공통 읽기 함수가 존재한다면 해당 계층을 교체할 수 있다.
하지만 저자는 이러한 확장성보다 비공개 콘텐츠의 실수 노출을 방지하는 것을 우선했다.
JSON과 MDX를 사용하는 이유
저자는 별도의 관리자 페이지와 데이터베이스를 만들지 않았다.
대신 다음 형식을 사용했다.
- JSON
- MDX
이러한 파일은 Git Repository에서 관리한다.
따라서 실제 변경 내용을 git diff로 확인할 수 있다.
또한 AI 코딩 도구나 자동화 Script가 파일을 직접 변경할 수 있다.
하지만 이러한 방식에는 새로운 위험도 존재한다.
AI가 잘못된 JSON을 생성할 수 있다.
또는 필수 필드를 누락할 수 있다.
실제 콘텐츠 파일이 잘못됐지만 애플리케이션은 정상적으로 빌드될 수 있다면 문제가 늦게 발견될 수 있다.
저자는 이를 방지하기 위해 Validation을 강화했다.
실패를 조용히 무시하지 않음
예를 들어 JSON 파일에 문법 오류가 존재한다고 가정해 보자.
일반적인 읽기 함수에서는 다음과 같이 처리할 수 있다.
- JSON Parsing 시도
- 오류 발생
- Catch에서 오류 처리
- 빈 배열 반환
이렇게 하면 페이지 자체는 정상적으로 표시될 수 있다.
하지만 실제 프로젝트 기록 하나가 사라질 수 있다.
문제는 해당 오류가 사용자에게 명확하게 드러나지 않는다는 것이다.
저자는 이를 가장 위험한 실패 방식 가운데 하나라고 판단했다.
따라서 JSON Parsing에 실패하면 예외를 발생시킨다.
또한 실제 파일 경로를 오류 메시지에 포함한다.
Zod Validation 오류
JSON의 문법이 올바르더라도 실제 데이터의 구조가 잘못됐을 수 있다.
예를 들어 다음 상황이다.
visibility: "external"
하지만 Schema는 다음 두 값만 허용한다.
- public
- internal
이 경우 Zod Validation에 실패한다.
저자는 관련 오류를 조용히 무시하지 않는다.
대신 실제 문제가 된 필드의 경로와 메시지를 출력한다.
예를 들어 다음과 같은 정보를 확인할 수 있다.
- 어떤 JSON 파일에서 오류가 발생했는가?
- 어떤 필드가 잘못됐는가?
- 실제 기대한 값은 무엇인가?
이러한 방식은 실제 콘텐츠를 수정하는 사람이 오류를 빠르게 찾을 수 있도록 한다.
전체 콘텐츠 Validation
저자는 개별 페이지가 렌더링될 때만 Validation을 수행하지 않았다.
별도의 명령도 만들었다.
npm run validate
이 명령은 전체 콘텐츠를 읽고 검사한다.
따라서 실제 사용자가 방문하지 않은 페이지의 파일에도 오류가 존재하는지 확인할 수 있다.
예를 들어 내부 프로젝트 100개와 공개 프로젝트 20개가 있다고 가정해 보자.
공개 페이지에서 실제 사용하는 파일만 읽는다면 내부 프로젝트의 데이터 오류는 발견되지 않을 수 있다.
하지만 전체 Validation을 실행하면 모든 프로젝트 기록의 구조를 검사할 수 있다.
이러한 검사는 특히 AI가 파일을 생성하거나 수정하는 프로젝트에서 유용할 수 있다.
공개 여부와 실제 보안 권한의 차이
이번 구현에는 중요한 제한사항이 존재한다.
visibility를 이용해 목록에서 내부 프로젝트를 제외한다고 해서 모든 비공개 정보가 자동으로 보호되는 것은 아니다.
예를 들어 실제 고객 정보가 포함된 이미지를 public/ 디렉터리에 저장했다고 가정해 보자.
콘텐츠 목록에서 해당 프로젝트를 숨겨도 이미지 파일의 직접 URL이 공개돼 있을 수 있다.
따라서 공개 여부를 관리하는 데이터 필드와 실제 파일 접근 권한은 구분해야 한다.
또한 Markdown 또는 JSON에 실제 민감 정보가 존재한다면 공개 빌드 결과에 포함되는지 확인해야 한다.
이번 구조는 콘텐츠의 실수 노출 가능성을 줄이지만 모든 보안 정책을 대체하지는 않는다.
구현의 한계
이번 글에는 Schema 변경 전후의 개발 생산성이나 오류 발생률에 관한 정량적인 Benchmark가 포함되지 않았다.
또한 실제로 운영하는 대규모 CMS와 비교한 결과도 없다.
저자가 제시한 것은 실제 프로젝트에서 발견한 요구사항과 그에 맞춰 변경한 데이터 구조다.
이번 글은 공개 가능한 콘텐츠와 내부 콘텐츠를 하나의 코드베이스에서 관리할 때 공개 여부를 데이터 모델과 읽기 계층, 빌드 Validation에 걸쳐 일관되게 강제하는 방법을 설명한다.
Web アプリのインフラ運営ゲームを作ってみる話
- 원제: Web アプリのインフラ運営ゲームを作ってみる話
- 저자: Daiius
- 매체: chot Inc. tech blog / Zenn
- 게시일: 2026-11-11
- 기술: Next.js, Vercel, Cloud Run, Cloudflare, vinext, ISR, Cache Components, Database
- 원문: https://zenn.dev/chot/articles/frontend-infra-cost-game
웹 애플리케이션을 개발할 때 프레임워크와 렌더링 방식, 실제 배포 인프라의 선택이 서로 어떤 영향을 주는지 이해하기 위해 인프라 운영 시뮬레이션 게임을 제작한 기록이다.
저자는 단순히 클라우드 서비스의 가격표를 비교하지 않았다.
실제 웹 애플리케이션이 성장하는 상황에서 어떤 인프라를 선택하고 어떤 기능을 개발할지 결정하는 구조를 만들었다.
이번 글의 특징은 프론트엔드의 렌더링 방식과 서버리스 인프라를 서로 독립적으로 선택할 수 없다는 점을 구체적인 사례로 보여준다는 것이다.
12개월 운영 시뮬레이션
게임은 하나의 웹 애플리케이션을 12개월 동안 운영하는 구조다.
사용자는 다음 요소를 선택한다.
- 서비스의 시나리오
- 프레임워크
- 배포 인프라
- 데이터베이스
- 페이지별 렌더링 및 캐시 방식
이후 시간이 흐르면서 다음 요소가 변경된다.
- 사용자 수
- 트래픽
- 인프라 비용
- 서비스 만족도
- 개발에 사용할 수 있는 시간
- 운영상의 문제
따라서 사용자는 실제 서버 비용만 최소화하는 것이 아니라 애플리케이션을 계속 개발하고 운영할 수 있는지 고려해야 한다.
예를 들어 VPS를 사용하면 단순한 서버 비용은 낮을 수 있다.
하지만 실제 보안 업데이트와 장애 복구를 직접 관리해야 할 수 있다.
이러한 작업에 많은 시간을 사용하면 새로운 기능 개발에 투자할 수 있는 시간이 감소한다.
게임은 이러한 관계를 모델에 반영한다.
Next.js와 배포 인프라의 결합
저자가 가장 중요하게 설명하는 부분은 Next.js의 일부 기능이 실제 배포 인프라와 연결된다는 사실이다.
예를 들어 다음 기능이다.
- Cache Components
- On-demand ISR
ISR(Incremental Static Regeneration)은 정적으로 생성한 페이지를 일정한 정책에 따라 갱신할 수 있도록 하는 기능이다.
On-demand ISR은 특정 이벤트가 발생했을 때 관련 페이지의 캐시를 무효화하거나 재검증하는 방식과 연결된다.
예를 들어 상품 가격이 변경됐다고 가정해 보자.
애플리케이션은 이전에 생성한 상품 상세 페이지를 갱신해야 할 수 있다.
문제는 실제 페이지의 Cache가 어디에 존재하는지다.
Vercel에서는 해당 플랫폼의 캐시와 배포 기능이 연결돼 있다.
반면 다른 인프라에서는 필요한 캐시 계층을 직접 구성해야 할 수 있다.
배포 플랫폼별 기능 차이
저자는 2026년 11월 기준으로 다음 환경을 비교했다.
| 플랫폼 | Cache Components | On-demand ISR |
|---|---|---|
| Vercel | 지원 | 지원 |
VPS + next start | 지원 | 추가 구성 필요 |
| Google Cloud Run | 지원 | 추가 구성 필요 |
| AWS Amplify Hosting | 지원 범위 제한 | 지원 범위 제한 |
| Cloudflare + vinext | 부분 지원 | 캐시 구성에 따라 달라짐 |
이 표는 저자의 조사에 기반한 비교다.
모든 환경에서 실제 지원 범위가 영구적으로 같다는 의미는 아니다.
특히 Next.js와 각 배포 플랫폼의 지원 범위는 이후 변경될 수 있다.
따라서 실제 프로젝트에 적용할 때는 사용하려는 Next.js 버전과 해당 플랫폼의 공식 문서를 확인해야 한다.
CDN과 ISR의 관계
저자는 CDN을 사용하는 환경에서 On-demand ISR이 기대한 대로 동작하지 않을 수 있다고 설명한다.
예를 들어 다음 구조를 생각할 수 있다.
- 사용자 브라우저
- CDN
- Next.js 서버
- 데이터베이스
Next.js 서버는 상품 페이지를 생성한다.
CDN은 해당 HTML을 캐시에 저장한다.
이후 상품 가격이 변경된다.
Next.js에서는 관련 캐시를 무효화한다.
하지만 실제 CDN에는 이전 HTML이 남아 있을 수 있다.
이 경우 사용자는 서버의 새로운 결과가 아니라 CDN의 이전 HTML을 받을 수 있다.
따라서 Next.js 서버의 캐시를 무효화했다는 사실과 실제 사용자에게 전달되는 모든 캐시가 무효화됐다는 사실은 다르다.
실제 페이지 생성과 캐시의 위치를 이해해야 한다.
여러 서버 인스턴스의 캐시 동기화
다음 문제는 여러 인스턴스가 존재하는 경우다.
예를 들어 Cloud Run에서 Next.js를 실행한다고 가정해 보자.
실제 요청량이 증가하면 여러 인스턴스가 실행될 수 있다.
각 인스턴스는 별도의 로컬 캐시를 가질 수 있다.
이때 인스턴스 A에서 캐시를 무효화해도 인스턴스 B의 캐시는 이전 상태를 유지할 가능성이 있다.
따라서 여러 인스턴스 사이에서 캐시 상태를 공유하는 구조가 필요할 수 있다.
저자는 다음 기능을 언급한다.
cacheHandler
이러한 기능을 이용해 실제 캐시 저장소와 갱신 정책을 구성할 수 있다.
하지만 cacheHandler를 작성했다는 사실만으로 CDN과 모든 외부 캐시까지 자동으로 동기화되는 것은 아니다.
각 캐시 계층의 범위와 무효화 방식을 확인해야 한다.
Cloudflare KV의 전파 지연
저자는 Cloudflare와 vinext를 사용하는 경우도 검토했다.
캐시 저장소로 다음 기능을 사용할 수 있다.
- R2
- Workers Cache
- KV
하지만 모든 저장소의 일관성 모델이 같은 것은 아니다.
예를 들어 KV는 전 세계 여러 지역에 데이터를 제공하도록 설계된 저장소다.
한 지역에서 특정 값을 변경하더라도 다른 지역에서 변경을 확인하기까지 시간이 필요할 수 있다.
저자는 Cloudflare의 문서를 근거로 일부 변경의 전파에 60초 이상이 걸릴 수 있다고 설명한다.
따라서 KV를 캐시 저장소로 선택한 경우 실제 사용자에게 보이는 데이터의 최신성에 영향을 줄 수 있다.
예를 들어 다음 상황을 생각할 수 있다.
- 서울에서 상품 가격 변경
- KV의 값 갱신
- 다른 지역에서는 이전 값 유지
- 일정 시간 뒤 변경된 값 확인
이러한 동작은 실제 캐시 정책에 따라 허용할 수 있는 서비스도 있고 그렇지 않은 서비스도 있다.
따라서 어떤 데이터를 KV에 저장할지 선택해야 한다.
데이터베이스 선택
게임에서는 데이터베이스도 여러 종류 가운데 선택할 수 있다.
대표적인 후보는 다음과 같다.
- Supabase
- Neon
- DynamoDB
- Aurora
이러한 데이터베이스는 사용 방식과 과금 구조가 다르다.
예를 들어 Serverless Database를 선택했다고 가정해 보자.
사용량이 적고 오랫동안 유휴 상태가 존재한다면 실제 비용을 줄일 수 있을 가능성이 있다.
하지만 실제 애플리케이션이 계속 요청을 처리한다면 유휴 상태가 거의 없을 수 있다.
이 경우 Serverless라는 이유만으로 저렴하다고 판단하기 어렵다.
또한 데이터베이스에 저장하는 데이터의 크기와 실제 Query의 패턴도 영향을 준다.
따라서 단순히 월 사용자 수만으로 실제 데이터베이스의 비용을 정확하게 예측하기는 어렵다.
Self-hosting의 실제 비용
저자는 자체 서버를 관리하는 방식도 게임에 포함했다.
예를 들어 VPS를 사용한다고 가정해 보자.
클라우드 관리형 플랫폼보다 기본 서버 요금이 낮을 수 있다.
하지만 실제 개발자가 다음 작업을 직접 수행해야 할 수 있다.
- 운영체제 업데이트
- 데이터베이스 백업
- 보안 패치
- 모니터링
- 장애 복구
- 서버 확장
이러한 작업에는 실제 시간이 필요하다.
저자는 해당 시간을 게임에서 기능 개발에 사용할 수 없는 자원으로 처리했다.
따라서 단순한 서버 가격이 낮더라도 실제 기능 개발에 사용할 수 있는 시간이 줄어들 수 있다.
이러한 구조는 인프라의 실제 비용을 클라우드 요금뿐 아니라 운영과 유지보수에 필요한 시간까지 포함해 생각하도록 만든 것이다.
게임 모델의 한계
저자는 실제 게임의 계산 결과가 정확한 인프라 비용 예측이 아니라고 명시했다.
대표적인 이유는 다음과 같다.
- 가격표가 항상 최신 상태로 유지되는 것은 아님
- 실제 트래픽과 게임의 사용량 모델이 다를 수 있음
- 서비스별 과금 정책이 복잡함
- 개발팀의 기술 숙련도가 다름
- 실제 장애와 운영 비용을 완벽하게 모델링하기 어려움
따라서 게임의 결과를 실제 클라우드 계약이나 비용 견적에 그대로 사용해서는 안 된다.
또한 특정 플랫폼을 선택했을 때 실제 비용이 반드시 몇 퍼센트 감소한다는 독립적인 Benchmark를 제공하지 않는다.
이번 글은 Next.js의 렌더링과 캐시 구조가 실제 배포 인프라의 특성과 연결되며, 서버 비용과 운영 부담, 개발 생산성 사이에 서로 다른 트레이드오프가 존재한다는 점을 시뮬레이션 게임의 설계 과정으로 설명한 자료다.