내 앱에서 사용자의 클로드·ChatGPT 구독을 그대로 쓰게 하는 법
(API 키 없이)

대상: 바이브코딩으로 자기 앱을 만드는 선생님 기준일: 2026-09-05 정책은 바뀝니다 — 원문 링크를 꼭 다시 확인하세요

바이브코딩으로 앱을 만들다 보면 반드시 한 번은 부딪히는 벽이 있어요.

"AI 기능을 넣고 싶은데, 그 비용은 누가 내지?"

선택지는 보통 셋입니다.

방식누가 내나문제
① 내가 API 키를 앱에 심고 내가 낸다개발자사용자가 늘수록 내 카드가 긁힌다. 키가 앱 안에 있으니 뜯기면 남이 내 돈을 쓴다
② 사용자에게 API 키를 입력받는다
(BYOK, Bring Your Own Key)
사용자선생님들은 API 키를 발급받아 본 적이 없다. "키가 뭔가요"에서 대부분 이탈한다
③ 사용자가 이미 내고 있는 구독을 그대로 쓴다
(BYOS, Bring Your Own Subscription)
사용자
(추가 비용 0)
이 글의 주제

③이 요즘 여기저기서 보이기 시작했어요. OpenMotion(openmotion.design)이라는 영상 제작 앱이 그렇습니다. 첫 화면 맨 아래에 "WORKS WITH Claude Code | Codex"라고 적혀 있고, 미리보기 화면의 오른쪽 채팅창을 보면 실제로 Codex가 돌아가고 있어요.

OpenMotion 첫 화면 — 상단에 'The motion designer you didn't have to hire', 가운데 편집기 미리보기, 맨 아래 'WORKS WITH Claude Code | Codex' 표시
OpenMotion 첫 화면 (openmotion.design, 2026-09-05 캡처). 맨 아래 "WORKS WITH Claude Code | Codex" — 그리고 편집기 오른쪽 채팅창 머리에 "Codex"가 보입니다.

같은 페이지 FAQ에는 이렇게 답해 뒀습니다.

OpenMotion FAQ — 'Does it work with Claude Code and Codex?' 질문에 'Yes. OpenMotion is designed to work with a Claude Code or Codex subscription, so you do not need to manage separate API keys.'라고 답한 화면
FAQ "Does it work with Claude Code and Codex?" 를 펼친 화면.
"Yes. OpenMotion is designed to work with a Claude Code or Codex subscription, so you do not need to manage separate API keys." 네. 클로드 코드나 코덱스 구독으로 동작하게 만들었으니, API 키를 따로 관리할 필요가 없습니다.

이걸 어떻게 만드는지, 그리고 어디까지가 약관상 허용인지를 정리합니다. OpenMotion 내부를 들여다본 건 아니고, 이 방식을 실제로 만들어 보며 배운 것들이에요.

1. 원리는 한 장이면 됩니다

사용자 PC에 Claude Code(claude)나 Codex(codex)라는 프로그램이 설치돼 있고, 사용자가 자기 구독으로 이미 로그인해 둔 상태라고 합시다. 이 둘은 각각 클로드·ChatGPT 회사가 만든 "컴퓨터에 설치하는 AI 도우미"예요.

내 앱은 그 프로그램을 "실행"만 합니다. 질문을 넘기고, 답을 받아서 화면에 그리는 거예요.

내 앱
실행
사용자 PC의
claude 프로그램
사용자 구독으로
Anthropic 서버

핵심은 내 앱이 토큰·비밀번호·키를 한 번도 만지지 않는다는 점이에요. 로그인은 그 프로그램이 스스로 하고, 내 앱은 "실행 → 결과 읽기"만 합니다. 그래서 API 키가 필요 없습니다.

비유하자면 엑셀에서 "계산기 열기"를 누르면 윈도우 계산기가 뜨죠. 엑셀이 계산기를 대신 실행한 거예요. 이 방식도 똑같아요 — 내 앱이 사용자 PC에 이미 있는 AI 프로그램을 대신 실행하고, 결과만 받아 옵니다. 그 프로그램에 로그인돼 있는 건 사용자 본인이고요.

이 글에 나오는 말 4개

