Skip to content

[♻️Refactor/304] 대시보드 초대 로컬스토리지에서 관리#310

Merged
yujin-fe merged 4 commits intomainfrom
refactor/304/dashboard-name-limit
Dec 1, 2025
Merged

[♻️Refactor/304] 대시보드 초대 로컬스토리지에서 관리#310
yujin-fe merged 4 commits intomainfrom
refactor/304/dashboard-name-limit

Conversation

@yujin-fe
Copy link
Collaborator

@yujin-fe yujin-fe commented Dec 1, 2025

✅ PR 체크리스트

1. 코드 & 기능

  • 변수/함수 이름 이해 가능한지
  • 기능 정상 동작 & 콘솔 에러 없음

2. UI

  • UI 동작 및 레이아웃 확인

3. 컨벤션

  • 팀 컨벤션에 맞게 함수 이름을 정의했는지

🔗 이슈 번호

✨ 작업한 내용

💁 리뷰 요청 / 코멘트

💡 참고사항

브랜치를 다른 곳에서 작업햇네요. . 이름만 다른 것이라서 기능상 문제는 없습니다!

Summary by CodeRabbit

  • 새로운 기능

    • 초대 흐름에 대시보드 ID가 연동되어, 이미 요청한 이메일은 자동으로 중복 검증되어 초대 버튼이 비활성화됩니다.
    • 초대 요청 상태가 로컬에 저장되어 요청 중인 이메일에 대한 재요청을 차단합니다.
  • 버그 수정

    • 초대 취소 시 관련된 로컬 저장 항목이 올바르게 제거되어 상태가 일관되게 유지됩니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@yujin-fe yujin-fe added this to the ✨ 페이지 기능 구현 milestone Dec 1, 2025
@yujin-fe yujin-fe requested review from aahreum and jung518 December 1, 2025 17:29
@yujin-fe yujin-fe self-assigned this Dec 1, 2025
@yujin-fe yujin-fe added ✨ Feature 새로운 기능 구현 ♻️ Refactor 코드 리팩토링 labels Dec 1, 2025
@yujin-fe yujin-fe linked an issue Dec 1, 2025 that may be closed by this pull request
2 tasks
@yujin-fe yujin-fe added the 🐛 Bug 버그 발견/해결 label Dec 1, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Walkthrough

로컬스토리지 기반 초대 중복 검증을 추가하고, DashboardInviteModal에 dashboardId prop을 전달하도록 폼 유효성·비활성화 흐름과 초대 저장/제거 로직을 수정합니다.

Changes

구성 / 파일 변경 요약
초대 모달 유효성 검증
src/components/dashboard/modal/DashboardInviteModal.tsx
DashboardInviteModalPropsdashboardId: string 추가. handleBlur와 disabled 로직에서 validateInvitation(inviteeEmail + dashboardId) 호출로 초대 중복 검증을 도입하고 에러 메시지 처리 변경
초대 처리(편집 페이지)
src/components/editpage/InvitesEdit.tsx
cancelMutation의 onSuccess 시그니처를 (_, value: DeleteInvitationParams)로 변경하여 삭제된 초대의 invitationId로 로컬스토리지 항목 제거 처리; 초대 API 호출 시 dashboardId.toString() 사용 및 DashboardInviteModaldashboardId 전달
상수 정의
src/constants/invitation.ts
export const REQUESTED_EMAIL = 'requestedEmail'; 추가
상세 레이아웃(초대 성공 저장)
src/pages/DetailLayout.tsx
초대 성공 시 localStorage.setItem(inviteeEmail + dashboardId, REQUESTED_EMAIL) 저장 및 DashboardInviteModaldashboardId prop 전달
유효성 유틸리티
src/utils/validation.ts
REQUESTED_EMAIL 가져와 validateInvitation(value: string) 추가(로컬스토리지 비교 후 중복 메시지 반환) 및 validatorsinvitation 항목 등록

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • InvitesEdit의 cancelMutation onSuccess 시그니처 변경과 로컬스토리지 제거 흐름 검증 필요
  • DashboardInviteModal에 전달되는 dashboardId 결합 문자열 사용(로컬스토리지 키) 일관성 확인 필요
  • validateInvitation의 로컬스토리지 접근과 국제화/메시지 하드코딩 점검 필요

