Files
sodalive-backend-spring-boot/docs/20260929_크리에이터_선물하기/admin-page-prompts.md
T

17 KiB

선물 관리자 페이지 구현 프롬프트

이 문서는 관리자 페이지의 선물 관련 4개 화면을 프론트엔드 구현 에이전트에게 전달하기 위한 프롬프트 모음이다.

공통 전제:

  • 관리자 메뉴에는 parent 메뉴 선물함 관리 아래에 다음 4개 하위 메뉴가 있다.
    • 선물함 리스트 → /gift/list
    • 받을 주소 → /gift/mailbox
    • 선물 카테고리 → /gift/category
    • 선물 사이즈 → /gift/size
  • 모든 API 응답은 기존 공통 envelope를 사용한다. 화면에서는 response.data를 실제 payload로 사용한다.
  • 인증/권한 처리는 기존 관리자 페이지의 API 클라이언트, 토큰 저장 방식, 에러 처리 방식을 따른다.
  • 새 디자인 시스템을 만들지 말고 기존 관리자 페이지의 테이블, 필터, 버튼, 모달, 폼, 토스트, 확인창 패턴을 재사용한다.
  • 화면 문구는 한국어로 작성한다.
  • API 실패 시 기존 관리자 페이지의 공통 에러 표시 방식을 따른다.

1. 선물함 리스트 페이지 프롬프트

관리자 선물함 리스트 페이지를 구현해줘.

목표:
- 관리자 메뉴 `선물함 관리 > 선물함 리스트`에서 전체 선물 신청 목록을 조회한다.
- 상태, 신청번호, 닉네임으로 필터링할 수 있다.
- 목록 row에서 선물 상세를 확인하고, 현재 상태에서 가능한 운영 액션을 실행할 수 있다.
- 기존 관리자 페이지의 테이블/필터/페이지네이션/모달/확인창 스타일을 그대로 따른다.

라우트:
- `/gift/list`

필수 화면 구성:
- 상단 제목: `선물함 리스트`
- 필터 영역:
  - 상태 select: 전체, 접수 완료, 발송 확인, 사서함 도착, 검수 완료, 전달 완료, 전달 불가, 신청 취소
  - 신청번호 검색 input: `applicationNo` 부분 검색
  - 닉네임 검색 input: 발송인 또는 수취인 닉네임 부분 검색
  - 검색 버튼, 초기화 버튼
- 목록 테이블 컬럼:
  - 신청번호 `applicationNo`
  - 발송인 닉네임 `senderNickname`
  - 수취인 닉네임 `recipientNickname`
  - 사이즈 `sizeName` / `sizeCode`
  - 카테고리 `categoryName`
  - 분류번호 `classificationNumber`
  - 택배사 `courierCompanyName`
  - 운송장번호 `trackingNumber`
  - 상태 `statusName`
  - 액션 버튼
- 페이지네이션:
  - 서버의 `page`, `size`, `totalCount`, `hasNext` 기준으로 기존 관리자 페이지 패턴에 맞춘다.

목록 조회 API:
- Method: `GET`
- URL: `/api/v2/admin/gifts`
- Query:
  - `status?: GiftStatus`
  - `applicationNo?: string`
  - `nickname?: string`
  - `page?: number` 기본 `0`
  - `size?: number` 기본 `20`
- 필터 조합:
  - 선택 필터는 AND 조건이다.
  - `nickname`은 발송인/수취인 닉네임 중 하나에 포함되면 매칭된다.

