Skip to content

fix: 3차 QA#193

Merged
ljh130334 merged 6 commits into
developfrom
qa/jihyeon
Aug 21, 2025
Merged

fix: 3차 QA#193
ljh130334 merged 6 commits into
developfrom
qa/jihyeon

Conversation

@ljh130334
Copy link
Copy Markdown
Member

@ljh130334 ljh130334 commented Aug 21, 2025

3차 QA 수정했습니다.

Summary by CodeRabbit

  • 신규 기능

    • 댓글/기록 섹션이 헤더뿐 아니라 섹션 전체 클릭 가능하도록 개선
    • 텍스트 기록에만 ‘피드에 고정’ 메뉴/버튼 노출
  • 버그 수정

    • 게시글 수정 시 이미지 전부 삭제해도 정상 반영되도록 요청 형식 보완
    • 그룹 상세/참여/멤버 목록에서 권한 없음·모집기간 종료 시 적절한 화면으로 자동 이동
  • 스타일

    • 그룹 생성 단계에 구분선 추가로 가독성 향상
    • 댓글/기록 섹션 커서 표시 개선 (클릭 가능 영역 명확화)

@vercel
Copy link
Copy Markdown

vercel Bot commented Aug 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
thip Ready Ready Preview Comment Aug 21, 2025 1:04am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 21, 2025

Walkthrough

방/그룹 관련 API와 페이지의 에러 처리에 특정 코드(만료·권한) 분기와 리다이렉트를 추가했다. 생성 화면 일부 섹션에 시각적 구분선(Section showDivider)을 삽입했다. 그룹 섹션의 클릭 영역을 헤더에서 전체 영역으로 이동하고 커서를 조정했다. 기록 고정 기능을 텍스트 타입에만 노출했다. 게시글 업데이트 시 remainImageUrls를 항상 전송하도록 수정했다.

Changes

Cohort / File(s) Change summary
Rooms API: 특정 에러코드 처리 추가
src/api/rooms/getRoomDetail.ts, src/api/rooms/getRoomMembers.ts, src/api/rooms/getRoomPlaying.ts
catch 타입을 any로 명시. 에러 response 코드 분기 추가: 100004(모집기간 만료)·140011(접근 권한 없음)에서 명시적 Error 메시지 throw. 기타 에러는 재throw.
그룹 상세/참여/멤버 페이지: 에러 기반 리다이렉트
src/pages/groupDetail/GroupDetail.tsx, src/pages/groupDetail/ParticipatedGroupDetail.tsx, src/pages/groupMembers/GroupMembers.tsx
에러 메시지 검사로 리다이렉트 분기 추가: 만료 시 참여 그룹 상세로, 권한 없음 시 /group으로 이동. 그 외는 기존 에러 표시 유지.
그룹 생성 섹션: 시각적 구분선 추가
src/components/creategroup/GenreSelectionSection.tsx, src/components/creategroup/MemberLimitSection.tsx, src/components/creategroup/PrivacySettingSection/PrivacySettingSection.tsx, src/components/creategroup/RoomInfoSection.tsx
루트/중간에 <Section showDivider /> 삽입으로 섹션 사이 Divider 표시. 로직·이벤트 처리 불변.
활동 기간 초기값 조정
src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx
getInitialEndDate가 ‘내일’에서 ‘모레’로 변경(오늘+2일).
그룹 섹션 클릭 영역 확장
src/components/group/CommentSection.tsx, src/components/group/CommentSection.styled.ts, src/components/group/RecordSection.tsx, src/components/group/RecordSection.styled.ts
onClick을 헤더에서 섹션 컨테이너로 이동. 커서 스타일을 컨테이너에 pointer로, 헤더에서는 제거.
기록 아이템: 고정 기능 노출 조건화
src/components/memory/RecordItem/RecordItem.tsx
더보기 메뉴 옵션을 동적 구성. onPin과 고정 버튼을 type==='text'일 때만 노출. 핸들러 deps에 type 추가.
게시글 수정 페이로드 고정 필드화
src/pages/post/UpdatePost.tsx
remainImageUrls를 항상 본문에 포함(빈 배열 포함)하도록 변경.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 사용자
  participant Page as GroupDetail/Participated/ Members Page
  participant API as getRoom* API
  participant Router as Router

  User->>Page: 방/그룹 화면 진입
  Page->>API: 방 정보/멤버/진행 정보 요청
  API-->>Page: 성공 응답
  Page-->>User: 화면 렌더링

  rect rgba(240,120,120,0.08)
  note over API,Page: 에러 플로우 (변경)
  Page->>API: 요청
  API-->>Page: 에러 응답(code 포함)
  alt code == 100004
    API-->>Page: throw Error("모집기간이 만료된 방입니다.")
    Page->>Router: replace to 참여중 상세
  else code == 140011
    API-->>Page: throw Error("방 접근 권한이 없습니다.")
    Page->>Router: replace to /group
  else 기타 에러
    API-->>Page: throw 원본 에러
    Page-->>User: 일반 에러 메시지 표시
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

📬 API, 🎨 Html&css

Suggested reviewers

  • heeeeyong

Poem

(\/) 깡총!
(•
•) 오늘은 에러 길잡이~
< 방 만료? 좌회전! 권한 없음? 유턴! >
└(•̀ᴗ•́)و 기록은 텍스트만 핀!
──── 구분선 반짝, 클릭영역 쫙 ────
빈 배열도 챙겨 넣고, 버그야 안녕!

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qa/jihyeon

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ljh130334 ljh130334 added the 🐞 BugFix Something isn't working label Aug 21, 2025
Copy link
Copy Markdown

@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: 6

🧹 Nitpick comments (17)
src/components/creategroup/MemberLimitSection.tsx (1)

20-20: 빈 Section(showDivider) 중첩은 여백/레이아웃 붕뜯 가능 — 외부 Section prop으로 대체 권장

현재 <Section> 내부에 또 다른 <Section showDivider />를 두면 DOM이 불필요하게 중첩되고, Section 자체의 패딩/마진이 적용되어 의도치 않은 간격이 생길 수 있습니다. 바깥 Section에 showDivider를 부여하는 방식이 더 간결하고 안전합니다.

