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
16 changes: 15 additions & 1 deletion src/api/rooms/getRoomDetail.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { apiClient } from '../index';
import { AxiosError } from 'axios';

// 방 상세 정보 응답 타입
export interface RoomDetailResponse {
Expand Down Expand Up @@ -42,8 +43,21 @@ 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: unknown) {
console.error('방 상세 정보 조회 API 오류:', error);

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

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

throw error;
}
};
9 changes: 8 additions & 1 deletion src/api/rooms/getRoomMembers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { apiClient } from '../index';
import { AxiosError } from 'axios';

export interface RoomMember {
userId: number;
Expand Down Expand Up @@ -47,8 +48,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: unknown) {
console.error('독서메이트 조회 API 오류:', error);

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

throw error;
}
};
9 changes: 8 additions & 1 deletion src/api/rooms/getRoomPlaying.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { apiClient } from '../index';
import { AxiosError } from 'axios';

// 투표 아이템 타입
export interface VoteItem {
Expand Down Expand Up @@ -65,8 +66,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: unknown) {
console.error('진행중인 방 상세 정보 조회 API 오류:', error);

// 방 접근 권한이 없는 경우
if (error instanceof AxiosError && 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
7 changes: 6 additions & 1 deletion src/components/group/CommentSection.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ export const CommentSection = styled.section`
margin: 20px 20px 0 20px;
padding: 16px 12px;
border-radius: 12px;
cursor: pointer;

&:focus-visible {
outline: 2px solid ${colors.purple.main};
outline-offset: 2px;
}
`;

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>
Comment on lines +18 to +19
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

비-버튼 요소 클릭: 키보드 접근성과 스크린 리더 지원 추가 필요

section 클릭으로 바뀌면서 탭 포커스/Enter/Space 조작이 불가합니다. role="button", tabIndex, onKeyDown을 추가해 주세요.

-    <StyledCommentSection onClick={onClick}>
-      <CommentSectionHeader>
+    <StyledCommentSection
+      role="button"
+      tabIndex={0}
+      onClick={onClick}
+      onKeyDown={handleKeyDown}
+      aria-label="오늘의 한마디 이동"
+    >
+      <CommentSectionHeader>

컴포넌트 내부에 핸들러를 추가하세요(타입 임포트가 번거로우면 any 생략 가능).

// return 위에 추가
const handleKeyDown = (e: React.KeyboardEvent) => {
  if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    onClick();
  }
};

필요 시 타입 전용 임포트:

import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
// ...
const handleKeyDown = (e: ReactKeyboardEvent) => { /* 동일 */ };
🤖 Prompt for AI Agents
In src/components/group/CommentSection.tsx around lines 18-19, the section
element lost keyboard accessibility when changed from a button; add
role="button" and tabIndex={0} to make it focusable, implement an internal
handleKeyDown that prevents default and calls onClick when e.key is "Enter" or "
" (Space), and attach it as onKeyDown={handleKeyDown}; keep onClick as-is. If
you prefer stricter typing, add a type-only import for React.KeyboardEvent and
type the handler, otherwise omit types for brevity.

<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;
`;
Comment on lines +13 to 14
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

섹션 전체 클릭 전환 OK — 포커스 가시성 추가 제안

section을 버튼처럼 사용하므로 키보드 포커스 링을 제공해야 합니다.

 export const RecordSection = styled.section`
   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: pointer;
+  &:focus-visible {
+    outline: 2px solid ${colors.purple.main};
+    outline-offset: 2px;
+  }
 `;
📝 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
cursor: pointer;
`;
export const RecordSection = styled.section`
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;
&:focus-visible {
outline: 2px solid ${colors.purple.main};
outline-offset: 2px;
}
`;
🤖 Prompt for AI Agents
In src/components/group/RecordSection.styled.ts around lines 13-14, the section
is styled as a clickable element but lacks keyboard focus visibility; make the
section keyboard-focusable (e.g., ensure it gets tabindex when used as a button)
and add focus styles using :focus and preferably :focus-visible to show a clear
outline or box-shadow (contrasting color and at least 2px) and preserve existing
hover/click styles so keyboard users see the same interactive affordance.


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>
Comment on lines +23 to +24
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

컨테이너 onClick 이동은 타당 — 키보드 조작 가능하도록 역할/핸들러 추가 필요

접근성 준수를 위해 role="button", tabIndex, onKeyDown을 추가해 주세요.

-    <StyledRecordSection onClick={onClick}>
-      <RecordSectionHeader>
+    <StyledRecordSection
+      role="button"
+      tabIndex={0}
+      onClick={onClick}
+      onKeyDown={handleKeyDown}
+      aria-label="기록장 이동"
+    >
+      <RecordSectionHeader>

컴포넌트 내부에 핸들러 추가:

// return 위에 추가
const handleKeyDown = (e: React.KeyboardEvent) => {
  if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    onClick();
  }
};
🤖 Prompt for AI Agents
In src/components/group/RecordSection.tsx around lines 23 to 24, the container
has an onClick but lacks keyboard accessibility attributes; add role="button"
and tabIndex={0} to the StyledRecordSection and implement an onKeyDown handler
that calls onClick when Enter or Space is pressed (preventDefault first). Define
the handleKeyDown function above the return to check for e.key === 'Enter' ||
e.key === ' ' (or 'Spacebar' for legacy) and call onClick(); attach it as
onKeyDown={handleKeyDown}. Ensure onClick is invoked safely (check it exists)
and keep the existing onClick prop.

<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 {
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: unknown) {
console.error('방 상세 정보 조회 실패:', error);

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

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

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: unknown) {
console.error('방 상세 정보 조회 오류:', err);

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

setError('방 정보를 불러오는 중 오류가 발생했습니다.');
} 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: unknown) {
console.error('독서메이트 조회 오류:', err);

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

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