Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ff9d3f5
Squashed commit of the following:
yulrang Nov 11, 2024
bc4056a
[WIP] Feat: GetCrewList API 연결
yulrang Nov 11, 2024
6ac757f
Merge branch 'develop' into Feat/98/CrewAPI
yulrang Nov 11, 2024
0936ad1
✨ Feat: 백엔드 api 연결
yulrang Nov 11, 2024
e64877f
🐛 Fix: 처음 로딩시 카테고리 초기화
yulrang Nov 11, 2024
ab7d7a9
🐛 Fix: 카테고리 동작 개선
yulrang Nov 12, 2024
b4a70ff
🐛 Fix: 지역 필터링 동작 개선
yulrang Nov 12, 2024
22f3307
✨ Feat: 지역 전체 필터 추가
yulrang Nov 12, 2024
60a3b12
Merge branch 'develop' into Feat/98/CrewAPI
yulrang Nov 12, 2024
1026125
🚨 Fix: 타입 개선, 빌드 오류 수정
yulrang Nov 12, 2024
56bea37
🚧 Chore: workflow 수정
yulrang Nov 12, 2024
1d430b1
♻️ Refactor 코드 개선
yulrang Nov 12, 2024
3a7ce8e
🐛 Fix: 코드 개선
yulrang Nov 12, 2024
ecd941d
✨ Feat: 검색 버튼 추가
yulrang Nov 12, 2024
9a3f685
🐛 Fix: 동작 개선
yulrang Nov 12, 2024
89632d1
🚧 Chore: 경로 변경
yulrang Nov 12, 2024
04225e6
Merge branch 'develop' into Feat/98/CrewAPI
yulrang Nov 12, 2024
ae1729a
🐛 Fix: 콘솔에러 추가
yulrang Nov 12, 2024
b4b356c
⚡️ Fix: 검색 엔터방식으로 변경
yulrang Nov 12, 2024
67886c3
🐛 Fix: 동작개선, 에러메시지 보이기
yulrang Nov 12, 2024
3213309
Merge branch 'Feat/98/CrewAPI' of https://github.com/CodeitFESI4-Team…
yulrang Nov 12, 2024
b9b5cb5
💄 Design: 스타일 수정
yulrang Nov 12, 2024
5687085
🐛 Fix: 에러처리 추가
yulrang Nov 13, 2024
c3382b1
🚨 Fix: 빌드 오류 수정
yulrang Nov 13, 2024
be1480d
🐛 Fix: 타입 수정
yulrang Nov 13, 2024
3c567f3
🐛 Fix: 디폴트이미지 삭제, 데브툴 조건부렌더링
yulrang Nov 13, 2024
abdc917
Merge branch 'develop' into Feat/98/CrewAPI
yulrang Nov 13, 2024
7642e5b
🐛 Fix: 렌더링 방식 변경
yulrang Nov 13, 2024
84780af
🚨 Fix: 빌드 오류 수정
yulrang Nov 13, 2024
47f45c4
🐛 Fix: env 추가
yulrang Nov 13, 2024
8f150e0
🚚 Chore: 파일 이동, 파일명 변경
yulrang Nov 13, 2024
3febcae
🚚 Chore: 파일명 변경
yulrang Nov 13, 2024
1ce21b6
♿️ Fix: 접근성 개선
yulrang Nov 13, 2024
268e90f
Merge branch 'develop' into Feat/98/CrewAPI
yulrang Nov 13, 2024
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
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { UseInfiniteQueryOptions } from '@tanstack/react-query';
import { ConditionTypes, MainCrewListResponse, PageableTypes } from '@/src/types/crew-card';
import { getCrewList } from '../_apis/crew/get-crew-list';
import { getCrewList } from '../../_apis/crew/crew-list';