자식 프로세스
내 앱이 다른 프로그램을 대신 실행시켜 부려 쓰는 것. 내 앱이 "부모", 실행된 프로그램이 "자식"이에요. 부모가 끝나면 자식도 정리해야 합니다.
인자(명령줄 인자)
프로그램을 실행할 때 같이 넘기는 값들. claude -p "질문" 에서 -p"질문"이 인자예요. 여기에 적은 글자는 그대로 프로그램에 전달됩니다.
표준 입력(stdin)
프로그램이 "키보드에서 뭔가 들어오길" 기다리는 통로. 내 앱이 실행한 프로그램은 키보드가 없으니, 이 통로를 닫아 줘야 기다리지 않아요.
환경변수
PC에 저장해 두는 "이름=값" 설정. ANTHROPIC_API_KEY처럼 API 키를 여기 넣어 두는 사람이 있어요. 자식 프로세스는 부모의 환경변수를 그대로 물려받습니다 — 이게 뒤에서 문제가 됩니다.

2. 먼저 — 이게 허용되는 일인가요? (세 회사 비교)

만들기 전에 이걸 봐야 해요. 셋이 완전히 다릅니다. 각 회사 원문을 그대로 가져왔고, 그 앞에 "쉽게 말하면"을 붙였어요. 원문은 접어 뒀으니 필요할 때 펼쳐 보세요.

클로드 (Anthropic) — ✅ 됩니다. 단, 조건 4개를 지켜야 해요.

출처: code.claude.com/docs/en/legal-and-compliance

쉽게 말하면 — 사용자가 자기 구독으로 수정 안 한 Claude Code에 로그인해서 쓰는 건, 그게 내 앱 안에서 일어나더라도 괜찮다고 명시돼 있어요. 대신:

그 밖에 두 가지 — "Claude Code를 실행합니다"라고 글로 적는 건 되지만, 로고를 내 앱에 넣거나 제품 이름에 쓰면 안 됩니다. 그리고 Pro·Max 한도는 "개인의 통상적 사용"을 전제로 한다고 적혀 있어요 — 앱이 뒤에서 무한정 돌리는 건 그 전제를 벗어납니다.

원문 보기
"Nor does it prevent an end user from signing in to the unmodified Claude Code binary with their own Claude subscription, including where a platform hosts Claude Code…" 사용자가 수정하지 않은 Claude Code 프로그램에 자기 구독으로 로그인하는 것은 막지 않는다 — 다른 플랫폼이 Claude Code를 품고 있는 경우도 포함.
"The Claude Code binary must not be modified." / "Customers may not pay for, resell, or intermediate Claude usage on their end users' behalf. Each end user must authenticate with their own … Claude subscription plan credentials … That usage is billed directly to the end user." 프로그램을 고치지 말 것. 사용자 대신 결제·재판매·중개하지 말 것. 사용자 각자가 자기 구독으로 로그인하고, 요금은 사용자에게 직접 청구된다.
"Anthropic does not permit third-party developers to offer Claude.ai login into their own applications, or to route requests through Free, Pro, or Max plan credentials on behalf of their users. Moreover, developers may not collect, store, or intermediate Claude.ai credentials or session tokens — sign-in to a Claude account must complete through Anthropic's own flow." 내 앱 안에 클로드 로그인 화면을 만들지 말 것. 사용자 대신 구독 자격으로 요청을 보내지 말 것. 토큰을 모으거나 저장하거나 중개하지 말 것. 로그인은 Anthropic 자체 흐름으로 끝나야 한다.

코덱스 (OpenAI) — △ 회색지대. 금지는 아니지만, 권장도 아니에요.

출처: learn.chatgpt.com/docs/auth

쉽게 말하면 — OpenAI 문서는 "프로그램이 자동으로 돌리는 건 API 키로 하라"고 안내해요. 그런데 그 예시가 전부 서버에서 돌리는 자동화(CI/CD)예요. "사용자 본인 PC에서, 본인이 버튼을 눌러서, 본인 로그인으로 도는 데스크톱 앱"은 언급 자체가 없습니다. 금지 문장도 없고 허용 문장도 없어요. 그래서 △입니다.

