Files
sodalive-backend-spring-boot/docs/20260929_크리에이터_선물하기/client-api-summary.md
T

15 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, isActive
  • AdminGiftListResponse: totalCount, items, page, size, hasNext
  • AdminGiftListItemResponse: applicationNo, senderNickname, recipientNickname, sizeCode, sizeName, categoryName, classificationNumber, courierCompanyName, trackingNumber, status, statusName, availableActions
  • AdminGiftDetailResponse: applicationNo, senderInfo, recipientInfo, productInfo, inboundDeliveryInfo, status, statusName, availableActions
  • AdminGiftSizePriceResponse: sizeCode, name, basePriceCan, salePriceCan, isActive
  • AdminGiftOperationStatusResponse: 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
)

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로 내려간다.