다음 변경으로 내부 빈 Section 제거:

-      <Section showDivider />

바깥 Section에 divider 적용(선택 범위 밖 예시):

return (
  <Section showDivider>
    <SectionTitle>인원 제한</SectionTitle>
    ...
  </Section>
);

해당 CommonSection.styled의 Section가 빈 콘텐츠에서도 패딩을 주는지 확인 부탁드립니다. 만약 패딩이 있다면 현재 구현은 실제 Divider보다 추가 여백이 생깁니다.

src/components/creategroup/RoomInfoSection.tsx (1)

19-19: 빈 Section(showDivider) 다중 삽입은 DOM/여백 증가 유발 — Divider 컴포넌트 또는 prop 기반으로 간결화 권장

<Section showDivider />를 별도 블록으로 여러 번 렌더하면 Section의 기본 패딩/마진이 누적될 수 있습니다. 전용 <Divider /> styled 컴포넌트(패딩 0, 높이만)로 대체하거나, 앞선 섹션의 상단/하단 경계선만 그리는 prop을 사용하는 편이 안전합니다.

최소 변경으로는 해당 빈 Section들을 제거하고, 인접 Section에 divider prop을 주는 패턴을 권장합니다.

선택 범위 내 제거 예시:

-      <Section showDivider />
...
-      <Section showDivider />
...
-      <Section showDivider />

리팩터링 예시(선택 범위 밖):

<>
  <Section divider="top"> ... </Section>
  <Section divider="top"> ... </Section>
</>

CommonSection.styled의 Section가 빈 요소에도 수직 여백을 부여하는지 확인 부탁드립니다. 여백이 있다면 현재는 시각적 구분선 이상의 공간이 추가됩니다.

Also applies to: 34-34, 50-51

src/components/creategroup/GenreSelectionSection.tsx (1)

15-15: 내부 빈 Section(showDivider) 사용 지양 — 외부 Section prop으로 단순화

외부 컨테이너 <Section> 내부에 빈 <Section showDivider />를 두면 불필요한 DOM과 여백이 생깁니다. 바깥 Section에 divider를 직접 적용하세요.

내부 빈 Section 제거:

-      <Section showDivider />

외부 Section에 divider 적용(선택 범위 밖):

return (
  <Section showDivider>
    <SectionTitle>책 장르</SectionTitle>
    ...
  </Section>
);

현재 스타일 정의에서 빈 Section의 패딩/마진 여부를 확인해, 실제로 불필요한 공백이 생기지 않는지도 함께 검증 부탁드립니다.

src/components/group/CommentSection.styled.ts (1)

13-13: 섹션 전체 클릭 전환에 따른 커서 표시 OK, 다만 재사용성과 접근성 보완 제안

지금처럼 항상 pointer를 주면, onClick 없이 렌더링되는 경우에도 “클릭 가능”으로 오인될 수 있습니다. prop으로 토글하고, 키보드 포커스 가시성도 함께 보완해 주세요.

아래처럼 적용하면 $clickable일 때만 커서가 바뀌고, 키보드 포커스가 보장됩니다.

-export const CommentSection = styled.section`
+export const CommentSection = styled.section<{ $clickable?: boolean }>`
   display: flex;
   flex-direction: column;
   width: calc(100% - 40px);
   gap: 12px;
   background: ${colors.darkgrey.dark};
   margin: 20px 20px 0 20px;
   padding: 16px 12px;
   border-radius: 12px;
-  cursor: pointer;
+  cursor: ${({ $clickable }) => ($clickable ? 'pointer' : 'default')};
+  &:focus-visible {
+    outline: 2px solid ${colors.purple.main};
+    outline-offset: 2px;
+  }

TSX 사용 예: <CommentSection $clickable onClick={...}>...</CommentSection>

src/api/rooms/getRoomPlaying.ts (1)

68-76: 방 접근 권한 에러 처리 일관화 및 코드 기반 분기 리팩토링 제안

현재 catch (error: any) 및 메시지 문자열 비교에 의존한 에러 처리로 인해

  • ESLint any 사용 경고 및 타입 안전성 저하
  • 메시지 기반 분기 로직으로 인해 서버 에러 코드를 활용한 downstream 분기 불가

이슈가 발견되었습니다. 아래와 같이 수정하고, 동일 패턴이 적용된 파일 전반에 리팩토링을 권장합니다.

■ 주요 수정 사항

  • catch (error: any)catch (error: unknown)
  • isAxiosError 내로잉으로 Axios 에러 여부 확인
  • ACCESS_DENIED_CODE = 140011 상수로 선언 후, Error 객체에 코드 프로퍼티로 보존
  • 기존 메시지 기반 분기부:
    if (error.response?.data?.code === ACCESS_DENIED_CODE) {
      throw Object.assign(new Error('방 접근 권한이 없습니다.'), { code: ACCESS_DENIED_CODE });
    }

■ 문자열 비교로 처리 중인 위치 (총 16箇所)

  • src/api/rooms/getRoomPlaying.ts: 행 73
  • src/api/rooms/getRoomMembers.ts: 행 55
  • src/api/rooms/getRoomDetail.ts: 행 55
  • src/pages/today-words/TodayWords.tsx: 행 147, 281
  • src/pages/recordwrite/RecordWrite.tsx: 행 77, 195
  • src/pages/pollwrite/PollWrite.tsx: 행 78, 210
  • src/pages/groupMembers/GroupMembers.tsx: 행 49
  • src/pages/groupDetail/GroupDetail.tsx: 행 110
  • src/pages/groupDetail/ParticipatedGroupDetail.tsx: 행 81
  • src/components/memory/RecordItem/PollRecord.tsx: 행 119, 123
  • src/components/memory/RecordItem/RecordItem.tsx: 행 88, 225

■ 추가 권장 사항

  • ACCESS_DENIED_CODE와 에러 변환 로직을 src/constants/errors.ts 또는 src/utils/error.ts 등에 모듈화
  • 프로젝트 전반에서 메시지 대신 error.code 기반 분기 사용으로 일관성 및 유지보수성 강화

이렇게 리팩토링하면 타입 안정성을 확보함과 동시에, 서버 에러 코드를 활용한 명확한 분기 처리가 가능해집니다.