export function useGetCrewListQuery(condition: ConditionTypes) {
return {
Expand Down
4 changes: 0 additions & 4 deletions src/app/(crew)/my-crew/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
'use client';

import { useState } from 'react';
import { useGetCrewListQuery } from '@/src/_queries/crew-queries';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';
import Tabs from '@/src/components/common/tab';
import { MyCrewListResponse } from '@/src/types/crew-card';

export default function MyCrewPage() {
const myPageTabs = [
Expand Down
146 changes: 125 additions & 21 deletions src/app/(crew)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,130 @@
import { getCrewList } from '@/src/_apis/crew/get-crew-list';
import FindCrew from '../_components/find-crew/find-crew';
'use client';

export default async function HomePage() {
const initialData = await getCrewList(
{
keyword: '',
mainLocation: '',
mainCategory: '',
subCategory: '',
sortType: 'LATEST',
},
{
page: 0,
size: 6,
sort: ['LATEST'],
},
);
import { useRef, useState } from 'react';
import Image from 'next/image';
import { Divider, 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';
import CategoryContainer from '@/src/app/_components/category/category-container';
import HeroCrew from '@/src/app/_components/hero/hero-crew';
import CrewCardList from '@/src/components/common/crew-list/crew-card-list';
import DropDown from '@/src/components/common/input/drop-down';
import { MainCrewListResponse } from '@/src/types/crew-card';
import IcoSearch from '@/public/assets/icons/ic-search.svg';

export default function HomePage() {
const [mainCategory, setMainCategory] = useState('');
const [subCategory, setSubCategory] = useState('');
const [sort, setSort] = useState<string | null>('latest');
const [region, setRegion] = useState<string>('');
const [search, setSearch] = useState('');
const searchRef = useRef<HTMLInputElement>(null);

const handleRegionChange = (newValue: string) => {
const selectedRegion = regionData.find((dataItem) => dataItem.main.value === newValue);
if (selectedRegion?.main.label === '지역 전체') return '';
return selectedRegion ? selectedRegion.main.label : '';
};

const infiniteData = {
pages: [initialData],
pageParams: [],
const handleSearch = () => {
if (searchRef.current) {
setMainCategory('');
setSubCategory('');
setSearch(searchRef.current.value);
}
};

return <FindCrew initialData={infiniteData} />;
const { data, ref, isFetchingNextPage } = useInfiniteScroll(
useGetCrewListQuery({
keyword: search,
mainLocation: handleRegionChange(region),
mainCategory,
subCategory,
sortType: sort === 'latest' ? 'LATEST' : 'POPULAR',
}),
);
Comment on lines +38 to +46
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

데이터 페칭 에러 처리 보완 필요

무한 스크롤 구현은 잘 되어있지만, 다음 사항들을 고려해보시면 좋을 것 같습니다:

  • 데이터 로딩 상태 표시
  • 에러 발생 시 사용자 피드백
  • 재시도 메커니즘
const { data, error, isLoading, ref, isFetchingNextPage } = useInfiniteScroll(
  useGetCrewListQuery({
    keyword: search,
    mainLocation: handleRegionChange(region),
    mainCategory,
    subCategory,
    sortType: sort === 'latest' ? 'LATEST' : 'POPULAR',
  }),
);

// JSX에 에러 상태 추가
{error && <ErrorMessage error={error} onRetry={() => refetch()} />}
{isLoading && <LoadingSpinner />}


return (
<div className="py-8 md:py-12.5">
<div className="flex flex-col px-3 md:px-8 lg:px-11.5">
<HeroCrew />
<CategoryContainer
mainCategory={mainCategory}
subCategory={subCategory}
setMainCategory={(newValue) => {
setMainCategory(newValue);
if (searchRef.current) searchRef.current.value = '';
if (search !== '') setSearch('');
}}
setSubCategory={(newValue) => {
setSubCategory(newValue);
if (searchRef.current) searchRef.current.value = '';
if (search !== '') setSearch('');
}}
/>
</div>
<Divider mx={{ base: 0, md: 32, lg: 34 }} my={24} size={2} color="#E5E7EB" />
<div className="px-3 md:px-8 lg:px-11.5">
<div className="flex flex-col justify-between gap-2 md:flex-row md:gap-4">
<div className="flex-1">
<TextInput
ref={searchRef}
leftSectionPointerEvents="none"
onKeyDown={(e) => {
if (e.key === 'Enter') handleSearch();
}}
rightSection={
<button type="button" className="flex h-5 w-5" onClick={handleSearch}>
<Image
src={IcoSearch}
alt="search"
width={20}
height={20}
className="-ml-1"
aria-label="검색하기"
/>
</button>
}
placeholder="크루 이름, 위치를 검색하세요."
classNames={{
input:
'h-11 w-full rounded-xl border-0 pr-10 font-pretendard text-base font-medium text-gray-800 placeholder:text-gray-500',
}}
/>
</div>
<div className="flex-0 flex justify-between gap-2 md:basis-67 md:gap-4">
<DropDown
name="region"
variant="default"
data={regionData.map((dataItem) => dataItem.main)}
placeholder="지역 전체"
value={region}
className="w-[130px]"
onChange={(newValue) => {
setRegion(newValue as string);
}}
/>
<DropDown
name="sort"
variant="sort"
data={[
{ value: 'latest', label: '최신순' },
{ value: 'best', label: '인기순' },
]}
placeholder="최신순"
value={sort}
className="w-[130px]"
onChange={(newValue) => {
setSort(newValue as string);
}}
/>
</div>
</div>
</div>
<div className="mt-8 px-3 md:px-8 lg:px-11.5">
{data && <CrewCardList data={data} ref={ref} isFetchingNextPage={isFetchingNextPage} />}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

데이터 로딩 상태 처리 개선 필요

현재 구현에서는 데이터가 없을 때의 상태 처리가 불충분합니다. 다음과 같은 상태들을 추가로 처리하면 좋을 것 같습니다:

  • 초기 로딩 상태
  • 데이터가 없는 경우
  • 에러 상태
- {data && <CrewCardList data={data} ref={ref} isFetchingNextPage={isFetchingNextPage} />}
+ {isLoading ? (
+   <LoadingSpinner />
+ ) : error ? (
+   <ErrorMessage error={error} />
+ ) : !data?.length ? (
+   <EmptyState message="검색 결과가 없습니다" />
+ ) : (
+   <CrewCardList data={data} ref={ref} isFetchingNextPage={isFetchingNextPage} />
+ )}

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

</div>
</div>
);
}
139 changes: 0 additions & 139 deletions src/app/_components/find-crew/find-crew.tsx

This file was deleted.

24 changes: 5 additions & 19 deletions src/components/common/crew-list/crew-card-list.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { InfiniteData } from '@tanstack/react-query';
import { useGetCrewListQuery } from '@/src/_queries/crew-queries';
import { useGetCrewListQuery } from '@/src/_queries/crew/crew-list-queries';
import { useInfiniteScroll } from '@/src/hooks/use-infinite-scroll';
import ClientProvider from '@/src/components/client-provider';
import { MainCrewListResponse } from '@/src/types/crew-card';
Expand Down Expand Up @@ -29,17 +29,8 @@ const meta: Meta = {
export default meta;
type Story = StoryObj<typeof meta>;

function RenderCrewCardList({
initialData,
}: {
initialData: InfiniteData<MainCrewListResponse | undefined>;
}) {
const [data, setData] = useState<InfiniteData<MainCrewListResponse | undefined>>(initialData);
const {
data: CrewCardListData,
ref,
isFetchingNextPage,
} = useInfiniteScroll(
function RenderCrewCardList() {
const { data, ref, isFetchingNextPage } = useInfiniteScroll(
useGetCrewListQuery({
keyword: '',
mainLocation: '',
Expand All @@ -49,16 +40,11 @@ function RenderCrewCardList({
}),
);

useEffect(() => {
if (CrewCardListData) {
setData(CrewCardListData);
}
}, [CrewCardListData]);

if (!data) return null;
return <CrewCardList data={data} ref={ref} isFetchingNextPage={isFetchingNextPage} />;
}

export const Default: Story = {
render: () => <RenderCrewCardList initialData={{ pages: [], pageParams: [] }} />,
render: () => <RenderCrewCardList />,
args: {},
};
2 changes: 1 addition & 1 deletion src/components/common/crew-list/crew-card-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CrewCard from './crew-card';

// CrewCardListProps 타입을 구분하여 정의
interface MainCrewCardListProps {
data: InfiniteData<MainCrewListResponse | undefined>;
data: InfiniteData<MainCrewListResponse>;
isFetchingNextPage: boolean;
inWhere?: undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion src/types/crew-card.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface PageableTypes {
}

export type MainCrewListResponse = {
content: MainCrewList[] | undefined;
content: MainCrewList[];
hasNext: boolean;
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function fetchApi<T>(
};

try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, fetchOptions);
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, fetchOptions); // API 요청 실행
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

URL 구성 로직 개선이 필요합니다

현재 구현에는 다음과 같은 잠재적인 문제가 있을 수 있습니다:

  1. 환경 변수가 없거나 잘못된 경우에 대한 처리가 없습니다.
  2. 베이스 URL과 경로 사이에 중복된 슬래시가 발생할 수 있습니다.

다음과 같이 개선하는 것을 제안드립니다:

- const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, fetchOptions);
+ const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL?.replace(/\/+$/, '') ?? '';
+ if (!baseUrl) {
+   throw new ApiError(500, 'API 기본 URL이 설정되지 않았습니다');
+ }
+ const cleanPath = url.replace(/^\/+/, '');
+ const response = await fetch(`${baseUrl}/${cleanPath}`, fetchOptions);
📝 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
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, fetchOptions); // API 요청 실행
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL?.replace(/\/+$/, '') ?? '';
if (!baseUrl) {
throw new ApiError(500, 'API 기본 URL이 설정되지 않았습니다');
}
const cleanPath = url.replace(/^\/+/, '');
const response = await fetch(`${baseUrl}/${cleanPath}`, fetchOptions);


if (!response.ok) {
let errorDetail;
Expand Down