Skip to content
5 changes: 2 additions & 3 deletions src/app/(crew)/my-crew/hosted/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Loader } from '@mantine/core';
import { useGetMyCrewHostedQuery } from '@/src/_queries/crew/my-crew-hosted-list-query';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';
import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list';

export default function MyCrewHostedPage() {
const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll(
Expand All @@ -15,9 +16,7 @@ export default function MyCrewHostedPage() {
<div>
<CrewCardList inWhere="my-crew" data={data ?? { pages: [], pageParams: [] }} />
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
<CrewSkeletonList num={6} column={1} />
) : (
<div ref={ref} className="h-[1px]" />
)}
Expand Down
5 changes: 2 additions & 3 deletions src/app/(crew)/my-crew/joined/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Loader } from '@mantine/core';
import { useGetMyCrewJoinedQuery } from '@/src/_queries/crew/my-crew-joined-list-query';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';
import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list';

export default function MyCrewJoinedPage() {
const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll(
Expand All @@ -15,9 +16,7 @@ export default function MyCrewJoinedPage() {
<div>
<CrewCardList inWhere="my-crew" data={data ?? { pages: [], pageParams: [] }} />
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
<CrewSkeletonList num={6} column={1} />
) : (
<div ref={ref} className="h-[1px]" />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@/src/_apis/auth/user-apis';
import { useUser } from '@/src/_queries/auth/user-queries';
import { useAuth } from '@/src/hooks/use-auth';
import ProfileSkeleton from '@/src/components/common/skeleton/profile-skeleton';
import ProfileCardPresenter from './presenter';

export default function ProfileCard() {
Expand Down Expand Up @@ -38,7 +39,7 @@ export default function ProfileCard() {
}
}, [user]);

if (userLoading || !isAuthChecked) return <div>로딩 중...</div>;
if (userLoading || !isAuthChecked) return <ProfileSkeleton />;
if (!user) return null;

const handleEdit = () => {
Expand Down
7 changes: 3 additions & 4 deletions src/app/(crew)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useRef, useState } from 'react';
import Image from 'next/image';
import { Divider, Loader, TextInput } from '@mantine/core';
import { Divider, Loader, Skeleton, TextInput } from '@mantine/core';
import { useGetCrewListQuery } from '@/src/_queries/crew/crew-list-queries';
import regionData from '@/src/data/region.json';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
Expand All @@ -11,6 +11,7 @@ import HeroCrew from '@/src/app/(crew)/_components/hero/hero-crew';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';
import Button from '@/src/components/common/input/button';
import DropDown from '@/src/components/common/input/drop-down';
import CrewSkeletonList from '@/src/components/common/skeleton/crew-skeleton-list';
import IcoSearch from '@/public/assets/icons/ic-search.svg';

export default function HomePage() {
Expand Down Expand Up @@ -133,9 +134,7 @@ export default function HomePage() {
<div className="mt-8 px-3 md:px-8 lg:px-11.5">
{data && <CrewCardList data={data} />}
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
<CrewSkeletonList num={6} column={2} />
) : (
<div ref={ref} className="h-[1px]" />
)}
Expand Down
18 changes: 18 additions & 0 deletions src/components/common/skeleton/crew-skeleton-list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import CrewSkeleton from '../crew-skeleton';

interface CrewSkeletonListProps {
num: number;
column?: number;
}

export default function CrewSkeletonList({ num, column }: CrewSkeletonListProps) {
const columnStyle = column === 2 ? 'lg:grid-cols-2' : 'lg:grid-cols-1';
return (
<div className={`grid grid-cols-1 gap-2 ${columnStyle}`}>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 개선이 필요합니다.

로딩 상태를 스크린 리더 사용자에게도 알려주기 위해 적절한 ARIA 속성을 추가하는 것이 좋습니다.

-    <div className={`grid grid-cols-1 gap-2 ${columnStyle}`}>
+    <div 
+      className={`grid grid-cols-1 gap-2 ${columnStyle}`}
+      role="status"
+      aria-label="콘텐츠 로딩 중"
+      aria-busy="true"
+    >
📝 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
<div className={`grid grid-cols-1 gap-2 ${columnStyle}`}>
<div
className={`grid grid-cols-1 gap-2 ${columnStyle}`}
role="status"
aria-label="콘텐츠 로딩 중"
aria-busy="true"
>

{[...Array(num)].map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<CrewSkeleton key={index} />
))}
</div>
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

컴포넌트 구현에 대한 몇 가지 개선사항이 있습니다.

  1. key 값으로 배열 인덱스를 사용하는 것은 React의 재조정(reconciliation) 과정에서 문제를 일으킬 수 있습니다.
  2. 배열 생성 방식을 최적화할 수 있습니다.
  3. 컬럼 스타일 로직을 더 명확하게 표현할 수 있습니다.

아래와 같이 개선하는 것을 제안드립니다:

-export default function CrewSkeletonList({ num, column }: CrewSkeletonListProps) {
-  const columnStyle = column === 2 ? 'lg:grid-cols-2' : 'lg:grid-cols-1';
+export default function CrewSkeletonList({ 
+  skeletonCount, 
+  columnCount 
+}: CrewSkeletonListProps) {
+  const columnStyle = `lg:grid-cols-${columnCount ?? 1}`;
   return (
     <div className={`grid grid-cols-1 gap-2 ${columnStyle}`}>
-      {[...Array(num)].map((_, index) => (
-        // eslint-disable-next-line react/no-array-index-key
-        <CrewSkeleton key={index} />
+      {Array.from({ length: skeletonCount }, (_, index) => (
+        <CrewSkeleton 
+          key={`skeleton-${index}-${Date.now()}`} 
+        />
       ))}
     </div>
   );
 }

변경사항 설명:

  1. Array.from을 사용하여 더 명확하고 효율적인 배열 생성
  2. 더 안정적인 key 생성 방식 적용
  3. 널 병합 연산자를 사용하여 컬럼 스타일 로직 단순화

스켈레톤 아이템의 개수가 많을 경우를 대비하여 성능 최적화를 위해 React.memo를 고려해보시는 것도 좋을 것 같습니다.

Committable suggestion skipped: line range outside the PR's diff.

15 changes: 15 additions & 0 deletions src/components/common/skeleton/crew-skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Skeleton } from '@mantine/core';

export default function CrewSkeleton() {
return (
<div className="flex overflow-hidden rounded-xl">
<Skeleton className="h-[203px] w-[230px]" />
<div className="relative flex flex-1 flex-col gap-2 p-6">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-4 w-32" />
<Skeleton className="h-4 w-40" />
<Skeleton className="absolute bottom-6 left-6 right-6 h-2 w-auto" />
</div>
</div>
);
}
16 changes: 16 additions & 0 deletions src/components/common/skeleton/gathering-skeleton-list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import GatheringSkeleton from '../gathering-skeleton';

interface GatheringSkeletonListProps {
num: number;
}

export default function GatheringSkeletonList({ num }: GatheringSkeletonListProps) {
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{[...Array(num)].map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<GatheringSkeleton key={index} />
))}
Comment on lines +13 to +16
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

배열 인덱스를 key로 사용하는 것은 피해야 합니다.

현재 구현에서는 배열 인덱스를 key로 사용하고 있는데, 이는 React의 재조정(reconciliation) 과정에서 문제를 일으킬 수 있습니다. 스켈레톤 아이템의 경우 정적이므로 큰 문제가 되지 않을 수 있으나, 더 안전한 방법을 사용하는 것이 좋습니다.

- {[...Array(num)].map((_, index) => (
-   // eslint-disable-next-line react/no-array-index-key
-   <GatheringSkeleton key={index} />
- ))}
+ {Array.from({ length: num }, (_, index) => (
+   <GatheringSkeleton key={`skeleton-${index}`} />
+ ))}

또한, Array.from()을 사용하면 스프레드 연산자를 사용하는 것보다 더 명확하고 효율적입니다.

📝 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
{[...Array(num)].map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<GatheringSkeleton key={index} />
))}
{Array.from({ length: num }, (_, index) => (
<GatheringSkeleton key={`skeleton-${index}`} />
))}

</div>
);
}
16 changes: 16 additions & 0 deletions src/components/common/skeleton/gathering-skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Skeleton } from '@mantine/core';