src/components/group/RecordSection.styled.ts (1)

13-13: 커서 포인터 추가는 의도와 일치. prop 기반 토글로 오용 방지 제안

CommentSection과 동일하게 $clickable prop으로 제어하면 재사용성이 좋아지고 오해 소지가 줄어듭니다. 포커스 가시성도 함께 보완을 권장합니다.

-export const RecordSection = styled.section`
+export const RecordSection = styled.section<{ $clickable?: boolean }>`
   display: flex;
   flex-direction: column;
   width: calc(100% - 40px);
   gap: 12px;
   background: ${colors.darkgrey.dark};
   margin: 20px 20px 0 20px;
   padding: 16px 12px;
   border-radius: 12px;
-  cursor: pointer;
+  cursor: ${({ $clickable }) => ($clickable ? 'pointer' : 'default')};
+  &:focus-visible {
+    outline: 2px solid ${colors.purple.main};
+    outline-offset: 2px;
+  }

TSX 사용 예: <StyledRecordSection $clickable onClick={...}>...</StyledRecordSection>

src/components/group/RecordSection.tsx (2)

23-24: 클릭 영역 확장은 UX 향상. 키보드 접근성(Enter/Space)도 함께 보장해 주세요

섹션 자체를 클릭 대상으로 만든 경우, 역할/탭 순서/키보드 이벤트를 추가해야 접근성이 확보됩니다. 인라인으로 간단히 보완 가능합니다.

-    <StyledRecordSection onClick={onClick}>
+    <StyledRecordSection
+      role="button"
+      tabIndex={0}
+      onClick={onClick}
+      onKeyDown={(e) => {
+        if (e.key === 'Enter' || e.key === ' ') {
+          e.preventDefault();
+          onClick();
+        }
+      }}
+    >
       <RecordSectionHeader>

추가 팁(선택): 섹션 전체가 “버튼” 역할이 되므로, 내부 chevron 이미지는 장식용이면 alt="" 또는 aria-hidden을 고려해 스크린리더 중복 안내를 줄이세요.


31-36: 진행률 값 클램프(0~100) 추천

API/계산 오차로 0 미만·100 초과가 들어오면 바 너비가 깨질 수 있습니다. 표시/스타일 모두 동일한 클램프를 적용하세요.

예시:

-          <ProgressText>{Math.floor(progress)}</ProgressText>
+          <ProgressText>{Math.floor(Math.max(0, Math.min(100, progress)))}</ProgressText>
...
-          <ProgressBarFill progress={progress} />
+          <ProgressBarFill progress={Math.max(0, Math.min(100, progress))} />
src/components/group/CommentSection.tsx (2)

18-18: 전체 영역 클릭 전환은 좋습니다. 단, 키보드 접근성 보완이 필요합니다.

비인터랙티브 컨테이너에 onClick만 두면 키보드 사용자가 접근하기 어렵습니다. role과 tabIndex, Enter/Space 키 핸들링을 추가해 주세요.

적용 예시(diff):

-    <StyledCommentSection onClick={onClick}>
+    <StyledCommentSection
+      onClick={onClick}
+      role="button"
+      tabIndex={0}
+      onKeyDown={e => {
+        if (e.key === 'Enter' || e.key === ' ') {
+          e.preventDefault();
+          onClick();
+        }
+      }}
+    >

21-22: Chevron은 장식용이므로 스크린리더 중복 안내를 피하세요.

섹션 전체가 클릭 가능해지면서 Chevron 이미지는 의미상 장식 아이콘에 가깝습니다. alt를 비우고 aria-hidden을 추가하는 편이 접근성에 유리합니다.

-        <CommentSectionChevron src={rightChevron} alt="한마디 이동 버튼" />
+        <CommentSectionChevron src={rightChevron} alt="" aria-hidden="true" />
src/pages/post/UpdatePost.tsx (1)

107-112: remainImageUrls 항상 전송 변경 👍; tagList도 초기화 지원을 위해 항상 포함 고려

이미지와 동일한 초기화(비우기) 시나리오가 태그에도 있을 수 있습니다. 현재는 태그가 비어지면 필드 자체가 빠져 서버가 “유지”로 해석할 여지가 있습니다. 삭제/초기화 의도가 있다면 tagList도 항상 포함하는 쪽이 일관됩니다.

-      ...(selectedTags.length ? { tagList: selectedTags } : {}),
+      tagList: selectedTags,
       remainImageUrls, // 이미지가 없어도 빈 배열로 전송하여 삭제 처리

추가 제안: 업데이트 실패 시 사용자 피드백을 위해 Line [116-119]에서 에러 스낵바 노출도 고려해 주세요.

src/components/creategroup/PrivacySettingSection/PrivacySettingSection.tsx (1)

27-29: 중첩 Section 대신 상위 Section에 divider 표현을 위임하는 구조가 깔끔합니다.

Section 안에 또 다른 Section을 넣어 구분선을 그리면 DOM이 불필요하게 깊어지고, 패딩/마진이 중첩될 수 있습니다. 상위 Section이 구분선을 렌더하도록 prop을 확장하거나, 별도 컴포넌트를 사용하는 편을 권장합니다.

가능하다면 아래처럼 단순화해 보세요.

-    <Section>
-      <Section showDivider />
+    <Section showDivider>
       <SectionTitle>공개 설정</SectionTitle>
src/pages/groupDetail/ParticipatedGroupDetail.tsx (1)

77-86: 문자열 기반 에러 분기 의존도 축소 제안

여러 페이지에서 동일한 메시지 문자열에 의존하면 i18n/문구 변경 시 리스크가 큽니다. API 레이어에서 code를 보존해 던지거나, 커스텀 Error 클래스로 code를 담아 페이지에서 code 기준으로 라우팅/표시를 결정하는 구조를 권장합니다.

간단한 예시(보조 코드):

// api/errors.ts
export class ApiError extends Error {
  constructor(public code: number, message: string) {
    super(message);
    this.name = 'ApiError';
  }
}

