LLM이 만드는 확장 기능, 어디까지 권한을 줄 것인가

목차

사용자가 “저장한 글에 내 기준으로 태그를 붙여 줘”라고 말하면 LLM은 분류 코드를 만들 수 있다. 이 기능을 제품에 넣는 개발자에게는 그 코드가 읽고 바꿀 수 있는 범위를 정하는 일이 남는다. 글 하나를 분류하는 데 전체 보관함과 임의의 외부 통신까지 허용할 필요는 없다.

Jeremy Morrell의 Extensible Software in the age of LLMs는 LLM으로 확장을 작성하기 쉬워진 만큼, 웹 제품도 안정된 핵심 위에 사용자별 기능을 얹을 수 있다는 가설을 제시한다. 저자가 강조하는 실행 조건에는 비용, 시작 지연, 자원 제한, 격리와 통제된 외부 작용이 포함된다. 넓은 API 접근을 사후에 제한하기보다 필요한 동작만 호출할 권한을 주자는 것이 이 글에서 살펴볼 관점이다.

원문은 2026년 8월 18일 게시됐으며, 저자는 Cloudflare 재직 사실을 밝히고 Dynamic Workers를 유력한 구현 수단으로 소개한다. 특정 플랫폼의 보안이나 성능을 독립적으로 비교한 연구는 아니다. 아래 읽기 목록 사례와 권한 설계는 원문에서 출발해 독립적으로 구성한 가상 예시다.

분류 규칙을 바꾸는 데 보관함 관리 권한은 필요하지 않다

읽기 목록 서비스에 글 하나가 추가됐다고 하자. 사용자는 “설치 절차를 설명하는 글에는 실습, 설계 선택을 설명하는 글에는 설계 태그를 붙인다”는 규칙을 원한다. 다른 사용자는 전혀 다른 분류 기준을 원할 수 있다.

제품의 핵심 기능은 글 저장, 사용자 인증, 보관함 접근 제어를 맡는다. 사용자 확장은 전달받은 글을 읽고 태그를 제안한다. 무엇을 분류할지는 유연하게 바꿀 수 있지만, 누구의 글에 접근할지는 제품이 결정한다.

가장 작은 계약은 다음과 같다. 아래는 실행 코드가 아닌 입출력 명세다.

제품이 확장에 전달:
  사용자가 분류를 요청한 글 하나의 제목과 본문

확장이 제품에 반환:
  제안하는 태그 이름 목록

제품이 검사하고 적용:
  태그 형식과 개수, 현재 사용자의 수정 권한
  처음 요청한 글에만 태그 반영

이 구조에서는 확장이 다른 글의 식별자를 지정할 이유가 없다. 제품은 실행을 시작할 때 정한 대상과 사용자 정보를 유지한다. 확장이 반환한 값으로 소유자나 적용 대상을 다시 결정하지 않는다. 태그 값도 신뢰할 수 없는 입력이므로 형식과 크기를 검사한 뒤, 화면에서는 텍스트로 안전하게 표시한다.

같은 분류 규칙을 전체 보관함 관리 API에 연결하면 이야기가 달라진다. 버그가 난 코드가 다른 글을 수정하거나 삭제할 수 있다. 분류 결과가 틀리는 문제에 데이터 손상 가능성까지 추가한 셈이다. 필요한 출력이 태그 목록뿐이라면, 먼저 입력과 결과만 주고받는 방식으로 요구를 충족할 수 있는지 살펴볼 만하다.

필요한 동작을 건네는 capability

이번에는 사용자가 “이 글의 태그를 확인하고, 허용된 태그를 하나 추가해 달라”고 요청한다고 하자. 현재 상태를 확인하고 변경해야 하므로 처음 입력만으로는 부족할 수 있다.

이때 capability는 특정 자원에 대해 특정 동작을 수행할 수 있게 건네는 호출 권한으로 이해할 수 있다. 예를 들어 제품이 선택한 글에만 연결된 addTag(tag)를 제공한다. 확장은 태그 값을 전달하지만 계정, 글 식별자, API 주소를 선택하지 않는다. 제품이 관리하는 쪽에서 현재 권한과 태그 값을 검사하고 변경을 수행한다.

확장에 주는 것확장이 선택할 수 있는 범위제품이 확인해야 할 부분
보관함 전체 API의 인증 정보해당 인증 정보가 허용하는 자원과 동작비밀 노출과 넓은 접근 범위
범용 요청 프록시프록시가 허용한 주소·메서드·입력우회 가능한 경로와 실제 대상 권한
선택된 글의 addTag(tag)그 글에 추가할 태그 값호출자의 현재 권한, 태그 규칙, 호출량

마지막 형태에서는 비밀을 확장에 줄 필요가 없다. 그러나 메서드 이름만 좁게 붙여 놓는 것으로 충분하지는 않다. addTag가 내부에서 다른 글의 식별자를 받거나, 호출자가 정한 계정으로 동작한다면 권한 범위는 다시 넓어진다. 실제 구현이 실행 대상과 사용자 범위를 고정해야 한다.

