Node.js와 Python
Node.js·Python 서버에서 인증을 생성하고 조회하는 예제
Node.js#
Node.js 22.12 이상에서 외부 패키지 없이 실행합니다.
javascript
// Node.js 22.12+. Server-side only. No dependencies.
const API = 'https://api.sendwich.kr';
export class SendwichError extends Error {
constructor(status, code, requestId) {
super(`sendwich: ${code} (${status})`);
this.status = status;
this.code = code;
this.requestId = requestId;
}
}
async function request(path, method = 'GET', body, idempotencyKey) {
const key = process.env.SENDWICH_API_KEY;
if (!key) throw new Error('Set the server-only SENDWICH_API_KEY variable.');
const response = await fetch(API + path, {
method,
signal: AbortSignal.timeout(10000),
headers: {
Authorization: `Bearer ${key}`,
...(body ? { 'Content-Type': 'application/json' } : {}),
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})
},
...(body ? { body: JSON.stringify(body) } : {})
});
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new SendwichError(
response.status, data.error?.code || 'request_failed',
data.error?.request_id || response.headers.get('X-Request-ID')
);
return data;
}
function sessionPath(id) {
if (!/^vrf_[A-Za-z0-9_-]+$/.test(id)) throw new Error('Invalid verification ID.');
return `/v1/verifications/${encodeURIComponent(id)}`;
}
export function createVerification(body, idempotencyKey) {
if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(idempotencyKey || '')) {
throw new Error('Provide a stable Idempotency-Key for this attempt.');
}
// Retry uncertain creates using the same body and key; do not auto-generate here.
return request('/v1/verifications', 'POST', body, idempotencyKey);
}
export const getVerification = id => request(sessionPath(id));
export const cancelVerification = id => request(sessionPath(id) + '/cancel', 'POST');같은 디렉터리에 start.mjs를 작성합니다.
javascript
import { randomUUID } from 'node:crypto';
import { createVerification, getVerification } from './sendwich-client.mjs';
const attempt = randomUUID();
const request = { mode: 'discover', client_reference: attempt, state: randomUUID() };
const session = await createVerification(request, attempt);
// 본인의 비공개 터미널에서 확인하는 실행 예제예요.
console.log('인증 ID:', session.id);
console.log('받는 번호:', session.destination);
console.log('보낼 문자:', session.sms_text);
console.log('만료:', session.expires_at);
// 이후 문자를 보낸 뒤 getVerification(session.id)로 조회해요.bash
SENDWICH_API_KEY='YOUR_LIVE_SECRET_KEY' node start.mjsPython#
Python 3.10 이상에서 표준 라이브러리로 실행합니다.
python
"""Python 3.10+. Standard library only; use on your server."""
import json
import os
import re
from urllib.error import HTTPError
from urllib.request import Request, urlopen
API = "https://api.sendwich.kr"
class SendwichError(Exception):
def __init__(self, status, code, request_id):
super().__init__(f"sendwich: {code} ({status})")
self.status, self.code, self.request_id = status, code, request_id
def _request(path, method="GET", body=None, idempotency_key=None):
key = os.environ["SENDWICH_API_KEY"]
headers = {"Authorization": f"Bearer {key}"}
if body is not None:
headers["Content-Type"] = "application/json"
if idempotency_key:
headers["Idempotency-Key"] = idempotency_key
data = json.dumps(body).encode("utf-8") if body is not None else None
try:
with urlopen(Request(API + path, data=data, headers=headers, method=method), timeout=10) as response:
return json.load(response)
except HTTPError as error:
with error:
try:
detail = json.load(error).get("error", {})
except (ValueError, AttributeError):
detail = {}
raise SendwichError(error.code, detail.get("code", "request_failed"),
detail.get("request_id") or error.headers.get("X-Request-ID")) from None
def create_verification(body, idempotency_key):
if not re.fullmatch(r"[A-Za-z0-9_.:-]{8,128}", idempotency_key):
raise ValueError("Provide a stable Idempotency-Key for this attempt")
return _request("/v1/verifications", "POST", body, idempotency_key)
def _session_path(verification_id):
if not re.fullmatch(r"vrf_[A-Za-z0-9_-]+", verification_id):
raise ValueError("Invalid verification ID")
return "/v1/verifications/" + verification_id
def get_verification(verification_id):
return _request(_session_path(verification_id))
def cancel_verification(verification_id):
return _request(_session_path(verification_id) + "/cancel", "POST")같은 디렉터리에서 다음과 같이 사용합니다. 실행 환경에 SENDWICH_API_KEY를 설정해야 합니다.
python
import uuid
from sendwich_client import create_verification, get_verification
attempt = str(uuid.uuid4())
session = create_verification(
{"mode": "discover", "client_reference": attempt, "state": str(uuid.uuid4())},
attempt,
)
# 본인의 비공개 터미널에서만 코드를 확인해요.
print(session["id"], session["destination"], session["sms_text"], session["expires_at"])
# 문자를 보낸 뒤 get_verification(session["id"])로 조회해요.서비스에 적용#
인증 ID, 요청 본문, Idempotency-Key를 사용자·시도와 함께 저장합니다. 생성 요청을 재시도할 때는 같은 키와 본문을 사용하고, 오류의 code와 status에 따라 재시도 조건을 적용합니다.
문자 전송 후 getVerification 또는 get_verification으로 조회합니다. 결과 반영 시 확인할 필드는 상태와 결과에 정리되어 있습니다.
예제의 콘솔 출력은 로컬 실행용입니다. 운영 환경에서는 인증 안내를 해당 사용자에게 전달합니다.
