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

기준 시점: 2026-10-24 06:00 KST
조사 범위: 2026-10-23 06:00 ~ 2026-10-24 06:00 KST
추천 글 범위: 기준 시점 당시 최근 7일
1️⃣ 프론트엔드
Cloudflare Flagship, 중첩 객체·배열 기반 Feature Flag 타기팅 지원 — 사용자 프로필을 변환하지 않고 점진적 기능 배포
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare Flagship, Workers Binding, Feature Flag Evaluation API, OpenFeature SDK
- SDK 지원: TypeScript·Python·Go SDK 0.6.0 이상
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-23-structured-evaluation-context/
Cloudflare가 Feature Flag의 평가 조건에 중첩 객체와 배열을 직접 전달할 수 있도록 Flagship의 타기팅 기능을 확장했다.
Feature Flag는 애플리케이션의 특정 기능을 코드 배포와 별개로 활성화하거나 비활성화할 수 있도록 하는 기능이다.
예를 들어 새로운 결제 화면을 개발했다고 가정해 보자.
개발자는 새로운 화면의 코드를 운영 환경에 배포하되 모든 사용자에게 즉시 노출하지 않을 수 있다.
처음에는 내부 테스트 사용자에게만 제공하고, 이후 일부 고객에게 공개한 뒤 문제가 없으면 전체 사용자에게 확대할 수 있다.
이러한 점진적 배포는 새로운 기능에서 문제가 발생했을 때 영향을 받는 사용자 범위를 제한하는 데 도움이 된다.
하지만 실제 서비스에서는 단순히 사용자 ID만으로 기능을 제공할 대상을 결정하지 않는다.
사용자의 요금제, 조직, 권한, 실험 참여 여부 등 여러 조건을 함께 고려해야 한다.
이번 업데이트는 복잡한 사용자 정보를 Feature Flag 평가 과정에 직접 전달할 수 있도록 한 변경이다.
기존 평가 문맥의 한계
Feature Flag는 일반적으로 평가 문맥(Evaluation Context)을 사용한다.
평가 문맥은 현재 기능을 요청한 사용자나 조직에 관한 정보다.
예를 들어 다음과 같은 값을 전달할 수 있다.
- 사용자 ID
- 요금제
- 국가
- 조직 ID
- 실험 그룹
기존에 단순한 값만 사용할 수 있다면 복잡한 사용자 객체를 여러 개의 평면적인 속성으로 변환해야 한다.
예를 들어 실제 애플리케이션의 사용자 데이터가 다음과 같다고 가정해 보자.
profile: { plan: "enterprise" }
Feature Flag 시스템이 중첩 객체를 처리하지 못한다면 개발자는 profile_plan처럼 별도의 필드를 생성해야 할 수 있다.
이러한 변환은 사용자 데이터 구조와 Feature Flag 평가 구조 사이에 불필요한 차이를 만든다.
또한 프로필 구조가 변경되면 변환 로직도 수정해야 할 수 있다.
이번 변경으로 Cloudflare Flagship은 다음 데이터를 직접 처리한다.
- 중첩 객체
- 배열
null
따라서 기존 애플리케이션의 사용자 프로필과 태그 목록을 평가 문맥에 그대로 전달할 수 있다.
중첩된 속성으로 타기팅
Cloudflare가 공개한 예제에서는 다음과 같은 평가 문맥을 사용한다.
targetingKey: "user-42"
profile: { plan: "enterprise" }
tags: ["beta", "early-access"]
이 정보로 새로운 결제 화면을 활성화할지 결정할 수 있다.
예를 들어 다음 두 조건을 동시에 만족하는 사용자에게만 기능을 제공하도록 설정할 수 있다.
첫 번째 조건은 사용자의 요금제가 Enterprise인지 확인하는 것이다.
profile.plan equals "enterprise"
두 번째 조건은 사용자의 태그 목록에 Beta 참여 정보가 있는지 확인하는 것이다.
tags has "beta"
두 조건을 모두 만족하면 새로운 결제 화면을 활성화한다.
이러한 구조에서는 프론트엔드에서 사용하는 기존 사용자 정보와 기능 배포 정책을 자연스럽게 연결할 수 있다.
다만 Flagship의 실제 평가를 Workers에서 수행한다면 사용자의 프로필 정보가 서버에서 올바르게 검증돼 있어야 한다.
클라이언트가 전달한 요금제 값을 그대로 신뢰해서 유료 기능의 접근 권한을 결정하는 방식은 안전하지 않다.
Feature Flag는 기능의 노출 여부를 제어하는 도구이며, 서버의 실제 인가(Authorization)를 대체하지 않는다.
배열을 대상으로 하는 has와 not_has
이번 업데이트에는 배열을 처리하기 위한 연산자도 추가됐다.
대표적으로 다음 두 연산자를 사용한다.
| 연산자 | 역할 |
|---|---|
has | 배열에 특정 값이 포함돼 있는지 확인 |
not_has | 배열에 특정 값이 포함돼 있지 않은지 확인 |
예를 들어 사용자의 태그 목록이 다음과 같다고 가정해 보자.
["beta", "early-access"]
이때 tags has "beta"는 참이다.
반면 tags not_has "internal-only"도 참이다.
이러한 조건은 하나의 사용자에게 여러 역할이나 속성이 부여되는 시스템에서 활용할 수 있다.
예를 들어 특정 조직의 관리자가 여러 권한을 가지고 있다면 권한 목록을 배열로 전달하고 필요한 권한의 존재 여부를 확인할 수 있다.
다만 앞서 설명했듯이 이러한 평가 결과만으로 실제 API의 접근 권한을 허용해서는 안 된다.
Feature Flag를 통해 버튼을 숨기더라도 사용자가 HTTP 요청을 직접 보낼 가능성은 존재하기 때문이다.
실제 서버에서는 필요한 권한을 독립적으로 확인해야 한다.
Percentage Rollout의 사용자 식별자
Feature Flag의 중요한 기능 가운데 하나는 Percentage Rollout이다.
Percentage Rollout은 전체 사용자 가운데 지정된 비율에만 기능을 제공하는 방식이다.
예를 들어 전체 사용자의 10%에게 새로운 화면을 제공할 수 있다.
하지만 이 과정에서는 사용자를 어떤 기준으로 분류할지 중요하다.
같은 사용자가 페이지를 새로고침할 때마다 서로 다른 실험 그룹에 배정되면 사용자 경험이 일관되지 않을 수 있다.
Cloudflare는 이번 업데이트에서 중첩된 사용자 식별자를 Percentage Rollout의 기준으로 사용할 수 있도록 했다.
예를 들어 다음 경로를 사용할 수 있다.
profile.id
이렇게 하면 실제 사용자 객체 안의 식별자를 이용해 기능 제공 그룹을 결정할 수 있다.
동일한 식별자를 일관되게 사용하면 사용자가 반복해서 요청하더라도 같은 기능 그룹에 배정되도록 구성할 수 있다.
다만 실제 배정의 안정성은 식별자의 일관성과 Feature Flag 설정에 영향을 받는다.
사용자 식별자가 변경되거나 Rollout 정책 자체가 변경된다면 기존 사용자의 배정 결과도 달라질 수 있다.
Workers Binding과 REST API 지원
구조화된 평가 문맥은 다음 경로에서 사용할 수 있다.
- Cloudflare Workers Binding
- Feature Flag Evaluation REST API
- TypeScript OpenFeature SDK
- Python OpenFeature SDK
- Go OpenFeature SDK
REST API에서는 JSON 본문을 사용하는 POST 요청으로 구조화된 평가 문맥을 전달한다.
SDK는 중첩 객체와 배열이 필요한 경우 적절한 HTTP 요청 방식을 자동으로 사용하도록 변경됐다.
지원되는 SDK 버전은 0.6.0 이상이다.
따라서 기존 SDK를 사용하는 프로젝트라면 해당 버전의 지원 여부를 확인해야 한다.
이번 기능은 새로운 프론트엔드 렌더링 API를 제공하는 것은 아니다.
기존 애플리케이션의 사용자 데이터를 Feature Flag 평가에 더 자연스럽게 연결하고, 복잡한 사용자 조건에 따른 점진적 기능 배포를 지원하도록 확장한 업데이트다.
2️⃣ 웹 플랫폼·브라우저
이번 조사 범위에서 포함할 만한 주요 신규 발표를 확인하지 못했습니다.
W3C, WHATWG, TC39, MDN, Chrome Developers·Chromium, Mozilla·Firefox, WebKit·Safari의 공식 발표와 JavaScript·CSS, 웹 성능 및 접근성 관련 공개 정보를 검토했지만, 이번 24시간에 새롭게 정식 출시되거나 안정화된 주요 브라우저 API는 확인하지 못했다.
현재 개발 중인 CSS 기능과 브라우저의 실험적 기능은 정식 지원과 구분했다.
특히 최근 CSS Values and Units Level 5의 random() 함수가 여러 기술 글에서 다뤄지고 있다. 그러나 새로운 기술 글이 게시됐다는 사실만으로 모든 브라우저에서 해당 기능이 안정적으로 제공된다고 판단할 수는 없다.
random() 함수의 실제 활용과 브라우저 지원 범위, 접근성 관련 제한사항을 다룬 기술 글은 마지막 추천 글 섹션에서 소개한다.
3️⃣ 백엔드·인프라
Cloudflare Origin Range Requests 정식 제공 — 대용량 파일의 부분 요청을 자동 분할·캐싱해 Origin 부하 감소
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare CDN, Cache Rules
- 지원 요금제: 모든 Cloudflare 요금제
- 원문: https://blog.cloudflare.com/origin-range-requests/
Cloudflare가 대용량 파일에 대한 HTTP Range Request를 자동으로 분할하고 캐시하는 Origin Range Requests 기능을 모든 요금제에서 사용할 수 있도록 공개했다.
이번 기능은 대용량 동영상과 다운로드 파일을 제공하는 웹서비스의 캐시 효율성과 Origin 서버 부하를 개선하기 위한 것이다.
일반적으로 브라우저가 파일을 다운로드할 때는 전체 파일을 한 번에 요청할 수 있다.
하지만 모든 상황에서 전체 파일이 필요한 것은 아니다.
예를 들어 사용자가 4GB 크기의 동영상을 재생한다고 가정해 보자.
동영상을 처음부터 끝까지 다운로드하지 않더라도 특정 구간의 데이터를 먼저 가져오면 재생을 시작할 수 있다.
또한 사용자가 재생 위치를 중간으로 이동하면 해당 구간의 데이터만 가져오는 편이 효율적이다.
이러한 작업에는 HTTP Range Request를 사용할 수 있다.
HTTP Range Request의 구조
HTTP Range Request는 리소스의 특정 바이트 범위만 요청할 수 있도록 하는 기능이다.
예를 들어 다음과 같은 Header를 사용할 수 있다.
Range: bytes=0-1048575
이는 파일의 첫 번째 바이트부터 1,048,575번째 바이트까지 요청한다는 의미다.
서버가 해당 요청을 정상적으로 처리하면 HTTP 206 Partial Content 응답을 반환할 수 있다.
응답에는 실제 반환한 바이트 범위를 나타내는 Content-Range가 포함된다.
이러한 방식은 다음과 같은 상황에서 활용된다.
- 동영상의 특정 구간 재생
- 다운로드 중단 이후 이어받기
- 대용량 파일의 일부 데이터 조회
- 브라우저 기반 미디어 편집기
- 특정 위치에 데이터가 저장된 아카이브 파일 처리
하지만 CDN에서 Range Request를 캐싱할 때는 여러 문제가 발생할 수 있다.
기존 Range Request 캐싱의 문제
예를 들어 사용자가 128MiB 크기의 파일에서 마지막 1MiB만 요청했다고 가정해 보자.
CDN은 요청한 데이터가 캐시에 존재하는지 확인한다.
하지만 캐시가 사용하는 저장 단위와 사용자가 요청한 범위가 서로 다를 수 있다.
기존 방식에서는 일부 상황에서 Origin 서버가 요청한 범위보다 훨씬 많은 데이터를 전송해야 했다.
예를 들어 사용자는 1MiB만 필요하지만 Origin에서는 64MiB 단위의 데이터를 가져오는 상황이 발생할 수 있다.
문제는 이러한 요청이 여러 번 발생할 때다.
사용자마다 서로 다른 범위를 요청하면 CDN은 이미 비슷한 데이터를 가져왔더라도 Origin에 중복 요청을 보낼 수 있다.
특히 대용량 파일이 많고 사용자가 임의의 위치를 자주 탐색하는 동영상 서비스에서는 불필요한 데이터 전송량이 증가할 수 있다.
Origin 서버가 R2 같은 Object Storage라면 이러한 요청은 저장소 요청량과 데이터 전송 비용에도 영향을 줄 수 있다.
고정된 Chunk를 이용한 캐싱
새로운 Origin Range Requests는 파일을 일정한 크기의 Chunk로 나누어 관리한다.
Chunk는 전체 파일을 여러 부분으로 나눈 데이터 단위다.
예를 들어 파일을 일정한 크기의 Chunk로 나누어 캐시에 저장한다고 가정해 보자.
사용자가 특정 바이트 범위를 요청하면 Cloudflare는 해당 범위가 어떤 Chunk에 포함되는지 계산한다.
이후 이미 캐시에 저장된 Chunk는 재사용한다.
없는 Chunk만 Origin에서 가져온다.
이러한 방식은 사용자 요청의 시작 위치와 끝 위치가 캐시의 Chunk 경계에 정확하게 맞지 않더라도 사용할 수 있다.
예를 들어 사용자가 두 Chunk에 걸친 데이터를 요청했다면 각각 필요한 Chunk를 확인한 뒤 실제 요청한 범위에 맞춰 응답을 조합할 수 있다.
이는 개발자가 Origin 서버에서 매번 파일의 범위를 직접 정렬하거나 분할하도록 구성하는 부담을 줄인다.
이미 진행 중인 다운로드도 재사용
이번 구현에서 중요한 부분은 아직 다운로드가 끝나지 않은 Chunk도 다른 요청에서 활용할 수 있다는 점이다.
예를 들어 사용자 A가 파일의 특정 구간을 요청했다고 가정해 보자.
Cloudflare는 해당 구간에 필요한 Chunk를 Origin에서 다운로드하기 시작한다.
이때 사용자 B가 같은 Chunk의 일부를 요청한다.
단순한 구현에서는 사용자 B를 위한 별도의 Origin 요청이 생성될 수 있다.
하지만 새로운 구조에서는 이미 진행 중인 다운로드 작업을 활용할 수 있다.
따라서 같은 데이터를 Origin에서 중복으로 가져오는 문제를 줄일 수 있다.
최종 응답은 다음 데이터로 구성될 수 있다.
- 캐시에 이미 저장된 Chunk
- 현재 다운로드 중인 Chunk
- 새롭게 Origin에서 가져온 Chunk
Cloudflare는 이러한 조합을 Pingora 기반의 요청 처리 구조와 연결했다.
ETag와 Content-Range 검증
Range Request를 캐싱할 때는 성능뿐 아니라 데이터의 정확성이 중요하다.
예를 들어 Origin 서버에서 파일이 변경됐다고 가정해 보자.
이전 버전의 첫 번째 Chunk와 새로운 버전의 두 번째 Chunk를 조합하면 원래 존재하지 않았던 파일이 만들어질 수 있다.
이는 단순한 캐시 성능 문제가 아니라 데이터 무결성 문제다.
Cloudflare는 이를 방지하기 위해 다음 정보를 검증한다.
- ETag
- Content-Range
- 전체 리소스 크기
- 조건부 Range Request
- 압축 및 응답 형식
ETag는 HTTP 리소스의 특정 버전을 식별하는 데 사용할 수 있는 값이다.
예를 들어 파일이 변경되면 ETag가 달라질 수 있다.
If-Range는 특정 조건이 만족되는 경우에만 부분 응답을 요청하는 데 사용된다.
Cloudflare는 이러한 정보를 이용해 서로 다른 버전의 데이터를 잘못 조합하지 않도록 처리한다.
또한 Origin의 응답이 요청한 Range와 실제로 일치하는지 확인한다.
따라서 이번 기능은 단순히 파일을 일정한 크기로 나누는 것보다 복잡하다.
캐시에 존재하는 여러 부분 데이터를 하나의 올바른 HTTP 응답으로 조합하기 위한 검증 과정이 포함돼 있다.
HTTP 206과 416 처리
정상적인 Range Request는 HTTP 206으로 응답할 수 있다.
하지만 클라이언트가 파일의 범위를 벗어나는 위치를 요청할 수도 있다.
예를 들어 실제 파일 크기가 100바이트인데 1,000번째 바이트부터 데이터를 요청하는 상황이다.
이 경우 Origin 또는 CDN은 HTTP 416 Range Not Satisfiable을 반환할 수 있다.
Cloudflare는 새로운 구현에서 정상적인 부분 요청뿐 아니라 유효하지 않은 Range Request와 전체 파일 요청에 대한 동작도 처리하도록 했다.
이러한 HTTP 의미를 유지하지 않으면 일부 다운로드 클라이언트나 미디어 플레이어에서 문제가 발생할 수 있다.
따라서 캐시를 최적화하더라도 원래 HTTP 프로토콜의 응답 의미는 유지해야 한다.
실제 운영 환경의 성능 측정
Cloudflare는 R2를 사용하는 내부 운영 환경에서 새 기능의 영향을 측정했다.
공개한 결과는 다음과 같다.
| 측정 항목 | 발표된 변화 |
|---|---|
| P99 Time to First Byte | Cache Hit와 Miss 모두 약 50% 감소 |
| Tiered Cache 트래픽 | 전주 대비 약 6% 감소 |
TTFB(Time to First Byte)는 요청을 보낸 뒤 응답의 첫 번째 바이트를 받기까지 걸린 시간을 나타낸다.
P99는 측정된 요청 가운데 99번째 백분위수에 해당하는 값이다.
즉 가장 느린 요청 일부의 성능을 확인하는 데 사용할 수 있다.
Cloudflare는 이번 구현을 적용한 이후 Cache Hit와 Cache Miss 모두에서 P99 TTFB가 감소했다고 설명한다.
하지만 이 수치는 Cloudflare의 자체 운영 환경에서 측정한 결과다.
독립적인 제3자가 동일한 조건에서 재현한 Benchmark는 아니다.
또한 모든 파일과 웹사이트에서 TTFB가 절반으로 감소한다는 의미는 아니다.
실제 효과는 파일 크기, 사용자 요청의 Range 분포, 기존 캐시 적중률과 Origin 서버의 응답 방식 등에 따라 달라질 수 있다.
적용 방법과 제한사항
기능은 Cloudflare Dashboard에서 활성화할 수 있다.
설정 위치는 다음과 같다.
Caching > Cache Rules
새로운 Cache Rule에서 Origin Range Requests를 활성화할 수 있다.
API에서는 origin_range_requests 설정을 사용할 수 있다.
대표적인 활성화 값은 다음과 같다.
{"mode":"on"}
하지만 기능을 활성화했다고 해서 모든 파일이 자동으로 캐싱되는 것은 아니다.
기존 Cloudflare 캐시 정책에 따라 캐싱 가능한 응답이어야 한다.
또한 Origin 서버가 Range Request를 적절하게 처리해야 한다.
예를 들어 Origin이 HTTP Range Request를 지원하지 않거나 항상 전체 파일을 반환한다면 기대한 방식의 최적화가 이루어지지 않을 수 있다.
따라서 이번 기능은 캐싱할 수 없는 데이터를 강제로 캐싱하는 기능이 아니라, 캐싱 가능한 대용량 리소스의 부분 요청을 더 효율적으로 처리하는 기능이다.
Cloudflare D1, 삭제된 데이터베이스 복구 요청을 7일 이내로 제한 — 복구 API의 허용 기간 명확화
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 적용 대상: Cloudflare D1 Undelete API
- 변경 유형: 복구 요청의 허용 기간 제한
- 원문: https://developers.cloudflare.com/changelog/post/2026-10-23-undelete-seven-day-window/
Cloudflare가 삭제된 D1 데이터베이스를 복구하는 Undelete API의 요청 허용 기간을 최근 7일로 제한했다.
D1은 Cloudflare가 제공하는 SQLite 기반 서버리스 데이터베이스다.
Cloudflare Workers와 연결해 웹 애플리케이션의 데이터를 저장할 수 있다.
예를 들어 다음 데이터를 D1에 저장할 수 있다.
- 사용자별 설정
- 게시글
- 주문 관련 정보
- 서비스의 일반적인 관계형 데이터
일반적인 데이터베이스 운영에서는 잘못된 데이터 삭제에 대비하는 것이 중요하다.
예를 들어 개발자가 테스트용 데이터베이스를 삭제하려다가 운영 데이터베이스를 삭제하는 실수를 할 수 있다.
이러한 상황을 대비해 일부 관리형 데이터베이스 서비스는 삭제된 리소스를 일정 기간 복구할 수 있는 기능을 제공한다.
Cloudflare D1도 Undelete API를 제공한다.
변경된 복구 기간
기존에는 삭제된 데이터베이스의 복구 요청을 처리하는 기간이 이번과 같은 방식으로 제한되지 않았다.
이번 업데이트 이후에는 삭제된 지 7일이 지나지 않은 데이터베이스에 대해서만 Undelete 요청을 허용한다.
예를 들어 10월 20일에 삭제한 데이터베이스라면 삭제 시점부터 7일 이내에 복구를 요청해야 한다.
반면 삭제된 지 7일이 넘었다면 Undelete API는 요청을 거부하고 오류를 반환한다.
중요한 점은 7일이 단순한 권장 기간이 아니라는 사실이다.
7일을 초과한 복구 요청은 API에서 실패하도록 변경됐다.
Cloudflare는 데이터베이스를 실수로 삭제했다면 가능한 한 빠르게 복구 요청을 수행하도록 안내한다.
Undelete와 Backup은 다르다
Undelete는 삭제된 데이터베이스를 되돌리는 기능이다.
하지만 데이터베이스의 장기적인 백업 정책을 대체하지 않는다.
예를 들어 다음 두 상황은 다르다.
첫 번째는 데이터베이스 자체를 삭제한 상황이다.
두 번째는 데이터베이스는 존재하지만 일부 데이터를 잘못 변경하거나 삭제한 상황이다.
Undelete API는 삭제된 데이터베이스 리소스의 복구를 다루는 기능이다.
반면 데이터베이스 내부의 특정 시점 데이터를 복원하려면 별도의 백업 및 복구 수단이 필요할 수 있다.
또한 7일이라는 기간이 모든 종류의 D1 데이터 복구에서 동일한 보존 기간을 의미하지는 않는다.
따라서 데이터베이스를 운영할 때는 리소스 삭제 복구와 실제 데이터 복구를 구분해야 한다.
이번 발표는 새로운 SQL 기능이나 D1의 처리 성능 개선이 아니다.
데이터베이스 삭제 이후의 복구 가능 기간을 명확하게 제한한 운영 정책 변경이다.
4️⃣ 개발 도구 및 보안
GitHub Apps, Enterprise 단위 설치 정식 출시 — Classic PAT 대신 세분화된 권한으로 기업 자동화 구성
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 안정화 단계: Generally Available
- 적용 대상: GitHub Enterprise
- GitHub Enterprise Server: 3.24부터 지원 예정
- 원문: https://github.blog/changelog/2026-10-23-enterprise-installations-for-github-apps-generally-available/
GitHub가 GitHub App을 Enterprise 단위로 설치하는 기능을 정식 출시했다.
이번 업데이트는 기업에서 사용하는 GitHub 자동화 시스템의 인증과 권한 관리 방식을 개선하기 위한 것이다.
기업은 GitHub에서 여러 종류의 자동화를 운영할 수 있다.
예를 들어 다음과 같은 작업이다.
- Repository 생성 및 관리
- Branch Protection 변경
- 접근 권한 관리
- 보안 정책 검사
- 조직 구성원 관리
- 감사 및 규정 준수 작업
이러한 작업을 자동화하기 위해 기존에는 Personal Access Token(PAT)을 사용하는 경우가 있었다.
특히 Classic PAT는 사용자 계정에 연결된 권한을 바탕으로 여러 API를 호출할 수 있다.
문제는 이러한 Token이 실제 자동화에 필요한 범위보다 넓은 권한을 가질 수 있다는 점이다.
예를 들어 Repository 설정만 변경하면 되는 프로그램이 다른 관리 기능에도 접근할 수 있는 상황이다.
이때 Token이 노출되면 의도하지 않은 리소스까지 영향을 받을 가능성이 있다.
GitHub App은 이러한 문제를 해결하기 위한 인증 방식 가운데 하나다.
GitHub App과 PAT의 차이
PAT는 일반적으로 사용자 계정과 연결된 인증 정보다.
반면 GitHub App은 애플리케이션 자체의 신원과 설치 범위를 기반으로 동작한다.
GitHub App은 설치할 때 필요한 권한을 선언할 수 있다.
또한 특정 조직이나 Repository에만 접근하도록 구성할 수 있다.
예를 들어 Repository 목록만 조회하는 도구라면 읽기 권한만 부여할 수 있다.
설정을 변경해야 하는 도구라면 필요한 쓰기 권한을 추가할 수 있다.
이번 업데이트에서는 GitHub App이 Enterprise 수준의 관리 기능까지 더 넓게 사용할 수 있도록 권한과 API 지원이 확대됐다.
따라서 기존에 Classic PAT가 필요했던 일부 기업 자동화를 GitHub App으로 이전할 수 있다.
Enterprise 정책과 Ruleset 관리
새로운 권한 가운데 하나는 Enterprise의 조직 및 Repository 정책을 관리하는 기능이다.
관리할 수 있는 설정에는 다음 항목이 포함된다.
- Private Repository Forking
- 기본 Repository 권한
- Repository 공개 범위 변경
- Repository 생성 및 삭제 정책
- Collaborator 초대 정책
- Deploy Key 정책
- 보호된 Branch 설정 변경
- 조직 프로젝트 관련 설정
또한 Enterprise Repository Ruleset을 관리할 수 있다.
Ruleset은 Repository의 코드 변경과 병합 등에 적용하는 정책이다.
예를 들어 특정 Branch에 직접 Push하지 못하도록 제한하거나, PR을 병합하기 전에 테스트 통과를 요구할 수 있다.
이번 기능으로 기업 자동화 도구가 적절한 권한을 가지고 이러한 정책을 관리하도록 구성할 수 있다.
하지만 GitHub App을 설치했다고 해서 모든 정책을 무조건 변경할 수 있는 것은 아니다.
실제 작업은 App에 부여된 권한과 설치 범위에 의해 제한된다.
보안 관리 권한 확대
이번 업데이트에서는 Enterprise 보안 설정과 관련된 권한도 확대됐다.
대표적으로 다음 기능을 관리할 수 있다.
- Two-factor Authentication 요구사항
- 허용하지 않는 인증 방식
- Verified Domain
- IP Allowlist
- Enterprise Access Restriction
- Repository Ruleset
예를 들어 보안팀이 모든 조직에 특정 인증 정책을 적용하려고 한다고 가정해 보자.
기존에는 관리자 계정의 PAT를 사용하는 자동화가 필요했을 수 있다.
이제는 해당 작업에 필요한 Enterprise 권한을 가진 GitHub App을 구성할 수 있다.
이렇게 하면 개인 관리자 계정에 의존하는 인증 구조를 줄일 수 있다.
다만 Enterprise의 인증 정책을 변경하는 작업은 조직 전체에 영향을 줄 수 있다.
따라서 자동화 도구에 필요한 권한을 부여하더라도 변경 승인 및 감사 절차를 별도로 마련할 수 있다.
Bypass Request 조회
새로운 기능에는 Enterprise 수준의 Bypass Request 조회도 포함됐다.
Bypass Request는 Repository에 적용된 보호 정책을 특정 상황에서 우회하기 위한 요청이다.
예를 들어 긴급한 보안 수정으로 인해 일반적인 코드 병합 절차를 예외적으로 변경해야 할 수 있다.
이때 누가 어떤 이유로 정책 우회를 요청했는지 확인할 수 있어야 한다.
GitHub App은 관련 요청을 읽을 수 있는 권한을 부여받을 수 있다.
이러한 정보는 보안 대시보드나 승인 자동화 시스템에서 활용할 수 있다.
다만 요청을 조회하는 권한과 실제로 정책을 변경하거나 우회를 허용하는 권한은 구분된다.
기존 API의 GitHub App 지원 확대
GitHub는 새로운 권한을 추가하는 것뿐 아니라 기존 권한으로 접근할 수 있는 API 범위도 확장했다.
대표적으로 Enterprise 구성원과 Repository 정보, 외부 협업자 정보에 대한 지원이 개선됐다.
Enterprise Billing 관련 일부 설정도 GitHub App에서 변경할 수 있도록 했다.
이러한 변경은 기존 GitHub App을 사용하는 자동화 시스템에도 영향을 줄 수 있다.
예를 들어 이전에는 특정 GraphQL 필드를 GitHub App으로 조회할 수 없어서 PAT를 추가로 사용했다면 해당 인증 경로를 단순화할 수 있다.
하지만 모든 API가 GitHub App의 모든 권한 유형을 지원한다는 의미는 아니다.
개별 API의 권한 요구사항을 확인해야 한다.
GitHub Enterprise Server 지원
이번 기능은 github.com의 Enterprise 환경에서 정식 출시됐다.
GitHub Enterprise Server에는 3.24 버전부터 포함될 예정이다.
따라서 자체 서버에서 이전 버전의 GitHub Enterprise Server를 사용하는 조직에서는 동일한 기능을 즉시 사용할 수 없을 수 있다.
이번 업데이트는 새로운 Git 프로토콜이나 Repository 저장 방식을 도입한 것이 아니다.
기업의 관리 자동화를 개인 사용자 Token에서 애플리케이션별 권한 모델로 이전할 수 있도록 GitHub App의 적용 범위를 확대한 보안 및 관리 기능이다.
Vercel Connect, Stripe API Key를 에이전트·작업별로 분리 — 최소 권한과 자동 회전 지원
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 적용 대상: Vercel Connect, Stripe
- 신규 연결 방식: Managed API Key
- 원문: https://vercel.com/changelog/vercel-connect-adds-managed-stripe-api-keys
Vercel이 Connect에서 Stripe API Key를 생성하고 관리하는 기능을 추가했다.
이번 기능은 결제 시스템에 접근하는 AI 에이전트나 서버 애플리케이션에 필요한 최소한의 권한만 부여하도록 설계됐다.
Stripe는 온라인 결제와 고객, 구독, 청구 등의 기능을 제공하는 결제 플랫폼이다.
웹 애플리케이션에서는 Stripe의 API를 이용해 여러 작업을 수행할 수 있다.
예를 들어 다음과 같은 기능이다.
- 결제 내역 조회
- 고객 생성
- 구독 상태 조회
- 청구서 관리
- 환불 처리
하지만 모든 프로그램이 이러한 작업을 전부 수행해야 하는 것은 아니다.
예를 들어 일일 매출 보고서를 생성하는 AI 에이전트라면 결제 데이터를 읽기만 하면 될 수 있다.
반면 고객 등록을 담당하는 에이전트는 새로운 고객을 생성할 권한이 필요할 수 있다.
두 에이전트가 동일한 광범위한 Secret Key를 사용하는 것은 불필요한 위험을 만든다.
기존 Secret Key의 문제
일반적인 Stripe 통합에서는 서버 애플리케이션에 Secret Key를 저장한다.
이 Key는 Stripe API 요청을 인증하는 데 사용된다.
하지만 기본 Secret Key는 계정의 여러 리소스에 대한 넓은 권한을 가질 수 있다.
예를 들어 읽기 전용 보고서를 생성하는 에이전트가 고객 정보 수정이나 결제 관련 변경 권한까지 가진 상황을 생각할 수 있다.
이 경우 에이전트가 잘못된 API를 호출하면 원래 작업에 필요하지 않았던 데이터까지 변경할 수 있다.
또한 Secret Key가 노출되면 해당 Key가 가진 권한 범위만큼 위험이 확대된다.
이번 Vercel Connect 업데이트는 이러한 문제를 줄이기 위해 에이전트 또는 작업별 Restricted API Key를 생성하도록 한다.
Managed API Key
Vercel Connect에서 Stripe 연결을 생성할 때 새로운 인증 방식을 선택할 수 있다.
이름은 다음과 같다.
Managed API Key
관리자는 해당 연결에 필요한 Stripe 권한을 지정한다.
Vercel은 Stripe 계정에 제한된 API Key를 생성한다.
이후 해당 Key를 Vercel Connect에서 관리한다.
예를 들어 다음과 같은 구성을 만들 수 있다.
| 에이전트 | 필요한 작업 | 부여할 권한 |
|---|---|---|
| 매출 보고 에이전트 | 결제 내역 조회 | Charges 읽기 |
| 고객 등록 에이전트 | 고객 생성 | Customers 쓰기 |
| 구독 조회 에이전트 | 구독 상태 확인 | 관련 리소스 읽기 |
이렇게 하면 하나의 Key가 모든 작업에 사용되는 구조를 피할 수 있다.
중요한 점은 권한을 에이전트가 스스로 결정하는 것이 아니라 연결 생성 과정에서 관리자가 지정한다는 사실이다.
따라서 에이전트가 잘못된 작업을 요청하더라도 해당 Key에 필요한 권한이 없다면 Stripe API에서 요청을 거부할 수 있다.
Key 생성과 저장
기존에는 Stripe Dashboard에서 API Key를 생성한 뒤 애플리케이션의 환경 변수에 복사하는 방식이 사용될 수 있었다.
이번 방식에서는 Vercel이 연결 생성 과정에서 Key를 만들고 저장한다.
따라서 개발자가 Secret Key를 직접 복사해 여러 환경에 전달하는 작업이 줄어든다.
Vercel CLI에서도 연결을 생성할 수 있다.
대표적인 명령은 다음과 같다.
vercel connect create stripe --connection-method managed-api-key --name acme-stripe
이후 생성한 연결을 특정 프로젝트와 환경에 연결한다.
vercel connect attach stripe/acme-stripe --project my-app --environment production
실제 애플리케이션에서는 Connect SDK의 getToken()을 이용해 필요한 Key를 가져올 수 있다.
이러한 방식은 Secret Key를 소스코드에 직접 작성하지 않도록 하는 데 도움이 된다.
하지만 Connect를 사용한다고 해서 Secret Key가 애플리케이션에 전혀 노출되지 않는다는 의미는 아니다.
실제 API 요청을 수행하는 서버 런타임은 해당 Key를 사용할 수 있어야 한다.
따라서 실행 로그나 오류 메시지에 인증 정보가 기록되지 않도록 관리해야 한다.
API Key 회전
이번 기능은 Key Rotation도 지원한다.
Key Rotation은 기존 인증 정보를 새로운 인증 정보로 교체하는 작업이다.
예를 들어 특정 Key가 장기간 사용됐다면 보안 정책에 따라 새로운 Key로 교체할 수 있다.
Vercel Connect에서는 연결 설정에서 Key를 회전할 수 있다.
애플리케이션을 다시 배포하지 않아도 새로운 Key로 교체할 수 있다.
이 과정에서는 기존 Key가 최대 24시간 동안 유효한 상태로 유지될 수 있다.
이러한 유예 기간은 실행 중인 애플리케이션이 새로운 Key로 전환할 시간을 확보하기 위한 것이다.
하지만 기존 Key가 즉시 폐기되는 구조는 아니다.
따라서 Key 유출 사고처럼 즉각적인 폐기가 필요한 상황에서는 유예 기간의 영향을 고려해야 한다.
또한 API Key를 회전했다고 해서 이전에 유출된 데이터나 이미 실행된 API 요청이 자동으로 복구되는 것은 아니다.
기존 연결 방식과의 관계
이번 기능은 기존 Stripe 연결 방식을 제거하지 않는다.
다음 방식은 계속 사용할 수 있다.
- MCP 연결
- 개발자가 직접 제공하는 API Key
- Managed API Key
따라서 기존 프로젝트가 즉시 새로운 인증 방식으로 변경해야 하는 것은 아니다.
다만 AI 에이전트마다 서로 다른 권한을 부여하고 싶다면 Managed API Key를 활용할 수 있다.
이번 업데이트는 Stripe API의 새로운 결제 기능을 추가한 것이 아니다.
AI 에이전트와 서버 애플리케이션이 결제 시스템에 접근할 때 필요한 권한을 분리하고, 인증 정보의 생성·보관·회전을 관리형 기능으로 제공하도록 확장한 보안 업데이트다.
Vercel CLI, 예산 관리 및 Build Cache 관측 기능 확대 — 배포 비용과 캐시 크기를 터미널에서 확인
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 적용 대상: Vercel CLI, Spend Management, Deployment Summary
- Build Cache 조회 지원: Vercel CLI 65.1.0 이상
- 예산 관리 원문: https://vercel.com/changelog/vercel-cli-adds-spend-management
- Build Cache 원문: https://vercel.com/changelog/deployments-now-show-build-cache-size
Vercel이 CLI에서 팀의 예산과 사용량을 관리하는 기능을 추가하고, 배포별 Build Cache 크기를 확인할 수 있도록 개선했다.
두 변경은 서로 다른 기능이지만 웹 애플리케이션의 배포 비용과 빌드 효율성을 관찰한다는 공통점이 있다.
특히 자동화된 배포 시스템이나 AI 코딩 에이전트를 사용하는 경우에는 실행 비용을 확인하고 제한하는 기능이 중요해질 수 있다.
CLI에서 예산 조회
새로운 기능에서는 다음 명령으로 현재 팀의 지출과 예산을 확인할 수 있다.
vercel budgets
이 명령은 현재 사용량과 설정된 예산을 반환한다.
예를 들어 팀의 예산이 200달러이고 현재 63.33달러를 사용했다면 다음 정보를 확인할 수 있다.
- 예산: 200달러
- 현재 지출: 63.33달러
- 예산 사용률: 약 32%
- 예산 상태
- 자동 중단 설정 여부
기존에는 이러한 정보를 Dashboard에서 확인해야 했다면 이제는 CLI와 자동화 스크립트에서도 사용할 수 있다.
이는 AI 에이전트가 배포 작업을 수행하기 전에 현재 예산 상태를 확인하는 구조에도 활용할 수 있다.
예산 초과 시 Production Deployment 중단
CLI에서는 새로운 예산을 설정할 수도 있다.
예를 들어 다음 명령을 사용할 수 있다.
vercel budgets set --amount 200 --pause
이 명령은 팀의 예산을 200달러로 설정하고 예산을 모두 사용했을 때 중단 정책을 적용한다.
중요한 점은 예산 설정 자체와 자동 중단 기능을 구분해야 한다는 것이다.
단순히 예산을 설정한다고 해서 모든 비용이 해당 금액에서 자동으로 멈추는 것은 아니다.
--pause 옵션을 지정하면 예산이 100%에 도달했을 때 Production Deployment를 사용할 수 없도록 한다.
하지만 이것이 이미 실행 중인 모든 리소스와 외부 서비스의 과금을 즉시 중단한다는 의미는 아니다.
실제로 어떤 작업이 중단되는지는 Vercel의 Spend Management 정책에 따라 달라진다.
따라서 예산을 설정했다는 사실만으로 모든 사용량이 엄격한 상한 안에 유지된다고 가정해서는 안 된다.
예산 관리 권한
예산 설정과 변경에는 적절한 계정 권한이 필요하다.
Vercel은 다음 역할의 사용자가 예산을 관리할 수 있다고 안내한다.
- Owner
- Billing
따라서 일반적인 개발자 권한을 가진 사용자는 예산을 조회할 수 있더라도 실제 변경은 제한될 수 있다.
또한 해당 기능은 Pro와 Flexible Commitment Enterprise 환경에서 무료로 제공된다.
프로젝트별 사용량을 자동화된 도구로 확인할 수 있다는 점은 편리하지만, 실제 예산 정책을 변경하는 권한은 일반적인 배포 권한과 분리해서 관리해야 한다.
Build Cache 크기 확인
두 번째 변경은 배포별 Build Cache 크기를 표시하는 기능이다.
Build Cache는 이전 빌드에서 생성한 데이터를 저장해 이후 빌드에서 재사용하는 기능이다.
예를 들어 다음 데이터가 캐시될 수 있다.
- 패키지 매니저의 설치 캐시
- 빌드 도구가 생성한 중간 결과
- 프레임워크의 빌드 캐시
- 반복적으로 사용하는 빌드 결과물
Build Cache를 사용하면 매번 모든 작업을 처음부터 실행하지 않아도 된다.
하지만 캐시에도 저장 용량 제한이 존재한다.
또한 캐시를 업로드하는 작업 자체에 시간이 필요하다.
이번 업데이트는 이러한 캐시의 크기를 배포 결과에서 직접 확인할 수 있도록 한다.
캐시 크기가 중요한 이유
예를 들어 빌드 결과를 캐싱하면 이후 배포가 빨라질 수 있다.
하지만 캐시에 지나치게 많은 파일을 포함하면 캐시 업로드 시간이 증가한다.
Vercel에서는 Build Cache를 업로드하는 시간도 과금 대상인 빌드 시간에 포함된다.
따라서 캐시를 많이 저장하는 것이 항상 유리하지는 않다.
또한 캐시 크기가 사용 중인 Build Machine의 제한을 초과하면 문제가 발생한다.
Vercel은 제한을 초과한 캐시를 유지하지 않고 버린다.
그 결과 다음 빌드는 이전 캐시를 사용하지 못한다.
즉 캐시가 지나치게 커지면 오히려 다음 배포에서 Cold Build가 발생할 수 있다.
Cold Build는 이전 빌드 캐시를 활용하지 못하고 필요한 작업을 다시 수행하는 빌드를 의미한다.
이번 기능에서는 이러한 문제를 분석하기 위해 다음 정보를 제공한다.
- 실제 저장한 Build Cache 크기
- Build Machine의 캐시 용량 제한에 얼마나 가까운지
Dashboard·CLI·REST API 지원
Build Cache 정보는 여러 경로에서 확인할 수 있다.
| 조회 경로 | 지원 조건 |
|---|---|
| Deployment Summary | Dashboard |
| Vercel CLI | 65.1.0 이상 |
| REST API | Get Deployment API |
CLI에서는 다음 명령을 사용할 수 있다.
vercel inspect
이 명령을 통해 배포 정보를 조회하면서 Build Cache의 크기도 확인할 수 있다.
또한 REST API에서 해당 정보를 가져올 수 있으므로 별도의 배포 분석 시스템과 연결할 수 있다.
예를 들어 여러 프로젝트의 빌드 시간이 갑자기 증가했다면 캐시 크기와 저장 여부를 함께 확인할 수 있다.
다만 캐시 크기가 증가했다는 사실만으로 빌드 시간이 증가한 원인을 단정할 수는 없다.
빌드 도구와 의존성, 실행 환경이 함께 변경됐을 가능성도 있기 때문이다.
적용 시점
Build Cache 표시 기능은 모든 요금제에 기본으로 적용된다.
대상은 2026년 10월 21일 이후에 빌드된 배포다.
따라서 오래된 배포에서 동일한 정보를 확인할 수 없을 수 있다.
이번 두 업데이트는 새로운 프레임워크나 빌드 알고리즘을 도입한 것이 아니다.
웹서비스의 운영 비용과 캐시 상태를 개발 도구에서 직접 관찰하고, 자동화된 배포 시스템에서도 사용할 수 있도록 관리 기능을 확대한 변경이다.
Cloudflare, 양자내성 암호화 기반 GeoKey Manager 비공개 Beta 공개 — 지정된 지역에서만 TLS 복호화 키 사용
- 발표일: 2026-10-23 — 정확한 게시 시각 미확인
- 안정화 단계: Closed Beta
- 정식 출시 목표: 2027년 1분기
- 적용 대상: Cloudflare Data Localization Suite
- 원문: https://blog.cloudflare.com/post-quantum-geo-key-manager/
Cloudflare가 양자내성 암호화(Post-Quantum Cryptography)를 적용한 GeoKey Manager를 비공개 Beta로 공개했다.
GeoKey Manager는 특정 지역의 서버에서만 TLS 복호화 키를 사용할 수 있도록 제한하는 기능이다.
이는 데이터 주권(Data Sovereignty)과 관련된다.
예를 들어 어떤 기업이 사용자 데이터를 특정 국가나 지역에서만 복호화하도록 요구한다고 가정해 보자.
전 세계에 서버를 운영하는 CDN은 여러 지역에서 같은 웹사이트의 요청을 처리할 수 있다.
하지만 기업의 정책에 따라 암호화된 데이터가 모든 지역에서 자유롭게 복호화돼서는 안 될 수 있다.
이러한 상황에서는 단순히 데이터를 저장하는 위치뿐 아니라 암호화 키가 실제로 어느 지역에서 사용될 수 있는지도 중요하다.
Cloudflare는 기존 GeoKey Manager의 지역 제한 기능에 양자내성 암호화 구조를 추가했다.
양자내성 암호화가 필요한 이유
현재 널리 사용되는 일부 공개키 암호화 방식은 특정 수준의 양자컴퓨터가 등장하면 안전성이 약화될 수 있다.
공격자는 현재 해독할 수 없는 암호화된 통신 데이터를 저장해 둘 수 있다.
이후 충분한 성능의 양자컴퓨터가 등장하면 과거에 수집한 데이터를 해독하려고 시도할 수 있다.
이러한 공격 전략을 다음과 같이 부른다.
Harvest Now, Decrypt Later
즉 지금 암호문을 수집하고 나중에 복호화한다는 의미다.
장기간 기밀성을 유지해야 하는 데이터에서는 이러한 위협을 고려할 수 있다.
예를 들어 금융 정보나 중요한 기업 데이터, 의료 관련 정보 등이 해당할 수 있다.
이번 GeoKey Manager는 이러한 장기적인 암호화 위험을 줄이는 방향으로 설계됐다.
기존 지역 제한 키 관리의 문제
Cloudflare는 전 세계 여러 지역에서 네트워크 요청을 처리한다.
이러한 구조에서 특정 지역의 서버만 복호화 키를 사용할 수 있도록 하려면 키의 배포와 복호화 권한을 제한해야 한다.
기존 GeoKey Manager는 속성 기반 암호화(Attribute-Based Encryption)를 활용해 지역별 키 접근을 제어하는 구조를 사용해 왔다.
하지만 Cloudflare에 따르면 기존에 사용하던 방식과 직접적으로 호환되는 생산 환경 수준의 양자내성 속성 기반 암호화 구현은 아직 충분히 준비되지 않았다.
따라서 기존 암호화 방식을 그대로 양자내성 알고리즘으로 교체하기 어려웠다.
Cloudflare는 이를 해결하기 위해 지역별로 다른 키 관리 구조를 설계했다.
지역별 Root Key와 키 래핑
새로운 구조에서는 지정된 지역에 지역별 Root Key를 배치한다.
Root Key는 다른 키를 보호하는 데 사용되는 상위 키다.
Cloudflare는 해당 Root Key를 지정된 지역 안에서 관리한다.
그리고 각 서비스에서 사용하는 복호화 키를 별도로 암호화해 저장한다.
이러한 과정을 Key Wrapping이라고 한다.
Key Wrapping은 실제 데이터를 복호화하는 키를 다시 다른 키로 암호화해 보호하는 방식이다.
예를 들어 특정 고객의 TLS 복호화 키가 있다고 가정해 보자.
이 키는 전 세계 Cloudflare 네트워크에 그대로 전달되지 않는다.
대신 지정된 지역에서만 복호화할 수 있도록 감싼 형태로 배포된다.
따라서 암호화된 키 정보가 다른 지역에 존재하더라도 해당 지역의 서버가 필요한 Root Key를 가지고 있지 않다면 실제 복호화 키를 사용할 수 없다.
ML-KEM-768과 X25519
이번 GeoKey Manager는 양자내성 키 교환 구조에 다음 알고리즘을 활용한다.
- ML-KEM-768
- X25519
ML-KEM은 양자내성 키 캡슐화 메커니즘이다.
X25519는 기존의 타원곡선 기반 키 교환에 널리 사용되는 방식이다.
두 알고리즘을 함께 사용하는 구조를 Hybrid 방식이라고 한다.
이러한 구성은 기존 암호화 기술과 새로운 양자내성 기술을 조합하는 접근이다.
새로운 알고리즘의 장기적인 보안 특성을 활용하면서 기존 암호화 방식도 함께 사용하는 것이다.
다만 Hybrid 암호화라고 해서 시스템 전체가 자동으로 모든 종류의 공격에 안전해지는 것은 아니다.
실제 보안은 키의 생성과 저장, 배포, 접근 제어 및 구현의 정확성에 영향을 받는다.
지정된 지역에서만 복호화
새로운 시스템에서는 특정 지역에 속한 서버만 필요한 키를 사용할 수 있도록 한다.
예를 들어 고객이 유럽 지역을 지정했다고 가정해 보자.
Cloudflare는 해당 지역에 필요한 Root Key를 배포한다.
다른 지역의 서버는 암호화된 키 데이터를 전달받을 수 있더라도 실제 복호화에 필요한 Root Key는 가지고 있지 않다.
따라서 해당 서버에서 복호화 키를 사용할 수 없도록 설계한다.
이때 지정되지 않은 지역의 서버가 요청을 처리하려고 한다면 필요한 키를 사용할 수 없어야 한다.
Cloudflare는 이러한 상황에서 Fail Closed 방식을 적용한다.
즉 복호화에 필요한 키가 없다면 더 넓은 권한으로 작업을 허용하는 것이 아니라 처리를 거부한다.
하지만 이 기능의 적용 범위를 구분해야 한다.
GeoKey Manager는 키를 어느 지역에서 사용할 수 있는지 제한하는 기능이지, 모든 네트워크 요청이 어느 지역을 경유하는지 직접 결정하는 기능은 아니다.
실제 요청 처리 지역의 제어에는 Cloudflare의 다른 Data Localization 기능이 함께 사용될 수 있다.
Closed Beta와 정식 출시 계획
이번 기능은 Closed Beta 단계다.
모든 Cloudflare 고객이 Dashboard에서 즉시 활성화할 수 있는 정식 기능은 아니다.
Cloudflare는 2027년 1분기 정식 출시를 목표로 한다고 설명했다.
하지만 이는 예정된 목표이며 확정된 출시일은 아니다.
또한 이번 발표에는 기존 GeoKey Manager와 새로운 양자내성 구현의 실행 비용이나 지연 시간을 독립적으로 비교한 Benchmark가 제공되지 않았다.
따라서 새로운 구조가 기존 방식보다 항상 빠르거나 비용이 낮다고 결론 내릴 수는 없다.
이번 발표는 글로벌 네트워크에서 데이터 복호화 권한을 지역별로 제한하면서 장기적인 양자컴퓨터 위협에도 대응하기 위한 키 관리 아키텍처의 변화다.
📚 추천 글
Embracing Playful Imperfection With The CSS random() Function
- 저자: Brecht De Ruyte
- 매체: Smashing Magazine
- 게시일: 2026-10-20
- 원문: https://www.smashingmagazine.com/2026/10/embracing-playful-imperfection-css-random-function/
CSS의 새로운 random() 함수를 이용해 JavaScript 없이 요소의 회전과 위치, 크기, 애니메이션 시간을 무작위로 변경하는 방법을 실제 UI 예제로 설명한 글이다.
웹 애플리케이션의 디자인 시스템에서는 일반적으로 일관성을 중요하게 생각한다.
예를 들어 Button의 크기와 간격, 글꼴, 색상을 정해 두고 여러 화면에서 동일하게 사용하도록 한다.
이러한 일관성은 사용자에게 예측 가능한 인터페이스를 제공한다.
하지만 모든 시각적 요소가 완벽하게 정렬돼 있어야 하는 것은 아니다.
예를 들어 오래된 인쇄물처럼 글자가 조금씩 기울어진 디자인이나 여러 장의 사진이 자연스럽게 흩어져 있는 레이아웃을 생각할 수 있다.
이러한 효과에서는 약간의 불규칙성이 시각적인 특징이 될 수 있다.
기존에는 이러한 불규칙성을 구현하기 위해 JavaScript로 난수를 생성하거나 Sass에서 여러 개의 CSS 규칙을 만들어야 할 수 있었다.
이번 글은 브라우저의 CSS 계산 과정에서 직접 난수를 생성하는 random() 함수를 사용한다.
CSS random()의 기본 구조
CSS random()은 CSS Values and Units Level 5 규격에서 정의하는 함수다.
기본적인 형태는 다음과 같다.
random(min, max)
개발자는 난수의 최소값과 최대값을 지정할 수 있다.
예를 들어 다음과 같이 사용할 수 있다.
rotate: random(-3deg, 3deg)
이 경우 요소는 -3도부터 3도 사이의 각도로 회전할 수 있다.
다음과 같이 크기를 변경할 수도 있다.
inline-size: random(20px, 80px)
이때 브라우저는 지정된 범위 안에서 값을 선택한다.
기존에 JavaScript로 Math.random()을 호출한 뒤 CSS 변수에 전달하던 구현의 일부를 CSS만으로 처리할 수 있다.
하지만 이 기능은 아직 모든 브라우저에서 동일하게 지원되지 않는다.
저자는 Safari에서 지원이 시작됐고 Chrome 156에서 배포가 진행되고 있다고 설명한다.
Firefox에서는 당시 안정적인 지원이 제공되지 않았다.
따라서 운영 중인 웹서비스에서는 브라우저 지원 여부를 확인하고 Fallback을 제공해야 한다.
Step을 이용한 값 제한
random()은 최소값과 최대값뿐 아니라 Step도 지정할 수 있다.
예를 들어 다음과 같다.
random(0px, 60px, 15px)
이 경우 다음 값 가운데 하나를 선택할 수 있다.
- 0px
- 15px
- 30px
- 45px
- 60px
이러한 방식은 완전히 자유로운 위치 이동보다 통제된 불규칙성을 구현하는 데 적합하다.
예를 들어 카드들이 조금씩 어긋나게 배치되더라도 정해진 간격 단위 안에서만 이동하도록 만들 수 있다.
이는 디자인 시스템에서 사용하는 간격 규칙을 유지하면서 일부 변화를 줄 수 있다는 의미다.
다만 Step을 지정한다고 해서 모든 요소가 서로 다른 값을 선택한다는 보장은 없다.
서로 다른 요소가 우연히 같은 값을 선택할 수도 있다.
단위 일치와 calc()
CSS는 값의 단위를 중요하게 처리한다.
예를 들어 각도는 deg로 표현한다.
길이는 px나 rem으로 표현할 수 있다.
시간은 s나 ms로 표현한다.
random()에서 최소값과 최대값을 지정할 때는 이러한 단위가 호환돼야 한다.
예를 들어 다음 표현은 유효하다.
random(-4deg, 4deg)
하지만 서로 다른 길이 단위를 아무렇게나 혼합하면 CSS Parser가 처리할 수 없는 상황이 발생할 수 있다.
저자는 필요한 경우 calc()를 이용해 단위가 올바르게 계산되도록 구성해야 한다고 설명한다.
이는 random()이 JavaScript 함수처럼 임의의 값을 받아 계산하는 범용 함수가 아니라 CSS의 값과 단위 규칙 안에서 동작하는 함수라는 점을 보여준다.
같은 난수를 여러 속성에서 사용하기
이번 글에서 중요한 부분은 여러 CSS 속성이 동일한 난수를 사용하도록 하는 방법이다.
예를 들어 하나의 카드에서 회전 각도와 그림자의 위치를 연결하고 싶다고 가정해 보자.
각 속성에서 별도로 random()을 호출하면 서로 다른 값이 선택될 수 있다.
그 결과 카드의 방향과 그림자의 방향이 어울리지 않을 수 있다.
저자는 CSS Custom Property와 Random Key를 이용해 이러한 값을 공유하는 방법을 소개한다.
이렇게 하면 하나의 요소에서 선택한 무작위 값을 여러 CSS 속성에 연결할 수 있다.
예를 들어 카드의 회전 각도와 이동 거리, 관련 장식 효과가 같은 난수에 따라 변경되도록 구성할 수 있다.
다만 Random Key의 범위와 값의 공유 방식은 규격에 정의된 규칙에 따라 달라진다.
같은 이름을 사용했다고 해서 모든 요소에서 항상 동일한 값을 공유하도록 구성하는 것이 적절하지는 않다.
각 요소가 독립적인 난수를 가져야 하는지, 하나의 그룹이 난수를 공유해야 하는지 먼저 결정해야 한다.
자연스럽게 기울어진 타이포그래피
저자는 첫 번째 예제로 손으로 만든 인쇄물처럼 보이는 텍스트를 구현한다.
각 글자는 조금씩 다른 각도로 회전한다.
또한 글자마다 위치가 약간 달라진다.
이러한 효과에서는 너무 큰 난수 범위를 사용하면 문제가 발생한다.
글자가 지나치게 기울어지거나 겹쳐 읽기 어려워질 수 있기 때문이다.
따라서 저자는 제한된 범위에서 작은 변화를 적용한다.
이는 시각적인 불규칙성과 텍스트 가독성 사이의 균형을 맞추기 위한 선택이다.
또한 시각적 효과를 구현하기 위해 텍스트를 여러 요소로 분리하는 경우에는 접근성을 고려해야 한다.
예를 들어 하나의 단어를 글자마다 다른 span으로 분리했다고 가정해 보자.
스크린 리더가 이러한 요소를 하나의 자연스러운 단어로 읽지 못할 수 있다.
저자는 원래 텍스트를 접근 가능한 이름으로 제공하고 시각적 장식에 사용한 개별 글자는 보조 기술에서 숨기는 방식을 소개한다.
이는 CSS로 복잡한 시각 효과를 만들더라도 실제 텍스트의 의미를 보존해야 한다는 점을 보여준다.
바운스 애니메이션의 시간 차이
다음 예제는 글자가 서로 다른 타이밍으로 튀어 오르는 애니메이션이다.
모든 글자가 같은 시점에 같은 속도로 움직이면 규칙적인 움직임이 만들어진다.
반면 각 글자의 애니메이션 시간과 지연을 조금씩 다르게 설정하면 더 자연스러운 움직임을 만들 수 있다.
저자는 random()을 이용해 애니메이션의 지속 시간과 시작 지연을 변경했다.
또한 음수 Animation Delay를 사용해 애니메이션이 이미 진행 중인 상태에서 시작하도록 구성했다.
음수 Animation Delay는 애니메이션의 시작 시점을 앞당기는 효과가 있다.
예를 들어 여러 요소에 -0.2초, -0.5초처럼 다른 값을 적용하면 모든 요소가 처음부터 동시에 움직이기 시작하는 문제를 줄일 수 있다.
하지만 이러한 방식은 움직임에 민감한 사용자에게 불편을 줄 수 있다.
따라서 실제 서비스에서는 prefers-reduced-motion을 이용해 사용자의 움직임 감소 설정을 고려해야 한다.
카드 레이아웃에서 활용
저자는 여러 장의 카드를 겹쳐 놓은 디자인도 구현했다.
각 카드는 조금씩 다른 회전 각도와 위치를 가진다.
이때 단순히 완전히 무작위로 배치하면 카드가 지나치게 겹치거나 일부 콘텐츠가 화면 밖으로 이동할 수 있다.
저자는 구조적인 배치와 작은 무작위 변화를 조합한다.
예를 들어 카드의 기본적인 순서는 sibling-index()와 sibling-count()를 이용해 계산할 수 있다.
sibling-index()는 요소가 형제 요소 가운데 몇 번째에 위치하는지 나타낸다.
sibling-count()는 형제 요소의 개수를 나타낸다.
이러한 값을 이용하면 카드 목록의 기본적인 위치와 순서를 계산할 수 있다.
그 위에 random()으로 작은 회전과 이동을 적용하면 카드가 규칙적인 구조를 유지하면서도 자연스럽게 흩어진 것처럼 보인다.
중요한 점은 전체 레이아웃을 난수로 결정하지 않고 기본적인 구조 위에 제한된 변화를 추가했다는 사실이다.
브라우저 지원과 Fallback
이번 글은 실험적인 CSS 기능을 다룬다.
따라서 현재 모든 사용자에게 동일한 UI를 제공할 수 있다고 가정해서는 안 된다.
저자는 @supports를 이용해 브라우저가 해당 기능을 지원하는지 확인하는 방법을 소개한다.
예를 들어 random()을 지원하지 않는 브라우저에서는 고정된 회전 각도나 기본적인 레이아웃을 사용할 수 있다.
이렇게 하면 최신 브라우저에서는 풍부한 시각 효과를 제공하면서도 다른 브라우저에서는 기본적인 콘텐츠를 정상적으로 표시할 수 있다.
이는 Progressive Enhancement의 사례다.
Progressive Enhancement는 기본적인 기능을 먼저 제공하고, 브라우저가 지원하는 고급 기능을 추가하는 설계 방식이다.
또한 CSS에서 난수를 생성할 수 있다는 사실이 JavaScript 기반의 모든 난수 처리를 대체한다는 의미는 아니다.
예를 들어 서버와 클라이언트에서 동일한 시드를 공유해야 하거나 무작위 결과를 데이터베이스에 저장해야 한다면 별도의 로직이 필요할 수 있다.
이번 글은 CSS가 단순히 정적인 스타일을 선언하는 언어에서 벗어나 제한된 범위의 동적인 시각적 변화를 표현할 수 있도록 발전하는 방향을 구체적인 예제로 보여준다.
Building an Isolated Preview for AI-Generated HTML
- 저자: Keshav Malik
- 매체: Master.dev
- 게시일: 2026-10-22
- 원문: https://blog.master.dev/building-an-isolated-preview-for-ai-generated-html/
AI가 생성한 HTML을 브라우저에서 미리보기로 표시할 때 iframe만 사용해서는 충분한 보안 경계를 만들 수 없다는 점을 실제 공격 실험으로 검증한 글이다.
최근에는 AI가 웹페이지와 컴포넌트를 직접 생성하는 서비스가 많아졌다.
예를 들어 사용자가 자연어로 로그인 페이지를 만들어 달라고 요청하면 AI가 HTML과 CSS, JavaScript를 생성할 수 있다.
애플리케이션은 생성된 결과를 사용자에게 보여줘야 한다.
가장 간단한 방법은 생성한 HTML을 iframe에 넣는 것이다.
하지만 여기에는 중요한 보안 문제가 존재한다.
AI가 생성한 HTML은 애플리케이션 개발자가 직접 검토한 신뢰할 수 있는 코드가 아니다.
모델이 잘못된 코드를 생성할 수도 있고, 사용자나 외부 문서에 포함된 악의적인 지시가 생성 결과에 영향을 줄 수도 있다.
따라서 AI가 생성한 HTML을 실제 애플리케이션의 실행 문맥에서 그대로 처리하면 보안 문제가 발생할 수 있다.
저자는 이러한 문제를 다섯 개의 실제 브라우저 실험으로 검증했다.
첫 번째 문제: srcdoc만 사용한 iframe
HTML의 iframe은 웹페이지 안에 다른 문서를 표시하기 위한 요소다.
개발자는 srcdoc 속성을 이용해 iframe 안에 HTML 문자열을 직접 전달할 수 있다.
예를 들어 AI가 생성한 HTML 문자열을 srcdoc에 전달하면 별도의 서버에 파일을 저장하지 않고도 미리보기를 표시할 수 있다.
겉으로 보기에는 iframe이 별도의 문서이므로 안전하게 격리된 것처럼 느껴질 수 있다.
하지만 sandbox가 없는 srcdoc iframe은 일반적으로 부모 페이지와 같은 Origin으로 동작한다.
Origin은 웹에서 프로토콜, 호스트, 포트의 조합으로 구분되는 보안 경계다.
같은 Origin에 속한 문서는 브라우저의 Same-Origin Policy에 따라 서로의 DOM에 접근할 수 있다.
따라서 sandbox가 없는 iframe 안의 JavaScript는 부모 페이지의 데이터를 읽을 수 있다.
저자는 실제 실험에서 iframe 안의 코드가 다음 정보에 접근하는 상황을 재현했다.
- 부모 페이지의 DOM
- Local Storage
- 부모 페이지에 표시된 임의의 테스트용 Token
예를 들어 부모 페이지의 DOM에 사용자 세션과 관련된 정보가 존재한다면 iframe 안의 코드가 해당 정보를 읽으려고 시도할 수 있다.
이 실험은 실제 인증 정보를 사용하지 않고 테스트용 문자열을 이용해 수행됐다.
하지만 브라우저의 Origin 규칙 자체는 동일하다.
따라서 AI가 생성한 HTML을 단순히 srcdoc에 넣는 것만으로는 충분한 격리가 이루어지지 않는다.
sandbox="allow-scripts"
저자는 이를 해결하기 위해 iframe에 다음 설정을 추가했다.
sandbox="allow-scripts"
sandbox는 iframe 안에서 허용할 수 있는 동작을 제한하는 HTML 속성이다.
기본적으로 비어 있는 Sandbox는 여러 기능을 차단한다.
예를 들어 JavaScript 실행과 Form 제출, Popup 생성, 최상위 페이지 이동 등이 제한된다.
하지만 AI가 생성한 웹페이지의 동작을 확인하려면 JavaScript 실행이 필요할 수 있다.
따라서 저자는 allow-scripts만 허용했다.
이렇게 하면 iframe 안에서 JavaScript를 실행할 수 있다.
하지만 같은 Origin으로 접근하는 권한은 부여하지 않는다.
브라우저는 iframe에 불투명한 Origin(Opaque Origin)을 부여한다.
따라서 iframe 안의 코드가 부모 페이지의 DOM이나 Local Storage에 접근하려고 하면 보안 오류가 발생한다.
저자의 테스트에서는 해당 접근이 SecurityError로 차단됐다.
allow-same-origin을 함께 사용하면 안 되는 이유
iframe을 개발하다 보면 일부 기능이 작동하지 않는다는 이유로 Sandbox에 다음 권한을 추가하고 싶을 수 있다.
allow-same-origin
하지만 같은 Origin에서 제공되는 srcdoc iframe에 allow-scripts와 allow-same-origin을 함께 사용하면 보안 경계가 약화될 수 있다.
JavaScript가 부모 페이지와 같은 Origin으로 실행된다면 부모 페이지의 DOM에 접근할 수 있기 때문이다.
또한 iframe 안의 코드가 부모 문서에 접근할 수 있다면 자신의 Sandbox 속성을 변경한 뒤 페이지를 다시 로딩하는 방식으로 제한을 우회하려고 시도할 수 있다.
따라서 신뢰할 수 없는 코드를 실행하는 iframe에서는 두 권한을 단순히 함께 활성화해서는 안 된다.
실제 기능에 필요한 권한을 하나씩 검토해야 한다.
두 번째 문제: 부모 페이지 이동과 Popup
저자는 iframe 안의 코드가 부모 페이지를 다른 주소로 이동시키는 공격도 테스트했다.
예를 들어 악의적인 코드가 다음 행동을 수행하려고 할 수 있다.
- 부모 페이지의 URL 변경
- 새로운 Popup 열기
- 외부 서버로 Form 제출
이러한 동작은 사용자에게 원래 서비스와 다른 화면을 표시하거나 외부 사이트로 이동시키는 데 악용될 수 있다.
저자는 sandbox="allow-scripts"만 적용한 경우 이러한 동작이 제한되는 것을 확인했다.
하지만 Sandbox에 다음 권한을 추가하면 일부 동작이 다시 허용될 수 있다.
allow-top-navigationallow-popupsallow-forms
따라서 미리보기 기능을 구현할 때 해당 권한을 필요한 이유 없이 추가해서는 안 된다.
예를 들어 단순한 UI 미리보기에서 Popup 생성 권한이 반드시 필요한지 검토해야 한다.
세 번째 문제: Sandbox만으로 외부 네트워크 요청을 차단할 수 없다
이번 글에서 가장 중요한 발견 가운데 하나는 Sandbox가 네트워크 통신 자체를 완전히 차단하지 않는다는 점이다.
예를 들어 iframe에 sandbox="allow-scripts"를 적용했다고 가정해 보자.
부모 페이지의 DOM에는 접근할 수 없다.
최상위 페이지로 이동하는 것도 제한된다.
하지만 iframe 안의 JavaScript는 외부 서버에 fetch() 요청을 보내려고 시도할 수 있다.
또한 Image 요소의 src를 외부 URL로 지정할 수도 있다.
이러한 요청의 URL에 특정 데이터를 포함하면 외부로 정보를 전송하는 통로가 될 수 있다.
저자는 실제로 다음 두 방식을 테스트했다.
첫 번째는 fetch()를 이용한 외부 요청이다.
두 번째는 이미지의 URL을 이용한 외부 요청이다.
중요한 점은 브라우저의 CORS 정책이 이러한 요청의 모든 전송을 막아주는 것은 아니라는 사실이다.
CORS는 주로 다른 Origin의 응답을 JavaScript가 읽을 수 있는지 제어하는 데 사용된다.
외부로 전송되는 모든 요청을 차단하는 보안 정책과는 다르다.
따라서 Sandbox만으로 외부 데이터 유출 가능성을 완전히 제거할 수 없다.
Content Security Policy 적용
저자는 이 문제를 해결하기 위해 CSP(Content Security Policy)를 추가했다.
CSP는 웹페이지가 어떤 리소스를 로딩하거나 실행할 수 있는지 제한하는 보안 정책이다.
예를 들어 다음과 같은 설정을 사용할 수 있다.
default-src 'none'
이 설정은 기본적으로 외부 리소스의 로딩을 차단한다.
이후 실제 미리보기 기능에 필요한 항목만 명시적으로 허용한다.
저자가 소개한 예제에서는 다음과 같은 정책을 사용했다.
default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'
이 정책은 기본적인 네트워크 리소스 로딩을 제한하면서도 미리보기 안의 Inline Script와 Inline Style을 허용한다.
여기서 주의해야 할 점은 unsafe-inline이라는 이름이다.
일반적인 웹 애플리케이션에서는 Inline Script를 무분별하게 허용하는 것이 위험할 수 있다.
하지만 이번 예제에서는 이미 신뢰할 수 없는 코드가 실행될 수 있도록 제한된 미리보기 환경을 구성하고 있다.
따라서 부모 애플리케이션의 CSP와 미리보기 전용 CSP는 서로 다른 목적을 가진다.
중요한 것은 미리보기에서 허용한 실행 권한이 부모 애플리케이션의 보안 경계를 넘어가지 않도록 구성하는 것이다.
Sandbox와 CSP는 서로 다른 역할을 한다
저자는 두 기능의 역할을 명확하게 구분한다.
| 보안 수단 | 주요 역할 |
|---|---|
| iframe Sandbox | 부모 페이지 접근, Popup, Navigation, Form 등의 동작 제한 |
| CSP | 외부 리소스 로딩 및 코드 실행 정책 제한 |
| 별도 Origin | 부모 애플리케이션의 데이터와 실행 환경 분리 |
postMessage 검증 | 허용된 메시지만 처리 |
따라서 하나의 기능만으로 모든 문제를 해결하려고 해서는 안 된다.
예를 들어 Sandbox는 부모 페이지 접근을 제한할 수 있다.
하지만 외부 네트워크 요청을 충분히 제한하지 못할 수 있다.
반면 CSP는 외부 요청을 차단할 수 있지만 iframe의 모든 Navigation 동작을 차단하지는 않는다.
서로 다른 보안 수단이 서로 다른 공격 경로를 제한한다.
저자는 이러한 이유로 여러 계층을 함께 사용하는 방식을 제안한다.
독립된 Preview Origin
저자는 중요한 서비스에서는 미리보기 콘텐츠를 별도의 Origin에서 제공하는 방식을 권장한다.
예를 들어 애플리케이션의 실제 주소가 다음과 같다고 가정해 보자.
https://app.example.com
미리보기는 별도의 호스트에서 제공할 수 있다.
https://preview.example.net
이렇게 하면 미리보기의 Sandbox 설정에 문제가 발생하더라도 실제 애플리케이션의 쿠키와 Local Storage에 직접 접근하기 어려운 구조를 만들 수 있다.
다만 별도 Origin을 사용하는 것만으로 모든 공격이 방지되는 것은 아니다.
미리보기 Origin에 민감한 데이터를 저장하거나, 부모 페이지가 잘못된 메시지를 신뢰한다면 여전히 문제가 발생할 수 있다.
따라서 별도 Origin은 Sandbox와 CSP를 대체하는 것이 아니라 추가적인 방어 계층이다.
postMessage를 이용한 통신
실제 미리보기는 부모 페이지와 데이터를 교환해야 할 수 있다.
예를 들어 iframe 안에 표시되는 콘텐츠의 높이를 측정하고 부모 페이지에 전달할 수 있다.
부모 페이지는 전달받은 높이를 이용해 iframe의 크기를 조절한다.
이러한 작업에는 postMessage를 사용할 수 있다.
하지만 외부에서 임의의 메시지를 보낼 가능성을 고려해야 한다.
저자는 다음 두 가지를 검증하도록 안내한다.
첫 번째는 메시지를 보낸 Window다.
event.source가 실제 미리보기 iframe의 contentWindow인지 확인한다.
두 번째는 메시지의 데이터 구조다.
예를 들어 높이를 전달받았다면 숫자인지 확인한다.
또한 지나치게 큰 값이나 음수가 전달되지 않도록 범위를 제한한다.
이러한 검증을 수행하지 않으면 악의적인 메시지로 부모 페이지의 UI를 변경하거나 오류를 발생시킬 수 있다.
Opaque Origin과 "null" 문제
Sandbox가 적용된 iframe은 불투명한 Origin을 사용할 수 있다.
이 경우 postMessage 이벤트의 origin 값이 다음 문자열로 표시될 수 있다.
"null"
문제는 여러 서로 다른 불투명한 Origin이 모두 같은 문자열을 사용한다는 것이다.
따라서 event.origin === "null"이라는 조건만으로 메시지 발신자를 신뢰해서는 안 된다.
저자는 실제 iframe의 Window 객체와 event.source를 비교하도록 안내한다.
필요한 경우 MessageChannel을 이용해 별도의 통신 채널을 구성할 수도 있다.
이렇게 하면 실제로 해당 Port를 가진 구성요소만 메시지를 교환하도록 설계할 수 있다.
CSP를 완화할 때의 트레이드오프
저자는 실제 프로젝트에서 모든 외부 요청을 완전히 차단하지 못했던 경험도 설명한다.
일부 미리보기 기능에서는 외부 이미지 CDN에서 이미지를 가져와야 했다.
따라서 CSP에 해당 CDN을 허용하는 규칙을 추가했다.
예를 들어 다음과 같은 설정이다.
img-src https://cdn.example.com
이렇게 하면 해당 CDN의 이미지를 표시할 수 있다.
하지만 동시에 외부 이미지 요청을 통해 일부 정보를 전달할 가능성도 생긴다.
저자는 이 변경으로 보안 경계가 일부 넓어졌다는 사실을 명시한다.
즉 기능을 구현하기 위해 보안 정책을 완화할 수는 있지만, 어떤 위험을 추가로 허용했는지 이해해야 한다는 것이다.
이번 글은 다섯 개의 실제 브라우저 실험을 통해 다음 문제를 각각 검증한다.
- 부모 페이지 데이터 접근
- 부모 페이지 이동과 Popup
- 외부 네트워크 요청
- 잘못된 메시지 처리
- 정상적인 크기 조절 메시지
독립적인 성능 Benchmark를 제공하는 글은 아니다.
대신 AI가 생성한 HTML을 브라우저에서 실행해야 하는 상황에서 Sandbox와 CSP, Origin 분리, 메시지 검증을 어떻게 조합해야 하는지 실제 공격 실험을 통해 설명한다.
AI 기반 웹페이지 생성 도구나 HTML 미리보기 서비스, 사용자 작성 코드를 실행하는 개발 도구에서 참고할 수 있는 보안 설계 사례다.
How Vercel Agent accesses production safely
- 저자: Tom Dale
- 매체: Vercel Engineering
- 게시일: 2026-10-23
- 원문: https://vercel.com/blog/how-vercel-agent-accesses-production-safely
Vercel이 AI 에이전트가 운영 환경의 리소스를 조사하고 변경할 때 필요한 권한을 어떻게 요청하고 제한하는지 설명한 기술 글이다.
이번 글은 새로운 에이전트 모델이나 기능 출시를 소개하는 내용이 아니다.
실제 운영 환경에 접근하는 AI 에이전트를 설계할 때 발생하는 권한 관리 문제를 구체적으로 다룬다.
예를 들어 사용자가 AI 에이전트에게 운영 중인 서비스의 배포 오류를 수정해 달라고 요청했다고 가정해 보자.
에이전트는 먼저 어떤 문제가 발생했는지 조사해야 한다.
다음과 같은 정보를 읽을 수 있다.
- 최근 배포 목록
- 실패한 Build Log
- 마지막으로 성공한 배포
- 배포 환경의 상태
하지만 문제의 원인을 확인한 이후에는 실제 리소스를 변경해야 할 수 있다.
예를 들어 마지막으로 정상 동작했던 버전으로 다시 배포할 수 있다.
이때 필요한 권한이 달라진다.
로그를 읽는 작업에는 읽기 권한이 필요하다.
실제 배포를 생성하는 작업에는 쓰기 권한이 필요하다.
따라서 처음부터 모든 권한을 부여하는 방식과 매 작업마다 사용자 승인을 요구하는 방식 사이에서 균형을 찾아야 한다.
모든 권한을 미리 부여하는 방식의 위험
에이전트에게 운영 환경의 모든 권한을 부여하면 작업을 빠르게 수행할 수 있다.
하지만 잘못된 판단이 실제 서비스의 변경으로 이어질 가능성이 있다.
예를 들어 사용자가 배포 오류 하나를 수정해 달라고 요청했는데 에이전트가 다른 프로젝트의 환경 변수를 변경할 수도 있다.
또한 모델이 외부 문서나 로그에 포함된 악의적인 지시를 신뢰하는 Prompt Injection 문제도 발생할 수 있다.
따라서 모델이 올바르게 동작할 것이라고 기대하는 것만으로는 충분하지 않다.
실제 API 요청을 처리하는 시스템에서도 권한을 검증해야 한다.
Vercel은 이를 위해 OAuth Scope와 별도의 실행 계획 승인 구조를 사용한다.
모든 작업마다 승인을 요구하는 문제
반대 방식도 존재한다.
예를 들어 에이전트가 API를 호출할 때마다 사용자에게 승인을 요청하는 것이다.
이 방식은 각 작업의 실행을 사용자가 확인할 수 있다는 장점이 있다.
하지만 복잡한 작업에서는 수십 번의 승인이 필요할 수 있다.
문제는 사용자가 반복적인 승인 요청에 익숙해질 수 있다는 점이다.
처음에는 각 요청을 자세하게 검토하더라도 시간이 지나면 내용을 충분히 확인하지 않고 승인 버튼을 누를 가능성이 있다.
Vercel은 이를 승인 피로(Approval Fatigue) 문제로 다룬다.
따라서 사용자가 실제로 수행하려는 작업 전체를 확인한 뒤 필요한 권한을 한 번에 승인하도록 하는 구조를 선택했다.
Plan을 권한 승인 단위로 사용
Vercel Agent는 실제 작업을 수행하기 전에 Plan을 작성할 수 있다.
Plan에는 다음 정보가 포함된다.
- 사용자가 원하는 결과
- 수행할 작업
- 작업에 필요한 권한
- 실제 권한을 강제할 방법
예를 들어 배포 오류를 수정하는 작업이라면 다음 두 Plan을 구성할 수 있다.
| Plan | 작업 | 필요한 Scope |
|---|---|---|
| 오류 조사 | 배포와 로그 조회 | read:deployment |
| 이전 버전 재배포 | 새로운 배포 생성 | read-write:deployment |
첫 번째 Plan은 읽기 전용 작업이다.
Vercel Agent는 기본적인 읽기 권한 안에서 조사를 진행할 수 있다.
이후 실제 배포 변경이 필요하다는 사실이 확인되면 두 번째 Plan을 사용자에게 제안한다.
사용자는 어떤 작업이 실행될지와 어떤 권한이 필요한지 함께 확인한다.
그리고 해당 Plan을 승인한다.
이 구조에서는 에이전트가 문제의 원인을 조사한 이후 필요한 권한을 요청한다.
따라서 실제로 필요하지 않은 쓰기 권한을 처음부터 부여할 필요가 줄어든다.
읽기 전용 작업의 범위
Vercel Agent는 현재 read:로 시작하는 등록된 Scope를 읽기 전용 기본 권한으로 취급한다.
예를 들어 다음과 같은 정보에 접근할 수 있다.
- 배포 상태
- Build Log
- 프로젝트 설정의 읽기 가능한 정보
하지만 읽기 전용 작업이라고 해서 항상 위험이 없는 것은 아니다.
운영 로그에는 민감한 정보가 포함될 수 있다.
예를 들어 오류 메시지에 사용자 데이터나 내부 시스템의 구조가 기록돼 있을 수 있다.
따라서 읽기 권한 역시 실제 데이터의 민감도에 따라 관리해야 한다.
Vercel도 자동 승인되는 읽기 작업이 완전히 무해한 작업이라는 의미는 아니라고 설명한다.
사용자의 기존 권한을 초과할 수 없다
이번 설계의 중요한 원칙은 에이전트가 사용자보다 더 높은 권한을 얻을 수 없다는 것이다.
Vercel Agent는 사용자가 OAuth를 통해 승인한 애플리케이션으로 실행된다.
사용자는 자신이 접근할 수 있는 Team 안에서 에이전트를 승인할 수 있다.
하지만 사용자가 특정 프로젝트의 설정을 변경할 권한이 없다면 에이전트도 해당 권한을 사용할 수 없다.
Plan을 승인했다고 해서 원래 존재하지 않았던 권한이 새롭게 생성되는 것은 아니다.
Vercel의 OAuth 서비스는 사용자가 승인한 범위 안에서 더 짧은 수명의 Token을 발급한다.
Token에는 필요한 Scope가 포함된다.
이후 실제 API는 각 요청에서 해당 Scope를 검사한다.
따라서 모델이 권한을 초과하는 작업을 요청하더라도 API에서 거부할 수 있다.
이는 모델의 Prompt에 권한 제한을 작성하는 것과 다르다.
실제 보안 경계가 모델의 지시문이 아니라 API의 권한 검사에 존재하기 때문이다.
Scope의 범위가 실제 작업보다 넓을 수 있다
저자는 이번 구조의 중요한 한계도 설명한다.
예를 들어 사용자가 특정 배포 하나만 다시 배포하는 Plan을 승인했다고 가정해 보자.
실제 Token에는 다음 Scope가 부여될 수 있다.
read-write:deployment
이 Scope는 특정 배포 하나에만 적용되는 권한보다 넓을 수 있다.
즉 Plan에 특정 배포 ID가 적혀 있다고 해서 Token 자체가 반드시 해당 배포 하나로 제한되는 것은 아니다.
Vercel은 이를 명확하게 인정한다.
Plan은 작업의 의도를 표현한다.
Token은 해당 작업에 필요한 API 권한을 제공한다.
하지만 Plan의 자연어 설명과 Token이 강제하는 리소스 범위가 완전히 동일한 것은 아니다.
따라서 실제 안전성은 Runtime의 작업 관리와 API의 Scope 검사, 사용자의 기존 권한 등 여러 계층에 의존한다.
이러한 한계는 AI 에이전트의 권한 관리에서 중요하다.
개발자가 어떤 작업을 수행하려고 했는지와 실제로 어떤 작업까지 수행할 권한을 가지고 있는지는 구분해야 하기 때문이다.
외부 서비스의 권한 분리
Vercel Agent는 Vercel API뿐 아니라 GitHub 같은 외부 서비스에도 접근할 수 있다.
예를 들어 배포 오류를 해결하기 위해 코드 변경 PR을 생성해야 할 수 있다.
이때 Vercel의 배포 권한과 GitHub의 Repository 권한은 서로 다른 것이다.
Vercel은 두 권한을 하나의 범용 Token으로 관리하지 않는다.
외부 제공업체별 인증 정보와 권한을 사용한다.
예를 들어 GitHub PR 생성 권한은 특정 Repository와 연결된다.
사용자가 acme/storefront에 PR을 생성하도록 승인했다면 해당 승인이 다른 Repository인 acme/payments에 자동으로 적용되지 않는다.
다른 Repository에서 작업해야 한다면 별도의 승인이 필요하다.
이러한 구조는 서로 다른 외부 서비스와 리소스 사이의 권한을 분리하는 데 도움이 된다.
Scope Registry를 단일 원천으로 사용
이번 글에서 흥미로운 문제 가운데 하나는 에이전트가 요청하는 Scope 목록과 실제 OAuth 서버가 지원하는 Scope 목록이 달라질 수 있다는 것이다.
예를 들어 특정 권한의 이름이 변경됐다고 가정해 보자.
에이전트의 코드에는 이전 Scope 이름이 남아 있다.
하지만 OAuth 서버에서는 새로운 Scope 이름만 사용한다.
이 경우 에이전트가 실제로 존재하지 않는 권한을 요청할 수 있다.
Vercel은 초기 구현에서 별도의 권한 목록을 유지했다.
하지만 두 목록이 서로 달라질 수 있다는 문제가 있었다.
이를 해결하기 위해 OAuth 서비스가 사용하는 Scope Registry를 에이전트에서도 직접 사용하도록 변경했다.
Scope Registry는 사용 가능한 권한의 정의를 관리하는 곳이다.
에이전트는 Session이 시작될 때 해당 목록을 가져온다.
이후 Plan을 생성할 때 다음 작업을 수행한다.
- 요청한 Scope의 이름을 정규화한다.
- 실제 존재하는 Scope인지 확인한다.
- 지원하지 않는 Scope를 거부한다.
- 필요한 경우 올바른 Scope를 다시 제안한다.
이렇게 하면 잘못된 권한 이름이 사용자에게 승인 요청으로 표시되기 전에 문제를 발견할 수 있다.
Scope Registry 캐싱의 트레이드오프
매번 OAuth 서버에서 Scope 목록을 가져오면 추가적인 네트워크 요청이 발생한다.
또한 OAuth 서버를 사용할 수 없는 상황에서는 Plan을 검증하기 어려울 수 있다.
Vercel은 이를 해결하기 위해 Scope 목록을 메모리에 캐싱한다.
이후 저장된 Plan을 다시 읽을 때도 해당 캐시를 이용한다.
하지만 캐시에는 한계가 있다.
Scope Registry가 변경되더라도 캐시가 갱신되기 전까지 이전 목록을 사용할 수 있기 때문이다.
따라서 권한 목록의 실시간 정확성과 캐시를 통한 응답 속도 사이에 트레이드오프가 존재한다.
Vercel은 이러한 상황을 인정하고 캐시 갱신 과정과 실제 Token 발급 단계의 권한 검증을 분리한다.
승인 이후 작업 재개
AI 에이전트의 작업은 한 번의 HTTP 요청 안에서 모두 완료되지 않을 수 있다.
예를 들어 사용자가 Plan을 승인하기 전에 대화 연결이 종료될 수 있다.
또는 승인을 받은 뒤 에이전트가 잠시 중단됐다가 다시 실행될 수도 있다.
이때 단순히 이전 Token이 남아 있다는 이유만으로 작업을 계속 수행해서는 안 된다.
사용자가 그사이에 Plan을 중단했을 가능성이 있기 때문이다.
Vercel은 Plan을 지속 저장소에 보관한다.
여기에는 다음 정보가 포함된다.
- 수행할 작업
- 요청한 권한
- 승인한 사용자
- 승인 시점
- 현재 상태
- 실행 진행 상황
에이전트가 작업을 다시 시작하면 먼저 Plan의 상태를 확인한다.
Plan이 여전히 활성 상태이고 필요한 승인이 존재한다면 해당 권한으로 작업을 이어갈 수 있다.
반면 Plan이 중단됐다면 Token의 만료 시간이 남아 있어도 실행을 허용하지 않는다.
이는 Token의 유효 기간과 사용자의 현재 작업 승인 상태를 별도로 검증하는 방식이다.
승인과 중단이 동시에 발생하는 경우
분산 시스템에서는 여러 이벤트가 거의 동시에 발생할 수 있다.
예를 들어 사용자가 Plan을 승인하는 순간 다른 요청에서는 해당 Plan을 중단하려고 할 수 있다.
이러한 상황에서는 실행 순서에 따라 잘못된 상태가 발생할 수 있다.
Vercel은 Plan의 상태 전환을 검사해 이미 중단된 Plan이 활성 상태로 변경되지 않도록 처리한다.
또한 실제 작업을 시작하기 전에 현재 Plan이 활성 상태인지 다시 확인한다.
이러한 방식은 승인과 중단 요청 사이의 경쟁 상태(Race Condition)를 줄이기 위한 것이다.
하지만 어떤 시스템에서도 외부 API 호출을 이미 시작한 이후에 발생한 모든 작업을 되돌릴 수 있는 것은 아니다.
예를 들어 배포 API 요청이 이미 처리됐다면 Plan을 중단했다고 해서 해당 배포가 자동으로 취소되는 것은 아닐 수 있다.
따라서 승인 취소와 실제 실행 중단, 이미 완료된 작업의 Rollback은 서로 구분해야 한다.
설계의 한계
이번 구조는 AI 에이전트의 권한을 여러 계층에서 검증하도록 한다.
하지만 모든 보안 문제가 해결됐다는 의미는 아니다.
예를 들어 다음 위험은 여전히 존재할 수 있다.
- 읽기 전용 로그에 민감한 데이터 포함
- 외부 문서를 통한 Prompt Injection
- 특정 Scope가 실제 작업보다 넓은 권한 제공
- 외부 API 호출의 중복 실행
- 이미 실행된 변경의 Rollback 문제
- 장시간 실행되는 작업의 상태 불일치
따라서 실제 서비스에서는 권한 관리뿐 아니라 실행 환경 격리와 로그 보호, 감사 기록, 오류 복구 정책도 함께 고려해야 한다.
이번 글은 AI 에이전트의 안전성을 모델의 지시문에만 의존하지 않고 승인 가능한 실행 계획과 OAuth Scope, 지속 저장소, API 권한 검사로 나누어 설계한 사례다.
특히 사용자가 승인한 작업의 의도와 실제 API가 허용하는 권한 사이의 차이를 명확하게 설명한다는 점에서 참고할 만한 기술 자료다.