허용된 메서드도 반복 호출할 수 있다. 동일 태그를 반복 추가했을 때 중복이 생기는지, 호출량을 넘으면 어떻게 거절하는지까지 제품의 계약에 포함해야 한다. 확장이 오류로 종료됐더라도 앞선 변경은 이미 적용됐을 수 있으므로, 실패 보고만 보고 아무 변화도 없었다고 가정해서는 안 된다.

실행 격리와 데이터 권한은 다른 질문이다

확장을 별도 실행 공간에 넣는 샌드박스는 호스트의 파일이나 실행 상태에 함부로 접근하지 못하도록 제한하는 수단이다. 그 안에 보관함 전체를 읽는 기능을 제공했다면, 격리가 잘 동작해도 그 기능으로 읽는 것은 허용된 행동이다.

따라서 다음 세 질문을 각각 확인해야 한다.

질문읽기 목록 예시에서 확인할 것
실행 공간을 벗어날 수 있는가호스트 파일·환경·다른 실행 상태 접근 제한
허용된 기능으로 너무 많은 일을 할 수 있는가선택된 글 이외의 읽기·변경 가능성
읽은 정보가 어디로 나갈 수 있는가네트워크, 로그, 결과 저장소와 화면의 독자

가령 글 하나를 읽는 기능만 주었어도, 임의 주소로 HTTP 요청을 보낼 수 있다면 본문을 다른 곳으로 전송할 경로가 생긴다. 반대로 일반 네트워크를 막았더라도 공유 채널에 메시지를 쓰는 기능을 주면 그 기능이 외부 전달 경로가 될 수 있다. 출력 로그를 다른 사용자가 읽을 수 있는 경우도 함께 살펴야 한다. 권한은 메서드 개수보다 읽은 데이터가 도달할 수 있는 곳까지 따라가며 판단해야 한다.

함수 몇 개를 객체에 담아 전달하는 JavaScript 코드 자체는 보안 경계가 아니다. 같은 프로세스에서 실행하면서 다른 전역 API나 파일 접근 경로를 남겨 두었다면, 전달한 객체 밖의 동작도 가능하다. 함수의 형태와 실제 격리 수단을 함께 설계해야 하는 이유다.

Dynamic Workers에서 대응하는 설정 읽기

Cloudflare 공식 문서의 Dynamic Workers는 실행할 코드를 동적으로 구성하면서 사용할 자원과 기능을 bindings로 전달하도록 설명한다. 여기서 binding은 실행 코드에 연결해 주는 자원이나 서비스의 참조다. 제품이 관리하는 메서드 안에서 호출을 검사하거나 거절할 수 있다. Bindings 문서

일반적인 외부 통신을 막는 설정은 globalOutbound: null이다. 공식 문서상 이 설정에서는 해당 Worker의 fetch()와 connect() 호출이 예외를 던진다. 그래도 별도로 전달한 binding을 통한 자원 접근은 가능하다. 따라서 이 설정을 “모든 데이터 유출 경로가 제거됐다”라고 해석하면 안 된다. 앞의 예시처럼 전달한 기능의 구현과 결과의 수신자도 확인해야 한다. 외부 통신 제어 문서

이 글에서는 해당 서비스를 배포하거나 격리 공격을 시험하지 않았다. 설정의 의미는 2026년 11월 9일 확인한 공식 문서를 기준으로 설명한다. 실제 채택 전에는 사용하는 실행 환경에서 허용한 호출과 금지한 호출을 각각 시험해야 한다. 특히 시간 초과 응답을 받는 것과 실행 자체가 중단되는 것은 별개이므로, 호출자가 기다리기를 멈춘 뒤에도 작업이 계속되는지 확인해야 한다.

확장 API는 다음 버전에도 남는 약속이다

분류 확장이 늘어나면 처음 만든 addTag의 의미를 바꾸기도 어려워진다. 예를 들어 없던 태그를 자동 생성하던 메서드를 기존 태그만 허용하도록 바꾸면 이전 확장이 실패할 수 있다. 사용자별 자유를 제공하는 만큼 제품에는 버전과 호환성을 관리할 책임이 생긴다.

처음에는 자동 반영 대신 태그를 제안하는 확장만 제공할 수 있다. 실제 사용에서 자동 변경이 필요해졌을 때 좁은 쓰기 기능을 추가하면 된다. 읽기 목록 분류가 단순한 설정으로 충분하다면 임의 코드 실행까지 열지 않는 선택도 가능하다.

확장 지점을 정할 때는 요청 하나를 써 놓고, 그 요청에 필요한 입력과 결과부터 나눠 보자. “이 글을 분류한다”에 필요한 것이 본문과 태그 목록이라면 거기서 시작한다. 더 넓은 권한을 추가할 때마다 어떤 사용자 요구 때문에 필요한지, 그 권한으로 원치 않는 결과도 만들 수 있는지를 함께 설명할 수 있어야 한다.