목록 조회 response `data`:
```ts
type AdminGiftListResponse = {
  totalCount: number;
  items: AdminGiftListItemResponse[];
  page: number;
  size: number;
  hasNext: boolean;
};

type AdminGiftListItemResponse = {
  applicationNo: string;
  senderNickname: string;
  recipientNickname: string;
  sizeCode: string;
  sizeName: string;
  categoryName: string;
  classificationNumber: string;
  courierCompanyName: string | null;
  trackingNumber: string | null;
  status: GiftStatus;
  statusName: string;
  availableActions: AdminGiftAction[];
};

상태 enum:

type GiftStatus =
  | "RECEIVED"
  | "TRACKING_REGISTERED"
  | "ARRIVED_AT_MAILBOX"
  | "INSPECTION_COMPLETED"
  | "DELIVERED"
  | "UNDELIVERABLE"
  | "CANCELED";

상태 표시명:

  • RECEIVED: 접수 완료
  • TRACKING_REGISTERED: 발송 확인
  • ARRIVED_AT_MAILBOX: 사서함 도착
  • INSPECTION_COMPLETED: 검수 완료
  • DELIVERED: 전달 완료
  • UNDELIVERABLE: 전달 불가
  • CANCELED: 신청 취소

상세 조회 API:

  • Method: GET
  • URL: /api/v2/admin/gifts/{applicationNo}
  • 용도:
    • 목록 row 클릭 또는 상세 버튼 클릭 시 상세 모달/상세 패널을 연다.

상세 response data:

type AdminGiftDetailResponse = {
  applicationNo: string;
  senderInfo: AdminGiftMemberInfoResponse;
  recipientInfo: AdminGiftMemberInfoResponse;
  productInfo: AdminGiftProductInfoResponse;
  inboundDeliveryInfo: AdminGiftInboundDeliveryInfoResponse;
  status: GiftStatus;
  statusName: string;
  availableActions: AdminGiftAction[];
};

type AdminGiftMemberInfoResponse = {
  nickname: string;
  name: string;
  phoneNumber: string;
  address: string;
};

type AdminGiftProductInfoResponse = {
  sizeCode: string;
  sizeName: string;
  categoryName: string;
  classificationNumber: string;
};

type AdminGiftInboundDeliveryInfoResponse = {
  courierCompanyName: string | null;
  trackingNumber: string | null;
};

상세 표시 요구사항:

  • 발송인 정보: 닉네임, 이름, 전화번호, 주소
  • 수취인 정보: 닉네임, 이름, 전화번호, 주소
  • 상품 정보: 사이즈, 카테고리, 분류번호
  • 입고 배송 정보: 택배사, 운송장번호
  • 현재 상태와 가능한 액션
  • 수취인이 배송지를 아직 입력하지 않은 경우 recipientInfo.nickname은 표시하고 name, phoneNumber, address는 빈 문자열로 온다. 화면에서는 미입력으로 표시해도 된다.

운영 액션 enum:

type AdminGiftAction =
  | "ARRIVE_MAILBOX"
  | "COMPLETE_INSPECTION"
  | "COMPLETE_DELIVERY"
  | "MARK_UNDELIVERABLE";

상태별 availableActions:

  • RECEIVED: 없음
  • TRACKING_REGISTERED: ARRIVE_MAILBOX, MARK_UNDELIVERABLE
  • ARRIVED_AT_MAILBOX: COMPLETE_INSPECTION, MARK_UNDELIVERABLE
  • INSPECTION_COMPLETED: COMPLETE_DELIVERY, MARK_UNDELIVERABLE
  • DELIVERED: 없음
  • UNDELIVERABLE: 없음
  • CANCELED: 없음

액션 버튼 문구:

  • ARRIVE_MAILBOX: 사서함 도착 처리
  • COMPLETE_INSPECTION: 검수 완료 처리
  • COMPLETE_DELIVERY: 전달 완료 처리
  • MARK_UNDELIVERABLE: 전달 불가 처리

액션 API:

  • 사서함 도착 처리
    • Method: POST
    • URL: /api/v2/admin/gifts/{applicationNo}/arrive-mailbox
    • Body: 없음
  • 검수 완료 처리
    • Method: POST
    • URL: /api/v2/admin/gifts/{applicationNo}/complete-inspection
    • Body: 없음
  • 전달 완료 처리
    • Method: POST
    • URL: /api/v2/admin/gifts/{applicationNo}/complete-delivery
    • Body: 없음
  • 전달 불가 처리
    • Method: POST
    • URL: /api/v2/admin/gifts/{applicationNo}/mark-undeliverable
    • Body:
type AdminGiftMarkUndeliverableRequest = {
  reason: string;
};

액션 response data:

type AdminGiftOperationStatusResponse = {
  applicationNo: string;
  status: GiftStatus;
  statusName: string;
  occurredAt: string; // UTC ISO string, e.g. 2026-09-30T12:00:00Z
};

액션 UX:

  • 모든 상태 변경 액션은 확인창을 띄운다.
  • MARK_UNDELIVERABLE은 사유 입력 모달을 띄운다.
  • 전달 불가 사유는 공백만 입력할 수 없고 255자 이내로 제한한다.
  • 액션 성공 후:
    • 상세 모달이 열려 있으면 상세를 다시 조회한다.
    • 목록도 현재 필터/페이지 기준으로 다시 조회한다.
    • 성공 토스트를 표시한다.
  • 액션 실패 시 기존 관리자 페이지 에러 토스트/알림 패턴을 따른다.

빈 값 표시:

  • courierCompanyName, trackingNumber가 null이면 -로 표시한다.
  • 수취인 개인정보가 빈 문자열이면 미입력으로 표시한다.

테스트/검증:

  • API 클라이언트 함수 단위 테스트 또는 페이지 테스트에서 query parameter가 올바르게 전달되는지 확인한다.
  • availableActions에 따라 버튼 노출이 달라지는지 확인한다.
  • 전달 불가 사유 빈 값 validation을 확인한다.

---

## 2. 받을 주소 설정 페이지 프롬프트

```text
관리자 선물 받을 주소 설정 페이지를 구현해줘.