// API 레이어에서 throw new ApiError(140011, '방 접근 권한이 없습니다.')
// 페이지:
catch (err: unknown) {
  if (err instanceof ApiError && err.code === 140011) {
    navigate('/group', { replace: true });
    return;
  }
  setError('방 정보를 불러오는 중 오류가 발생했습니다.');
}
src/api/rooms/getRoomDetail.ts (2)

48-56: 매직 넘버(100004/140011) 상수화 제안

에러 코드가 여러 곳에서 반복되면 유지보수 비용이 커집니다. 의미 있는 이름의 상수/enum으로 선언해 사용하세요. (다른 API 모듈에서도 재사용 가능)

다음과 같은 상수 정의를 모듈 상단에 추가한 뒤 사용하는 것을 권장합니다:

export const ROOM_ERROR_CODE = {
  RECRUITMENT_CLOSED: 100004,
  FORBIDDEN: 140011,
} as const;

그리고 분기문은 아래처럼:

if (code === ROOM_ERROR_CODE.RECRUITMENT_CLOSED) { ... }
if (code === ROOM_ERROR_CODE.FORBIDDEN) { ... }

48-56: 메시지 문자열 결합도 완화: UI 분기는 코드/타입 기반으로

현재는 Error(message) 문자열을 기준으로 상위(UI)에서 라우팅이 결정됩니다. 문구 변경만으로 흐름이 깨질 수 있어 취약합니다. API 레이어에서 코드/타입을 보존하여 UI에서 안전하게 분기하도록 개선을 권장합니다.

예시:

  • 공용 에러 타입 정의
export type RoomDomainErrorCode = 'ROOM_RECRUITMENT_CLOSED' | 'ROOM_FORBIDDEN';

export class RoomDomainError extends Error {
  constructor(public code: RoomDomainErrorCode, message: string) {
    super(message);
    this.name = 'RoomDomainError';
  }
}
  • 매핑 시 메시지와 함께 코드 부여
if (code === ROOM_ERROR_CODE.RECRUITMENT_CLOSED) {
  throw new RoomDomainError('ROOM_RECRUITMENT_CLOSED', '모집기간이 만료된 방입니다.');
}
if (code === ROOM_ERROR_CODE.FORBIDDEN) {
  throw new RoomDomainError('ROOM_FORBIDDEN', '방 접근 권한이 없습니다.');
}
  • UI에서는 instanceof RoomDomainError 또는 error.code로 분기.
    이 패턴은 Axios response interceptor로도 중앙화할 수 있습니다(권장).
src/pages/groupDetail/GroupDetail.tsx (2)

103-114: 문구 기반 분기 → 코드/타입 기반 분기로 전환 권장

문구가 바뀌면 라우팅 로직이 깨집니다. API 레이어에서 제안한 도메인 에러(예: RoomDomainError) 또는 에러 코드 상수를 사용해 안정적으로 분기하세요.

적용 예:

} catch (err: unknown) {
  if (err instanceof RoomDomainError) {
    if (err.code === 'ROOM_RECRUITMENT_CLOSED') {
      navigate(`/group/detail/joined/${roomId}`, { replace: true });
      return;
    }
    if (err.code === 'ROOM_FORBIDDEN') {
      navigate('/group', { replace: true });
      return;
    }
  }
  setError('방 정보를 불러오는데 실패했습니다.');
}

100-114: 전역 에러 메시지·코드 상수화 및 일관성 확보 제안

현재 아래와 같이 동일한 에러 메시지와 코드가 여러 곳에 하드코딩되어 있습니다.
추후 유지보수성과 코드/타입 기반 분기 처리로 전환하기 위해 공통 상수 모듈로 추출하고, 하드코딩된 비교 로직을 대체할 것을 권장드립니다.

• 에러 메시지 사용처
– ‘모집기간이 만료된 방입니다.’:
• src/pages/groupDetail/GroupDetail.tsx (라인 104)
• src/api/rooms/getRoomDetail.ts (라인 50)
– ‘방 접근 권한이 없습니다.’:
• src/pages/groupDetail/GroupDetail.tsx (라인 110)
• src/pages/groupDetail/ParticipatedGroupDetail.tsx (라인 81)
• src/pages/groupMembers/GroupMembers.tsx (라인 49)
• src/pages/recordwrite/RecordWrite.tsx (라인 77, 195)
• src/pages/pollwrite/PollWrite.tsx (라인 78, 210)
• src/pages/today-words/TodayWords.tsx (라인 147, 281)
• src/components/memory/RecordItem/RecordItem.tsx (라인 88, 225)
• src/components/memory/RecordItem/PollRecord.tsx (라인 119, 123)
• src/api/rooms/getRoomDetail.ts (라인 55)
• src/api/rooms/getRoomMembers.ts (라인 55)
• src/api/rooms/getRoomPlaying.ts (라인 73)

• 에러 코드 사용처
– 100004 (‘모집기간 만료’): src/api/rooms/getRoomDetail.ts (라인 49)
– 140011 (‘접근 권한 없음’):
• API: getRoomDetail.ts (라인 54), getRoomMembers.ts (라인 54), getRoomPlaying.ts (라인 72)
• 컴포넌트: RecordItem.tsx (라인 87, 224), PollRecord.tsx (라인 118, 122)

• catch(error: any) 패턴
– src/pages/groupDetail/GroupDetail.tsx (라인 100)
– src/pages/groupDetail/ParticipatedGroupDetail.tsx (라인 77)
– src/pages/groupMembers/GroupMembers.tsx (라인 45)
– src/api/rooms/getRoomDetail.ts (라인 45)
– src/api/rooms/getRoomMembers.ts (라인 50)
– src/api/rooms/getRoomPlaying.ts (라인 68)

제안

  1. src/constants/error.ts 등 별도 모듈에 메시지·코드 상수로 추출
  2. if (error.message === …) 비교 대신 if (axiosError.response?.data?.code === ERROR_CODE.X) 형태로 전환
  3. catch 블록에서 any 대신 unknown 타입 후 안전 검증 또는 커스텀 에러 클래스 사용 검토

위 리팩토링을 통해 전역 일관성을 확보하고, 향후 타입 기반 분기 로직으로 쉽게 전환할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 25a1d82 and d825705.

