API 공통 사항
Book Print API의 인증, 요청/응답 형식, 페이지네이션, Rate Limiting 등 공통 규칙을 안내합니다.
인증
모든 API 요청에는 Bearer Token 인증이 필요합니다. 파트너 포털에서 발급받은 API Key를 Authorization 헤더에 포함하세요.
API Key는 SB로 시작하는 12자 prefix(SB + 10자 식별자)와 secret이 .으로 연결된 형식입니다. Sandbox 키와 Live 키가 분리되어 있으므로 환경에 맞는 키를 사용하세요.
Authorization: Bearer SB{10자 식별자}.{secret}
예시 (더미):
Authorization: Bearer SBXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxBase URL
| 환경 | Base URL | 용도 |
|---|---|---|
| Sandbox | https://api-sandbox.sweetbook.com/v1 | 개발 및 테스트 |
| Live | https://api.sweetbook.com/v1 | 실제 운영 |
요청 / 응답 형식
대부분의 API 요청은 JSON 형식을 사용합니다. 파일 업로드가 필요한 엔드포인트는 multipart/form-data를 사용합니다.
| Content-Type | 대상 엔드포인트 |
|---|---|
application/json | 일반 API (Orders, Webhooks 등) |
multipart/form-data | 파일 업로드 (Cover, Contents, Photos) |
응답 필드
성공/실패 모두 공통 shape을 사용합니다. 실패 응답은 6필드 고정(success·errorCode·message·data·errors·fieldErrors)이며 errorCode·errors·fieldErrors가 항상 존재합니다. errorCode는 HTTP 상태 기반 기본값이 자동 주입되어 빈 문자열이 되지 않으며, errors·fieldErrors는 해당 항목이 없으면 빈 배열([])로 반환됩니다.
| 필드 | 타입 | 성공 | 실패 | 설명 |
|---|---|---|---|---|
success | boolean | O | O | 요청 성공 여부(성공 true / 실패 false) |
errorCode | string | - | O | 실패 분기용 기계 판독 식별자. ERR_* 형식. 실패 시 항상 존재 |
message | string | O | O | HTTP 상태의 영어 라벨(예: "Success", "Bad Request") |
data | object | null | O | O | 응답 데이터. 실패 시 null이 기본이나 일부 에러는 진단 객체 포함(예: 402 {required, balance, currency}) |
errors | string[] | - | O | 사용자 표시용 한글 메시지 배열. 실패 시 항상 존재(비어 있으면 []) |
fieldErrors | object[] | - | O | 필드 단위 구조화 에러 배열. 실패 시 항상 존재(해당 없으면 []) |
errorCode(+ 필요 시 fieldErrors[].constraint)로 하세요. 사용자 표시용 한글은 errors[0]을 사용하고, message는 영어 라벨이라 표시 용도가 아닙니다. 메시지 문자열을 파싱해 분기하는 방식은 금지합니다(문구가 바뀔 수 있음). 전체 errorCode 카탈로그는 에러 코드 & 트러블슈팅을 참조하세요.성공 응답
요청이 성공하면 success: true와 함께 data 필드에 결과가 포함됩니다.
// 성공 응답
{
"success": true,
"message": "Success",
"data": { ... }
}에러 응답
요청이 실패하면 success: false와 함께 6필드 고정 shape으로 반환됩니다. errorCode는 항상 존재하며, 단순 에러는 errors 메시지만, 필드 단위 검증 실패는 fieldErrors에 구조화 정보가 포함됩니다.
참고: 도메인 검증(예: 페이지 제약, finalize 전제조건)의 errors·message는 한글로 반환되지만, 요청 본문의 필수 필드가 아예 누락되는 케이스(모델 바인딩 자동 검증)는 영어 라벨(예: "The PostalCode field is required.")로 반환될 수 있습니다. 클라이언트 분기는 errorCode와 fieldErrors[].constraint로 하세요.
{
"success": false,
"errorCode": "ERR_NOT_FOUND",
"message": "Not Found",
"data": null,
"errors": ["책을 찾을 수 없습니다: bk_xxx"],
"fieldErrors": []
}{
"success": false,
"errorCode": "ERR_VALIDATION_FAILED",
"message": "Bad Request",
"data": null,
"errors": [
"shipping.address1: The Address1 field is required.",
"shipping.postalCode: The PostalCode field is required.",
"shipping.recipientName: The RecipientName field is required.",
"shipping.recipientPhone: The RecipientPhone field is required."
],
"fieldErrors": [
{ "field": "shipping.address1", "message": "The Address1 field is required.", "constraint": "required" },
{ "field": "shipping.postalCode", "message": "The PostalCode field is required.", "constraint": "required" },
{ "field": "shipping.recipientName", "message": "The RecipientName field is required.", "constraint": "required" },
{ "field": "shipping.recipientPhone", "message": "The RecipientPhone field is required.", "constraint": "required" }
]
}fieldErrors 항목 구조
필드 단위 검증 실패는 fieldErrors 배열의 각 항목이 아래 필드를 가집니다. field·message는 항상 존재하며, 나머지 3개는 해당 맥락에서 값이 있을 때만 포함됩니다(없으면 키 자체가 생략).
| 필드 | 타입 | 항상 포함 | 설명 |
|---|---|---|---|
field | string | O | 에러가 발생한 필드명. 중첩 필드는 점 표기(shipping.recipientPhone) |
message | string | O | 해당 필드의 한글 설명 |
currentValue | any | - | 현재 전달된 값(표시 가능할 때만) |
requiredValue | any | - | 기대값 또는 허용 범위. 단일 값(예: 24)일 수도 있고 객체(예: {"min": 50, "increment": 2}) 형태일 수도 있음 |
constraint | string | - | 제약 종류. 클라이언트 분기에 사용. 아래 enum 6종 |
constraint enum
| 값 | 의미 |
|---|---|
required | 필수 필드 누락 |
min | 최솟값 미만(숫자) 또는 최소 개수 미만(배열/페이지) |
max | 최댓값 초과(숫자) 또는 최대 개수 초과(배열/페이지) |
increment | 증분 규칙 위반(예: 페이지 수가 지정된 증분 단위의 배수가 아님) |
enum | 허용 목록 밖의 값 |
pattern | 정규식 패턴 불일치(형식 오류) |
{
"field": "pageCount",
"message": "최소 24 페이지가 필요합니다",
"currentValue": 10,
"requiredValue": 24,
"constraint": "min"
}페이지네이션
목록 조회 API는 limit/offset 기반 페이지네이션을 사용합니다. 응답 구조는 data.{리소스명} 배열과 data.pagination 객체가 분리된 형태입니다. 리소스명은 엔드포인트에 따라 다릅니다 (books, orders, templates 등).
Query 파라미터
| 파라미터 | 기본값 | 최대값 | 설명 |
|---|---|---|---|
limit | 20 | 100 | 한 페이지에 반환할 항목 수 |
offset | 0 | - | 건너뛸 항목 수 |
공통 응답 필드
| 필드 | 타입 | 설명 |
|---|---|---|
data.{리소스} | array | 항목 배열. 키 이름은 엔드포인트마다 다름 (books, orders, templates 등) |
data.pagination.total | number | 전체 항목 수 |
data.pagination.limit | number | 현재 요청의 limit |
data.pagination.offset | number | 현재 요청의 offset |
data.pagination.hasNext | boolean | 다음 페이지 존재 여부 |
data 자체가 배열로 반환되고, 페이지네이션 메타는 pagination 최상위 형제 필드로 노출됩니다. 이전 패턴(data.books·data.transactions·data.templates·data.photos 등 리소스 키 중첩)은 더 이상 사용되지 않습니다. 단건 GET·단건 액션·통계 리포트는 변경 없습니다.{
"success": true,
"message": "성공",
"data": [ /* ... book 항목들 */ ],
"pagination": {
"total": 0,
"limit": 10,
"offset": 0,
"hasNext": false
}
}{
"success": true,
"message": "성공",
"data": [ /* ... order 항목들 */ ],
"pagination": {
"total": 42,
"limit": 20,
"offset": 0,
"hasNext": true
}
}{
"success": true,
"message": "성공",
"data": [ /* ... template 항목들 */ ],
"pagination": {
"total": 120,
"limit": 100,
"offset": 0,
"hasNext": true
}
}Rate Limiting
| 정책 | 대상 | 제한 | 기준 |
|---|---|---|---|
| auth | 인증 엔드포인트 | 10 req/min | IP 기반 |
| general | 일반 API | 300 req/min | API Key 기반 |
| upload | 파일 업로드 / Contents | 200 req/min | API Key 기반 |
Rate Limit을 초과하면 429 Too Many Requests 응답이 반환됩니다. 응답의 Retry-After 헤더(초 단위)를 확인한 뒤 재시도하세요. errorCode는 ERR_TOO_MANY_REQUESTS로 반환됩니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json{
"success": false,
"errorCode": "ERR_TOO_MANY_REQUESTS",
"message": "Too Many Requests",
"data": null,
"errors": ["요청 한도를 초과했습니다. Retry-After 헤더 값만큼 대기 후 재시도하세요."],
"fieldErrors": []
}멱등성 (Idempotency)
네트워크 오류로 동일한 요청이 중복 실행되는 것을 방지하기 위해 Idempotency-Key 헤더를 지원합니다.Books/Credits/Orders 주요 POST 엔드포인트(POST /books, POST /orders, POST /credits/sandbox/charge 등)에서 사용을 강력히 권장합니다.
동일한 Idempotency-Key + 같은 요청 본문으로 재시도하면 최초 응답이 그대로 재사용됩니다(중복 처리 방지). 동일한 키 + 다른 요청 본문을 보내면 422 Unprocessable Entity + errorCode: "ERR_IDEMPOTENCY_KEY_MISMATCH"가 반환됩니다. 따라서 재시도 시에는 반드시 원본과 동일한 본문을 같은 키로 보내야 합니다.
| 엔드포인트 | 설명 |
|---|---|
POST /books | 책 중복 생성 방지 |
POST /books/{bookUid}/cover | 표지 추가 중복 요청 방지 |
POST /books/{bookUid}/contents | 내지 추가 중복 요청 방지 |
POST /books/{bookUid}/finalization | 최종화 중복 요청 방지 |
POST /orders | 충전금 이중 차감 방지 |
POST /orders/{orderUid}/cancel | 전체 취소 중복 요청 방지 (환불 이중 처리 방지) |
POST /orders/{orderUid}/items/{itemUid}/cancel | 부분 취소 중복 요청 방지 (환불 이중 처리 방지) |
curl -X POST 'https://api-sandbox.sweetbook.com/v1/orders' \
-H 'Authorization: Bearer {YOUR_API_KEY}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: unique-request-id-12345' \
-d '{ ... }'주요 HTTP 에러 코드
자주 만나는 HTTP 상태 코드와 기본 errorCode 대응입니다. 상태 코드별 상세 케이스·응답 예시·마이그레이션 가이드는 에러 코드 & 트러블슈팅을 참조하세요.
| 상태 코드 | 기본 errorCode | 원인 | 해결 방법 |
|---|---|---|---|
400 Bad Request | ERR_VALIDATION_FAILED | 요청 파라미터 또는 바디 유효성 검증 실패 | fieldErrors를 확인하여 오류 필드 수정 |
401 Unauthorized | ERR_UNAUTHORIZED | API Key 누락 또는 유효하지 않음 | Authorization 헤더에 올바른 API Key 포함 |
402 Payment Required | ERR_INSUFFICIENT_CREDIT | 충전금 잔액 부족 | data의 required·balance 확인 후 충전금 충전 |
403 Forbidden | ERR_FORBIDDEN | 접근 권한 없음 | 파트너 포털에서 API Key 상태 및 IP 제한 설정 확인 |
404 Not Found | ERR_NOT_FOUND | 요청한 리소스가 존재하지 않거나 접근 권한 없음 | UID 및 파트너 소유권 확인 |
409 Conflict | ERR_CONFLICT | 리소스 상태 충돌(예: 이미 등록된 표지 PDF에 POST 재호출) | 리소스 현재 상태 확인 후 적절한 메서드 사용 |
422 Unprocessable Entity | ERR_IDEMPOTENCY_KEY_MISMATCH | 동일 Idempotency-Key로 다른 요청 본문 전송 | 동일 키는 원본과 같은 본문으로만 재시도. 본문이 다르면 새 키 사용 |
429 Too Many Requests | ERR_TOO_MANY_REQUESTS | Rate Limit 초과 | Retry-After 헤더 값(초) 대기 후 재시도 |
500 Internal Server Error | ERR_INTERNAL_ERROR | 서버 내부 오류 | 잠시 후 재시도, 지속 시 support@sweetbook.com 문의 |
501 Not Implemented | ERR_SANDBOX_UNSUPPORTED | Sandbox에서 의도적으로 차단된 엔드포인트 호출 (Live 전용) | Live 환경에서만 호출. 자동 재시도 화이트리스트에서 제외. 엔드포인트별 환경 호출 가능 여부는 환경 매트릭스 참조 |
PDF 다운로드(GET /books/{bookUid}/pdf-cover·GET /books/{bookUid}/pdf-contents)는 책의 creationType에 따라 위 일반 에러 외에 5종 추가 errorCode(ERR_PDF_NOT_UPLOADED 404 / ERR_PDF_NOT_GENERATED 409 / ERR_PDF_PENDING 409 / ERR_PDF_GENERATION_FAILED 422 / ERR_PDF_FILE_MISSING 500)로 분기됩니다. 자세한 매트릭스는 Books — PDF 기반 / Books — 템플릿 기반 / 에러 코드 & 트러블슈팅의 PDF 다운로드 섹션을 참조하세요.
날짜 형식
모든 날짜/시간 필드는 ISO 8601 형식(UTC)을 사용합니다. 응답 필드에 따라 밀리초 단위까지 포함될 수 있습니다.
| 형식 | 예시 | 사용 맥락 |
|---|---|---|
| 초 단위 | 2026-04-22T07:16:39.000Z | 대부분의 타임스탬프 (createdAt, orderedAt, paidAt 등) |
| 밀리초 포함 | 2026-04-22T01:04:38.104Z | 주문 생성 등 시각 정밀도가 필요한 필드 (createdAt 일부) |