16 KiB
16 KiB
선물하기 클라이언트 API 요약
문서 목적
- 이 문서는 클라이언트 구현 프롬프트 작성용 빠른 참조다.
- 상세 request/response 계약은
prd.md의8. API 계약을 기준으로 한다. - 여기서는 사용자 역할별 URI, 역할, 핵심 request/response 필드만 요약한다.
문서 분리 결정
PRD에는 상세 API 계약, request/response 예시, 요구사항을 유지한다. 클라이언트 프롬프트용 역할별 인벤토리는 별도 문서로 둔다.
- 이유: PRD에 같은 URI 정보를 역할별로 다시 넣으면 상세 계약과 요약표를 동시에 갱신해야 한다.
- 기준: PRD는 제품 요구사항과 API 계약의 원본, 이 문서는 클라이언트 구현을 위한 탐색용 색인이다.
공통/조회 API
| 사용자 | Method | URI | 역할 | Request | Response |
|---|---|---|---|---|---|
| 비회원/회원 | GET | /api/v2/gifts/form-options |
선물 신청 폼에 필요한 활성 카테고리와 사이즈별 가격 옵션을 조회한다. | 없음 | sizes[], categories[] |
| 팬/크리에이터 | GET | /api/v2/gifts?type=ALL&page=0&size=20 |
내 선물함 목록을 조회하고 `type=ALL | SENT | RECEIVED`로 전체/보낸/받은 선물을 필터링한다. |
| 팬/크리에이터 | GET | /api/v2/gifts/{applicationNo} |
선물 상세를 조회하며 로그인 회원이 팬인지 크리에이터인지에 따라 노출 정보와 CTA flag가 달라진다. | path: applicationNo |
applicationNo, direction, status, statusName, giftInfo, senderInfo, recipientAddress, mailbox, trackingRequired, recipientAddressRequired, delivery, statusTimeline, review |
팬 API
| 사용자 | Method | URI | 역할 | Request | Response |
|---|---|---|---|---|---|
| 팬 | POST | /api/v2/gifts |
크리에이터에게 선물을 신청하고 최종 결제 금액만큼 캔을 차감한다. | body: recipientMemberId, senderName, senderPhoneNumber, senderZipCode, senderAddress, senderAddressDetail, sizeCode, categoryId, senderTermsAgreed, senderPrivacyAgreed, damageWaiverAgreed |
applicationNo, status, statusName, priceCan, trackingDeadlineAt |
| 팬 | POST | /api/v2/gifts/{applicationNo}/cancel |
운송장 등록 전 선물 신청을 취소하고 전액 환불한다. | path: applicationNo |
applicationNo, status, statusName, priceCan, canceledAt |
| 팬 | POST | /api/v2/gifts/{applicationNo}/tracking |
발송 후 택배사와 운송장 번호를 등록한다. | path: applicationNo, body: courierCompanyName, trackingNumber |
applicationNo, status, statusName, courierCompanyName, trackingNumber, trackingRegisteredAt, recipientAddressDeadlineAt |
| 팬 | POST | /api/v2/gifts/{applicationNo}/review |
전달 완료된 선물에 리뷰를 작성한다. | path: applicationNo, body: rating, keywords, comment |
reviewId, applicationNo, rating, keywords, comment, createdAt |
크리에이터 API
| 사용자 | Method | URI | 역할 | Request | Response |
|---|---|---|---|---|---|
| 크리에이터 | POST | /api/v2/gifts/{applicationNo}/recipient-address |
운송장 등록 이후 배송지와 수령 약관 동의를 등록한다. | path: applicationNo, body: recipientName, recipientPhoneNumber, recipientZipCode, recipientAddress, recipientAddressDetail, recipientTermsAgreed, recipientPrivacyAgreed |
applicationNo, status, statusName, recipientAddressRegisteredAt |
| 크리에이터 | POST | /api/v2/gifts/{applicationNo}/delivery-complete |
검수 완료된 선물을 직접 수령 완료 처리한다. | path: applicationNo |
applicationNo, status, statusName, deliveredAt |
관리자 API
| 사용자 | Method | URI | 역할 | Request | Response |
|---|---|---|---|---|---|
| 관리자 | GET | /api/v2/admin/gift-categories |
선물 카테고리 목록을 조회한다. | 없음 | AdminGiftCategoryResponse[] |
| 관리자 | GET | /api/v2/admin/gifts |
전체 선물함 목록을 조회하고 상태/신청번호/닉네임으로 검색한다. | query: status, applicationNo, nickname, page, size |
totalCount, items[], page, size, hasNext |
| 관리자 | GET | /api/v2/admin/gifts/{applicationNo} |
선물 상세와 발송인/수취인 개인정보, 상품/배송/상태 정보를 조회한다. | path: applicationNo |
applicationNo, senderInfo, recipientInfo, productInfo, inboundDeliveryInfo, status, statusName, availableActions |
| 관리자 | POST | /api/v2/admin/gift-categories |
선물 카테고리를 등록한다. | body: classificationNumber, categoryCode, name, receiptCode, representativeItem, requiresDamageWaiver, isActive |
categoryId, classificationNumber, categoryCode, name, receiptCode, representativeItem, requiresDamageWaiver, isActive |
| 관리자 | PUT | /api/v2/admin/gift-categories/{categoryId} |
선물 카테고리를 수정한다. | path: categoryId, body: classificationNumber, categoryCode, name, receiptCode, representativeItem, requiresDamageWaiver, isActive |
categoryId, classificationNumber, categoryCode, name, receiptCode, representativeItem, requiresDamageWaiver, isActive |
| 관리자 | DELETE | /api/v2/admin/gift-categories/{categoryId} |
선물 카테고리를 비활성화한다. | path: categoryId |
categoryId, classificationNumber, categoryCode, name, receiptCode, representativeItem, requiresDamageWaiver, isActive=false |
| 관리자 | GET | /api/v2/admin/gift-size-prices |
사이즈별 기본가와 판매가를 조회한다. | 없음 | AdminGiftSizePriceResponse[] |
| 관리자 | PUT | /api/v2/admin/gift-size-prices/{sizeCode} |
사이즈별 기본가와 판매가를 수정한다. | path: sizeCode, body: basePriceCan, salePriceCan, isActive |
sizeCode, name, basePriceCan, salePriceCan, isActive |
| 관리자 | POST | /api/v2/admin/gifts/{applicationNo}/arrive-mailbox |
발송 확인 선물을 사서함 도착 처리한다. | path: applicationNo |
applicationNo, status, statusName, occurredAt |
| 관리자 | POST | /api/v2/admin/gifts/{applicationNo}/complete-inspection |
사서함 도착 선물을 검수 완료 처리한다. | path: applicationNo |
applicationNo, status, statusName, occurredAt |
| 관리자 | POST | /api/v2/admin/gifts/{applicationNo}/mark-undeliverable |
진행 중 선물을 전달 불가 처리하고 사유를 저장한다. | path: applicationNo, body: reason |
applicationNo, status, statusName, occurredAt |
| 관리자 | POST | /api/v2/admin/gifts/{applicationNo}/complete-delivery |
검수 완료 선물을 운영자가 전달 완료 처리한다. | path: applicationNo |
applicationNo, status, statusName, occurredAt |
관리자 응답 DTO 요약:
AdminGiftCategoryResponse:categoryId,classificationNumber,categoryCode,name,receiptCode,representativeItem,requiresDamageWaiver,isActiveAdminGiftListResponse:totalCount,items,page,size,hasNextAdminGiftListItemResponse:applicationNo,senderNickname,recipientNickname,sizeCode,sizeName,categoryName,classificationNumber,courierCompanyName,trackingNumber,status,statusName,availableActionsAdminGiftDetailResponse:applicationNo,senderInfo,recipientInfo,productInfo,inboundDeliveryInfo,status,statusName,availableActionsAdminGiftSizePriceResponse:sizeCode,name,basePriceCan,salePriceCan,isActiveAdminGiftOperationStatusResponse:applicationNo,status,statusName,occurredAt
Kotlin DTO 형태
아래는 JSON으로 변환되기 직전의 API request/response data class 형태다. companion object 변환 함수는 클라이언트 입력/출력 형태와 직접 관련이 없어 생략했다. 응답 시각 필드는 UTC ISO 문자열이다. 예: 2026-09-30T12:00:00Z.
사용자 Request DTO
data class GiftApplicationRequest(
val recipientMemberId: Long,
val senderName: String,
val senderPhoneNumber: String,
val senderZipCode: String,
val senderAddress: String,
val senderAddressDetail: String?,
val sizeCode: GiftSize,
val categoryId: Long,
val senderTermsAgreed: Boolean,
val senderPrivacyAgreed: Boolean,
val damageWaiverAgreed: Boolean
)
data class GiftTrackingRegistrationRequest(
val courierCompanyName: String,
val trackingNumber: String
)
data class GiftRecipientAddressRegistrationRequest(
val recipientName: String,
val recipientPhoneNumber: String,
val recipientZipCode: String,
val recipientAddress: String,
val recipientAddressDetail: String?,
val recipientTermsAgreed: Boolean,
val recipientPrivacyAgreed: Boolean
)
data class GiftReviewRequest(
val rating: Int,
val keywords: List<String>,
val comment: String?
)
사용자 Response DTO
data class GiftFormOptionsResponse(
val sizes: List<GiftSizeOptionResponse>,
val categories: List<GiftCategoryOptionResponse>
)
data class GiftSizeOptionResponse(
val sizeCode: String,
val name: String,
val basePriceCan: Int,
val salePriceCan: Int
)
data class GiftCategoryOptionResponse(
val categoryId: Long,
val name: String,
val requiresDamageWaiver: Boolean
)
// name은 사용자 폼 옵션에서만 "분류명/대표품목" 형태로 조합된다.
data class GiftListResponse(
val totalCount: Long,
val items: List<GiftListItemResponse>,
val page: Int,
val size: Int,
val hasNext: Boolean
)
data class GiftListItemResponse(
val applicationNo: String,
val direction: String,
val status: String,
val statusName: String,
val priceCan: Int,
val categoryName: String,
val sizeName: String,
val createdAt: String?
)
data class GiftDetailResponse(
val applicationNo: String,
val direction: String,
val status: String,
val statusName: String,
val giftInfo: GiftDetailInfoResponse,
val senderInfo: GiftAddressResponse?,
val recipientAddress: GiftAddressResponse?,
val mailbox: GiftMailboxResponse?,
val trackingRequired: Boolean,
val recipientAddressRequired: Boolean,
val recipientAddressDeadlineAt: String?,
val delivery: GiftDeliveryInfoResponse,
val statusTimeline: List<GiftStatusTimelineResponse>
)
data class GiftDetailInfoResponse(
val recipientCreatorNickname: String?,
val senderNickname: String?,
val sizeName: String,
val categoryName: String,
val applicationNo: String?,
val paidCan: Int?,
val tracking: String?,
val shippingRequestedAt: String?
)
data class GiftAddressResponse(
val name: String,
val phoneNumber: String,
val address: String
)
data class GiftMailboxResponse(
val name: String,
val address: String,
val phoneNumber: String
)
// mailbox는 direction=SENT이고 status가 RECEIVED, TRACKING_REGISTERED,
// ARRIVED_AT_MAILBOX, INSPECTION_COMPLETED일 때만 값이 있다.
// DELIVERED, UNDELIVERABLE, CANCELED 또는 direction=RECEIVED이면 null이다.
data class GiftDeliveryInfoResponse(
val canceledAt: String?,
val undeliverableAt: String?,
val undeliverableReason: String?
)
data class GiftStatusTimelineResponse(
val status: String,
val statusName: String,
val occurredAt: String?
)
data class GiftApplicationResponse(
val applicationNo: String,
val status: String,
val statusName: String,
val priceCan: Int,
val trackingDeadlineAt: String
)
data class GiftCancellationResponse(
val applicationNo: String,
val status: String,
val statusName: String,
val priceCan: Int,
val canceledAt: String
)
data class GiftTrackingRegistrationResponse(
val applicationNo: String,
val status: String,
val statusName: String,
val courierCompanyName: String,
val trackingNumber: String,
val trackingRegisteredAt: String,
val recipientAddressDeadlineAt: String
)
data class GiftRecipientAddressRegistrationResponse(
val applicationNo: String,
val status: String,
val statusName: String,
val recipientAddressRegisteredAt: String
)
data class GiftDeliveryConfirmationResponse(
val applicationNo: String,
val status: String,
val statusName: String,
val deliveredAt: String
)
data class GiftReviewResponse(
val reviewId: Long,
val applicationNo: String,
val rating: Int,
val keywords: List<String>,
val comment: String?,
val createdAt: String?
)
관리자 Request DTO
data class AdminGiftCategoryRequest(
val classificationNumber: String,
val categoryCode: String,
val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean,
val isActive: Boolean
)
data class AdminGiftSizePriceRequest(
val basePriceCan: Int,
val salePriceCan: Int,
val isActive: Boolean
)
data class AdminGiftMarkUndeliverableRequest(
val reason: String
)
관리자 Response DTO
data class AdminGiftCategoryResponse(
val categoryId: Long,
val classificationNumber: String,
val categoryCode: String,
val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean,
val isActive: Boolean
)
data class AdminGiftListResponse(
val totalCount: Long,
val items: List<AdminGiftListItemResponse>,
val page: Int,
val size: Int,
val hasNext: Boolean
)
data class AdminGiftListItemResponse(
val applicationNo: String,
val senderNickname: String,
val recipientNickname: String,
val sizeCode: String,
val sizeName: String,
val categoryName: String,
val classificationNumber: String,
val courierCompanyName: String?,
val trackingNumber: String?,
val status: String,
val statusName: String,
val availableActions: List<String>
)
data class AdminGiftDetailResponse(
val applicationNo: String,
val senderInfo: AdminGiftMemberInfoResponse,
val recipientInfo: AdminGiftMemberInfoResponse,
val productInfo: AdminGiftProductInfoResponse,
val inboundDeliveryInfo: AdminGiftInboundDeliveryInfoResponse,
val status: String,
val statusName: String,
val availableActions: List<String>
)
data class AdminGiftMemberInfoResponse(
val nickname: String,
val name: String,
val phoneNumber: String,
val address: String
)
data class AdminGiftProductInfoResponse(
val sizeCode: String,
val sizeName: String,
val categoryName: String,
val classificationNumber: String
)
data class AdminGiftInboundDeliveryInfoResponse(
val courierCompanyName: String?,
val trackingNumber: String?
)
data class AdminGiftSizePriceResponse(
val sizeCode: String,
val name: String,
val basePriceCan: Int,
val salePriceCan: Int,
val isActive: Boolean
)
data class AdminGiftOperationStatusResponse(
val applicationNo: String,
val status: GiftStatus,
val statusName: String,
val occurredAt: String
)
클라이언트 구현 참고
- 선물 푸시 딥링크는
deepLinkValue=GIFT_DETAIL,deepLinkId=applicationNo를 사용한다. - 딥링크 진입 후에는
GET /api/v2/gifts/{applicationNo}로 상세를 조회해direction,trackingRequired,recipientAddressRequired를 기준으로 화면과 CTA를 결정한다. - 운송장 미등록 자동취소는 서버 스케줄러가 처리하며, 팬 클라이언트는 상세 조회의
status=CANCELED와 푸시를 기준으로 표시한다. - 모든 응답은 기존
ApiResponse.ok(...)envelope 안의data로 내려간다.