📒 Files selected for processing (17)
  • src/api/rooms/getRoomDetail.ts (1 hunks)
  • src/api/rooms/getRoomMembers.ts (1 hunks)
  • src/api/rooms/getRoomPlaying.ts (1 hunks)
  • src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx (1 hunks)
  • src/components/creategroup/GenreSelectionSection.tsx (1 hunks)
  • src/components/creategroup/MemberLimitSection.tsx (1 hunks)
  • src/components/creategroup/PrivacySettingSection/PrivacySettingSection.tsx (1 hunks)
  • src/components/creategroup/RoomInfoSection.tsx (2 hunks)
  • src/components/group/CommentSection.styled.ts (1 hunks)
  • src/components/group/CommentSection.tsx (1 hunks)
  • src/components/group/RecordSection.styled.ts (1 hunks)
  • src/components/group/RecordSection.tsx (1 hunks)
  • src/components/memory/RecordItem/RecordItem.tsx (3 hunks)
  • src/pages/groupDetail/GroupDetail.tsx (1 hunks)
  • src/pages/groupDetail/ParticipatedGroupDetail.tsx (1 hunks)
  • src/pages/groupMembers/GroupMembers.tsx (1 hunks)
  • src/pages/post/UpdatePost.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/group/CommentSection.tsx (1)
src/components/group/CommentSection.styled.ts (1)
  • CommentSectionHeader (16-20)
src/components/group/RecordSection.tsx (1)
src/components/group/RecordSection.styled.ts (1)
  • RecordSectionHeader (16-20)
🪛 ESLint
src/api/rooms/getRoomMembers.ts

[error] 50-50: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/api/rooms/getRoomDetail.ts

[error] 45-45: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/api/rooms/getRoomPlaying.ts

[error] 68-68: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/pages/groupDetail/ParticipatedGroupDetail.tsx

[error] 77-77: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/pages/groupDetail/GroupDetail.tsx

[error] 100-100: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/components/memory/RecordItem/RecordItem.tsx

[error] 272-272: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/pages/groupMembers/GroupMembers.tsx

[error] 45-45: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🔇 Additional comments (5)
src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx (1)

88-96: 최소 종료일 일관성 확인 및 내일 날짜 선택 가능성 검증 요청

  • getInitialEndDate는 오늘 + 2일을 초기값으로 설정하지만, 검증 로직은 여전히 “내일(+1일)” 기준을 사용하고 있습니다.
  • 검증에서 new Date()(시·분·초 포함)와 비교하기 때문에 “내일 00:00”을 선택해도 현재 시각이 지난 경우 < 조건이 참이 되어 자동으로 +2일로 보정됩니다.
  • 초기값과 검증 기준이 일치하도록, 자정 기준 날짜 계산(startOfToday)과 일정 오프셋(MIN_END_DATE_OFFSET_DAYS)을 도입하는 리팩터링을 권장합니다.

점검 위치:

  • src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx
    • getInitialEndDate 함수 (약 L88–96)
    • 검증 로직 isEndDate 분기 (약 L119–129)

요청 사항:

  1. QA 요구사항에 따라 MIN_END_DATE_OFFSET_DAYS를 1(내일) 또는 2(모레)로 확정 부탁드립니다.
  2. 초기값/검증 모두 startOfToday() + MIN_END_DATE_OFFSET_DAYS 구조로 통일 검토 부탁드립니다.
  3. 변경 후 UI에서 “내일” 날짜(특히 오후 시간대)가 정상 선택되는지 확인해 주세요.
src/components/memory/RecordItem/RecordItem.tsx (2)

292-301: 의존성 배열에 type 추가 LGTM

type 변화에 따라 메뉴 옵션이 갱신되어야 하므로 의존성에 포함한 판단이 타당합니다.


375-392: 핀 버튼 노출 조건을 텍스트 타입으로 제한 — 의도에 부합하며 안전합니다

UI 상의 오동작(투표에 핀 노출)을 방지하고, 주석으로 의도가 명확해졌습니다. 클릭 이벤트 전파 차단 처리도 적절합니다.

src/api/rooms/getRoomDetail.ts (1)

48-56: 의도 명확성 및 사용자 메시지 적절성 좋습니다

만료/권한 오류를 도메인 친화적인 한국어 메시지로 변환해 상위에서 바로 UX 대응(리다이렉트/에러 노출)을 할 수 있게 한 점은 👍입니다. 코드를 상수화/타입화하면 현재 장점을 유지하면서 안정성을 더 높일 수 있습니다.

src/pages/groupDetail/GroupDetail.tsx (1)

103-114: 리다이렉트 UX 처리 적절합니다

만료 시 진행중 방으로, 권한 없음 시 목록으로 즉시 이동하고 replace: true로 히스토리를 정리한 점은 사용자 경험 측면에서 적합합니다.

