Skip to content

feat(ADMIN-336): 쿠폰 관리 추가 - #5

Merged
ilcm96 merged 2 commits into
mainfrom
feat/ADMIN-336
Feb 11, 2026
Merged

feat(ADMIN-336): 쿠폰 관리 추가#5
ilcm96 merged 2 commits into
mainfrom
feat/ADMIN-336

Conversation

@ilcm96

@ilcm96 ilcm96 commented Feb 11, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 쿠폰 관리 페이지 추가: 쿠폰 생성·수정·삭제 가능
    • 쿠폰 코드 관리: 코드 생성 및 삭제 지원
    • 쿠폰 발급 기능: 선택한 회원들에게 일괄 발급 가능
    • 검색 및 필터링: 쿠폰·코드·발급 항목 통합 검색 및 필터링
    • 탭 기반 UI: 쿠폰, 코드, 발급 별 분리된 관리 화면 제공

@ilcm96 ilcm96 self-assigned this Feb 11, 2026
@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

관리자용 쿠폰 관리 기능을 추가합니다. 새로운 쿠폰 관리 페이지 컴포넌트와 관련된 타입 정의, 범용 API 요청 유틸리티, 쿠폰 CRUD 및 코드/발행 관련 API 헬퍼들이 추가되며 라우트가 등록됩니다. (50단어 이내)

Changes

Cohort / File(s) Summary
라우팅
src/App.tsx
/coupon 경로 추가 및 Coupon 컴포넌트 임포트/라우팅 등록
타입 정의
src/api/coupon/types.ts
AdminCoupon, AdminCouponCode, AdminIssuedCoupon, AdminMemberSummary, ApiResult 제네릭 인터페이스 추가
공통 요청 유틸
src/api/coupon/request.ts
base URL 사용, JSON 파싱, 에러 추출, credentials 포함 반환형 ApiResult인 requestApi 구현
조회(GET) API
src/api/coupon/get-coupons.ts, src/api/coupon/get-coupon-codes.ts, src/api/coupon/get-issued-coupons.ts, src/api/coupon/get-admin-members.ts
관리자용 쿠폰/코드/발행/회원 목록을 가져오는 GET 헬퍼 함수 추가
생성(POST) API
src/api/coupon/post-coupon.ts, src/api/coupon/post-coupon-code.ts, src/api/coupon/post-issued-coupons.ts
쿠폰 생성, 쿠폰 코드 생성, 쿠폰 발행(회원 대상) POST 헬퍼 추가
수정(PATCH) API
src/api/coupon/patch-coupon-name.ts
쿠폰 이름 업데이트를 위한 PATCH 헬퍼 추가
삭제(DELETE) API
src/api/coupon/delete-coupon.ts, src/api/coupon/delete-coupon-code.ts, src/api/coupon/delete-issued-coupon.ts
쿠폰/쿠폰 코드/발행된 쿠폰 삭제용 DELETE 헬퍼 추가
페이지 컴포넌트
src/page/coupon.tsx
쿠폰/코드/발행 탭, 조회·검색·생성·수정·삭제·발행 UI 및 상태·로직을 포함한 관리자용 CouponPage 컴포넌트 추가

Sequence Diagram(s)

sequenceDiagram
    participant 관리자 as 관리자
    participant UI as CouponPage
    participant API as requestApi 헬퍼 / API 함수
    participant 서버 as 백엔드

    관리자->>UI: 쿠폰 페이지 접속
    activate UI
    UI->>API: getCoupons(), getCouponCodes(), getIssuedCoupons(), getAdminMembers()
    activate API
    API->>서버: GET /admin/coupons<br/>GET /admin/coupons/code<br/>GET /admin/coupons/issued<br/>GET /admin/members
    activate 서버
    서버-->>API: 데이터 응답 (JSON)
    deactivate 서버
    API-->>UI: ApiResult<T> 반환
    deactivate API
    UI->>UI: 상태 갱신 및 렌더링
    deactivate UI
    UI-->>관리자: 데이터 표시
Loading
sequenceDiagram
    participant 관리자 as 관리자
    participant UI as CouponPage
    participant API as requestApi 헬퍼 / API 함수
    participant 서버 as 백엔드

    관리자->>UI: 쿠폰 발행 요청 (쿠폰 선택, 회원 선택)
    activate UI
    UI->>UI: 입력 검증
    alt 검증 성공
        UI->>API: createIssuedCoupons(couponId, memberIds[])
        activate API
        API->>서버: POST /admin/coupons/issued { couponId, memberIds }
        activate 서버
        서버-->>API: 발행된 AdminIssuedCoupon[] 응답
        deactivate 서버
        API-->>UI: ApiResult<AdminIssuedCoupon[]>
        deactivate API
        UI->>UI: 알림 표시 및 getIssuedCoupons() 호출로 갱신
    else 검증 실패
        UI-->>관리자: 오류 메시지 표시
    end
    deactivate UI