목표:
- 관리자 메뉴 `선물함 관리 > 받을 주소`에서 팬이 선물을 보낼 전역 단일 주소를 조회하고 저장한다.
- 이 주소는 팬의 선물 상세 API에서 운송장 등록 전 상태일 때 `mailbox`로 노출된다.
- 기존 관리자 페이지의 폼, 저장 버튼, 토스트, 에러 표시 패턴을 그대로 따른다.

라우트:
- `/gift/mailbox`

필수 화면 구성:
- 상단 제목: `받을 주소`
- 설명 문구: `팬이 선물을 발송할 때 확인하는 받을 주소입니다.`
- 입력 폼:
  - 받을 사람 이름 `name`
  - 연락처 `phoneNumber`
  - 우편번호 `zipCode`
  - 주소 `address`
  - 상세주소 `addressDetail`
- 저장 버튼: `저장`

초기 조회 API:
- Method: `GET`
- URL: `/api/v2/admin/gift-mailbox`
- response `data`:
```ts
type AdminGiftMailboxResponse = {
  name: string;
  address: string;
  phoneNumber: string;
} | null;
  • data가 null이면 빈 폼을 표시한다.

저장 API:

  • Method: PUT
  • URL: /api/v2/admin/gift-mailbox
  • Request:
type AdminGiftMailboxRequest = {
  name: string;
  phoneNumber: string;
  zipCode: string;
  address: string;
  addressDetail: string | null;
};
  • Response data:
type AdminGiftMailboxResponse = {
  name: string;
  address: string;
  phoneNumber: string;
};

Validation:

  • name, phoneNumber, zipCode, address는 필수다.
  • 공백만 입력할 수 없다.
  • 저장 실패 시 기존 관리자 페이지의 공통 에러 표시 방식을 따른다.

저장 성공 UX:

  • 성공 토스트를 표시한다.
  • 저장 API response 기준으로 화면 값을 갱신한다.
  • address는 서버가 조립한 (우편번호) 주소, 상세주소 형식으로 표시해도 되고, 입력 필드는 사용자가 입력한 값을 유지해도 된다.

테스트/검증:

  • 초기 조회 시 null이면 빈 폼을 표시하는지 확인한다.
  • 저장 시 PUT body가 정확히 전달되는지 확인한다.
  • 필수값 공백 validation을 확인한다.

---

## 3. 선물 카테고리 CRUD 페이지 프롬프트

```text
관리자 선물 카테고리 CRUD 페이지를 구현해줘.

목표:
- 관리자 메뉴 `선물함 관리 > 선물 카테고리`에서 선물 카테고리를 조회, 등록, 수정, 비활성화한다.
- 기존 관리자 페이지의 CRUD 테이블, 등록/수정 모달, 삭제 확인창, 토스트 패턴을 그대로 따른다.

라우트:
- `/gift/category`