Comment on lines +45 to +56
} catch (error: any) {
console.error('방 상세 정보 조회 API 오류:', error);

// 모집기간이 만료된 방인 경우
if (error.response?.data?.code === 100004) {
throw new Error('모집기간이 만료된 방입니다.');
}

// 방 접근 권한이 없는 경우
if (error.response?.data?.code === 140011) {
throw new Error('방 접근 권한이 없습니다.');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

catch 문의 any 제거 및 Axios 타입 가드로 안전한 분기

ESLint가 지적한 대로 any 사용을 제거하고, Axios 에러에만 안전하게 분기하도록 가드를 추가하는 편이 좋습니다. 또한 rethrow 시 원본 에러 객체를 유지하십시오.

아래와 같이 변경을 제안합니다:

-  } catch (error: any) {
-    console.error('방 상세 정보 조회 API 오류:', error);
-    
-    // 모집기간이 만료된 방인 경우
-    if (error.response?.data?.code === 100004) {
-      throw new Error('모집기간이 만료된 방입니다.');
-    }
-    
-    // 방 접근 권한이 없는 경우
-    if (error.response?.data?.code === 140011) {
-      throw new Error('방 접근 권한이 없습니다.');
-    }
-    
-    throw error;
+  } catch (err: unknown) {
+    console.error('방 상세 정보 조회 API 오류:', err);
+
+    // Axios 에러에만 안전하게 접근
+    if (isAxiosError(err)) {
+      const code = err.response?.data?.code;
+
+      // 모집기간이 만료된 방인 경우
+      if (code === 100004) {
+        throw new Error('모집기간이 만료된 방입니다.');
+      }
+
+      // 방 접근 권한이 없는 경우
+      if (code === 140011) {
+        throw new Error('방 접근 권한이 없습니다.');
+      }
+    }
+
+    throw err;
   }

추가로, 파일 상단에 아래 import가 필요합니다(선택 적용):

+import { isAxiosError } from 'axios';

Also applies to: 58-58

🧰 Tools
🪛 ESLint

[error] 45-45: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/api/rooms/getRoomDetail.ts around lines 45 to 56 (and also line 58),
remove the catch(error: any) typing and replace it with a narrowed Axios guard:
import axios at the top and use axios.isAxiosError(error) to check for Axios
errors before accessing error.response?.data?.code; handle codes 100004 and
140011 by throwing new Error('모집기간이 만료된 방입니다.') or new Error('방 접근 권한이 없습니다.')
but preserve the original error by passing it as the cause (new Error(message, {
cause: error })), and for non-Axios or unhandled cases rethrow the original
error (throw error).

Comment on lines +50 to +57
} catch (error: any) {
console.error('독서메이트 조회 API 오류:', error);

// 방 접근 권한이 없는 경우
if (error.response?.data?.code === 140011) {
throw new Error('방 접근 권한이 없습니다.');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

catch 문의 any 제거 및 오류 정제 — ESLint(no-explicit-any) 해결

ESLint 에러(Unexpected any)를 유발하는 catch (error: any)unknown으로 바꾸고, 안전한 축소로 처리하세요. 또한 140011 처리 시 원인 보존(cause)은 선택사항입니다.

다음과 같이 수정 제안:

-  } catch (error: any) {
+  } catch (error: unknown) {
     console.error('독서메이트 조회 API 오류:', error);
-    
-    // 방 접근 권한이 없는 경우
-    if (error.response?.data?.code === 140011) {
-      throw new Error('방 접근 권한이 없습니다.');
-    }
-    
+
+    // 방 접근 권한이 없는 경우
+    const apiCode =
+      typeof (error as { response?: { data?: { code?: number } } })?.response?.data?.code === 'number'
+        ? (error as { response?: { data?: { code?: number } } }).response!.data!.code
+        : undefined;
+    if (apiCode === 140011) {
+      throw new Error('방 접근 권한이 없습니다.');
+      // TS 4.6+ 사용 시 원인 보존:
+      // throw new Error('방 접근 권한이 없습니다.', { cause: error });
+    }
     
     throw error;
   }

추가 제안:

  • 동일한 코드(140011, 100004 등)를 여러 API에서 처리하고 있으니, 공통 유틸(예: mapApiErrorToAppError(e): never) 또는 Axios 응답 인터셉터로 중앙집중화하면 중복이 줄고 유지보수가 쉬워집니다.

검증 스크립트(리포지토리 내 any 기반 catch 탐지):


🏁 Script executed:

#!/bin/bash
# catch (error: any) 패턴 점검
rg -nP --type=ts --type=tsx -C1 'catch\s*\(\s*error\s*:\s*any\s*\)'

Length of output: 97


rg가 tsx 파일 타입을 기본으로 인식하지 않아 검색되지 않은 것 같습니다. 아래 스크립트로 .ts.tsx 파일을 모두 대상으로 재시도해 주세요.


🏁 Script executed:

#!/bin/bash
# catch (error: any) 패턴 점검 (.ts, .tsx 파일)
rg -n -C1 "catch\s*\(\s*error\s*:\s*any\s*\)" -g "*.ts" -g "*.tsx"

Length of output: 901


catch(error: any) 제거 및 unknown으로 대체 — 여러 파일에 일관성 있게 적용 필요

ESLint no-explicit-any 규칙을 준수하기 위해, 다음 파일들에서 catch (error: any) 패턴을 모두 찾아 unknown으로 변경하고 안전하게 축소(narrowing) 처리하세요:

  • src/pages/groupDetail/GroupDetail.tsx (라인 100)
  • src/api/rooms/getRoomPlaying.ts (라인 68)
  • src/api/rooms/getRoomDetail.ts (라인 45)
  • src/api/rooms/getRoomMembers.ts (라인 50)

예시 (src/api/rooms/getRoomMembers.ts):

-  } catch (error: any) {
+  } catch (error: unknown) {
     console.error('독서메이트 조회 API 오류:', error);

-    // 방 접근 권한이 없는 경우
-    if (error.response?.data?.code === 140011) {
-      throw new Error('방 접근 권한이 없습니다.');
-    }

+    // 방 접근 권한이 없는 경우
+    const apiCode =
+      typeof (error as { response?: { data?: { code?: number } } })?.response?.data?.code === 'number'
+        ? (error as { response?: { data?: { code?: number } } }).response!.data!.code
+        : undefined;
+    if (apiCode === 140011) {
+      // TS 4.6+ 환경에서는 원인(cause)도 함께 전달 가능
+      // throw new Error('방 접근 권한이 없습니다.', { cause: error });
+      throw new Error('방 접근 권한이 없습니다.');
+    }

     throw error;
   }

추가 제안:

  • 140011, 100004 등의 공통 에러 코드를 처리하는 로직이 여러 곳에 중복되어 있으니, 공통 유틸 함수(e.g. mapApiErrorToAppError(error): never) 또는 Axios 인터셉터를 도입하여 중앙 집중화하면 중복 제거와 유지보수성이 크게 개선됩니다.

검증 스크립트 (catch(error: any) 패턴 전역 점검):

rg -n -C1 "catch\s*\(\s*error\s*:\s*any\s*\)" -g "*.ts" -g "*.tsx"
🧰 Tools
🪛 ESLint

[error] 50-50: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/api/rooms/getRoomMembers.ts (lines 50-57) and also apply the same change
in src/pages/groupDetail/GroupDetail.tsx (line 100),
src/api/rooms/getRoomPlaying.ts (line 68), src/api/rooms/getRoomDetail.ts (line
45): replace catch(error: any) with catch(error: unknown) and then safely narrow
the unknown inside the catch before accessing properties — e.g., use a type
guard to detect Axios-like errors (check for typeof error === 'object' && error
!== null && 'response' in error), or use isAxiosError from axios, then read
error.response?.data?.code; throw or rethrow a mapped application Error after
narrowing; apply the same pattern in all listed files to satisfy no-explicit-any
and keep behavior unchanged.