Possibly related PRs

Suggested reviewers

  • aahreum
  • jung518

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive PR 설명이 필수 섹션들(체크리스트, 이슈 번호, 작업 내용)을 포함하고 있으나 '작업한 내용' 섹션이 비어있습니다. 변경사항에 대한 구체적인 설명을 '✨ 작업한 내용' 섹션에 추가해주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 코드 변경사항이 #308의 요구사항을 충족합니다: 로컬스토리지에 초대된 이메일 저장, 중복 입력시 버튼 비활성화 및 에러메시지 표시, 거절시 로컬스토리지에서 삭제.
Out of Scope Changes check ✅ Passed 모든 코드 변경사항이 #308의 요구사항 범위 내에 있으며, 초대 로컬스토리지 관리 기능 구현에 집중되어 있습니다.
Title check ✅ Passed PR 제목이 변경 사항의 핵심을 명확하게 반영하고 있습니다. 대시보드 초대 기능을 로컬스토리지에서 관리하도록 리팩토링한다는 주요 목표를 잘 표현하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/304/dashboard-name-limit

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

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/components/editpage/InvitesEdit.tsx (2)

96-99: 불필요한 옵셔널 체이닝을 제거하세요.

Line 96-98에서 inviteData가 null/undefined인지 체크하고 early return하므로, Line 99에서 inviteData?.invitations의 옵셔널 체이닝(?.)은 불필요합니다.

  if (!inviteData) {
    return null;
  }
- const invitations: Invitation[] = inviteData?.invitations;
+ const invitations: Invitation[] = inviteData.invitations;

120-120: 불필요한 toString() 호출을 제거하세요.

dashboardIduseParams에서 가져온 string 타입이며, Line 101-103에서 이미 null/undefined 체크를 했습니다. inviteDashboard API도 string을 받으므로 .toString() 호출은 불필요합니다.

- const resData = await inviteDashboard(dashboardId.toString(), { email: inviteeEmail });
+ const resData = await inviteDashboard(dashboardId, { email: inviteeEmail });
src/constants/invitation.ts (1)

1-2: JSDoc 주석 형식을 표준에 맞게 수정하세요.

현재 주석 형식 /**초대된 이메일 **/은 표준 JSDoc 형식이 아닙니다. /** 뒤와 */ 앞에 공백을 추가해야 합니다.

-/**초대된 이메일 **/
+/** 초대된 이메일 */
 export const REQUESTED_EMAIL = 'requestedEmail';
src/components/dashboard/modal/DashboardInviteModal.tsx (1)

43-46: 불필요한 화살표 함수를 제거하세요.

Line 44에서 setErrorMsg(() => validateInvitation(inviteeEmail))는 불필요한 화살표 함수를 사용하고 있습니다. validateInvitation은 이미 문자열을 반환하므로 직접 전달할 수 있습니다.

  const handleBlur = () => {
-   if (validateInvitation(inviteeEmail)) {
-     setErrorMsg(() => validateInvitation(inviteeEmail));
-     return;
-   }
+   const invitationError = validateInvitation(inviteeEmail);
+   if (invitationError) {
+     setErrorMsg(invitationError);
+     return;
+   }
    const message = validateEmail(inviteeEmail);
    setErrorMsg(message);
  };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0092302 and 4f26c49.

📒 Files selected for processing (5)
  • src/components/dashboard/modal/DashboardInviteModal.tsx (2 hunks)
  • src/components/editpage/InvitesEdit.tsx (4 hunks)
  • src/constants/invitation.ts (1 hunks)
  • src/pages/DetailLayout.tsx (2 hunks)
  • src/utils/validation.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

⚙️ CodeRabbit configuration file

