Skip to content

정산 내역 수정 기능 추가#61

Merged
yoouyeon merged 3 commits into
developfrom
feat/MD-45
Jun 7, 2026
Merged

정산 내역 수정 기능 추가#61
yoouyeon merged 3 commits into
developfrom
feat/MD-45

Conversation

@yoouyeon

@yoouyeon yoouyeon commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

💻 작업 내용

총무가 정산 상세 페이지에서 정산 내역을 수정할 수 있는 기능을 추가했습니다.

헤더 관리 드롭다운 (ManageMenu)

  • MANAGER 역할일 때만 헤더 우측에 "관리" 버튼 노출
  • 클릭 시 드롭다운 메뉴 표시 (정산 내역 수정 / 계좌 수정)
  • "정산 내역 수정" 클릭 → payment.exists() 호출
  • 입금 요청이 이미 존재하면 수정 불가 안내 모달 표시
  • 없으면 정산 내역 수정 페이지로 이동

정산 내역 수정 페이지 (editExpenses)

  • 로더에서 인증 → MANAGER 역할 → 입금 요청 존재 여부를 순차 검증 (진입 가드)
  • useFunnel로 confirm / add / edit 스텝 구성 (기존 CreateExpensePage 컴포넌트 재사용)
  • 완료 또는 뒤로가기 시 expenseDetail로 복귀

✅ 테스트 리스트

  • MANAGER: "관리" 버튼 노출 확인
  • 비 MANAGER: "관리" 버튼 미노출 확인
  • 입금 요청 없음 → 정산 내역 수정 페이지 진입
  • 입금 요청 있음 → 수정 불가 모달 표시
  • URL 직접 접근 시 로더 가드가 동작하고 정산 상세 페이지로 이동하는 것 확인

📸 스크린샷

관리 메뉴 드롭다운 참여자 화면에는 관리 버튼 없음 정산 수정 불가 모달
총무관리버튼 참여자관리버튼없음 정산수정불가모달

👻 리뷰 요구사항

정산 생성 단계에서 사용하던 페이지들을 모두 재사용해서 정산 확인/추가/수정 로직은 그대로입니다!!

정산 수정 로직이 의도한대로 잘 동작하는지 동작 위주로 리뷰해주시면 감사하겠습니다...

그리고 여은님이 작업하시는 부분인 "계좌 수정" 기능으로 진입하는 부분도 만들어둬서 그 부분도 확인 부탁드립니다!! (src/pages/expenseDetail/ui/ManageMenu/index.tsx 의 TODO 주석)

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 정산 내역 수정 페이지 추가 - 관리자 역할 사용자가 정산 내역을 수정할 수 있는 새로운 페이지 구현
    • 관리 메뉴 추가 - 지출 상세 페이지에서 관리자 사용자만 접근 가능한 관리 메뉴 제공
    • 접근 권한 검증 강화 - 수정 페이지 접근 시 사용자 인증 및 권한(관리자), 그룹 상태 사전 검증

yoouyeon added 3 commits June 7, 2026 00:57
- 로더에서 인증, MANAGER 역할, 입금 요청 존재 여부 순차 검증
- payment.exists(), getGroupDetail()은 Promise.all로 병렬 호출
- MANAGER 역할일 때만 헤더에 관리 드롭다운 표시
- 정산 내역 수정 전 입금 요청 존재 여부 확인 후 분기
@yoouyeon yoouyeon added the ✨ Feature 신규 기능 추가 label Jun 6, 2026
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

정산 수정 기능을 위한 라우팅, 액세스 제어, 다단계 UI 페이지, 그리고 지출 상세 페이지의 관리 메뉴 드롭다운을 추가합니다. 승인 역할만 접근 가능하며, 진행 중인 정산이 있으면 차단됩니다.

Changes

정산 내역 수정 및 관리 메뉴 기능

