모든 Stripe 연동은 같은 질문에서 시작됩니다. 어떤 키가 어디로 가야 할까요? Stripe는 publishable key, secret key, 그리고 제한된 키(restricted key)를 발급할 수 있는 옵션을 제공하며, 각각은 테스트와 라이브 버전으로 나뉩니다. 잘못된 키를 잘못된 곳에 두면 돈을 옮길 수 있는 권한이 유출되거나, 401 오류로 오후를 날리게 됩니다. 이 가이드에서는 각 Stripe API 키가 하는 일, 어디에 위치하는지, 첫 테스트 모드 요청을 보내는 방법, 그리고 라이브 키를 공유되는 어떤 곳에도 노출되지 않게 유지하는 방법을 다룹니다. 일반적인 개념부터 시작하려면 API 키란 무엇인가를 먼저 읽어보세요. 아래의 모든 내용은 2026년 9월에 확인한 Stripe의 API 키 문서를 따릅니다.
Stripe API 키가 대시보드 어디에 있는지
dashboard.stripe.com/apikeys에서 API 키 페이지를 여세요. 이 페이지는 대시보드의 Developers 아래에 있으며, 현재 보고 있는 계정 또는 샌드박스의 키를 보여줍니다. 토글로 샌드박스 모드와 라이브 모드를 전환할 수 있고, 키 목록도 함께 바뀝니다.
페이지에는 두 개의 목록이 있습니다.
- 표준 키(Standard keys)에는 publishable key와 secret key가 있습니다.
- 제한된 키(Restricted keys)에는 범위가 지정된 권한으로 생성한 키가 있습니다.
표시 방식은 모드에 따라 다릅니다. 샌드박스에서는 언제든 모든 키를 볼 수 있습니다. 라이브 모드에서는 직접 생성한 secret key가 생성 시점에 한 번만 표시됩니다. 대화 상자를 닫기 전에 반드시 vault에 복사하세요. 분실하면 키를 교체(rotate)해야 합니다. 라이브 모드에서 다시 볼 수 있는 것은 Stripe가 생성한 기본 키뿐입니다.
이 페이지에서 보여주지 않는 한 가지: 웹훅 서명 시크릿입니다. 이는 엔드포인트별로 Webhooks 아래에 있으며, API 키가 전혀 아닙니다.
Publishable 키 vs secret 키
이름이 신뢰 경계를 설명합니다.

