Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/api/rooms/getRoomDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,19 @@ export const getRoomDetail = async (roomId: number): Promise<RoomDetailResponse>
try {
const response = await apiClient.get<RoomDetailResponse>(`/rooms/${roomId}/recruiting`);
return response.data;
} catch (error) {
} 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('방 접근 권한이 없습니다.');
}
Comment on lines +45 to +56
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).


throw error;
}
};
8 changes: 7 additions & 1 deletion src/api/rooms/getRoomMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ export const getRoomMembers = async (roomId: number): Promise<RoomMembersRespons
try {
const response = await apiClient.get<RoomMembersResponse>(`/rooms/${roomId}/users`);
return response.data;
} catch (error) {
} catch (error: any) {
console.error('독서메이트 조회 API 오류:', error);

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

Comment on lines +50 to +57
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.

throw error;
}
};
8 changes: 7 additions & 1 deletion src/api/rooms/getRoomPlaying.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,14 @@ export const getRoomPlaying = async (roomId: number): Promise<RoomPlayingRespons
try {
const response = await apiClient.get<RoomPlayingResponse>(`/rooms/${roomId}/playing`);
return response.data;
} catch (error) {
} catch (error: any) {
console.error('진행중인 방 상세 정보 조회 API 오류:', error);

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

throw error;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const ActivityPeriodSection = ({

const getInitialEndDate = () => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setDate(tomorrow.getDate() + 2);
return {
year: tomorrow.getFullYear(),
month: tomorrow.getMonth() + 1,
Expand Down
1 change: 1 addition & 0 deletions src/components/creategroup/GenreSelectionSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const GenreSelectionSection = ({ selectedGenre, onGenreSelect }: GenreSelectionS

return (
<Section>
<Section showDivider />
<SectionTitle>책 장르</SectionTitle>
<GenreButtonGroup>
{genres.map(genre => (
Expand Down
1 change: 1 addition & 0 deletions src/components/creategroup/MemberLimitSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const MemberLimitSection = ({ memberLimit, onMemberLimitChange }: MemberLimitSec

return (
<Section>
<Section showDivider />
<SectionTitle>인원 제한</SectionTitle>
<MemberLimitContainer>
<MemberWheelContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const PrivacySettingSection = ({
}: PrivacySettingSectionProps) => {
return (
<Section>
<Section showDivider />
<SectionTitle>공개 설정</SectionTitle>
<PrivacyToggleContainer>
<PrivacyLabel>비공개로 설정하기</PrivacyLabel>
Expand Down
3 changes: 3 additions & 0 deletions src/components/creategroup/RoomInfoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const RoomInfoSection = ({
}: RoomInfoSectionProps) => {
return (
<>
<Section showDivider />
<Section>
<SectionTitle>방 제목</SectionTitle>
<TextAreaBox>
Expand Down Expand Up @@ -45,6 +46,8 @@ const RoomInfoSection = ({
<CharacterCount>{roomDescription.length} / 75</CharacterCount>
</TextAreaBox>
</Section>

<Section showDivider />
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/group/CommentSection.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export const CommentSection = styled.section`
margin: 20px 20px 0 20px;
padding: 16px 12px;
border-radius: 12px;
cursor: pointer;
`;

export const CommentSectionHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
`;

export const CommentSectionTitle = styled.h3`
Expand Down
4 changes: 2 additions & 2 deletions src/components/group/CommentSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ interface CommentSectionProps {

const CommentSection = ({ message, onClick }: CommentSectionProps) => {
return (
<StyledCommentSection>
<CommentSectionHeader onClick={onClick}>
<StyledCommentSection onClick={onClick}>
<CommentSectionHeader>
<CommentSectionTitle>오늘의 한마디</CommentSectionTitle>
<CommentSectionChevron src={rightChevron} alt="한마디 이동 버튼" />
</CommentSectionHeader>
Expand Down
2 changes: 1 addition & 1 deletion src/components/group/RecordSection.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export const RecordSection = styled.section`
margin: 20px 20px 0 20px;
padding: 16px 12px;
border-radius: 12px;
cursor: pointer;
`;

export const RecordSectionHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
`;

export const RecordSectionTitle = styled.h3`
Expand Down
4 changes: 2 additions & 2 deletions src/components/group/RecordSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ interface RecordSectionProps {

const RecordSection = ({ currentPage, progress, onClick }: RecordSectionProps) => {
return (
<StyledRecordSection>
<RecordSectionHeader onClick={onClick}>
<StyledRecordSection onClick={onClick}>
<RecordSectionHeader>
<RecordSectionTitle>기록장</RecordSectionTitle>
<RecordSectionChevron src={rightChevron} alt="기록장 이동 버튼" />
</RecordSectionHeader>
Expand Down
15 changes: 11 additions & 4 deletions src/components/memory/RecordItem/RecordItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,20 @@ const RecordItem = ({ record, shouldBlur = false }: RecordItemProps) => {
const handleClick = useCallback(() => {
// 클릭으로 더보기 메뉴 표시
if (isMyRecord) {
openMoreMenu({
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 {
Comment on lines +272 to 286
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.

openMoreMenu({
onReport: handleReport,
Expand All @@ -285,6 +291,7 @@ const RecordItem = ({ record, shouldBlur = false }: RecordItemProps) => {
}
}, [
isMyRecord,
type,
openMoreMenu,
handleReport,
handleEdit,
Expand Down Expand Up @@ -365,7 +372,7 @@ const RecordItem = ({ record, shouldBlur = false }: RecordItemProps) => {
<img src={commentIcon} alt="댓글" />
<span>{commentCount}</span>
</ActionButton>
{isMyRecord && (
{isMyRecord && type === 'text' && (
<ActionButton
onClick={
shouldBlur
Expand Down
15 changes: 14 additions & 1 deletion src/pages/groupDetail/GroupDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,21 @@ const GroupDetail = () => {
} else {
setError(response.message);
}
} catch (error) {
} 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;
}

Comment on lines +100 to +114
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.

setError('방 정보를 불러오는데 실패했습니다.');
} finally {
setIsLoading(false);
Expand Down
11 changes: 9 additions & 2 deletions src/pages/groupDetail/ParticipatedGroupDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,16 @@ const ParticipatedGroupDetail = () => {
} else {
setError(response.message);
}
} catch (err) {
setError('방 정보를 불러오는 중 오류가 발생했습니다.');
} catch (err: any) {
console.error('방 상세 정보 조회 오류:', err);

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

setError('방 정보를 불러오는 중 오류가 발생했습니다.');
Comment on lines +77 to +86
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.

} finally {
setLoading(false);
}
Expand Down
11 changes: 9 additions & 2 deletions src/pages/groupMembers/GroupMembers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ const GroupMembers = () => {
} else {
setError(response.message);
}
} catch (err) {
setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.');
} catch (err: any) {
console.error('독서메이트 조회 오류:', err);

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

setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.');
} finally {
Comment on lines +45 to 55
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.

setLoading(false);
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/post/UpdatePost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const UpdatePost = () => {
contentBody: postContent.trim(),
isPublic: !isPrivate,
...(selectedTags.length ? { tagList: selectedTags } : {}),
...(remainImageUrls.length ? { remainImageUrls } : {}),
remainImageUrls, // 이미지가 없어도 빈 배열로 전송하여 삭제 처리
};

const result = await updateExistingFeed(Number(feedId), body);
Expand Down