Comment on lines +272 to 286
const menuOptions: any = {
onEdit: handleEdit,
onDelete: handleDeleteConfirm,
onPin: handlePinConfirm,
onClose: closePopup,
type: 'post',
isWriter: true,
});
};

// 기록(text)일 때만 핀하기 기능 추가
if (type === 'text') {
menuOptions.onPin = handlePinConfirm;
}

openMoreMenu(menuOptions);
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

ESLint: any 사용으로 린트 오류 발생 — 명시적 타입으로 교체 필요

menuOptions에 any를 사용해 (@typescript-eslint/no-explicit-any) 오류가 납니다. Menu 옵션 타입을 선언해 적용해 주세요.

변경 예시(diff, 해당 블록):

-      const menuOptions: any = {
+      const menuOptions: MoreMenuOptions = {
         onEdit: handleEdit,
         onDelete: handleDeleteConfirm,
         onClose: closePopup,
         type: 'post',
         isWriter: true,
       };
 
       // 기록(text)일 때만 핀하기 기능 추가
       if (type === 'text') {
-        menuOptions.onPin = handlePinConfirm;
+        menuOptions.onPin = handlePinConfirm;
       }
 
       openMoreMenu(menuOptions);

파일 상단(예: import 아래)에 타입 추가(보조 코드):

type MoreMenuOptionsBase = {
  onClose: () => void;
  type: 'post';
  isWriter: boolean;
};

type WriterMenuOptions = MoreMenuOptionsBase & {
  onEdit: () => void;
  onDelete: () => void;
  onPin?: () => void; // text일 때만 노출
};

type ReaderMenuOptions = MoreMenuOptionsBase & {
  onReport: () => void;
  isWriter: false;
};

// 이 블록에서는 작성자 옵션을 사용
type MoreMenuOptions = WriterMenuOptions;

openMoreMenu가 공용 타입을 갖고 있다면 해당 타입으로 교체하는 것이 최선입니다.

🧰 Tools
🪛 ESLint

[error] 272-272: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/components/memory/RecordItem/RecordItem.tsx around lines 272 to 286,
menuOptions is declared with type any causing an ESLint/TypeScript lint error;
replace the any with a proper typed interface (e.g., define MoreMenuOptionsBase
with onClose, type and isWriter, then WriterMenuOptions adding onEdit, onDelete
and optional onPin, and set MoreMenuOptions = WriterMenuOptions for this branch)
and change the menuOptions declaration to use that type; also update the
openMoreMenu call signature if necessary to accept the new MoreMenuOptions type
so the compiler and linter are satisfied.

Comment on lines +100 to +114
} catch (error: any) {
console.error('방 상세 정보 조회 실패:', error);

// 모집기간이 만료된 방인 경우 - 진행중인 방으로 리다이렉트
if (error.message === '모집기간이 만료된 방입니다.') {
navigate(`/group/detail/joined/${roomId}`, { replace: true });
return;
}

// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (error.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

any 제거 및 안전한 message 접근(+ 변수명 섀도잉 해소)

ESLint 에러(@typescript-eslint/no-explicit-any)를 해결하고, state 변수 error와 catch 변수명이 겹치는 섀도잉을 피하세요. 또한 unknown에서 message를 안전하게 추출해야 합니다.

아래처럼 수정을 제안합니다:

-      } catch (error: any) {
-        console.error('방 상세 정보 조회 실패:', error);
-        
-        // 모집기간이 만료된 방인 경우 - 진행중인 방으로 리다이렉트
-        if (error.message === '모집기간이 만료된 방입니다.') {
+      } catch (err: unknown) {
+        console.error('방 상세 정보 조회 실패:', err);
+
+        const msg = err instanceof Error ? err.message : '';
+
+        // 모집기간이 만료된 방인 경우 - 진행중인 방으로 리다이렉트
+        if (msg === '모집기간이 만료된 방입니다.') {
           navigate(`/group/detail/joined/${roomId}`, { replace: true });
           return;
         }
-        
-        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
-        if (error.message === '방 접근 권한이 없습니다.') {
+
+        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
+        if (msg === '방 접근 권한이 없습니다.') {
           navigate('/group', { replace: true });
           return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error: any) {
console.error('방 상세 정보 조회 실패:', error);
// 모집기간이 만료된 방인 경우 - 진행중인 방으로 리다이렉트
if (error.message === '모집기간이 만료된 방입니다.') {
navigate(`/group/detail/joined/${roomId}`, { replace: true });
return;
}
// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (error.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}
} catch (err: unknown) {
console.error('방 상세 정보 조회 실패:', err);
const msg = err instanceof Error ? err.message : '';
// 모집기간이 만료된 방인 경우 - 진행중인 방으로 리다이렉트
if (msg === '모집기간이 만료된 방입니다.') {
navigate(`/group/detail/joined/${roomId}`, { replace: true });
return;
}
// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (msg === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}
🧰 Tools
🪛 ESLint

[error] 100-100: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/pages/groupDetail/GroupDetail.tsx around lines 100 to 114, remove the use
of `any` and avoid shadowing the component state variable `error` by renaming
the catch parameter (e.g., to `caught`) and safely extract a message from an
`unknown` using a type‑guard (check `typeof caught === 'object' && caught !==
null && 'message' in caught`), assign `const message = typeof caught ===
'string' ? caught : (typeof caught === 'object' && caught !== null && 'message'
in caught ? String((caught as any).message) : '')`, then replace `error.message`
checks with `message === '...'` so ESLint no-explicit-any and shadowing issues
are resolved and message access is safe.

Comment on lines +77 to +86
} catch (err: any) {
console.error('방 상세 정보 조회 오류:', err);

// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (err.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}

setError('방 정보를 불러오는 중 오류가 발생했습니다.');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

ESLint: any 제거 및 안전한 에러 판별로 보강 필요

catch (err: any)는 린트 규칙에 위배됩니다. unknown으로 받고 Error 인스턴스 여부를 판정해 주세요. 메시지 비교도 그 안에서 수행하면 안전합니다.

-      } catch (err: any) {
+      } catch (err: unknown) {
         console.error('방 상세 정보 조회 오류:', err);
-        
-        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
-        if (err.message === '방 접근 권한이 없습니다.') {
+        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
+        if (err instanceof Error && err.message === '방 접근 권한이 없습니다.') {
           navigate('/group', { replace: true });
           return;
         }
         
         setError('방 정보를 불러오는 중 오류가 발생했습니다.');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err: any) {
console.error('방 상세 정보 조회 오류:', err);
// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (err.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}
setError('방 정보를 불러오는 중 오류가 발생했습니다.');
} catch (err: unknown) {
console.error('방 상세 정보 조회 오류:', err);
// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (err instanceof Error && err.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}
setError('방 정보를 불러오는 중 오류가 발생했습니다.');
🧰 Tools
🪛 ESLint

[error] 77-77: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/pages/groupDetail/ParticipatedGroupDetail.tsx around lines 77 to 86,
replace the catch signature using any with unknown, then inside the catch block
narrow the type by checking if the caught value is an instance of Error (or has
a string message) before accessing its message; only perform the "방 접근 권한이
없습니다." comparison after that check and handle other cases by logging the safe
error info and calling setError('방 정보를 불러오는 중 오류가 발생했습니다.'); ensure
navigate('/group', { replace: true }) is only invoked when the error message is
confirmed from the narrowed type.

Comment on lines +45 to 55
} catch (err: any) {
console.error('독서메이트 조회 오류:', err);

// 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
if (err.message === '방 접근 권한이 없습니다.') {
navigate('/group', { replace: true });
return;
}

setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.');
} finally {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

catch (err: any) → ESLint 에러와 타입 안전성 문제. 최소 수정으로 unknown 내로잉 권장

현 상태는 ESLint 오류(no-explicit-any)로 CI 실패 가능성이 높습니다. 기능 유지하며 타입을 안전하게 바꾸세요.

-      } catch (err: any) {
-        console.error('독서메이트 조회 오류:', err);
-        
-        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
-        if (err.message === '방 접근 권한이 없습니다.') {
-          navigate('/group', { replace: true });
-          return;
-        }
-        
-        setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.');
+      } catch (err: unknown) {
+        console.error('독서메이트 조회 오류:', err);
+
+        // 방 접근 권한이 없는 경우 - 모임 홈으로 리다이렉트
+        if (err instanceof Error && err.message === '방 접근 권한이 없습니다.') {
+          navigate('/group', { replace: true });
+          return;
+        }
+
+        setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.');

선택 개선(후속 PR 권장): 메시지 문자열 비교 대신 에러의 code === 140011 을 지원해 더 견고하게 만들기.

다음 유틸을 도입하면 메시지/코드 모두 지원 가능합니다.

const ACCESS_DENIED_CODE = 140011 as const;
const isAccessDenied = (e: unknown) =>
  (typeof (e as any)?.code === 'number' && (e as any).code === ACCESS_DENIED_CODE) ||
  (e instanceof Error && e.message === '방 접근 권한이 없습니다.');

그리고 if (isAccessDenied(err)) { ... }로 치환.

레포 전반 메시지 의존 여부 점검 스크립트:


🏁 Script executed:

#!/bin/bash
# 문자열 의존 탐색
rg -nP --glob '!**/node_modules/**' "방 접근 권한이 없습니다\."

Length of output: 1447


catch (err: any)catch (err: unknown) 일괄 변경 필요
ESLint no-explicit-any 방지 및 타입 안전성 확보를 위해 아래 위치의 에러 처리부를 모두 unknown으로 변경 부탁드립니다.

  • src/pages/groupMembers/GroupMembers.tsx (Lines 45–55)
  • src/pages/groupDetail/ParticipatedGroupDetail.tsx (Line 81)
  • src/pages/groupDetail/GroupDetail.tsx (Line 110)
  • src/pages/recordwrite/RecordWrite.tsx (Lines 77, 195)
  • src/pages/today-words/TodayWords.tsx (Lines 147, 281)
  • src/pages/pollwrite/PollWrite.tsx (Lines 78, 210)
  • src/components/memory/RecordItem/PollRecord.tsx (Lines 119, 123)
  • src/components/memory/RecordItem/RecordItem.tsx (Lines 88, 225)
  • src/api/rooms/getRoomDetail.ts (Line 55)
  • src/api/rooms/getRoomMembers.ts (Line 55)
  • src/api/rooms/getRoomPlaying.ts (Line 73)

예시: GroupMembers.tsx

- } catch (err: any) {
-   console.error('독서메이트 조회 오류:', err);
+ } catch (err: unknown) {
+   console.error('독서메이트 조회 오류:', err);

선택 리팩토링 제안
에러 메시지/코드를 모두 지원하는 isAccessDenied 헬퍼 유틸 도입을 권장드립니다. string 비교 의존도를 줄이고, 코드의 견고함을 높일 수 있습니다.

const ACCESS_DENIED_CODE = 140011 as const;
const isAccessDenied = (e: unknown) =>
  (typeof (e as any)?.code === 'number' && (e as any).code === ACCESS_DENIED_CODE) ||
  (e instanceof Error && e.message === '방 접근 권한이 없습니다.');

if (isAccessDenied(err)) {
  navigate('/group', { replace: true });
  return;
}
🧰 Tools
🪛 ESLint

[error] 45-45: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In src/pages/groupMembers/GroupMembers.tsx around lines 45–55 (and the other
listed files/lines), change catch(err: any) to catch(err: unknown) and update
the handling to narrow the unknown before accessing .message (e.g., check if err
is an Error via instanceof or typeof checks), or introduce and use a shared
isAccessDenied helper that accepts unknown and encapsulates both code and
message checks; replace direct string comparisons with that type-safe helper and
apply the same catch->unknown change across the other files listed in the
review.

@ljh130334 ljh130334 merged commit ac65401 into develop Aug 21, 2025
3 checks passed
This was referenced Aug 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 BugFix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant