테스트 모드
pk_test_ / sk_test_ 키, 매직 테스트 번호, 시나리오, 고정 코드와 테스트 수신함
테스트 키를 쓰면 실제 SMS를 보내거나 크레딧을 쓰지 않고 연동을 만들고 시험할 수 있습니다. 테스트 키도 같은 API를 같은 요청·응답 형식으로 호출하지만 매직 테스트 번호만 받으며, API가 그 번호의 전달과 검증을 시뮬레이션합니다. 아무것도 발송되지 않고 과금되지 않으며 결과가 항상 같으므로 CI에서 돌릴 수 있습니다.
테스트 모드는 K-OTP를 흉내 낼 뿐입니다. 출시 전에는 실발송 몇 건으로 실제 경로를 확인하세요(실발송으로 운영 확인 참고).
테스트 키
콘솔에서 앱을 열고 API 키의 테스트 탭에서 새 테스트 키를 누르면 만들 수 있습니다.
| 라이브 키 | 테스트 키 | |
|---|---|---|
| 접두사 | pk_, sk_ | pk_test_, sk_test_ |
| 휴대폰 번호 | 실제 번호(매직 번호는 거절) | 매직 테스트 번호만(실제 번호는 거절) |
| 발송과 크레딧 | 실제로 발송하고 크레딧을 씀 | 발송하지 않고 크레딧도 쓰지 않음 |
| scope | scope 참고 | 라이브 키와 같은 규칙 |
pk_ 허용 Origin | 정확한 Origin 1–20개 | localhost, 127.0.0.1의 모든 포트 + 등록한 정확한 Origin 0–20개 |
| 호출 제한 | 키·IP·휴대폰 번호별(키 규칙 적용) | 고정: 키당 120/1m, 하루 5,000회(발급·검증·조회 각각) |
| 앱당 키 수 | 콘솔 한도(기본 20개) | 앱당 활성 테스트 키 최대 5개(따로 셈) |
| 결과 확인 | 사용량, 발급 내역, 크레딧 원장 | 앱의 테스트 수신함(테스트 발급은 사용량·내역·원장에 나타나지 않음) |
- 모드는 키 접두사로 정해집니다. 발급·검증·상태·발급 목록·잔액 응답에는
mode: "live"또는mode: "test"가 들어 있습니다. - 라이브와 테스트 데이터는 분리됩니다. 라이브 키로 테스트
issueId를 조회하면(반대도 마찬가지)404 NOT_FOUND입니다. 멱등 키도 따로 관리되므로 모드가 다르면 같은 값을 써도 충돌하지 않습니다. pk_test_키는localhost와127.0.0.1의http://,https://Origin을 포트와 관계없이 등록하지 않아도 허용하므로 로컬 개발에 바로 쓸 수 있습니다. 미리보기 배포처럼 다른 Origin은 라이브pk_키처럼 정확히 등록해야 합니다.[::1]과 그 밖의 사설 주소는 포함되지 않습니다.- 테스트 키에는 사용자 지정 호출 제한을 둘 수 없습니다. 제한을 넘으면 라이브 키처럼
429 TOO_MANY_REQUESTS가 오며,data.policy는"platform",data.limit은 해당 규칙(분당이면perKey, 하루 상한이면perKeyDaily)입니다. 테스트 키는 조회(GET /v1/status,/v1/issues,/v1/balance,/v1/credit-ledger)도 셉니다. - 새 테스트 발급은 UTC 00:00에 초기화되는 일일 예산도 씁니다. 조직당 10,000건(
data.limit: "perOrgDaily")과 전체 테스트 트래픽 예산(data.limit: "testModeDaily")입니다. 넉넉한 값이며, 멈추지 않는 테스트 루프 같은 폭주를 막기 위한 상한입니다. 새 발급만 세며, 같은 멱등 키로 다시 보낸 요청은 세지 않습니다. sk_test_키도 라이브 키처럼 비밀 값으로 보관하세요(예: CI 시크릿). 폐기 방법도 라이브 키와 같습니다.
curl "https://api.k-otp.dev/v1/issue" \
-H "Authorization: Bearer $KOTP_TEST_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "phoneNumber": "010-0000-0000", "purpose": "login" }'매직 테스트 번호
마지막 두 자리(xx)가 시나리오를 정합니다. 모든 지역이 같은 표를 씁니다.
| 지역 | 형식 | 예(00) | 안전한 이유 |
|---|---|---|---|
| 한국 | 010-0000-00xx, +82 10-0000-00xx | 010-0000-0000 | 010-0으로 시작하는 번호는 가입자에게 부여되지 않음 |
| 영국 | +44 7700 9000xx | +44 7700 900000 | Ofcom 방송·드라마용 예약 번호대 07700 900000–900999 |
| 미국 | +1 NXX 555-01xx | +1 201 555-0100(지역 번호 무관) | NANP 가상 번호대 555-0100–0199 |
번호는 숫자만 남겨 비교하므로(+82 10…은 010…으로 봅니다) 하이픈과 공백은 상관없습니다.
시나리오
xx | 시나리오 | 발급 응답 | 전달 상태(GET /v1/status) | 고정 코드로 검증 |
|---|---|---|---|---|
00 | 성공 | 200 | 1초 뒤 sent, 2초 뒤 delivered | verified |
01 | 전달 실패 | 200 | 1초 뒤 sent, 3초 뒤 failed | verified(전달과 검증은 독립) |
02 | 느린 전달 | 200 | 30초 동안 queued/sent, 그 뒤 delivered | verified |
03 | 결과 불명 | 200 | sent + providerOutcomeAmbiguous: true(결과 불명) | verified |
04 | 만료 | 200, expiresAt = 발급 + 10초 | delivered | 10초 뒤 EXPIRED |
05 | 코드 불일치 | 200 | delivered | 어떤 코드도 MISMATCH, maxAttempts번째는 MAX_ATTEMPTS |
06 | 호출 제한 | 429 TOO_MANY_REQUESTS(perPhone, Retry-After: 60) | — | — |
07 | 크레딧 부족 | 402 PAYMENT_REQUIRED(data.code: INSUFFICIENT_CREDIT) | — | — |
08 | 무료 체험 일일 한도 | 402 PAYMENT_REQUIRED(data.code: TRIAL_DAILY_LIMIT_EXCEEDED) | — | — |
09 | 서비스 일시 중단 | 503 SERVICE_UNAVAILABLE(Retry-After: 2) | — | — |
10 | 알림톡 대체 발송 | 200 | 알림톡 실패 → 4초 뒤 SMS 대체 발송 delivered(아래 참고) | verified |
11–99 | 예약 | 지금은 00과 같음 |
- 시나리오
10은 알림톡 발송(기본 채널)일 때만 대체 발송합니다.smsFallback: false면 3초 뒤failed,channel: "sms"면00과 같습니다. - 오류 응답은 라이브 오류와 같은 형식과 필드를 씁니다(오류 참고). 오류 처리 코드를 그대로 시험할 수 있습니다.
- 같은 번호와
purpose로 다시 발급하면 이전 발급이replaced가 되며, 멱등 키도 라이브와 같이 동작합니다. - 템플릿은 라이브와 똑같이 렌더링하므로 없는 템플릿이나 빠진 변수는 그대로
400 BAD_REQUEST입니다.
고정 코드
모든 매직 번호는 고정 코드 000000(코드 자릿수만큼 0)으로 검증합니다. 테스트 발급에는 무작위 코드가 없으며, 라이브와 마찬가지로 API가 코드를 돌려주지 않습니다.
curl "https://api.k-otp.dev/v1/verify" \
-H "Authorization: Bearer $KOTP_TEST_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "issueId": "<발급 응답의 issueId>", "code": "000000" }'테스트 모드에서만 나는 오류
| 상태 | code | data.code | 언제 |
|---|---|---|---|
| 400 | BAD_REQUEST | TEST_NUMBER_REQUIRED | 테스트 키로 매직 테스트 번호가 아닌 번호를 보냈을 때 |
| 400 | BAD_REQUEST | TEST_NUMBER_IN_LIVE_MODE | 라이브 키로 매직 테스트 번호를 보냈을 때(발송 안 됨) |
두 오류 모두 첫 요청에서 드러나므로, 운영에 잘못 들어간 테스트 키나 라이브 코드에 남은 매직 번호를 SMS를 기다리기 전에 알 수 있습니다.
잔액과 크레딧 원장
테스트 키는 실제 지갑을 읽지 않습니다.
-
GET /v1/balance는 테스트 발급으로 줄지 않는 고정 모의 잔액을 돌려줍니다. 잔액 부족 경로는 시나리오07,08로 시험하세요.{ "appId": "app_...", "walletId": "test:app_...", "walletScope": "test", "organizationId": "...", "balance": 1000000, "currency": "CREDIT", "updatedAt": "2026-10-11T03:00:00.000Z", "promoBalance": 0, "promoNextExpiry": null, "mode": "test" } -
GET /v1/credit-ledger는 빈 목록(items: [], 다음 커서 없음)을 돌려줍니다.
테스트 수신함
콘솔의 앱마다 테스트 수신함이 있습니다(앱을 열고 테스트 수신함). 최근 7일 동안의 테스트 발급을 시각, 매직 번호, 시나리오, 전달·검증 상태, 시도 횟수, 고정 코드와 함께 보여 주며, 복사 버튼이 있는 매직 번호 표도 있습니다. 수신함은 보통 API보다 1분 정도, 앱이 바쁠 때는 최대 15분 늦을 수 있으니 실시간 상태는 테스트 키로 GET /v1/status를 호출해 확인하세요.
권장 사용법
| 상황 | 권장 |
|---|---|
| 로컬 개발, CI 통합 테스트 | 테스트 키와 매직 번호. 비용이 없고 결과가 항상 같습니다. sk_test_ 키는 CI 시크릿에 둡니다. |
| SDK를 쓰는 앱의 단위 테스트 | 지금은 직접 만든 가짜 객체. 테스트 모드용 SDK 도우미(매직 번호 상수, 메모리 transport)는 이후 SDK 릴리스에서 제공될 예정입니다. |
| 운영 반영 확인 | 라이브 키로 팀 휴대폰에 몇 건만 실발송(크레딧 사용). 라이브 모드에서 매직 번호는 거절됩니다. |
| 장애 경로 연습 | 시나리오 06(429), 07(402), 09(503), 03(결과 불명) |
실발송으로 운영 확인
시뮬레이터는 위 시나리오를 따르지만, 실제 통신사나 메시징 사업자의 동작을 모두 재현하지는 못합니다. 출시 전과 템플릿·채널을 바꾼 뒤에는 라이브 키로 본인 소유의 실제 휴대폰에 OTP를 몇 건 보내 전달과 검증을 처음부터 끝까지 확인하세요. 이 발송은 다른 라이브 발송처럼 크레딧을 씁니다.