| 키 | 접두사 | 노출해도 안전한가 | 할 수 있는 일 |
|---|---|---|---|
| Publishable | pk_test_ / pk_live_ |
예 | 계정 식별, Stripe.js, Elements, 모바일 SDK에서 카드 정보로 토큰 및 PaymentMethod 생성 |
| Secret | sk_test_ / sk_live_ |
아니오 | 모든 것: 결제 생성, 환불 발행, 고객 데이터 조회, 지급(payout) 트리거 |
| Restricted | rk_test_ / rk_live_ |
아니오 | 부여한 리소스와 권한만 |
publishable key는 브라우저나 모바일 앱에 속합니다. 카드 번호를 토큰으로 바꾸는 것 외에는 거의 아무것도 하지 못합니다. 결제를 생성하거나 계정 데이터를 읽을 수 없으므로, 프런트엔드 코드에 포함시키는 것은 예상된 일입니다.
secret key는 서버에만 속하며 다른 어디에도 두면 안 됩니다. Stripe의 지침은 바뀌었습니다. 문서는 이제 "새로운 사용 사례에는 secret key를 사용하지 않을 것을 권장"하며 대신 제한된 키를 가리킵니다. 무제한 secret key는 여전히 작동하지만, 기본값이 아니라 비상용(break-glass)으로 취급하세요.
테스트 모드 키 vs 라이브 모드 키
Stripe는 각자 고유한 키 세트를 가진 두 개의 별도 세계를 운영합니다. 테스트 키(최신 문서에서는 테스트 쪽을 샌드박스라고 부르지만 접두사는 여전히 _test_입니다)는 시뮬레이션된 객체를 반환합니다. 가짜 고객, 가짜 PaymentIntent, 명령에 따라 성공 또는 거절하는 테스트 카드, 실제 돈은 없습니다. 라이브 키는 카드 네트워크에 연결되어 실제 자금을 이동시킵니다.
분리는 엄격합니다. sk_test_로 생성한 고객은 sk_live_ 입장에서는 존재하지 않으며, 그 반대도 마찬가지입니다. 두 세계를 넘나들 때 나오는 Stripe의 오류 코드 livemode_mismatch는 이를 명확히 알려줍니다. "테스트 및 라이브 모드 API 키, 요청, 객체는 해당 모드 내에서만 사용할 수 있습니다."
여기서 두 가지 실용적인 규칙이 따릅니다.
- 전부
sk_test_또는rk_test_로 빌드하고 테스트하세요. Stripe는 새 연동에는 별도의 범용 샌드박스를 사용하여 설정과 데이터가 격리되도록 유지할 것을 권장합니다. - 라이브로 전환하는 것은 코드 변경이 아니라 설정 변경입니다. 서버는 환경 변수나 vault에서 키를 읽으며, 라이브 전환이란 그 값을 바꾸는 것을 의미합니다.
모든 API 응답에는 livemode 불리언이 포함됩니다. 테스트에서 이 값을 검증하세요. 잘못된 키 배포를 막는 가장 저렴한 방어책입니다.
제한된 키와 최소 권한
제한된 API 키(Stripe는 RAK로 줄여 부름)는 권한 마스크가 있는 secret key입니다. 생성할 때 각 리소스 그룹에 대해 None, Read, Write 중 하나를 선택합니다. 모든 것은 기본적으로 None입니다. Write는 Read를 포함하며, HTTP로의 매핑은 단순합니다. GET은 Read가 필요하고, POST와 DELETE는 Write가 필요합니다.
생성하려면 API 키 페이지에서 Create restricted key를 클릭하고, 미리 구성된 권한 세트를 선택하거나 처음부터 시작하여 이름을 지정하고, 권한을 설정한 뒤 2단계 인증 프롬프트를 완료하세요. 라이브 모드에서 직접 생성한 secret key와 마찬가지로 값은 한 번만 볼 수 있습니다.
제한된 키 가이드는 서비스당 하나의 키를 권장합니다. 청구 서비스는 Invoices와 Customers에 대한 Write를, 리포팅 작업은 Charges와 Disputes에 대한 Read를 받습니다. 서비스 하나가 침해되더라도 공격자는 그 키의 권한 안에 갇힙니다. 읽기 전용 분쟁 키는 결제를 생성하거나 지급을 건드릴 수 없습니다.
샌드박스에서 새 키에 광범위한 권한을 부여하고 테스트 스위트를 실행한 다음, 키의 요청 로그(오버플로 메뉴, View request logs)를 열고 트래픽이 전혀 없었던 모든 권한을 잘라내세요. Stripe는 또한 자율 AI 에이전트용 키에 태그를 지정하여 지급과 환불에 대한 승인 규칙 아래에 두도록 허용합니다. 일반 secret key 대신 이 방식을 사용하세요.
curl과 Python으로 첫 요청 보내기
Stripe는 HTTP Basic Auth로 인증합니다. 키가 사용자 이름이고 비밀번호는 비어 있습니다. 뒤의 콜론은 curl에 프롬프트를 표시하지 말라고 알려줍니다.
export STRIPE_API_KEY=sk_test_... # your sandbox secret key
curl https://api.stripe.com/v1/payment_intents \
-u "$STRIPE_API_KEY:" \
-d amount=2000 \
-d currency=usd \
-d "automatic_payment_methods[enabled]=true"
응답은 pi_로 시작하는 id, status: "requires_payment_method", 그리고 livemode: false가 있는 PaymentIntent입니다. 마지막 필드는 테스트 키를 사용했음을 확인시켜 줍니다.
Stripe는 -H "Authorization: Bearer $STRIPE_API_KEY"도 허용하며, 이는 Apidog와 대부분의 HTTP 클라이언트가 사용하는 형식입니다. 둘 다 같은 키를 전달합니다. 어떤 스타일이 스택에 맞는지 확실하지 않다면, API 키와 bearer 토큰 비교에서 장단점을 다룹니다. 요청은 HTTPS를 통해야 하며, 평문 HTTP는 인증 레퍼런스에 따라 완전히 실패합니다.
Python에서는 현재 SDK 패턴이 환경에서 키를 읽는 StripeClient입니다.
import os
import stripe
client = stripe.StripeClient(os.environ["STRIPE_API_KEY"])
intent = client.v1.payment_intents.create({
"amount": 2000,
"currency": "usd",
"automatic_payment_methods": {"enabled": True},
})
print(intent.id, intent.livemode) # pi_..., False
파일 어디에도 키 문자열이 나타나지 않습니다. 환경이 제공하는 것에 따라 동일한 코드가 테스트 또는 라이브에 대해 실행됩니다.
코딩 에이전트가 Apidog CLI로 Stripe 키를 테스트하게 하기
curl은 키가 한 번 작동함을 증명합니다. 코딩 에이전트는 Apidog CLI를 통해 이를 반복 가능한 검사로 바꾸고, 키를 공유 프로젝트 밖에 유지할 수 있습니다. 한 번 설정하세요(토큰은 Account Settings, API Access Token 아래에 있음).
npm install -g apidog-cli@latest
apidog login --with-token <YOUR_TOKEN>
npx -y skills add https://apidog.com # pick your agent when prompted
그런 다음 에이전트에 프롬프트 하나를 주세요.
Use Apidog CLI on project <id>. Create a "Stripe Test" environment with base URL
https://api.stripe.com. Add POST /v1/payment_intents with Bearer auth from
{{stripe_secret_key}} and a form-urlencoded body: amount=2000, currency=usd.
Create a test case asserting status 200, an id starting with pi_ and
livemode false, then run it and summarize any failures.
실행되는 명령은 Apidog CLI 2.2.7에서 확인한 것입니다.
apidog environment create "Stripe Test" --project <id> --base-url https://api.stripe.com
apidog cli-schema get endpoint-create
apidog endpoint create --project <id> --file payment-intents.json
apidog cli-schema validate test-case-create --file pi-case.json
apidog test-case create --project <id> --file pi-case.json
apidog run --test-case <caseId> --project <id> -e <envId> \
--env-var "stripe_secret_key=$STRIPE_API_KEY" -r cli,json
프롬프트가 폼 인코딩을 명시하는 이유는 Stripe의 v1 엔드포인트가 JSON을 받지 않기 때문입니다. livemode 검증은 당신의 트립와이어입니다. 누군가 라이브 키로 케이스를 실행하면 두 번째 요청이 나가기 전에 실패합니다. CI에서 같은 apidog run 줄이 파이프라인의 시크릿 저장소에서 키를 읽습니다. Apidog CLI CI/CD 파이프라인 가이드에 복사해서 붙일 수 있는 버전이 있습니다. 요청 정의는 공유되지만, 라이브 자격 증명은 절대 공유되지 않습니다.
키 교체와 키가 유출되었을 때 해야 할 일
Stripe 자체의 키 처리 가이드는 직설적입니다. 누군가 봤는지 확실하지 않더라도, 저장소에 커밋되었거나, 채팅에 붙여넣어졌거나, 로그에 출력된 것은 무엇이든 침해된 것으로 가정하세요.
키 교체는 대시보드에 내장되어 있습니다. 키의 오버플로 메뉴에서 Rotate key를 선택하고, 이전 키의 만료를 설정한 뒤 대체 키를 복사하세요. 최대 7일의 유예 기간 동안 둘 다 작동하므로 점진적으로 롤아웃하고 요청 로그에서 뒤처진 곳을 모니터링할 수 있습니다. Now는 이전 키를 즉시 폐기합니다.
secret 또는 제한된 키가 유출된 경우:
- 짧은 만료로 즉시 교체하세요.
- 이를 사용한 모든 서버를 업데이트하고, 요청 로그에서 트래픽을 확인한 뒤 이전 키를 만료시키세요.
- Workbench에서 최근 활동을 검토하여 연동이 하지 않을 일이 없는지 확인하세요. 인식할 수 없는 연결 계정, 시작하지 않은 지급, 새 은행 계좌 등입니다.
- 의심스러운 지급을 일시 중지하고, 이미 보낸 것은 되돌리며, 사기성 청구를 신속히 환불하여 분쟁을 미리 막으세요.
- 다른 키도 교체하세요. 하나가 유출되었다면 그 유출 경로는 더 많은 것을 봤다고 가정하세요.
Stripe는 노출된 키를 스캔하고 알려주며, 때로는 선제적으로 키를 비활성화하기도 하지만, 탐지가 보장되지는 않습니다. 저장소와 CI 설정에서 sk_live_와 rk_live_를 검색하는 것은 5분짜리 감사이며, pre-commit 훅의 시크릿 스캐너는 다음 유출이 발생하는 것을 막습니다. Stripe는 절대 secret key를 요구하지 않으므로, 이를 요청하는 메시지는 모두 피싱입니다.
흔한 Stripe API 키 오류