원문 보기
"Use API key authentication for programmatic Codex CLI workflows, such as CI/CD jobs. Don't expose Codex execution in untrusted or public environments." / "Access tokens are intended for trusted scripts, schedulers, and private CI runners." 프로그램적 워크플로(예: CI/CD)는 API 키로. 신뢰할 수 없거나 공개된 환경에 코덱스 실행을 노출하지 말 것. 액세스 토큰은 신뢰된 스크립트·스케줄러·비공개 CI 러너용.
"Codex caches login details locally in a plaintext file at ~/.codex/auth.json … treat it like a password." 로그인 정보가 이 파일에 평문으로 저장된다. 비밀번호처럼 다룰 것.

구글 (Gemini CLI · Antigravity) — ❌ 안 됩니다. 두 제품 다 명시적으로 금지.

출처: antigravity.google/docs/faq · geminicli.com/docs/resources/tos-privacy

쉽게 말하면 — 구글은 아이디어 자체를 막아 놨어요. FAQ 질문 제목부터가 "왜 제3자 소프트웨어(Claude Code, OpenClaw, OpenCode 등)로 내 Antigravity 로그인을 쓸 수 없나요?"이고, 답은 "약관 위반"입니다. Gemini CLI 쪽 약관도 같은 말을 하고, 계정 정지·해지 사유가 될 수 있다고 적혀 있어요.

원문 보기
(Antigravity FAQ) "Why can't I use third party software (e.g. Claude Code, OpenClaw, OpenCode) with my Antigravity login?" — "Using third party software, tools, or services to access Antigravity is a violation of our Terms of Service" / "If you would like to use a third party coding agent with Gemini, we recommend using a Vertex or AI Studio API key."
(Gemini CLI 약관) "Directly accessing the services powering Gemini CLI using third-party software, tools, or services … is a violation of applicable terms and policies. Such actions may be grounds for suspension or termination of your account."

한 줄 정리

구독 로그인으로 내 앱에서 실행지켜야 할 것
클로드✅ 됨프로그램 무수정 · 사용자 본인 로그인 · 토큰 미접촉 · 개발자는 상업 약관 동의
코덱스△ 회색프로그램적 사용은 API 키 권장(서버 자동화 예시). 데스크톱 앱은 언급 없음. auth.json 절대 읽지 말 것
구글❌ 안 됨두 제품 다 "제3자 소프트웨어 접근 = 약관 위반, 계정 정지 가능". API 키가 유일한 길

3. 만드는 법 (Node.js 기준)

일렉트론이든 Tauri 사이드카든, 결국 자식 프로세스 하나 띄우고 출력을 읽는 일이에요. 세 단계로 나눠 갑니다. 코드는 그대로 AI에게 붙여 넣어도 되고, 코드 앞의 설명만 읽고 넘어가도 됩니다.

1단계 — 설치돼 있나, 로그인돼 있나

무슨 일인가: 사용자 PC에 프로그램이 있는지는 "버전 알려줘"(--version)를 실행해서 답이 오는지로 알 수 있어요. 로그인돼 있는지는 "로그인 상태 알려줘"(auth status)를 실행해서 성공으로 끝나는지(종료 코드 0)로 알 수 있고요. 둘 다 그 프로그램이 원래 제공하는 명령이고, 내 앱은 실행만 합니다.

import { spawn } from 'node:child_process';

// 명령 하나 돌려서 { status, stdout } 받기.
// ★동기 방식(spawnSync)은 쓰지 마세요 — 그동안 앱 화면이 얼어요 (함정 6번)
function run(file, args) {
  return new Promise((resolve) => {
    let out = '';
    const child = spawn(file, args, {
      stdio: ['ignore', 'pipe', 'pipe'],   // ★표준 입력을 닫는다 (함정 1번)
      windowsHide: true,                   // 검은 창이 번쩍 뜨지 않게
    });
    child.stdout.on('data', (d) => (out += d));
    child.on('close', (status) => resolve({ status, stdout: out }));
    child.on('error', () => resolve({ status: null, stdout: '' }));
    setTimeout(() => child.kill(), 20_000); // 20초 넘게 멈추면 포기
  });
}