export default function GatheringSkeleton() {
return (
<div className="flex flex-col overflow-hidden rounded-xl">
<Skeleton className="h-[160px] w-full" />
<div className="relative flex min-h-[184px] flex-col gap-2 p-4">
<Skeleton className="h-4 w-40" />
<Skeleton className="h-4 w-32" />
<Skeleton className="h-4 w-24" />
<Skeleton circle className="absolute right-4 top-4 h-8 w-8" />
<Skeleton className="absolute bottom-4 left-4 right-4 h-10 w-auto" />
</div>
</div>
);
}
17 changes: 17 additions & 0 deletions src/components/common/skeleton/my-gathering-skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Skeleton } from '@mantine/core';

export default function MyGatheringSkeleton() {
return (
<div className="flex gap-6 p-6">
<Skeleton className="h-28 w-28 rounded-xl md:h-32 md:w-32" />
<div className="relative flex flex-col gap-3">
<Skeleton className="h-4 w-40" />
<div className="flex gap-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-4 w-24" />
</div>
<Skeleton className="absolute bottom-0 h-4 w-24" />
</div>
</div>
);
}
Comment on lines +1 to +17
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

스켈레톤 컴포넌트가 현재 미사용 상태입니다

검증 결과, MyGatheringSkeleton 컴포넌트는 정의된 파일(src/components/common/skeleton/my-gathering-skeleton/index.tsx) 외에 다른 곳에서 사용되지 않고 있습니다. 이는 다음 두 가지 가능성을 시사합니다:

  • 새로 추가된 컴포넌트로 아직 사용되지 않은 상태
  • 더 이상 필요하지 않은 미사용 컴포넌트

개발자의 의도에 따라 다음 조치가 필요합니다:

  • 새로운 기능을 위한 컴포넌트라면 사용 계획 확인
  • 불필요한 컴포넌트라면 제거 고려
🔗 Analysis chain

컴포넌트 사용 패턴 확인 필요

이 스켈레톤 컴포넌트가 다른 페이지들과 일관되게 사용되고 있는지 확인이 필요합니다.


Let me try different commands to gather the necessary information.


Let me try one more search with different commands to find the component usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 스켈레톤 컴포넌트의 사용 패턴 확인

# MyGatheringSkeleton 컴포넌트의 사용처 확인
echo "MyGatheringSkeleton 사용 현황:"
rg "MyGatheringSkeleton" --type tsx

# 다른 스켈레톤 컴포넌트들과의 일관성 확인
echo "\n다른 스켈레톤 컴포넌트 현황:"
fd --type f ".*skeleton.*\.tsx$" src/components

Length of output: 223


Script:

#!/bin/bash
# MyGatheringSkeleton 컴포넌트 사용처 확인
echo "MyGatheringSkeleton 사용 현황:"
rg "MyGatheringSkeleton" -t ts -t tsx

# 다른 스켈레톤 컴포넌트 검색
echo -e "\n스켈레톤 컴포넌트 파일 목록:"
find src/components -type f -name "*skeleton*.tsx"

# 스켈레톤 컴포넌트 임포트 패턴 확인
echo -e "\n스켈레톤 컴포넌트 임포트 패턴:"
rg "import.*Skeleton" -t ts -t tsx

# MyGatheringSkeleton 구현 확인
echo -e "\nMyGatheringSkeleton 구현 확인:"
ast-grep --pattern 'export default function MyGatheringSkeleton() { $$$ }'

Length of output: 1965


Script:

#!/bin/bash
# 전체 코드베이스에서 MyGatheringSkeleton 문자열 검색
echo "MyGatheringSkeleton 사용 현황:"
find . -type f -exec grep -l "MyGatheringSkeleton" {} \;

# 스켈레톤 관련 파일 검색 (대소문자 구분 없이)
echo -e "\n스켈레톤 관련 파일:"
find . -type f -iname "*skeleton*.tsx" -o -iname "*skeleton*.ts"

# Mantine Skeleton 임포트 검색
echo -e "\nMantine Skeleton 임포트:"
find . -type f -exec grep -l "@mantine/core.*Skeleton" {} \;

Length of output: 187

13 changes: 13 additions & 0 deletions src/components/common/skeleton/profile-skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Skeleton } from '@mantine/core';

export default function ProfileSkeleton() {
return (
<div className="flex items-center gap-6.5">
<Skeleton circle className="h-28 w-28" />
<div className="flex flex-col gap-3">
<Skeleton className="h-6 w-40" />
<Skeleton className="h-4 w-36" />
</div>
</div>
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 개선이 필요합니다.

스켈레톤 UI의 목적을 스크린 리더 사용자에게도 전달하기 위해 적절한 ARIA 속성을 추가하는 것이 좋습니다.

다음과 같이 수정해보세요:

 export default function ProfileSkeleton() {
   return (
-    <div className="flex items-center gap-6.5">
+    <div 
+      className="flex items-center gap-6.5"
+      role="status"
+      aria-label="프로필 정보 로딩 중"
+    >
       <Skeleton circle className="h-28 w-28" />
       <div className="flex flex-col gap-3">
         <Skeleton className="h-6 w-40" />
         <Skeleton className="h-4 w-36" />
       </div>
     </div>
   );
 }
📝 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
export default function ProfileSkeleton() {
return (
<div className="flex items-center gap-6.5">
<Skeleton circle className="h-28 w-28" />
<div className="flex flex-col gap-3">
<Skeleton className="h-6 w-40" />
<Skeleton className="h-4 w-36" />
</div>
</div>
);
}
export default function ProfileSkeleton() {
return (
<div
className="flex items-center gap-6.5"
role="status"
aria-label="프로필 정보 로딩 중"
>
<Skeleton circle className="h-28 w-28" />
<div className="flex flex-col gap-3">
<Skeleton className="h-6 w-40" />
<Skeleton className="h-4 w-36" />
</div>
</div>
);
}

8 changes: 7 additions & 1 deletion src/components/gathering-list/liked-list-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { toast } from 'react-toastify';
import { addLike, removeLike } from '@/src/_apis/liked/liked-apis';
import { useGetLikedListQuery } from '@/src/_queries/liked/liked-queries';
import { ApiError } from '@/src/utils/api';
import GatheringSkeletonList from '../common/skeleton/gathering-skeleton-list';
import LikedListPresenter from './liked-list-presenter';

export default function LikedList() {
Expand Down Expand Up @@ -37,7 +38,12 @@ export default function LikedList() {
setPage(newPage);
};

if (isLoading) return <div>로딩중...</div>;
if (isLoading)
return (
<div className="mx-auto max-w-[1200px] px-4">
<GatheringSkeletonList num={6} />
</div>
);

// 에러 처리: error 또는 gatheringData가 undefined일 경우
if (error || gatheringData === undefined) {
Expand Down
Loading