Skip to content

Commit

Permalink
feat: 프로젝트 리스트 무한스크롤 적용 (#1270)
Browse files Browse the repository at this point in the history
* feat: 프로젝트 리스트 무한스크롤 적용

* chore: 미사용 코드 제거

* feat: 프로젝트 전체 갯수 부활
  • Loading branch information
juno7803 authored Jan 19, 2024
1 parent f56e696 commit ba56acc
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 44 deletions.
42 changes: 42 additions & 0 deletions src/api/endpoint/projects/getProjects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { QS } from '@toss/utils';
import { z } from 'zod';

import { createEndpoint } from '@/api/typedAxios';

export interface ProjectsRequestParams {
limit?: number;
cursor?: number;
name?: string;
}

const LinkSchema = z.object({
linkId: z.number(),
linkTitle: z.string(),
linkUrl: z.string(),
});

const ProjectSchema = z.object({
id: z.number(),
name: z.string(),
generation: z.number(),
category: z.string(),
serviceType: z.array(z.string()),
summary: z.string(),
detail: z.string(),
logoImage: z.string(),
thumbnailImage: z.string(),
links: z.array(LinkSchema),
});

export const getProjects = (params: ProjectsRequestParams) =>
createEndpoint({
request: {
method: 'GET',
url: 'api/v1/projects',
data: QS.create({ params }),
},
serverResponseScheme: z.object({
projectList: z.array(ProjectSchema),
hasNext: z.boolean(),
}),
});
13 changes: 1 addition & 12 deletions src/api/endpoint_LEGACY/hooks/projects.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';

import { getMemberProfileById } from '@/api/endpoint_LEGACY/members';
import { getProjectById, getProjects } from '@/api/endpoint_LEGACY/projects';
import { getProjectById } from '@/api/endpoint_LEGACY/projects';

// project id로 조회
export const useGetProjectById = (id?: string) => {
Expand All @@ -25,14 +25,3 @@ export const useGetProjectById = (id?: string) => {
},
});
};

// project 전체 조회
export const useGetProjects = () => {
return useQuery({
queryKey: ['getProjects'],
queryFn: async () => {
const data = await getProjects();
return data;
},
});
};
11 changes: 6 additions & 5 deletions src/api/endpoint_LEGACY/projects/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { QS } from '@toss/utils';

import { axiosInstance } from '@/api';
import { ProjectDetail, ProjectInput } from '@/api/endpoint_LEGACY/projects/type';
import { ProjectDetail, ProjectInput, ProjectsRequestParams } from '@/api/endpoint_LEGACY/projects/type';

// project id로 조회
export const getProjectById = async (id: string) => {
Expand All @@ -12,12 +14,11 @@ export const getProjectById = async (id: string) => {
};

// project 전체 조회
export const getProjects = async () => {
const { data } = await axiosInstance.request<ProjectDetail[]>({
export const getProjects = async (params: ProjectsRequestParams) => {
const { data } = await axiosInstance.request<{ projectList: ProjectDetail[]; hasNext: boolean; totalCount: number }>({
method: 'GET',
url: 'api/v1/projects',
url: `api/v1/projects${QS.create(params)}`,
});

return data;
};

Expand Down
6 changes: 6 additions & 0 deletions src/api/endpoint_LEGACY/projects/type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { PROJECT_CATEGORY } from '@/components/projects/constants';

export interface ProjectsRequestParams {
limit?: number;
cursor?: number;
name?: string;
}

export type ProjectDetail = {
id: number;
name: string;
Expand Down
31 changes: 10 additions & 21 deletions src/components/projects/main/ProjectList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';
import { ImpressionArea } from '@toss/impression-area';
import { uniqBy as _uniqBy } from 'lodash-es';
import Link from 'next/link';

Expand All @@ -15,19 +16,11 @@ import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery';
import { textStyles } from '@/styles/typography';

const ProjectList = () => {
const { data: projects, isLoading } = useGetProjectListQuery();
const { data, isLoading, fetchNextPage } = useGetProjectListQuery({
limit: 20,
});
const { logClickEvent } = useEventLogger();

// 최신순
const sortedProjects = projects && [...projects].sort((a, b) => b.id - a.id);

const uniqueProjects =
sortedProjects &&
sortedProjects.filter((project, index) => {
const latestProjectIndex = sortedProjects.findIndex(({ name }) => name === project.name);
return latestProjectIndex === index;
});

return (
<StyledContainer>
<StyledContent>
Expand All @@ -50,7 +43,7 @@ const ProjectList = () => {
</Responsive>
</TopWrapper>
<LengthWrapper>
{uniqueProjects && <StyledLength typography='SUIT_18_M'>{uniqueProjects.length}개의 프로젝트</StyledLength>}
{data?.pages && <StyledLength typography='SUIT_18_M'>{data.pages[0].totalCount}개의 프로젝트</StyledLength>}
<ProjectMobileUploadButton
onClick={() =>
logClickEvent('projectUpload', {
Expand All @@ -62,18 +55,14 @@ const ProjectList = () => {
<IconPen />
</ProjectMobileUploadButton>
</LengthWrapper>
{!isLoading && uniqueProjects == null ? (
{!isLoading && data?.pages == null ? (
<StyledNoData>현재 등록된 프로젝트가 없습니다.</StyledNoData>
) : (
<StyledGridContainer>
{uniqueProjects?.map((project) => (
<ProjectCard
key={project.id}
{...project}
// FIXME: 서버쪽에서 link가 중복으로 내려오는 이슈가 있어 임시처리합니다.
links={_uniqBy(project.links, 'linkId')}
/>
))}
{data?.pages.map((page) =>
page.projectList.map((project) => <ProjectCard key={project.id} {...project} />),
)}
<ImpressionArea onImpressionStart={fetchNextPage} />
</StyledGridContainer>
)}
</StyledContent>
Expand Down
24 changes: 18 additions & 6 deletions src/components/projects/upload/hooks/useGetProjectListQuery.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { useQuery } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query';

import { getProjects } from '@/api/endpoint_LEGACY/projects';
import { ProjectsRequestParams } from '@/api/endpoint_LEGACY/projects/type';

export const getProjectListQueryKey = () => ['getProjectListQuery'];
export const getProjectListQueryKey = (params: ProjectsRequestParams = {}) => ['getProjectListQuery', params];

const useGetProjectListQuery = () => {
return useQuery({
queryKey: getProjectListQueryKey(),
queryFn: getProjects,
const useGetProjectListQuery = (params: ProjectsRequestParams = {}) => {
return useInfiniteQuery({
queryKey: getProjectListQueryKey(params),
queryFn: ({ pageParam = 0 }) =>
getProjects({
...params,
cursor: pageParam,
}),
initialPageParam: 0,
getNextPageParam: (lastPage) => {
if (!lastPage.hasNext) {
return undefined;
}
return lastPage.projectList[lastPage.projectList.length - 1].id;
},
});
};

Expand Down

0 comments on commit ba56acc

Please sign in to comment.