const version  = await run(claudePath, ['--version']);                 // "2.1.258 (Claude Code)"
const signedIn = (await run(claudePath, ['auth', 'status'])).status === 0;
// codex 는 ['--version'] / ['login', 'status']

로그인이 안 돼 있으면 — 내 앱이 로그인 화면을 만들면 안 됩니다(약관). 그 프로그램의 로그인 명령을 그냥 띄우세요. 브라우저가 열리고 그 회사 로그인 화면이 뜹니다. 내 앱은 끝나기를 기다렸다가 auth status만 다시 보면 돼요.

spawn(claudePath, ['auth', 'login'], { stdio: 'ignore', windowsHide: false });
// codex 는 ['login']

설치가 안 돼 있으면 — 각 회사가 공개한 설치 명령을 새 터미널 창에서 실행해 주세요. 내 앱이 파일을 내려받아 설치하는 게 아니라, 공식 명령을 대신 쳐 주는 것뿐입니다. 사용자에게는 "검은 창이 하나 열리고 글자가 올라가는데, 각 회사 공식 설치 명령이 도는 거예요"라고 안내하세요.

claude (윈도우):  irm https://claude.ai/install.ps1 | iex
codex  (윈도우):  winget install OpenAI.Codex

2단계 — 질문 보내고 답 받기

무슨 일인가: 질문을 인자로 넘기면서 프로그램을 실행하면, 프로그램이 답을 한 줄씩(JSON 형식) 흘려 보내 줘요. 내 앱은 그 줄들을 읽어서 글자를 화면에 붙이면 됩니다. 인자 중에 "내장 도구 끄기", "사용자 PC 설정 무시", "대화 기록 안 남기기"가 들어가는데, 각각 왜 필요한지는 아래 함정에서 설명합니다.

const args = [
  '-p', prompt,                                   // 질문. 표준 입력이 아니라 인자로
  '--output-format', 'stream-json',               // 답을 한 줄씩 JSON 으로
  '--verbose', '--include-partial-messages',      // 글자가 오는 대로 받기
  '--tools', '',                                  // ★내장 도구(파일 읽기·쉘) 전부 끄기 (함정 4번)
  '--restricted',                                 // 사용자 PC 의 설정 파일·훅 무시
  '--permission-mode', 'dontAsk',                 // 물어보지 않게
  '--no-session-persistence',                     // 대화를 사용자 디스크에 안 남김
  // '--model', 'sonnet',                         // 선택
  // '--append-system-prompt', '...',             // 내 앱의 지시문
];

const child = spawn(claudePath, args, {
  stdio: ['ignore', 'pipe', 'pipe'],
  windowsHide: true,
  env: stripKeys(process.env),                    // ★API 키를 뺀 환경변수 (함정 7번)
  detached: process.platform !== 'win32',         // 나중에 트리째 죽이려고
});

let buf = '';
child.stdout.setEncoding('utf-8').on('data', (chunk) => {
  buf += chunk;
  const lines = buf.split('\n'); buf = lines.pop();    // 줄 단위로 자르기
  for (const line of lines) {
    const msg = JSON.parse(line);
    if (msg.type === 'stream_event' && msg.event?.delta?.type === 'text_delta') {
      appendToScreen(msg.event.delta.text);        // 글자가 오는 대로 화면에
    }
    if (msg.type === 'rate_limit_event') {         // 남은 한도 (함정 9번)
      const w = msg.rate_limit_info?.unifiedWindows?.five_hour;
      showQuota(w?.utilization, w?.resetsAt);      // 0.83 이면 "83% 사용"
    }
  }
});

코덱스는 명령 모양이 다릅니다. 하는 일은 같아요.

const args = [
  'exec', '--json', '--skip-git-repo-check',
  '-C', emptyFolder,           // 빈 폴더를 작업 폴더로 — 사용자 문서 폴더에서 돌리지 말 것
  '-s', 'read-only',           // 파일을 못 고치게
  '--ignore-user-config',      // 사용자 PC 의 codex 설정 무시 (로그인은 그대로 살아 있음)
  prompt,                      // 질문은 맨 마지막 인자
];
// 답: {"type":"item.completed","item":{"type":"agent_message","text":"..."}} 줄을 읽으면 됩니다

