
이용자의 지갑을 망가뜨리지 않는 AI 플러그인 설계 ― 토큰 비용은 누구에게 귀속되는가
요약
AI 플러그인 설계 시 발생하는 토큰 비용 귀속 문제와 이용자 이탈 방지를 위한 설계 전략을 다룹니다. 이용자에게 API 키를 요구할 것인지, 제작자가 비용을 부담할 것인지에 따른 장단점과 비용 통제 방안을 제시합니다.
핵심 포인트
- AI 서비스의 비용 구조는 월정액에서 토큰 기반 종량제로 변화하는 추세임
- 이용자는 예상치 못한 추가 지출에 대해 강한 거부감을 느낌
- API 키 직접 입력 방식은 비용 부담은 낮으나 진입 장벽이 높음
- 제작자 부담 방식은 사용자 경험은 좋으나 적자 리스크가 존재함
AI 채팅 봇 플러그인을 공개했을 때, 가장 많았던 이탈 지점은 어디였을까요? 기능 설명도, 설정 화면도 아닌, "API 키를 취득하여 결제를 설정해 주세요"라는 문구 앞이었습니다. 설치는 됩니다. 하지만 모르는 회사에 카드를 등록하고, 비용이 얼마나 들지 모르는 수도꼭지를 자신 쪽으로 여는 그 직전에서 사람들이 사라집니다. 설치 수와 실제로 채팅이 작동한 수의 차이를 보며, 그 골짜기의 깊이를 깨달았습니다.
토큰 (Token) 비용이라는 보이지 않는 수도꼭지는 제작자가 아니라 이용자 쪽에 열립니다. 이 글은 그 전제를 바탕으로, 이용자의 지갑을 망가뜨리지 않기 위해 설계 단계에서 할 수 있는 일들을 정리한 것입니다. AI를 사용하는 쪽의 지갑(본인이 지불)과, 기능을 탑재하는 쪽의 지갑(이용자가 지불)으로 나누어, 후자의 설계를 구현 측면에서 작성하겠습니다.
가격이나 플랜은 변화가 빠른 영역입니다. 아래 내용은 확인한 시점의 기준이므로, 최신 정보는 각사의 공식 요금 페이지에서 확인하시기 바랍니다.
- 사용하는 쪽: Claude의 구독 (실기 및 공식 현재 가격 확인), Codex CLI
- 탑재하는 쪽의 루트: OpenRouter, 각사의 API
- 대상: 직접 제작한 WordPress 플러그인 (AI 채팅 봇)
다음 코드는 설명을 위한 최소한의 예시입니다. 단가표, 통화 표시, 프로바이더 (Provider) 분기, nonce 검증은 구현 측에서 채운다는 전제로 읽어주세요.
얼마 전까지만 해도 AI 도구는 월정액 고정이 당연했습니다. 하지만 그 전제가 무너지기 시작했습니다. GitHub Copilot은 2026년 6월 1일에 종량제 (Pay-as-you-go)로 전환되며, 요청 수가 아니라 입력·출력·캐시의 토큰 (Token) 양에 따라 크레딧을 소비하는 형태가 되었습니다. 배경은 에이전트 (Agent)화로 인해 계산 비용이 불어나면서, 월정액 고정 방식으로는 감당할 수 없게 되었기 때문입니다.
여기서 보이는 일반 원칙은 단순합니다. AI의 계산에는 실비가 들며, 그것은 반드시 어딘가의 지갑으로 귀속됩니다. 월정액 고정은 제공자가 일단 부담하여 평평하게 보여주었을 뿐이며, 종량제는 수도꼭지를 이용자의 손으로 되돌려 놓은 것입니다. 개인 개발에서도 마찬가지로, 내가 낼 것인지 이용자가 낼 것인지 모호한 상태로 둘 수는 없습니다.
본인이 지불하는 쪽은 고삐를 스스로 쥘 수 있습니다. 정액과 종량을 용도에 따라 나누거나, 자율 주행 범위를 제한하거나, 매번 발생하는 비용에 대한 전제를 가볍게 만드는 식입니다. 사용법에 따라 증감이 결정되므로 아직은 통제하기 쉽습니다.
어려운 것은 탑재하는 쪽이었습니다. AI 기능을 제품에 탑재하면 지불하는 것은 이용자이고, 설계하는 것은 자신입니다. 자신의 지갑이라면 "비싸니까 쓰지 말자"라는 브레이크가 자연스럽게 작동하지만, 이용자의 지갑에는 자신의 브레이크가 통하지 않습니다. 서두의 이탈은 그 상징이었습니다. 직접 만든 Rapls AI Chatbot을 공개하며 처음으로 이 비대칭성을 보았습니다.
WordPress 플러그인은 "설치하면 무료로 작동한다"는 감각이 배어 있습니다. 거기에 "사용할 때마다 외부 AI에 종량제로 결제"라는 조건을 내걸면 그 전제와 정면 충돌합니다. 이용자가 경계하는 것은 가격의 높음보다, 예상치 못한 종류의 지출이라는 측면이 큽니다. 처음에 지출의 성격을 솔직하게 전달하고, 우선 무료로 체험할 수 있는 입구를 마련하여 놀라게 하지 않는 것이 효과적입니다.
설계의 최상류에는 갈림길이 있습니다. 이용자에게 키 (Key)를 가져오게 할 것인가 (제작자는 지불하지 않지만, 첫 절차가 장벽이 됨) 아니면 제작자가 모아서 지불하고 정액으로 제공할 것인가 (경험은 매끄럽지만, 토큰 비용과 폭주 리스크를 제작자가 짊어짐)입니다.
후자의 경우, 정액으로 받는 금액은 정해져 있는데 나가는 토큰 비용에는 상한이 없어, 헤비 유저일수록 적자가 불어나는 구조가 될 수 있습니다. 개인이 감당하기에는 무겁습니다. 그래서 저는 이용자가 키를 가져오는 형태를 기본으로 하되, 그 첫 단계의 번거로움을 최대한 가볍게 만드는 방향으로 설계했습니다. 그 내용은 다음과 같습니다.
먼저, 요청 1회를 처리하는 전체 흐름을 골격으로 둡니다. 어떤 가드 (Guard)를 호출하기 전에 배치하고, 어떤 것을 호출한 후에 배치하느냐에 따라 효과가 결정됩니다.
function rapls_chat_handle( $user_id, $message ) {
// 1. 호출 전 제어 (횟수·간격)
$gate = rapls_chat_check_limits( $user_id );
...
이 방법이 가장 효과적이었습니다. 카드 등록을 하기 전에, 채팅이 한 번 작동하는 것을 직접 보게 하는 것입니다. OpenRouter의 무료 체험 범위를 사용하여, 키 발행과 카드 등록 사이의 단계를 처음에는 건너뛸 수 있도록 했습니다. 작동하는 것을 확인한 후에 본격적인 이용을 위한 키를 고민하게 하면 됩니다.
무료 범위에는 횟수와 속도 제한이 있으며, 사양 또한 제공 측의 사정에 따라 변합니다. 무료 동선은 "한 번 작동시켜 테스트하는" 입구로 한정하고, 계속 이용하려면 자신의 키를 사용해야 한다는 경로를 처음부터 보여주어야 합니다. 무료 범위에만 의존하는 설계는 그 범위가 변경되는 날 제품 전체가 멈추게 됩니다.
일일 상한선(Daily ceiling)으로 총량을 제한하고, 최단 간격(Minimum interval)을 설정하여 연타나 루프를 차단합니다. 특히 후자가 중요한데, 사람이 보고 있지 않은 시간에 호출이 무한히 이어지는 사고는 간격 가드(Interval guard) 하나만으로도 상당 부분 방지할 수 있습니다.
function rapls_chat_check_limits( $user_id, $daily = 100, $min_interval = 2 ) {
$today = 'rapls_chat_count_' . $user_id . '_' . gmdate( 'Ymd' );
$last = 'rapls_chat_last_' . $user_id;
...
악의적인 의도뿐만 아니라, 단순한 설정 실수나 에러 루프(Error loop)로도 폭주는 발생합니다. 이용자를 믿느냐의 문제가 아니라, 사고는 선의로도 발생하기 때문에 설계로 막아야 합니다.
간단한 용건에 최상위 모델을 사용하는 것은 과잉입니다. 이용자의 명시적 지정(Explicit specification)을 최우선으로 하고, 지정이 없다면 용건의 무게에 따라 배분하며, 답변이 약할 때만 딱 한 번 상위 모델로 다시 올립니다.
function rapls_chat_pick_model( $user_id, $message ) {
$chosen = get_user_meta( $user_id, 'rapls_chat_model', true );
if ( $chosen ) {
...
"저렴하게 받고, 답변이 약하면 한 번만 올린다"를 구현하면 다음과 같습니다. 명확한 상향 요구는 처음부터 상위 모델로, 그 외에는 저렴한 모델로 처리하되 답변이 부족할 때만 다시 올립니다.
function rapls_chat_answer( $user_id, $message, $context ) {
if ( preg_match( '/もっと詳しく|詳細に|長めに|きちんと/u', $message ) ) {
return rapls_chat_call_api( 'strong-model', $message, $context );
...
다시 올리는 과정이 실행되면, 해당 용건은 저렴한 모델과 상위 모델을 두 번 호출하게 되어 1회분 비용이 두 배가 될 수 있습니다. 재호출은 단 한 번으로 제한하며, 이 두 번의 호출도 횟수 상한선(Ceiling) 내에서 계산합니다. 상향 조건을 다소 까다롭게 설정하는 것이 적절한 타협점이었습니다.
고정된 인격 설정(System Prompt)은 매번 동일하므로, 대응이 가능하다면 캐시(Cache)에 올리고 변하는 질문만 매번 보냅니다. 출력 토큰(Output token)은 입력 토큰(Input token)보다 단가가 높은 경우가 많으므로, 응답 상한을 두면서 동시에 "간결하게 답할 것"이라고 방향을 잡아줍니다. 짧고 정확한 것이 지갑에도, 사용자 경험(UX)에도 좋았습니다. 프로바이더(Provider)별 전송 방식의 차이(엔드포인트, 인증, 캐시 지시 형식)는 rapls_chat_call_api 내부에 캡슐화하여, 상위 로직에서 프로바이더를 신경 쓰지 않고 작성할 수 있도록 했습니다.
function rapls_chat_call_api( $model, $message, $options = array() ) {
$provider = rapls_chat_provider_of( $model ); // 'openrouter' / 'anthropic' 등
$system = rapls_chat_system_prompt(); // 고정된 인격 설정 (매번 동일)
...
cache_control의 형식도, 엔드포인트도, 인증 방식도 프로바이더마다 다릅니다. 위 예시는 특정 회사의 작성 방식에 맞춘 것이므로 실제로는 분기 처리가 필요하며 사양도 달라집니다. OpenRouter와 같이 여러 프로바이더를 통합하여 호출할 수 있는 인터페이스를 사용하면 이러한 분기 자체를 간소화할 수 있으며, 무료 유도 경로와도 궁합이 좋았습니다.
기록된 사용량을 단가표와 곱해 개략적인 비용으로 변환하여 이용자에게 보여줍니다. 우선 토큰 수에 단가표를 곱하는 견적 과정입니다.
const RAPLS_CHAT_PRICE = array(
'cheap-model' => array( 'in' => 0.0, 'out' => 0.0 ), // 공개 시 단가표로 채움
'strong-model' => array( 'in' => 0.0, 'out' => 0.0 ),
...
이를 호출할 때마다 월간 사용량에 합산합니다.
function rapls_chat_record_usage( $user_id, $model, $usage ) {
$cost = rapls_chat_estimate_cost( $model, $usage ); // 단가표 × 토큰 수
$key = 'rapls_chat_usage_' . gmdate( 'Ym' );
...
합산된 월간 데이터를 읽어 한 마디로 요약하는 함수도 준비합니다.
function rapls_chat_usage_summary( $user_id ) {
$s = get_user_meta( $user_id, 'rapls_chat_usage_' . gmdate( 'Ym' ), true );
if ( ! is_array( $s ) ) {
...
합산된 결과는 이용자의 프로필 화면에 보여주는 것이 가장 간편했습니다. 모델 선택(pick_model이 존중하는 rapls_chat_model)과 이번 달 예상 사용량을 같은 화면에 나란히 배치합니다.
add_action( 'show_user_profile', 'rapls_chat_profile_fields' );
add_action( 'edit_user_profile', 'rapls_chat_profile_fields' );
function rapls_chat_profile_fields( $user ) {
...
이렇게 하면 이용자는 스스로 모델을 선택할 수 있고, 이번 달에 얼마나 사용했는지에 대한 가이드라인도 같은 화면에서 확인할 수 있습니다. 대략적인 수치는 실제 청구 금액과 일치하지 않으므로 "예상치입니다"라는 문구를 덧붙입니다. 그럼에도 횟수와 대략적인 금액을 볼 수 있다는 것만으로도 불안감은 상당히 줄어들었습니다. 금액 그 자체보다 '알 수 없다'는 사실이 불안의 정체였기 때문입니다.
- 좋은 것을 만들면 지불할 것이라고 믿는다. 실제 장벽은 지불하는 행위 자체보다 "얼마인지 모른다"는 것이다.
- 최상위 모델을 기본값으로 설정한다. 이용자 입장에서는 가장 비싼 수도꼭지를 말없이 끝까지 틀어놓은 것과 같다.
- 상한선(Limit)을 두지 않는다. 내 지갑이라면 감각적으로 멈추겠지만, 이용자의 지갑에 내 감각은 통하지 않는다.
- 무료 진입로를 아낀다. 입구에서 막히면 가져갈 몫도 아무것도 없다. 시도조차 해보지 못하게 만드는 손해가 더 크다.
- 답변이 길수록 친절하다고 생각한다. 긴 답변은 비용이 많이 들고, 읽기도 번거로우며, 채팅으로서도 장황하다.
이 모든 것은 내가 지불하는 쪽의 관점을 유지한 채, 서비스를 제공하는 쪽을 설계했기에 발생한 착오였습니다.
토큰 비용은 반드시 누군가의 지갑으로 귀속됩니다. 내가 지불한다면 통제권을 쥘 수 있지만, 제공하는 쪽은 '지불하는 것은 이용자이고 설계하는 것은 나'라는 뒤틀린 구조를 받아들여야 합니다. 우선 누구의 지갑에 부담을 지울지를 결정하고, 이용자가 직접 비용을 부담한다면 진입 장벽(Friction)을, 제작자가 부담한다면 상한선과 요금 설계를 철저히 수호해야 합니다. 그 토대 위에서 무료 진입로, 선택 가능한 모델, 단계별 사용, 상한선, 투명성을 갖추어야 합니다. 이 모든 것은 수도꼭지 끝에 누군가의 지갑이 있다는 사실을 잊지 않기 위한 장치입니다.
시각화 구현은 아직 숙제로 남아 있습니다. 수도꼭지 끝에는 언제나 누군가의 지갑이 있다는 것, 그것만은 잊지 않고 싶습니다.
- GitHub Copilot is moving to usage-based billing - The GitHub Blog (2026년 6월 1일부터 종량제 과금으로 전환)
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기