필수 화면 구성:
- 상단 제목: `선물 카테고리`
- 상단 우측 `카테고리 등록` 버튼
- 목록 테이블 컬럼:
  - 카테고리 ID `categoryId`
  - 분류번호 `classificationNumber`
  - 카테고리 코드 `categoryCode`
  - 카테고리명 `name`
  - 접수 코드 `receiptCode`
  - 대표 품목 `representativeItem`
  - 파손면책 동의 필요 여부 `requiresDamageWaiver`
  - 활성 여부 `isActive`
  - 관리 버튼: 수정, 비활성화
- 등록/수정 폼 필드:
  - `classificationNumber`
  - `categoryCode`
  - `name`
  - `receiptCode`
  - `representativeItem`
  - `requiresDamageWaiver`
  - `isActive`

목록 조회 API:
- Method: `GET`
- URL: `/api/v2/admin/gift-categories`
- Request: 없음
- Response `data`:
```ts
type AdminGiftCategoryResponse = {
  categoryId: number;
  classificationNumber: string;
  categoryCode: string;
  name: string;
  receiptCode: string;
  representativeItem: string;
  requiresDamageWaiver: boolean;
  isActive: boolean;
};

등록 API:

  • Method: POST
  • URL: /api/v2/admin/gift-categories
  • Body:
type AdminGiftCategoryRequest = {
  classificationNumber: string;
  categoryCode: string;
  name: string;
  receiptCode: string;
  representativeItem: string;
  requiresDamageWaiver: boolean;
  isActive: boolean;
};
  • Response data: AdminGiftCategoryResponse

수정 API:

  • Method: PUT
  • URL: /api/v2/admin/gift-categories/{categoryId}
  • Body: AdminGiftCategoryRequest
  • Response data: AdminGiftCategoryResponse

비활성화 API:

  • Method: DELETE
  • URL: /api/v2/admin/gift-categories/{categoryId}
  • Body: 없음
  • Response data: AdminGiftCategoryResponse
  • 서버는 실제 삭제가 아니라 isActive=false 논리 삭제로 처리한다.

폼 validation:

  • classificationNumber: 필수, 숫자 3자리. 예: 100
  • categoryCode: 필수, 50자 이하. 예: DOLL
  • name: 필수, 50자 이하. 예: 인형
  • receiptCode: 필수, 영문 대문자 1~9자. 예: A
  • representativeItem: 필수, 100자 이하. 예: 피규어
  • requiresDamageWaiver: boolean
  • isActive: boolean

UX 요구사항:

  • 등록 성공 후 목록을 다시 조회하고 모달을 닫는다.
  • 수정 성공 후 목록을 다시 조회하고 모달을 닫는다.
  • 비활성화 버튼은 확인창을 띄운 뒤 호출한다.
  • 이미 비활성화된 row는 비활성화 버튼을 disabled 처리한다.
  • boolean 값은 기존 관리자 페이지 표현에 맞춰 예/아니오, 활성/비활성 또는 badge로 표시한다.
  • categoryCode는 수정 폼에 표시하되 서버 쪽 엔티티는 코드 변경을 실제로 반영하지 않을 수 있으므로, 가능하면 수정 시 읽기 전용으로 두고 나머지 운영 필드만 수정하도록 UX를 구성한다.

테스트/검증:

  • 목록 조회가 GET /api/v2/admin/gift-categories를 호출하는지 확인한다.
  • 등록/수정 body가 AdminGiftCategoryRequest 형태와 일치하는지 확인한다.
  • validation 실패 시 API를 호출하지 않고 폼 에러를 표시한다.
  • 비활성화 성공 후 해당 row의 isActive=false가 반영되는지 확인한다.

---

## 4. 선물 사이즈 가격 CRUD 페이지 프롬프트

```text
관리자 선물 사이즈 가격 CRUD 페이지를 구현해줘.

목표:
- 관리자 메뉴 `선물함 관리 > 선물 사이즈`에서 선물 사이즈별 기본가/판매가/활성 여부를 조회하고 수정한다.
- 현재 API는 사이즈 가격의 목록 조회와 수정만 제공한다. 신규 생성과 물리 삭제 기능은 만들지 않는다.
- 기존 관리자 페이지의 테이블, 수정 모달, 저장 확인/토스트 패턴을 그대로 따른다.

