Cloudflare가 에이전트에게 기억하는 브라우저를 제공하다: TypeScript로 작은 세션 키퍼 구축하기
요약
Cloudflare가 에이전트에게 세션과 쿠키를 유지하는 지속적인 브라우저 도구를 제공하며, 이는 에이전트의 기능을 크게 향상시킵니다. 이 글은 TypeScript를 사용하여 이러한 '세션 키퍼'를 직접 구축하는 방법을 단계별로 안내합니다.
핵심 포인트
- 지속적 브라우저는 탭, 쿠키, 로그인 세션을 유지하여 에이전트 경험을 개선합니다.
- 에이전트는 세션 만료나 충돌 시 재시작되었음을 감지하고 이를 처리해야 합니다.
- TypeScript를 활용해 가짜 브라우저와 시간을 제어하는 방식으로 세션 키퍼를 구현할 수 있습니다.
대부분의 브라우저 에이전트는 매 턴(turn)마다 제로 상태에서 시작합니다.
새로운 브라우저.
쿠키 없음.
탭 없음.
다시 로그인해야 합니다.
이는 느리지만 정직합니다. 에이전트는 실제로 로그아웃된 상태일 때 여전히 로그인되어 있다고 생각하지 않습니다.
지속적인(Persistent) 브라우저는 이를 변경합니다.
2026년 10월 13일, Cloudflare는 Agents SDK에서 새로운 지속적 브라우저 도구를 출시했습니다. 변경 로그에는 다음과 같이 나와 있습니다:
에이전트는 웹사이트를 열고, 페이지를 검사하며, 브라우저 세션이 살아있는 동안 탭, 쿠키 및 로그인 세션을 그대로 유지한 채 돌아올 수 있습니다.
마지막 구절이 제가 계속 생각하는 부분입니다.
브라우저 세션이 살아있는 동안.
같은 변경 로그는 세션이 그렇지 않을 때 무슨 일이 발생하는지 명시합니다:
기본 유휴 시간 초과(idle timeout)는 10분입니다. 세션이 만료되거나 충돌하면, 도구는 새로운 브라우저를 생성하고
restarted: true를 보고합니다. 이전 탭과 로그인 상태는 사라집니다.
그리고 매우 중요한 또 다른 문장:
각 Pi 대화는 기본적으로 자체 활성 탭을 갖습니다. 쿠키와 로그인은 브라우저 내에서 공유됩니다.
이는 Meta가 개발자를 위한 Muse Wonder 출시 게시물 (2026년 10월 12일)을 통해
이것은 제가 구상한 아이디어를 담은 작은 모델입니다. Cloudflare의 브라우저 도구가 내부적으로 작동하는 방식과는 다릅니다. 아래 제시된 모든 사이트, 타이밍, 그리고 권한 부여는 예시 입력값일 뿐입니다. 데모에 사용된 10분 유휴 시간 제한은 Cloudflare가 문서화한 기본값을 반영하지만, 가짜 시계는 제가 만든 것입니다.
목차
- 문제점 (The Problem)
- 단계 1: 가짜 브라우저와 시계 (A Fake Browser and a Clock)
- 단계 2: 매번 브라우저 보장하기 (Ensure the Browser Every Turn)
- 단계 3: 대화별 탭, 브라우저별 로그인
- 단계 4: 공유 로그인 범위 지정하기 (Scope the Shared Login)
- 단계 5: 유휴 세션 정리하기 (Sweep Idle Sessions)
- 단계 6: 실행하기 (Run It)
- 문제가 발생하는 지점 (Where It Breaks Down)
- 더 큰 아이디어 (The Bigger Idea)
1. 문제점 (The Problem)
지속적인 브라우저는 에이전트에게 이전에 없던 세 가지 것을 제공합니다.
다시 돌아올 수 있는 탭들.
턴(turn)을 넘어서도 유지되는 쿠키들.
한 번만 완료하면 되는 로그인들.
또한, 이는 에이전트가 잘못될 수 있는 새로운 방법 세 가지를 제공합니다.
브라우저가 재시작된 후에도 로그인이 여전히 존재한다고 생각할 수 있습니다.
다른 대화에서 생성된 로그인을 사용할 수 있습니다.
누구도 필요하지 않은 지 오래 지속되는 로그인 상태의 브라우저를 남겨둘 수 있습니다.
이것들 중 어느 것도 브라우저 버그가 아닙니다. 이것들은 계획상의 버그입니다.
브라우저는 재시작되었다고 알려줄 수 있습니다. 여러분의 루프는 그것을 감지해야 합니다.
2. 단계 1: 가짜 브라우저와 시계 (A Fake Browser and a Clock)
우리는 의도적으로 종료할 수 있는 브라우저와 빠르게 시간을 건너뛸 수 있는 시계가 필요합니다.
다음 TypeScript 블록을 순서대로 session.ts로 저장하세요.
// tiny-session-keeper: an agent can trust a persistent browser session.
// Fake browser, fake clock, fake sites. No API key. No network.
...
cookies는 사이트(site)를 키로 사용합니다. tabs는 대화(conversation)를 키로 사용합니다.
이 분리는 의도적으로 변경 로그에서 가져온 것입니다. 대화당 하나의 활성 탭, 브라우저당 하나의 공유 쿠키 저장소입니다.
lastUsed만이 유휴 시간 초과가 신경 쓰는 유일한 부분입니다.
3. 단계 2: 매번 브라우저 보장하기 (Ensure the Browser Every Turn)
이 파일에서 가장 중요한 메서드입니다.
// Every login, with who did it and when.
type Login = { site: string; by: string; at: number };
...
모든 턴은 ensure()로 시작합니다.
만약 이전 브라우저가 살아 있고 유휴(idle) 창 안에 있다면, 그것을 돌려받고 restarted는 false입니다.
그렇지 않다면, 새로운 브라우저를 받고, restarted: true와 어떤 것이 정확히 사라졌는지 알려주는 lost 목록을 받습니다. 여기에는 어느 로그인과 어느 탭이 포함됩니다.
그 목록이 핵심입니다. "재시작됨(Restarted)"은 불리언 값입니다. "청구서 로그인과 청구서 탭을 잃었습니다"는 플래너가 조치할 수 있는 무언가입니다.
여기서, 키퍼(keeper) 역시 자체 로그인 기록을 지운다는 점에 주목하세요. 죽은 브라우저에 대한 오래된 증거는 증거가 아닙니다.
4. 단계 3: 대화별 탭, 브라우저별 로그인
작은 도우미 두 개입니다.
tab(conversation: string, url?: string): Tab {
const b = this.browser!;
const existing = b.tabs.get(conversation);
...
tab()은 새로운 곳으로 이동하지 않는 한 대화의 기존 탭을 반환합니다.
logIn()은 쿠키를 설정하고 누가, 언제 했는지 기록합니다. 실제 시스템에서 이 단계는 사람이 로그인 또는 MFA(Multi-Factor Authentication)를 완료하는 것입니다. Cloudflare의 브라우저 문서에서는 정확히 그 작업을 위해 실행을 일시 중지하고, 동일한 탭과 쿠키로 재개하는 방법을 설명합니다.
기록(record)이 사람들이 건너뛰는 부분입니다. 이것 없이는 다음 질문에 답할 수 없습니다.
5. 단계 4: 공유 로그인 범위 지정
여기에 "쿠키와 로그인이 브라우저 내에서 공유된다"라는 위험이 숨어 있습니다.
만약 청구서 대화(billing conversation)가 로그인하면, 리서치 대화(research conversation)도 이제 로그인된 상태입니다. 같은 브라우저. 같은 쿠키 저장소입니다.
// Cookies are shared inside the browser. Permission should not be.
mayUseLogin(conversation: string, site: string): string | null {
if (!this.browser!.cookies.has(site)) return `not logged in to ${site}`;
...
두 가지 확인 절차입니다.
- 아예 로그인이 되어 있는가?
- 이 대화가 그것을 사용할 권한이 있는가?
권한은 명시적인 쌍(pair)으로 부여됩니다: 대화와 사이트. 데모는 billing에게 billing.example에 대한 접근만 허용하고 그 외에는 아무것도 허용하지 않습니다.
확인에 실패하면, 이유가 누가 로그인을 소유하는지 알려줍니다. 이것은 조용한 권한 누출을 로그의 한 줄로 바꿉니다.
Shared cookies are an implementation detail. Shared permission should be a decision.
6. Step 5: 유휴 세션 정리(Sweep Idle Sessions)
사용하지 않는 로그인된 브라우저는 공짜가 아닙니다. 데이터센터에 있는 활성 자격 증명입니다.
// 스케줄링 작업에서 실행합니다. 사용하지 않는 브라우저를 회수합니다.
sweep(): string | null {
const b = this.browser;
...
sweep()은 설정된 시간보다 오랫동안 유휴 상태인 모든 브라우저를 닫습니다. Cloudflare의 문서에서도 같은 이유로 스케줄링 작업을 통해 connector.sweep()을 호출할 것을 제안합니다.
crash()는 데모가 의도적으로 브라우저를 종료하기 위해 존재하는 기능일 뿐입니다.
다음은 턴 러너와 데모들입니다:
type Step = {
minute: number;
conversation: string;
...
중요한 규칙은 runTurn에 있습니다.
만약 브라우저가 재시작되었고, 로그인 정보가 누락되었으며, 이 단계는 로그인 상태를 가정하고 계획되었다면, 에이전트는 멈추고 사람에게 도움을 요청합니다. 사이트가 유용한 무언가를 보여주기를 바라며 로그아웃된 페이지에 대해 부수 효과(side effect)를 시도하지 않습니다.
const grants: Grant[] = [{ conversation:
스윕(sweep)은 12분 동안 유휴 상태인 `browser-1`을 종료합니다. 3분 후 `billing`이 환불 제출을 요청합니다. `ensure()`는 `restarted: true`와 손실된 로그인 목록을 담아 `browser-2`를 반환합니다. 그 결과, 로그아웃 페이지로 전송되는 대신 환불은 중단됩니다.
사람이 다시 로그인합니다. 그러면 새로운 브라우저에서 환불이 진행됩니다.
그런 다음 시뮬레이션된 충돌이 발생하고, 한 턴 후에 동일한 현상이 발생합니다.
이것이 전체 기능입니다.
## 8. 문제점 (Where It Breaks Down)
**시간은 가짜입니다.** 실제 유휴 시간 초과는 제공업체, 최대 절전 모드(hibernation), 네트워크에 따라 다릅니다. 10분이라는 값은 약속이 아니라 확인을 위한 기본값으로 취급해야 합니다.
**재시작 감지는 도구가 알려주는 것에 달려 있습니다.** Cloudflare는 `restarted: true`를 보고합니다. 다른 브라우저 도구는 그렇지 않을 수 있습니다. 만약 귀하의 도구가 이를 제공하지 않는다면, 저장한 세션 ID와 반환받은 세션 ID를 비교해야 합니다.
**권한(Grants)은 정적입니다.** 실제 시스템에는 만료되는 권한, 작업별 권한(인보이스는 읽을 수 있지만 카드를 변경할 수는 없는 등), 그리고 사람이 일회성 사용을 승인할 수 있는 방법이 필요합니다.
**쿠키가 유일한 상태 정보는 아닙니다.** 로컬 스토리지, 열려 있는 양식, 미완료 결제 과정, 다운로드 모두 재시작 시 사라집니다. 여기서 `lost` 목록은 로그인과 탭에 대해서만 알고 있습니다.
**
저는 '에이전트가 브라우저를 사용할 수 있는가'라는 질문은 이미 오래된 질문이라고 생각합니다. 흥미로운 질문은 에이전트가 턴(turn) 사이에 브라우저에 대해 무엇을 믿고 있는지, 그리고 그 믿음이 틀렸음을 얼마나 빨리 알아차리는지입니다.
Cloudflare가 도구 결과(tool result) 안에 `restarted: true`를 바로 넣는 것은 올바른 직관입니다. 이는 실패를 명확하게 만듭니다.
나머지는 우리에게 달렸습니다.
**기억하는 브라우저는 기능(feature)이고, 확인하는 에이전트가 제품(product)입니다.**
## Roster 사용해 보기
저는 이 아이디어를 바탕으로 Roster를 구축하고 있습니다: 실제 책임, 도구, 메모리, 스케줄 및 컴퓨터 접근 권한을 가진 AI 직원들. 그들은 자신만의 영역을 유지하며, 어떤 로그인 정보가 자신들의 것인지 알고, 상황이 변할 때 추측하는 대신 인간에게 도움을 요청합니다.
만약 똑같은 후속 조치(follow-ups), 인계(handoffs), 그리고 로그인 루프가 매주 시간을 잡아먹고 있다면, 그것들을 AI 직원에게 맡기세요.
[Roster 사용해 보기 →](https://get-roster.com)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기