3단계 — 중단·종료 때 자식까지 정리하기

무슨 일인가: 사용자가 [중단]을 누르거나 앱을 끄면, 내 앱이 실행해 둔 프로그램(자식)도 끝까지 꺼야 해요. 자식은 또 자기 자식을 만들기도 하거든요(프로세스 "트리"). 안 그러면 보이지 않는 곳에서 계속 돌면서 사용자 구독 사용량을 먹습니다.

if (process.platform === 'win32') {
  spawnSync('taskkill', ['/PID', String(child.pid), '/T', '/F']);   // /T = 자식의 자식까지
} else {
  process.kill(-child.pid, 'SIGTERM');                              // detached 로 띄웠으므로 그룹째
}

앱이 정상 종료될 때(before-quit)뿐 아니라, 예상 못 한 오류로 죽을 때(uncaughtException)에도 부르세요. 앱만 죽고 자식은 살아남는 게 제일 흔한 사고예요.

4. 실제로 만들어 보면 걸리는 함정 10개 (실측)

문서만 보고는 절대 모르는 것들이에요. 전부 직접 걸려 본 것입니다. 각각 무슨 일이 생기나 → 왜 → 어떻게 순서로 적었어요.

1. 표준 입력을 닫지 않으면 멈춥니다

증상 클로드는 3초를 그냥 버리고 시작해요. 코덱스는 영원히 기다립니다(실측 184초 타임아웃, 출력 0줄).

프로그램이 "키보드에서 뭔가 더 들어오나?" 하고 기다리는 거예요. 내 앱이 실행한 프로그램에는 키보드가 없는데도요.

해법 stdio: ['ignore', ...] — 첫 칸이 표준 입력이에요. "없음"으로 닫아 주면 바로 시작합니다.

2. 윈도우에서 claude.cmd 를 직접 실행하면 오류(EINVAL)가 납니다

증상 터미널에서는 claude가 잘 되는데, 앱에서 실행하면 "잘못된 인자"라며 실패해요.

npm으로 설치하면 %APPDATA%\npm\claude.cmd라는 껍데기 파일이 생겨요. 그런데 Node 20.12.2 이후로는 보안 문제(CVE-2024-27980) 때문에 .cmdshell: true 없이 못 띄웁니다. 그렇다고 shell: true를 켜면 사용자가 친 질문이 명령줄 명령으로 해석돼요 — 질문에 &|가 들어가면 엉뚱한 게 실행됩니다. 위험해요.

해법 껍데기 뒤에 있는 진짜 파일을 찾아서 띄우세요.
클로드: %APPDATA%\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe
코덱스: %APPDATA%\npm\node_modules\@openai\codex\bin\codex.jsnode codex.js …로 실행 (일렉트론 앱이면 ELECTRON_RUN_AS_NODE=1을 켜서 자기 자신을 node로 써도 됩니다)

3. %APPDATA%\npm 폴더가 PATH에 없을 수 있습니다

증상 터미널에서 where claude는 되는데, 앱은 "설치 안 됨"이라고 해요.

그 폴더가 윈도우 레지스트리의 PATH에는 있는데, 앱이 실행될 때 물려받은 PATH에는 빠져 있는 경우가 있어요.

해법 프로그램을 찾아볼 폴더 목록에 %APPDATA%\npm을 직접 넣어 두세요.

4. --disallowedTools로 도구를 막으면 샙니다

증상 "파일 읽기·쉘 실행은 못 하게 막았다"고 생각했는데, 모델이 CronCreate·ListAgents 같은 내장 도구 12개 이상을 그대로 보고 있었어요.

--disallowedTools블랙리스트예요 — 적은 것만 막고 나머지는 다 열려 있어요. --allowedTools도 "자동 승인 목록"이지 "쓸 수 있는 목록"이 아니고요.

해법 --tools "" — 내장 도구를 통째로 없애는 옵션이에요. 실측: 34초·3턴·훅 7건 → 15초·2턴·훅 0건. 빨라지기까지 합니다.

5. --bare를 쓰면 구독 로그인을 안 읽습니다

증상 로그인이 돼 있는데도 "인증 안 됨"이 나요.