라우트:
- `/gift/size`

필수 화면 구성:
- 상단 제목: `선물 사이즈`
- 목록 테이블 컬럼:
  - 사이즈 코드 `sizeCode`
  - 사이즈명 `name`
  - 기본가 `basePriceCan`
  - 판매가 `salePriceCan`
  - 활성 여부 `isActive`
  - 관리 버튼: 수정
- 수정 폼 필드:
  - `sizeCode`: 읽기 전용
  - `name`: 읽기 전용
  - `basePriceCan`: number input
  - `salePriceCan`: number input
  - `isActive`: checkbox/switch

목록 조회 API:
- Method: `GET`
- URL: `/api/v2/admin/gift-size-prices`
- Request: 없음
- Response `data`:
```ts
type AdminGiftSizePriceResponse = {
  sizeCode: GiftSize;
  name: string;
  basePriceCan: number;
  salePriceCan: number;
  isActive: boolean;
};

수정 API:

  • Method: PUT
  • URL: /api/v2/admin/gift-size-prices/{sizeCode}
  • Body:
type AdminGiftSizePriceRequest = {
  basePriceCan: number;
  salePriceCan: number;
  isActive: boolean;
};
  • Response data: AdminGiftSizePriceResponse

사이즈 enum:

type GiftSize = "SMALL" | "MEDIUM" | "LARGE";

서버 validation:

  • salePriceCan은 0보다 커야 한다.
  • salePriceCan은 basePriceCan보다 클 수 없다.

프론트 validation:

  • basePriceCan: 필수, 1 이상 정수
  • salePriceCan: 필수, 1 이상 정수
  • salePriceCan <= basePriceCan
  • validation 실패 시 API 호출을 막고 필드 에러를 표시한다.

UX 요구사항:

  • 가격은 숫자 입력이지만 테이블 표시에서는 기존 관리자 페이지의 숫자 포맷을 따른다.
  • 수정 버튼 클릭 시 현재 row 값을 폼 초기값으로 넣는다.
  • 저장 전 확인창은 기존 관리자 페이지 패턴이 있으면 따른다.
  • 저장 성공 후 목록을 다시 조회하고 모달을 닫는다.
  • 신규 생성/삭제 버튼은 만들지 않는다.

테스트/검증:

  • 목록 조회가 GET /api/v2/admin/gift-size-prices를 호출하는지 확인한다.
  • 수정 저장 시 PUT /api/v2/admin/gift-size-prices/{sizeCode}와 request body가 정확한지 확인한다.
  • salePriceCan > basePriceCan일 때 API를 호출하지 않고 validation 메시지를 표시하는지 확인한다.
  • 저장 성공 후 목록 refresh가 일어나는지 확인한다.

---

## 구현 시 참고할 공통 타입

```ts
type ApiResponse<T> = {
  success: boolean;
  data: T;
  message?: string;
};

type GiftStatus =
  | "RECEIVED"
  | "TRACKING_REGISTERED"
  | "ARRIVED_AT_MAILBOX"
  | "INSPECTION_COMPLETED"
  | "DELIVERED"
  | "UNDELIVERABLE"
  | "CANCELED";

type AdminGiftAction =
  | "ARRIVE_MAILBOX"
  | "COMPLETE_INSPECTION"
  | "COMPLETE_DELIVERY"
  | "MARK_UNDELIVERABLE";

type GiftSize = "SMALL" | "MEDIUM" | "LARGE";

구현 순서 추천

  1. API client 함수와 TypeScript 타입을 먼저 추가한다.
  2. /gift/list 선물함 리스트와 상세/상태변경 모달을 구현한다.
  3. /gift/mailbox 받을 주소 설정 화면을 구현한다.
  4. /gift/category 카테고리 CRUD를 구현한다.
  5. /gift/size 사이즈 가격 수정 화면을 구현한다.
  6. 기존 관리자 메뉴의 선물함 관리 하위 route와 연결되는지 확인한다.
  7. 각 페이지별 loading/empty/error/success 상태를 확인한다.