| 증상 | 의미 | 해결 |
|---|---|---|
401 "No valid API key provided" |
키가 없거나, 형식이 잘못되었거나, 폐기됨 | 헤더를 확인하고 키가 교체되지 않았는지 확인 |
api_key_expired |
키가 교체되거나 만료됨 | 대시보드에서 현재 키를 가져오기 |
secret_key_required |
서버 측 엔드포인트에 pk_ 키를 보냄 |
백엔드에서 sk_ 또는 rk_ 사용 |
존재하는 걸 아는 id에 대한 resource_missing |
모드 불일치: 테스트 키로 라이브 객체에 접근하거나 그 반대 | 키 모드를 객체에 맞추기 |
403 "doesn't have permissions to perform the request" |
제한된 키에 해당 리소스에 대한 Read 또는 Write가 없음 | 키의 권한을 편집; 오류 본문에 무엇이 빠졌는지 명시됨 |
livemode_mismatch |
한 요청에 테스트와 라이브 키 또는 객체를 혼용 | 각 환경을 완전히 한 모드로 유지 |
401과 403의 경우는 오류 레퍼런스에 따라 Python SDK에서 stripe.AuthenticationError와 stripe.PermissionError에 매핑되므로 특정하게 잡을 수 있습니다. 한 가지 덜 명확한 사례: 180일 동안 이체나 지급을 생성하지 않은 라이브 키는 해당 작업에 대해 제한되며, 키에 Restore access 옵션이 있습니다.
FAQ
Stripe publishable key를 프런트엔드 코드에 넣어도 안전한가요?
예. pk_ 키는 카드 정보를 토큰화하지만 결제를 생성하거나 고객을 읽거나 돈을 옮길 수 없습니다. secret과 제한된 키만 서버 측에 두세요.
테스트 모드에서 제한된 키를 사용할 수 있나요?
예. 제한된 키는 두 모드(rk_test_와 rk_live_)에 모두 존재합니다. Stripe는 먼저 테스트 버전을 조정한 뒤 일치하는 권한으로 라이브 키를 만들 것을 권장합니다.
웹훅 서명 시크릿이 내 API 키와 같은가요?
아니요. 서명 시크릿은 웹훅 엔드포인트별로 발급되며, 들어오는 이벤트가 Stripe에서 왔는지만 검증합니다. 이는 API 키 페이지가 아니라 Webhooks 아래에 있습니다. 이를 연결하고 있다면 CI에서 Stripe 웹훅 테스트하기를 참고하세요.
프로덕션에서 테스트 키를 사용하면 어떻게 되나요?
요청은 성공하지만 카드가 청구되지 않고 돈이 이동하지 않습니다. 모든 객체에서 livemode가 false로 돌아오므로, 이 필드에 대한 검증이 고객보다 먼저 실수를 잡아냅니다.
요약
Publishable 키는 클라이언트에, secret과 제한된 키는 서버에 두고, 테스트와 라이브는 절대 섞지 않으며, 범위를 지정할 수 있을 때는 언제나 제한된 키가 secret을 이깁니다. 라이브 키는 vault에 보관하고, 테스트 키는 런타임에 Apidog CLI에 전달하며, 에이전트가 작성하는 모든 테스트 케이스에 livemode 검증을 유지하세요.