Layer / File(s) Summary
라우트 설정 및 라우터 연결
src/shared/config/route.ts, src/app/Router.tsx
ROUTE.editExpenses 경로 상수를 정의하고, 동적 로딩 컴포넌트(LazyEditExpenses)와 데이터 로더(editExpensesLoader)를 라우터에 연결합니다.
정산 존재 여부 확인 API
src/entities/payment/api/payment.ts
/groups/{groupToken}/payments/exists 엔드포인트를 조회하는 payment.exists(groupToken) 메서드를 추가합니다.
정산 수정 페이지 액세스 제어 및 데이터 로드
src/pages/editExpenses/loader.ts
로그인 확인, 승인(MANAGER) 역할 검증, 프로필 존재 여부 확인, 진행 중인 정산 요청 검사를 수행하고, 조건에 따라 리다이렉트하거나 로더 데이터를 반환합니다. Axios 401/404 에러는 경계 에러로 변환합니다.
정산 수정 페이지 UI 및 다단계 퍼널
src/pages/editExpenses/EditExpensesPage.tsx, src/pages/editExpenses/index.ts
로더 데이터의 groupToken을 사용하여 다단계 퍼널(확인 → 추가/수정)을 초기화하고, 각 단계별 페이지를 조건부 렌더링하며, 이벤트 핸들러를 통해 라우터 네비게이션과 히스토리 관리를 연결합니다.
지출 상세 페이지 관리 메뉴 UI 및 통합
src/pages/expenseDetail/ExpenseDetailPage.styles.ts, src/pages/expenseDetail/ExpenseDetailPage.tsx, src/pages/expenseDetail/ui/ManageMenu/index.styles.ts, src/pages/expenseDetail/ui/ManageMenu/index.tsx
트리거 버튼, 메뉴 카드, 메뉴 항목 버튼의 스타일을 정의하고, ManageMenu 컴포넌트를 구현하여 정산 존재 여부 확인 후 차단 모달 표시 또는 수정 페이지로 네비게이트합니다. 승인 역할일 때만 헤더의 우측 아이콘 영역에 메뉴를 조건부 렌더링합니다.

Possibly related PRs

  • moddo-kr/moddo-frontend#27: src/entities/payment/api/payment.ts의 공유 payment API 객체를 모두 확장하는 변경입니다.
  • moddo-kr/moddo-frontend#38: src/entities/payment/api/payment.ts/groups/{code}/payments... 클라이언트 메서드를 추가하는 관련 변경입니다.
  • moddo-kr/moddo-frontend#32: src/entities/payment/api/payment.tspayment API 객체를 확장하는 동일 파일 관련 변경입니다.

Suggested labels

RABBIT_REVIEW

Poem

🐰 정산을 수정하고 메뉴를 더하며,
승인자만 접근하는 문을 열었네.
다단계 퍼널로 단계 밟고,
모달로 차단하고 네비게이션 펼치며,
우아한 UI로 드롭다운 내린다! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/MD-45

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.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

📝 관련 이슈

MD-45

@yoouyeon

yoouyeon commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

🧹 Nitpick comments (3)
src/pages/editExpenses/loader.ts (2)

37-37: ⚡ Quick win

리다이렉트 경로에 ROUTE 상수 사용 권장.

정산 상세 페이지로 리다이렉트하는 경로가 여러 곳에서 하드코딩되어 있습니다. ROUTE.expenseDetail을 사용하면 라우트 경로 변경 시 유지보수가 용이합니다.

♻️ 제안하는 수정
     if (myProfile.role !== 'MANAGER') {
-      return redirect(`/expense-detail/${groupToken}`);
+      return redirect(ROUTE.expenseDetail.replace(':groupToken', groupToken));
     }
 
     // ...
 
     if (exists) {
-      return redirect(`/expense-detail/${groupToken}`);
+      return redirect(ROUTE.expenseDetail.replace(':groupToken', groupToken));
     }

Also applies to: 47-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/editExpenses/loader.ts` at line 37, The redirect call in loader.ts
is hardcoding the expense detail path; replace the literal string in both
occurrences (the redirect(`/expense-detail/${groupToken}`) calls) with the ROUTE
constant (use ROUTE.expenseDetail and inject groupToken appropriately) so route
changes are centralized; update both occurrences (lines referencing redirect
with groupToken) to construct the URL via ROUTE.expenseDetail instead of the
hardcoded string.

22-25: ⚡ Quick win

returnUrl 생성 시 ROUTE 상수 사용 권장.

현재 경로를 직접 하드코딩하고 있습니다. ROUTE.editExpenses를 활용하면 라우트 경로 변경 시 일관성을 유지할 수 있습니다.

♻️ 제안하는 수정
     if (!auth?.authenticated) {
       const returnUrl = encodeURIComponent(
-        `/expense-detail/${groupToken}/edit-expenses`
+        ROUTE.editExpenses.replace(':groupToken', groupToken)
       );
       return redirect(`/login?returnUrl=${returnUrl}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/editExpenses/loader.ts` around lines 22 - 25, Use the ROUTE
constant instead of hardcoding the path when building returnUrl: replace the
literal string `/expense-detail/${groupToken}/edit-expenses` with a template
based on ROUTE.editExpenses (or appropriate ROUTE key) and interpolate
groupToken into that route before calling encodeURIComponent and redirect;
update the expression that assigns returnUrl (and its use in
redirect(`/login?returnUrl=${returnUrl}`)) to reference ROUTE.editExpenses to
keep routes consistent.
src/entities/payment/api/payment.ts (1)

24-27: 💤 Low value

파라미터 이름 일관성 고려.

기존 create 메서드(line 21)는 code 파라미터를 사용하지만, 새로 추가된 exists 메서드는 groupCode를 사용합니다. 더 명확한 groupCode로 통일하거나, 기존 컨벤션을 따르는 것이 일관성 측면에서 좋습니다.

♻️ 일관성을 위한 제안

기존 create 메서드도 함께 리팩토링하는 경우:

- create: (code: string): Promise<PaymentActionResult> =>
-   axiosInstance.post(`/groups/${code}/payments`).then((res) => res.data),
+ create: (groupCode: string): Promise<PaymentActionResult> =>
+   axiosInstance.post(`/groups/${groupCode}/payments`).then((res) => res.data),

또는 기존 컨벤션을 따르는 경우:

- exists: (groupCode: string): Promise<{ exists: boolean }> =>
+ exists: (code: string): Promise<{ exists: boolean }> =>
   axiosInstance
-    .get(`/groups/${groupCode}/payments/exists`)
+    .get(`/groups/${code}/payments/exists`)
     .then((res) => res.data),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/entities/payment/api/payment.ts` around lines 24 - 27, Unify the