클로드 CLI 도움말에 그대로 적혀 있어요: "Anthropic auth is strictly ANTHROPIC_API_KEY … (OAuth and keychain are never read)" — --bare 모드는 API 키만 보고 구독 로그인은 아예 안 봅니다.

해법 구독으로 붙이려면 --bare를 쓰지 마세요.

6. spawnSync(동기 실행)로 상태를 확인하면 앱이 통째로 멈춥니다

증상 앱을 켤 때, 설정을 열 때, 다른 창 갔다 돌아올 때마다 화면이 몇 초씩 얼어요.

"버전 알려줘" + "로그인 상태 알려줘" 두 번이 클로드는 0.7+1.6초, 코덱스는 PC가 바쁠 때 4.4+8.3초까지 걸렸어요. 동기 방식은 그동안 앱이 아무것도 못 합니다.

해법 반드시 비동기(spawn)로, 두 프로그램은 나란히(Promise.all). 화면에는 "확인 중…"을 보여 주세요.

7. 환경변수를 통째로 물려주면 안 됩니다

증상 사용자가 "구독료로 되는 줄 알았는데 왜 돈이 더 나가지?"라고 물어요.

사용자 PC에 ANTHROPIC_API_KEYOPENAI_API_KEY가 설정돼 있으면, 프로그램이 구독 대신 그 키로 붙어요. 키로 붙으면 구독과 별도로 쓴 만큼 요금(종량제)이 나갑니다.

해법 자식에게 넘기는 환경변수에서 이 키들(+ ANTHROPIC_BASE_URL, CLAUDE_CODE_USE_BEDROCK 같은 우회 스위치)을 뺀 사본을 만들어 넘기세요. 키를 읽는 게 아니라, 보이지 않게 하는 거예요.

8. 윈도우 명령줄은 32,767자가 한계입니다

증상 긴 자료를 붙여서 물어보면 실행 자체가 실패하고 "다시 시도하세요"류 안내가 떠요. 다시 눌러도 똑같이 실패합니다.

질문을 인자로 넘기니까, 인자 전체가 이 길이를 넘으면 윈도우가 프로그램을 아예 못 띄워요.

해법 넘칠 부분을 미리 자르고, 잘랐다고 사용자에게 알려 주세요. 조용히 자르면 안 돼요.

9. 남은 한도는 미리 알 수 있습니다

증상 사용자가 한창 쓰다가 갑자기 "한도 초과"로 막혀요.

클로드가 답을 흘려 보낼 때 rate_limit_event라는 줄도 같이 와요. 그 안에 5시간 창의 사용률(utilization, 0~1)과 초기화 시각(resetsAt)이 들어 있습니다.

해법 80%쯤부터 "한도 83% 사용"처럼 보여 주면 사용자가 놀라지 않아요.

10. 코덱스의 item.type === "error"는 치명적이지 않습니다

증상 "error"가 왔길래 실패로 처리했더니, 사실은 잘 끝난 거였어요.

경고성 메시지도 그 경로로 와요.

해법 성공/실패는 종료 코드와 stderr로 판단하세요.

5. 개인정보 — 인자에 실은 글자가 곧 밖으로 나가는 글자입니다

이 방식은 내 서버를 거치지 않아서 내 서버에는 아무것도 안 남아요. 좋은 점이죠. 그런데 뒤집어 말하면, 내 앱이 인자(-p …)에 실은 글자가 그대로 Anthropic·OpenAI 서버로 갑니다. 그래서 이 체크리스트가 필요해요.

마지막으로

이 글은 바이브코딩할 때 그대로 AI에게 붙여 넣으라고 쓴 글이에요. "이 글의 함정 10개를 전부 반영해서 만들어 줘"라고 하면 위에서 걸린 곳을 대부분 피해 갑니다.

정책은 바뀝니다. 특히 코덱스의 회색지대는 언제든 어느 쪽으로든 닫힐 수 있어요. 만들기 전에, 그리고 출시 전에 위 세 링크를 날짜 찍고 다시 읽으세요.

기준일 2026-09-05 · 약관 원문은 각 회사 페이지에서 직접 확인한 것입니다.