**/*.ts: language: "ko-KR"

reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 항상 const 우선 사용
- 파일명은 camelCase로 작성
- 필요 없는 코드는 즉시 제거 (미사용 변수/함수 금지)
- 함수를 제외한 유틸 파일은 default export 대신 named export 권장

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어** 형태로 작성
- 데이터 조회 함수: get / fetch / load
- 데이터 수정 함수: set / update / save / remove
- 이벤트 함수: handle 사용
- 계산 / 변환 함수: calculate / convert / parse / format
- 조건 검사 함수: is / has / can

[디렉토리 & 파일 구조]
- 디렉토리명은 kebab-case 사용
- 에셋(icon, image, logos)은 index 파일을 통한 일괄 export
- import 시 `@/assets/icons` 형태로 그룹 단위 import

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[주석 & 문서화]
- TODO 주석 적극 활용 (급한 PR / 미구현 영역)
- TSDoc 사용 가능 (컴포넌트 설명, 사용 예시 추가)
- @param은 TypeScript 타입으로 설명하기 때문에 비사용

Files:

  • src/constants/invitation.ts
  • src/utils/validation.ts
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/pages/DetailLayout.tsx
  • src/components/editpage/InvitesEdit.tsx
  • src/components/dashboard/modal/DashboardInviteModal.tsx
🧬 Code graph analysis (4)
src/utils/validation.ts (1)
src/constants/invitation.ts (1)
  • REQUESTED_EMAIL (2-2)
src/pages/DetailLayout.tsx (1)
src/constants/invitation.ts (1)
  • REQUESTED_EMAIL (2-2)
src/components/editpage/InvitesEdit.tsx (2)
src/lib/apis/Invitations.ts (2)
  • DeleteInvitationParams (37-40)
  • inviteDashboard (4-7)
src/types/invitations.ts (1)
  • Invitation (18-27)
src/components/dashboard/modal/DashboardInviteModal.tsx (1)
src/utils/validation.ts (2)
  • validateInvitation (21-23)
  • validateEmail (3-5)
🔇 Additional comments (3)
src/components/editpage/InvitesEdit.tsx (1)

153-165: LGTM - dashboardId 숫자 변환이 올바르게 적용되었습니다.

+dashboardId를 사용하여 string을 number로 변환하는 방식이 handleCancel 함수의 타입과 일치합니다.

src/components/dashboard/modal/DashboardInviteModal.tsx (1)

51-54: LGTM - 유효성 검사가 올바르게 적용되었습니다.

초대 중복 체크를 포함한 disabled 조건이 정확하게 구현되었습니다.

src/utils/validation.ts (1)

21-23: 유효성 검사 로직이 올바르게 구현되었습니다.

validateInvitation 함수는 localStorage의 키-값 패턴과 일치하게 구현되었습니다. 다만 src/pages/DetailLayout.tsx Line 37에서 지적한 대시보드 범위 이슈가 이 함수에도 영향을 미칩니다.

대시보드별 초대 관리로 개선할 경우, 이 함수도 다음과 같이 수정이 필요합니다:

export const validateInvitation = (value: string, dashboardId: string) => {
  const storageKey = `${REQUESTED_EMAIL}_${dashboardId}`;
  const invitedEmails = JSON.parse(localStorage.getItem(storageKey) || '[]');
  return invitedEmails.includes(value) ? '이미 요청한 이메일입니다.' : '';
};

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/components/editpage/InvitesEdit.tsx (1)

75-82: localStorage 제거 로직에서 invitations 참조 및 비교 방식 개선 제안

이 콜백에서 invitations를 참조하는 방식과 비교/키 생성 방식이 다소 불안정해 보입니다.

  • invitations가 아래에서 선언되어 있어 스코프상 문제는 없지만, 읽는 입장에서는 “어디서 온 값인지” 추적이 어렵습니다. inviteData?.invitations를 직접 참조하거나, handleCancel 호출 시 필요한 정보를 함께 넘기는 쪽이 더 명확합니다.
  • inv.id.toString() === value.invitationId.toString() 대신 두 값이 숫자 타입이라면 inv.id === value.invitationId처럼 직접 비교하는 편이 타입/성능 면에서 더 낫습니다.
  • invitation이 존재하는 조건문 안에서는 invitation.invitee.email 접근이 가능하므로, 과도한 옵셔널 체이닝은 제거해도 됩니다. (단, 런타임에서 비어 있거나 잘못된 이메일 값이 들어올 수 있다면 typeof inviteeEmail === 'string' && inviteeEmail 정도의 가드를 한 번 더 두는 것도 고려해 볼 수 있습니다.)

전체적으로 다음과 같이 정리하면 가독성과 타입 안정성이 좋아질 것 같습니다:

onSuccess: (_, value: DeleteInvitationParams) => {
  const invitation = inviteData?.invitations.find(
    (inv) => inv.id === value.invitationId
  );

  if (invitation && invitation.invitee.email) {
    const key = invitation.invitee.email + dashboardId;
    localStorage.removeItem(key);
  }

  setDeleteMessage('초대 취소가 완료되었습니다.');
  openCancelModal();
  refetch();
},
🧹 Nitpick comments (3)
src/components/editpage/InvitesEdit.tsx (3)

96-103: 초기 가드 위치/타입 정리 관련 소소한 리팩터링 제안

if (!inviteData) return null; 이후에 바로 const invitations: Invitation[] = inviteData?.invitations;를 두어 타입 단언까지 하고 있는데, 가드가 이미 있으므로 옵셔널 체이닝과 타입 명시는 생략해도 됩니다.

if (!inviteData || !dashboardId) {
  return null;
}

const invitations = inviteData.invitations;

처럼 두 가드를 하나로 합치고 invitations의 타입은 DashboardInvitationResponse에서 자동 추론되도록 두면, 코드가 조금 더 단순해집니다. (현재 구현도 동작 상의 문제는 없어 선택 사항입니다.)


120-120: dashboardId.toString() 호출은 불필요해 보입니다

dashboardId는 라우트 파라미터에서 이미 문자열로 들어오고, 위에서 !dashboardId 가드 후에만 이 로직이 실행되기 때문에 굳이 toString()을 호출하지 않아도 됩니다.

const resData = await inviteDashboard(dashboardId, { email: inviteeEmail });

처럼 바로 넘기면 타입도 더 명확해집니다. (기능에는 영향 없는 선택적 리팩터링입니다.)


153-165: onCancel 핸들러 중복을 줄여 가독성 개선 가능

세 곳에서 동일한 inline 핸들러 () => handleCancel(+dashboardId, invitation.id)를 반복하고 있어서, 약간의 중복이 있습니다. 아래처럼 공통 핸들러를 하나 정의해 두고 재사용하면 가독성이 좋아집니다.

const handleItemCancel = (id: number) => {
  handleCancel(+dashboardId, id);
};

// 사용처
onCancel={() => handleItemCancel(invitation.id)}

현재 구현도 동작에는 문제가 없으므로, 가독성 관점에서만 고려해 보시면 좋겠습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f26c49 and bacc209.

📒 Files selected for processing (3)
  • src/components/dashboard/modal/DashboardInviteModal.tsx (4 hunks)
  • src/components/editpage/InvitesEdit.tsx (5 hunks)
  • src/pages/DetailLayout.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/dashboard/modal/DashboardInviteModal.tsx
  • src/pages/DetailLayout.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/editpage/InvitesEdit.tsx
🧬 Code graph analysis (1)
src/components/editpage/InvitesEdit.tsx (2)
src/lib/apis/Invitations.ts (2)
  • DeleteInvitationParams (37-40)
  • inviteDashboard (4-7)
src/types/invitations.ts (1)
  • Invitation (18-27)
🔇 Additional comments (1)
src/components/editpage/InvitesEdit.tsx (1)

223-223: DashboardInviteModal에 dashboardId 전달은 요구사항과 잘 맞습니다

중복 초대 방지를 위해 localStorage 키에 email + dashboardId를 사용하고, 모달 단에서 유효성/버튼 비활성화를 제어하려는 의도로 보입니다. 여기서 dashboardId를 명시적으로 넘겨주는 구조가 요구사항(대시보드별 초대 관리)과 잘 맞습니다.

특별히 수정할 부분 없이 이 변경은 그대로 괜찮아 보입니다.

@yujin-fe yujin-fe changed the title [Refactor/304] 대시보드 초대 로컬스토리지에서 관리 [♻️Refactor/304] 대시보드 초대 로컬스토리지에서 관리 Dec 1, 2025
@yujin-fe yujin-fe merged commit bb2d23d into main Dec 1, 2025
2 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Taskify Project Dec 1, 2025
@yujin-fe yujin-fe deleted the refactor/304/dashboard-name-limit branch December 1, 2025 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 Bug 버그 발견/해결 ✨ Feature 새로운 기능 구현 ♻️ Refactor 코드 리팩토링

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[♻️ Refactor] 초대하기 중복 이메일 입력시 버튼 비활성화 [♻️ Refactor] 대시보드 생성, 수정에서 글자 수 제한

1 participant