parameter name between the payment API methods: rename the parameter in exists
to match the existing create method (or update both to use groupCode) so they’re
consistent; locate the exists function and the create function in
src/entities/payment/api/payment.ts and change their parameter identifier (and
any internal references) to the chosen name (e.g., groupCode or code) and update
the axios path/template argument accordingly so both methods use the same
parameter name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/entities/payment/api/payment.ts`:
- Around line 24-27: Unify the parameter name between the payment API methods:
rename the parameter in exists to match the existing create method (or update
both to use groupCode) so they’re consistent; locate the exists function and the
create function in src/entities/payment/api/payment.ts and change their
parameter identifier (and any internal references) to the chosen name (e.g.,
groupCode or code) and update the axios path/template argument accordingly so
both methods use the same parameter name.

In `@src/pages/editExpenses/loader.ts`:
- Line 37: The redirect call in loader.ts is hardcoding the expense detail path;
replace the literal string in both occurrences (the
redirect(`/expense-detail/${groupToken}`) calls) with the ROUTE constant (use
ROUTE.expenseDetail and inject groupToken appropriately) so route changes are
centralized; update both occurrences (lines referencing redirect with
groupToken) to construct the URL via ROUTE.expenseDetail instead of the
hardcoded string.
- Around line 22-25: Use the ROUTE constant instead of hardcoding the path when
building returnUrl: replace the literal string
`/expense-detail/${groupToken}/edit-expenses` with a template based on
ROUTE.editExpenses (or appropriate ROUTE key) and interpolate groupToken into
that route before calling encodeURIComponent and redirect; update the expression
that assigns returnUrl (and its use in
redirect(`/login?returnUrl=${returnUrl}`)) to reference ROUTE.editExpenses to
keep routes consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7d53237a-5584-426c-8716-b6678db27a2a

📥 Commits

Reviewing files that changed from the base of the PR and between 9fb4fb7 and 4d6df19.

📒 Files selected for processing (10)
  • src/app/Router.tsx
  • src/entities/payment/api/payment.ts
  • src/pages/editExpenses/EditExpensesPage.tsx
  • src/pages/editExpenses/index.ts
  • src/pages/editExpenses/loader.ts
  • src/pages/expenseDetail/ExpenseDetailPage.styles.ts
  • src/pages/expenseDetail/ExpenseDetailPage.tsx
  • src/pages/expenseDetail/ui/ManageMenu/index.styles.ts
  • src/pages/expenseDetail/ui/ManageMenu/index.tsx
  • src/shared/config/route.ts

@ongheong ongheong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

정연님 코드 및 동작 확인했습니다!
매니저/참여자 역할에 따른 관리 버튼 노출 여부와, GET payments/exists 응답에 따라 정산 수정 페이지 진입을 나누고 편집 후 정산 상세 페이지로 돌아오는 흐름까지 확인했습니다.

프론트 로직은 문제 없어 보이고, 백엔드 응답만 한 번 확인이 필요할 것 같습니다.
현재 정산 완료된 참여자가 있어도 exists: false로 내려오는 경우가 있어서,payments/exists가 입금 확인 요청뿐만 아니라 정산 완료된 참여자까지 포함해서 체크하는지 확인하면 좋을 것 같습니다!

@yoouyeon

yoouyeon commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

@ongheong 확인감사합니다!!!!! 백엔드 응답 관련해서는 좀있다가 지윤님께 여쭤보면 될 것 같아요 😢 우선 이 PR은 머지하겠습니다!!!

@yoouyeon yoouyeon merged commit b551cbd into develop Jun 7, 2026
7 checks passed
@yoouyeon yoouyeon deleted the feat/MD-45 branch June 7, 2026 12:30
@yoouyeon yoouyeon mentioned this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 신규 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants