Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
39 changes: 28 additions & 11 deletions src/_apis/crew/crew.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { fetchApi } from '@/src/utils/api';
import { CreateCrewRequestTypes, CreateCrewResponseTypes } from '@/src/types/create-crew';
import {
CreateCrewRequestTypes,
CreateCrewResponseTypes,
EditCrewRequestTypes,
EditCrewResponseTypes,
} from '@/src/types/create-crew';

export async function createCrew(data: CreateCrewRequestTypes) {
try {
const response: { data: CreateCrewResponseTypes; status: number } = await fetchApi(
`/api/crews`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
const response: { data: CreateCrewResponseTypes } = await fetchApi(`/api/crews`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
);
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
});
if (!response.data) {
throw new Error('Failed to create crew: No data received');
}
Expand All @@ -23,3 +25,18 @@ export async function createCrew(data: CreateCrewRequestTypes) {
throw error;
}
}

export async function editCrew(id: number, data: EditCrewRequestTypes) {
try {
await fetchApi(`/api/crews/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include authentication credentials
body: JSON.stringify(data),
});
} catch (error) {
throw error;
}
}
Comment on lines +29 to +42
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

editCrew 함수의 구현을 개선해야 합니다.

다음과 같은 중요한 개선사항들이 필요합니다:

  1. 응답 처리가 누락되어 있습니다
  2. 에러 처리가 미흡합니다
  3. 반환 타입이 명시되어 있지 않습니다

다음과 같이 수정하는 것을 제안드립니다:

-export async function editCrew(id: number, data: EditCrewRequestTypes) {
+export async function editCrew(id: number, data: EditCrewRequestTypes): Promise<EditCrewResponseTypes> {
   try {
-    await fetchApi(`/api/crews/${id}`, {
+    const response: { data: EditCrewResponseTypes } = await fetchApi(`/api/crews/${id}`, {
       method: 'PUT',
       headers: {
         'Content-Type': 'application/json',
       },
       credentials: 'include',
       body: JSON.stringify(data),
     });
+    if (!response.data) {
+      throw new Error('크루 수정 실패: 서버로부터 데이터를 받지 못했습니다');
+    }
+    return response.data;
   } catch (error) {
-    throw error;
+    if (error instanceof TypeError) {
+      throw new Error('크루 수정 실패: 네트워크 오류가 발생했습니다');
+    }
+    throw new Error(`크루 수정 실패: ${error.message}`);
   }
 }

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

🧰 Tools
🪛 Biome

[error] 40-40: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

46 changes: 45 additions & 1 deletion src/_queries/crew/crew-detail-queries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,53 @@
import { useQuery } from '@tanstack/react-query';
import { toast } from 'react-toastify';
import { useRouter } from 'next/navigation';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { createCrew, editCrew } from '@/src/_apis/crew/crew';
import { getCrewDetail } from '@/src/_apis/crew/crew-detail-apis';
import { CreateCrewRequestTypes, EditCrewRequestTypes } from '@/src/types/create-crew';

export function useGetCrewDetailQuery(id: number) {
return useQuery({
queryKey: ['crewDetail', id],
queryFn: () => getCrewDetail(id),
});
}

export function useCreateCrewQuery() {
const router = useRouter();
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: CreateCrewRequestTypes) => createCrew(data),
onSuccess: (data) => {
if (data === null || data === undefined) {
return;
}
queryClient.invalidateQueries({ queryKey: ['crewLists', 'crewDetail'] });
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

쿼리 무효화 시 올바른 사용법 적용

queryClient.invalidateQueries({ queryKey: ['crewLists', 'crewDetail'] });로 여러 쿼리를 무효화하려고 하고 있습니다. 그러나 이 방식은 의도한 대로 동작하지 않을 수 있습니다. 각각의 쿼리를 별도로 무효화하는 것이 좋습니다.

수정 제안:

-queryClient.invalidateQueries({ queryKey: ['crewLists', 'crewDetail'] });
+queryClient.invalidateQueries(['crewLists']);
+queryClient.invalidateQueries(['crewDetail']);
📝 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
queryClient.invalidateQueries({ queryKey: ['crewLists', 'crewDetail'] });
queryClient.invalidateQueries(['crewLists']);
queryClient.invalidateQueries(['crewDetail']);

toast.success('크루가 생성되었습니다.');
router.push(`/crew/detail/${data.crewId}`);
},
onError: (error) => {
toast.error(error.message);
},
});
}

export function useEditCrewQuery(id: number) {
const router = useRouter();
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: EditCrewRequestTypes) => editCrew(id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['crewDetail'] });
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

쿼리 무효화 시 정확한 키 사용

현재 queryClient.invalidateQueries({ queryKey: ['crewDetail'] });로 쿼리를 무효화하고 있습니다. 그러나 crewDetail 쿼리는 id를 포함하므로 정확한 쿼리 키를 사용하여 무효화해야 합니다.

수정 제안:

-queryClient.invalidateQueries({ queryKey: ['crewDetail'] });
+queryClient.invalidateQueries(['crewDetail', id]);

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

toast.success('크루 정보가 수정되었습니다.');
if (router) {
router.push(`/crew/detail/${id}`);
}
},
onError: (error) => {
toast.error(error.message);
},
retry: false,
});
}
15 changes: 7 additions & 8 deletions src/app/(crew)/crew/_components/create-crew-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import DropDown from '@/src/components/common/input/drop-down';
import FileInputWrap from '@/src/components/common/input/file-input-wrap';
import TextInput from '@/src/components/common/input/text-input';
import Textarea from '@/src/components/common/input/textarea';
import { CreateCrewFormTypes } from '@/src/types/create-crew';
import { CreateCrewFormTypes, EditCrewResponseTypes } from '@/src/types/create-crew';
import ImgCrewSampleUrls from '@/public/assets/images/crew-sample';

export interface CreateCrewFormProps {
data: CreateCrewFormTypes;
data: CreateCrewFormTypes | EditCrewResponseTypes;
isEdit?: boolean;
onEdit?: (data: CreateCrewFormTypes) => void;
onSubmit?: (data: CreateCrewFormTypes) => void;
Expand Down Expand Up @@ -105,7 +105,6 @@ export default function CreateCrewForm({
)}
/>
</div>

<div className="flex flex-col gap-3">
<label
htmlFor="crew-category"
Expand All @@ -123,7 +122,7 @@ export default function CreateCrewForm({
{...field}
variant="default"
inWhere="form"
placeholder="메인 카테고리"
placeholder={isEdit ? field.value : '메인 카테고리'}
data={categoryData.map((category) => category.title)}
className="flex-1"
onChange={(value) => {
Expand All @@ -142,7 +141,7 @@ export default function CreateCrewForm({
{...field}
variant="default"
inWhere="form"
placeholder="세부 카테고리"
placeholder={isEdit && field.value ? field.value : '세부 카테고리'}
data={categoryData[categoryIndex]?.items || []}
className="flex-1"
error={errors.subCategory?.message}
Expand Down Expand Up @@ -205,7 +204,7 @@ export default function CreateCrewForm({
{...field}
variant="default"
inWhere="form"
placeholder="특별시/도"
placeholder={isEdit ? field.value : '특별시/도'}
data={regionData.map((region) => region.main)}
className="flex-1"
onChange={(value) => {
Expand All @@ -225,7 +224,7 @@ export default function CreateCrewForm({
{...field}
variant="default"
inWhere="form"
placeholder="시/군/구"
placeholder={isEdit && field.value ? field.value : '시/군/구'}
data={regionData[regionIndex]?.areas || []}
className="flex-1"
error={errors.subLocation?.message}
Expand Down Expand Up @@ -271,7 +270,7 @@ export default function CreateCrewForm({
크루 소개
</label>
<span>
<span className="text-blue-500">{introduce.length}</span>/100
<span className="text-blue-500">{introduce?.length}</span>/100
</span>
</div>
<Controller
Expand Down
44 changes: 14 additions & 30 deletions src/app/(crew)/crew/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
'use client';

import { toast } from 'react-toastify';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { Loader } from '@mantine/core';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { createCrew } from '@/src/_apis/crew/crew';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import { useCreateCrewQuery } from '@/src/_queries/crew/crew-detail-queries';
import CreateCrewForm from '@/src/app/(crew)/crew/_components/create-crew-form';
import { CreateCrewFormTypes, CreateCrewRequestTypes } from '@/src/types/create-crew';
import IcoCreateCrew from '@/public/assets/icons/ic-create-crew.svg';

export default function CreateCrewPage() {
const router = useRouter();
const initialValue: CreateCrewFormTypes = {
title: '',
mainCategory: '',
Expand All @@ -23,36 +19,24 @@ export default function CreateCrewPage() {
totalCount: 4,
introduce: '',
};
const queryClient = useQueryClient();
const { isPending, mutate } = useMutation({
mutationFn: (data: CreateCrewRequestTypes) => createCrew(data),
onSuccess: (data) => {
if (data === null || data === undefined) {
return;
}
queryClient.invalidateQueries({ queryKey: ['crewLists', 'crewDetail'] });
router.push(`/crew/detail/${data.crewId}`);
},
onError: (error) => {
toast.error(error.message);
},
});

const handleSubmit = async (data: CreateCrewFormTypes) => {
let newImageUrl = data.imageUrl as string;
if (data.imageUrl instanceof File) {
const imgResponse = await getImageUrl(data.imageUrl, 'CREW');
const { isPending, mutate } = useCreateCrewQuery();
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

에러 처리가 누락되어 있습니다

useCreateCrewQuery 훅의 구현을 확인한 결과, onError 핸들러가 구현되어 있지 않습니다. 이는 다음과 같은 문제를 초래할 수 있습니다:

  • 사용자에게 오류 발생 시 적절한 피드백이 제공되지 않음
  • 네트워크 오류나 서버 오류 발생 시 적절한 처리가 되지 않음

수정 제안:

  • useMutation 설정에 onError 핸들러를 추가하여 에러 상황을 적절히 처리하도록 구현
  • 사용자에게 오류 메시지를 표시하는 UI 피드백 추가
🔗 Analysis chain

에러 처리 검증이 필요합니다.

useCreateCrewQuery 훅에서 에러 처리가 적절히 구현되어 있는지 확인이 필요합니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify error handling implementation in useCreateCrewQuery hook

# Check error handling in the query hook
ast-grep --pattern 'export function useCreateCrewQuery() {
  $$$
  onError($$$) {
    $$$
  }
  $$$
}'

Length of output: 328


Script:

#!/bin/bash
# Let's first find the hook file
fd "useCreateCrewQuery" --type f

# Then examine its content
rg "useCreateCrewQuery" -A 10 -B 10

Length of output: 4026


const handleSubmit = async (createdData: CreateCrewFormTypes) => {
let newImageUrl = createdData.imageUrl as string;
if (createdData.imageUrl instanceof File) {
const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW');
newImageUrl = imgResponse?.imageUrl as string;
}
const newData: CreateCrewRequestTypes = {
title: data.title,
mainCategory: data.mainCategory,
subCategory: data.subCategory ?? '',
title: createdData.title,
mainCategory: createdData.mainCategory,
subCategory: createdData.subCategory ?? '',
imageUrl: newImageUrl ?? '',
mainLocation: data.mainLocation,
subLocation: data.subLocation ?? '',
totalCount: data.totalCount,
introduce: data.introduce,
mainLocation: createdData.mainLocation,
subLocation: createdData.subLocation ?? '',
totalCount: createdData.totalCount,
introduce: createdData.introduce,
};

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

이미지 업로드 실패에 대한 에러 처리가 필요합니다.

getImageUrl 호출 시 발생할 수 있는 에러에 대한 처리가 없습니다. 이미지 업로드 실패 시 사용자에게 적절한 피드백을 제공해야 합니다.

 const handleSubmit = async (createdData: CreateCrewFormTypes) => {
   let newImageUrl = createdData.imageUrl as string;
   if (createdData.imageUrl instanceof File) {
-    const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW');
-    newImageUrl = imgResponse?.imageUrl as string;
+    try {
+      const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW');
+      if (!imgResponse?.imageUrl) {
+        throw new Error('이미지 URL을 받아오지 못했습니다.');
+      }
+      newImageUrl = imgResponse.imageUrl;
+    } catch (error) {
+      console.error('이미지 업로드 실패:', error);
+      // TODO: 에러 처리 로직 추가 (예: 토스트 메시지 표시)
+      return;
+    }
   }

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

mutate(newData);
Expand Down
48 changes: 30 additions & 18 deletions src/app/(crew)/crew/detail/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,46 @@

import Image from 'next/image';
import { useParams } from 'next/navigation';
import { useGetCrewDetailQuery } from '@/src/_queries/crew/crew-detail-queries';
import { Loader } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import { useEditCrewQuery, useGetCrewDetailQuery } from '@/src/_queries/crew/crew-detail-queries';
import CreateCrewForm from '@/src/app/(crew)/crew/_components/create-crew-form';
import { CreateCrewFormTypes } from '@/src/types/create-crew';
import { CreateCrewFormTypes, EditCrewRequestTypes } from '@/src/types/create-crew';
import IcoCreateCrew from '@/public/assets/icons/ic-create-crew.svg';

export default function EditCrewPage() {
const { id } = useParams();
const { data, isLoading, error } = useGetCrewDetailQuery(Number(id));

const { isPending, isSuccess, mutate } = useEditCrewQuery(Number(id));
if (data === undefined) return null;

// TODO : 테스트중
const initialValue: CreateCrewFormTypes = {
title: '',
mainCategory: '',
subCategory: '',
imageUrl:
'https://crewcrew.s3.ap-northeast-2.amazonaws.com/crew/0e05d971-15a8-4a32-bf03-80d12cae392e',
mainLocation: '',
subLocation: '',
totalCount: 4,
introduce: '',
};
const handleEdit = async (editedData: CreateCrewFormTypes) => {
let newImageUrl = editedData.imageUrl as string;

const handleEdit = () => {
// TODO : PATCH API 연결
if (editedData.imageUrl instanceof File && newImageUrl !== data.imageUrl) {
const imgResponse = await getImageUrl(editedData.imageUrl, 'CREW');
newImageUrl = imgResponse?.imageUrl as string;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

TypeScript 타입 캐스팅 관련 오류 수정 필요

editedData.imageUrlFile 타입일 때, newImageUrl을 문자열로 단언(as string)하고 있습니다. 그러나 실제로는 File 객체이기 때문에 타입 불일치로 인한 오류가 발생할 수 있습니다. 이로 인해 newImageUrl !== data.imageUrl 비교에서 예상치 못한 결과가 나올 수 있습니다. 정확한 타입 체크와 변환이 필요합니다.

수정을 위한 제안:

const handleEdit = async (editedData: CreateCrewFormTypes) => {
-   let newImageUrl = editedData.imageUrl as string;
+   let newImageUrl: string;

    if (editedData.imageUrl instanceof File && newImageUrl !== data.imageUrl) {
      const imgResponse = await getImageUrl(editedData.imageUrl, 'CREW');
      newImageUrl = imgResponse?.imageUrl as string;
    } else {
+     newImageUrl = editedData.imageUrl as string;
    }
    const newData: EditCrewRequestTypes = {
      // ... 생략 ...
    };
    mutate(newData);
};

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

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

이미지 업로드 응답에 대한 오류 처리가 필요합니다.

getImageUrl 응답이 실패하거나 imageUrl이 undefined인 경우에 대한 처리가 누락되어 있습니다.

다음과 같이 수정을 제안합니다:

  if (editedData.imageUrl instanceof File && newImageUrl !== data.imageUrl) {
    const imgResponse = await getImageUrl(editedData.imageUrl, 'CREW');
+   if (!imgResponse?.imageUrl) {
+     throw new Error('이미지 업로드에 실패했습니다.');
+   }
    newImageUrl = imgResponse?.imageUrl as string;
  }
📝 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
if (editedData.imageUrl instanceof File && newImageUrl !== data.imageUrl) {
const imgResponse = await getImageUrl(editedData.imageUrl, 'CREW');
newImageUrl = imgResponse?.imageUrl as string;
}
if (editedData.imageUrl instanceof File && newImageUrl !== data.imageUrl) {
const imgResponse = await getImageUrl(editedData.imageUrl, 'CREW');
if (!imgResponse?.imageUrl) {
throw new Error('이미지 업로드에 실패했습니다.');
}
newImageUrl = imgResponse?.imageUrl as string;
}

const newData: EditCrewRequestTypes = {
title: editedData.title,
mainCategory: editedData.mainCategory,
subCategory: editedData.subCategory ?? '',
imageUrl: newImageUrl ?? '',
mainLocation: editedData.mainLocation,
subLocation: editedData.subLocation ?? '',
totalCount: editedData.totalCount,
introduce: editedData.introduce,
};

mutate(newData);
};

if (isLoading || isPending)
return (
<div className="fixed inset-0 flex items-center justify-center">
<Loader size="sm" />
</div>
);
return (
<div className="lg:px-8.5 flex flex-col gap-3 px-3 py-8 md:gap-4 md:px-8 md:py-12.5 lg:gap-8">
<div className="flex items-center gap-3">
Expand All @@ -43,7 +55,7 @@ export default function EditCrewPage() {
</figure>
<h2 className="text-2xl font-bold text-gray-900 md:text-3.5xl">크루 수정하기</h2>
</div>
<CreateCrewForm isEdit data={initialValue} onEdit={handleEdit} />
<CreateCrewForm isEdit data={data as EditCrewRequestTypes} onEdit={handleEdit} />
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

타입 캐스팅이 안전하지 않습니다.

CreateCrewFormProps 인터페이스에 따르면 data prop은 CreateCrewFormTypes | EditCrewResponseTypes 타입을 받도록 정의되어 있는데, EditCrewRequestTypes로 캐스팅하는 것은 타입 안전성을 해칠 수 있습니다.

  • src/app/(crew)/crew/_components/create-crew-form/index.tsx에서 정의된 인터페이스와 일치하도록 타입을 수정해야 합니다.
  • 타입 캐스팅 대신 EditCrewResponseTypes 타입으로 데이터를 변환하는 로직을 추가하거나, 컴포넌트의 타입 정의를 수정해야 합니다.
🔗 Analysis chain

타입 캐스팅의 안전성 검증이 필요합니다.

data를 EditCrewRequestTypes로 캐스팅하기 전에 타입 가드를 사용하여 안전성을 보장하는 것이 좋습니다.

다음 스크립트로 CreateCrewForm 컴포넌트의 props 타입을 확인해보세요:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# CreateCrewForm 컴포넌트의 props 타입 정의 확인
ast-grep --pattern 'interface $interface {
  $$$
  data?: $type;
  $$$
}'

# EditCrewRequestTypes 타입 정의 확인
ast-grep --pattern 'type EditCrewRequestTypes = {
  $$$
}'

Length of output: 136


Script:

#!/bin/bash
# Search for CreateCrewForm component props interface
rg -A 10 "interface.*CreateCrewFormProps" 

# Search for EditCrewRequestTypes type definition
rg -A 10 "type EditCrewRequestTypes"

# Search for CreateCrewForm component implementation
ast-grep --pattern 'const CreateCrewForm = ($props: $type) => {
  $$$
}'

Length of output: 1122


Script:

#!/bin/bash
# Search for EditCrewResponseTypes and CreateCrewFormTypes definitions
rg -A 5 "type (EditCrewResponseTypes|CreateCrewFormTypes)"

# Search for actual usage of data prop in CreateCrewForm
ast-grep --pattern 'const { data, $$ } = $props'

# Search for type validation or guards
rg -A 3 "data as|is.*data"

Length of output: 12144

</div>
);
}
6 changes: 3 additions & 3 deletions src/app/(crew)/my-crew/hosted/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';

export default function MyCrewHostedPage() {
const { data, status, ref, isFetchingNextPage } = useInfiniteScroll(
const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll(
useGetMyCrewHostedQuery({
pageable: { page: 0, size: 6, sort: ['createdAt,desc'] },
}),
);
return (
<div>
<CrewCardList inWhere="my-crew" data={data ?? { pages: [], pageParams: [] }} />
{status === 'pending' || isFetchingNextPage ? (
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
) : (
<div ref={ref} className="h-[1px]" />
)}
{status === 'error' && <p className="py-10 text-center">에러가 발생했습니다.</p>}
{error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
</div>
);
}
6 changes: 3 additions & 3 deletions src/app/(crew)/my-crew/joined/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';

export default function MyCrewJoinedPage() {
const { data, status, ref, isFetchingNextPage } = useInfiniteScroll(
const { data, isLoading, error, ref, isFetchingNextPage } = useInfiniteScroll(
useGetMyCrewJoinedQuery({
pageable: { page: 0, size: 6, sort: ['createdAt,desc'] },
}),
);
return (
<div>
<CrewCardList inWhere="my-crew" data={data ?? { pages: [], pageParams: [] }} />
{status === 'pending' || isFetchingNextPage ? (
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
) : (
<div ref={ref} className="h-[1px]" />
)}
{status === 'error' && <p className="py-10 text-center">에러가 발생했습니다.</p>}
{error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리를 더 상세하게 개선하면 좋겠습니다.

현재는 일반적인 에러 메시지만 표시되고 있습니다. 사용자에게 더 구체적인 정보와 해결 방법을 제공하면 좋을 것 같습니다.

다음과 같이 개선해보세요:

- {error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
+ {error && (
+   <div className="py-10 text-center">
+     <p className="text-red-500 mb-2">데이터를 불러오는 중 문제가 발생했습니다.</p>
+     <button
+       onClick={() => window.location.reload()}
+       className="text-blue-500 underline"
+     >
+       다시 시도하기
+     </button>
+   </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
{error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
{error && (
<div className="py-10 text-center">
<p className="text-red-500 mb-2">데이터를 불러오는 중 문제가 발생했습니다.</p>
<button
onClick={() => window.location.reload()}
className="text-blue-500 underline"
>
다시 시도하기
</button>
</div>
)}

</div>
);
}
6 changes: 3 additions & 3 deletions src/app/(crew)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function HomePage() {
}
};

const { data, status, isFetchingNextPage, ref } = useInfiniteScroll(
const { data, isLoading, error, isFetchingNextPage, ref } = useInfiniteScroll(
useGetCrewListQuery({
condition: {
keyword: search,
Expand Down Expand Up @@ -133,14 +133,14 @@ export default function HomePage() {
</div>
<div className="mt-8 px-3 md:px-8 lg:px-11.5">
{data && <CrewCardList data={data} />}
{status === 'pending' || isFetchingNextPage ? (
{isLoading || isFetchingNextPage ? (
<div className="flex justify-center py-10">
<Loader size="sm" />
</div>
) : (
<div ref={ref} className="h-[1px]" />
)}
{status === 'error' && <p className="py-10 text-center">에러가 발생했습니다.</p>}
{error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리를 더 자세하게 개선해 주세요.

현재 에러 메시지가 너무 단순합니다. 사용자에게 더 구체적인 정보와 문제 해결 방법을 제공하면 좋겠습니다.

- {error && <p className="py-10 text-center">에러가 발생했습니다.</p>}
+ {error && (
+   <div className="flex flex-col items-center py-10 text-center">
+     <p className="mb-2 text-red-500">데이터를 불러오는 중 문제가 발생했습니다.</p>
+     <p className="mb-4 text-sm text-gray-600">잠시 후 다시 시도해 주세요.</p>
+     <Button
+       onClick={() => window.location.reload()}
+       className="btn-filled px-4 py-2 text-sm"
+     >
+       새로고침
+     </Button>
+   </div>
+ )}

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

</div>
</div>
);
Expand Down
Loading