Loading
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 변경 사항의 주요 내용을 명확하게 요약합니다. 쿠폰 관리 기능 추가라는 핵심 변경 사항을 효과적으로 전달합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/page/coupon.tsx`:
- Around line 530-532: The UI shows filteredMembersForIssue.length vs
selectedMemberIdsForIssue.length which can mismatch when a filter is applied;
compute the count of selected members that are within the current filter (e.g.
derive selectedAmongFiltered by filtering selectedMemberIdsForIssue against
filteredMembersForIssue ids) and display that filtered-selected count beside
filteredMembersForIssue.length, optionally also showing the global
selectedMemberIdsForIssue.length in a secondary label; update the render logic
where filteredMembersForIssue and selectedMemberIdsForIssue are used to show
counts (the span that currently renders “총 {filteredMembersForIssue.length}명 중
{selectedMemberIdsForIssue.length}명 선택”).
🧹 Nitpick comments (6)
src/api/coupon/request.ts (2)

7-15: Headers 객체 전달 시 스프레드가 올바르게 동작하지 않을 수 있습니다.

init.headersHeaders 인스턴스나 [string, string][] 배열일 경우, 객체 스프레드(...initHeaders)로는 헤더가 병합되지 않습니다. 현재 PR의 모든 호출부에서는 커스텀 헤더를 전달하지 않아 당장 문제가 되지 않지만, requestApi가 범용 헬퍼로 설계된 만큼 방어적으로 처리해두면 좋겠습니다.

♻️ 제안
-    const initHeaders = init?.headers ?? {};
+    const initHeaders = new Headers(init?.headers);
+    if (!initHeaders.has('accept')) {
+      initHeaders.set('accept', 'application/json');
+    }
+    if (!initHeaders.has('Content-Type')) {
+      initHeaders.set('Content-Type', 'application/json');
+    }
     const response = await fetch(`${API_BASE_URL}${path}`, {
       ...init,
       credentials: 'include',
-      headers: {
-        accept: 'application/json',
-        'Content-Type': 'application/json',
-        ...initHeaders,
-      },
+      headers: initHeaders,
     });

11-14: GET/DELETE 요청에도 Content-Type: application/json이 설정됩니다.

body가 없는 요청(GET, DELETE)에 Content-Type 헤더를 설정하면 일부 서버나 프록시에서 예기치 않은 동작을 유발할 수 있습니다. body가 있을 때만 Content-Type을 설정하는 것을 권장합니다.

src/page/coupon.tsx (4)

239-260: 비동기 작업 중 버튼 중복 클릭 방지가 없습니다.

handleCreateCoupon, handleUpdateCouponName, handleDeleteCoupon 등 모든 mutation 핸들러에서 요청 진행 중에도 버튼이 활성화 상태로 남아 있어 중복 요청이 발생할 수 있습니다. 특히 쿠폰 생성/삭제/발급은 서버 상태를 변경하므로, 진행 중 상태(isSubmitting 등)를 추가하여 버튼을 비활성화하는 것을 권장합니다.


131-136: loadAlluseEffect 의존성 배열에 누락되어 있습니다.

eslint-plugin-react-hooksexhaustive-deps 규칙에서 경고가 발생할 수 있습니다. loadAlluseCallback으로 감싸거나, useEffect 내부에 인라인으로 정의하는 방식을 고려해주세요.


87-129: 모든 mutation 후 전체 데이터를 다시 로드합니다.

각 생성/수정/삭제 작업 후 loadAll()을 호출하여 4개 API를 모두 다시 호출합니다. 관리자 페이지이므로 트래픽이 적어 현재로서는 괜찮지만, 향후 데이터가 많아지면 변경된 리소스만 선택적으로 갱신하는 방식이 효율적일 수 있습니다.


63-805: CouponPage 컴포넌트의 크기가 상당히 큽니다.

현재 하나의 컴포넌트에 상태 관리, 이벤트 핸들러, 3개 탭의 렌더링 로직이 모두 포함되어 있습니다 (~740줄). 장기적으로 유지보수를 위해 각 탭의 테이블과 액션 패널을 별도 컴포넌트로 분리하는 것을 고려해주세요.

Comment thread src/page/coupon.tsx
@ilcm96
ilcm96 merged commit 645651e into main Feb 11, 2026
1 check was pending
@ilcm96
ilcm96 deleted the feat/ADMIN-336 branch May 16, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant