Skip to content

Commit

Permalink
Feature : 성능 개선 (#588)
Browse files Browse the repository at this point in the history
* feat : 무한스크롤에서 다음 페이지 불러올 때, 조건 변경

* feat : 그루핑 페이지 react-query Hydration

* fix: Hydration에러로 인해 if분기 처리 제거

* fix : 브라우저 단에서 QueryClient 생성하도록 useState 활용

* feat : 평가 탭의 api prefetching

* feat : QueryClient 객체 서버단에서 새로 생성

서버 단에서는 QueryClient를 처음 생성할텐데 cache가 필요한 지 모르겠습니다.

* feat : Footer에서 NavLink로 교체

* feat : 이미지 placeholder 컴포넌트 구현

* feat : onClick에서 NavLink로 교체

* Revert "feat : 무한스크롤에서 다음 페이지 불러올 때, 조건 변경"

This reverts commit 7e78b9f.

* fix : group페이지에서도 Hydration적용되도록 queryFn 추가
  • Loading branch information
guesung authored Jan 23, 2024
1 parent f213532 commit 1c98996
Show file tree
Hide file tree
Showing 16 changed files with 90 additions and 43 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"cssnano": "^6.0.1",
"date-fns": "^2.30.0",
"framer-motion": "^10.16.4",
"https": "^1.0.0",
"i18next": "^23.5.1",
"i18next-browser-languagedetector": "^7.1.0",
"i18next-resources-to-backend": "^1.1.4",
Expand Down
2 changes: 1 addition & 1 deletion src/apis/groups/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useMemo } from 'react';
export const useGetGroups = () => {
const { data, ...rest } = useSuspenseInfiniteQuery({
queryKey: Keys.getGroups(),
queryFn: ({ pageParam = 0 }) => getGroups(pageParam),
queryFn: ({ pageParam }) => getGroups(pageParam),
getNextPageParam: (lastPage) =>
lastPage.totalPage !== lastPage.currentPage ? lastPage.currentPage + 1 : undefined,
initialPageParam: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ export default function LocationSection({
language: lng,
region: 'KR',
});
console.log(place?.url);

if (typeof google === 'undefined') return null;

return (
<section className="p-20 pb-8">
Expand Down
6 changes: 5 additions & 1 deletion src/app/[lng]/(main)/grouping/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import CreateGroupButton from './components/CreateGroupButton.client';
import GroupingCardList from './components/GroupingCardList.client';
import GroupingHeader from './components/GroupingHeader';
import { Keys, getGroups } from '@/apis/groups';
import { Footer } from '@/components/Footer';
import { Loading } from '@/components/Loading';
import { HydrationProvider } from '@/components/Provider';
import { Spacing } from '@/components/Spacing';
import { Suspense } from 'react';

Expand All @@ -17,7 +19,9 @@ export default function GroupingPage({ params: { lng } }: GroupingPageProps) {
<>
<GroupingHeader />
<Suspense fallback={<Loading />}>
<GroupingCardList />
<HydrationProvider queryFn={() => getGroups(0)} queryKey={Keys.getGroups()} isInfiniteQuery>
<GroupingCardList />
</HydrationProvider>
</Suspense>
<CreateGroupButton />
<Spacing size={60} />
Expand Down
3 changes: 3 additions & 0 deletions src/app/[lng]/(main)/meeting/participate/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getMeetingNotEstimated,
getMeetingParticipating,
getMeetingRejected,
getMeetingWaiting,
} from '@/apis/meeting';
import { Footer } from '@/components/Footer';
import { Loading } from '@/components/Loading';
Expand All @@ -29,12 +30,14 @@ export default function MeetingPage({ params: { lng } }: MeetingPageProps) {
getMeetingHosting,
getMeetingRejected,
getMeetingNotEstimated,
getMeetingWaiting,
]}
queryMultipleKey={[
Keys.getMeetingParticipating(),
Keys.getMeetingHosting(),
Keys.getMeetingRejected(),
Keys.getMeetingNotEstimated(),
Keys.getMeetingWaiting(),
]}
>
<ContentSection />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export default function ProfileDetailSection({ profileData }: ProfileDetailProps
placeholder={!introduce ? t('home.noSelfIntro') : ''}
/>
</section>
<Spacing size={70} />
</>
);
}
12 changes: 7 additions & 5 deletions src/app/[lng]/(main)/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import ProfileDetail from './components/ProfileDetail.client';
import ProfileHeader from './components/ProfileHeader.client';
import { Keys, getProfile } from '@/apis/profile';
import { Footer } from '@/components/Footer';
import { Loading } from '@/components/Loading';
import { HydrationProvider } from '@/components/Provider';
import { Spacing } from '@/components/Spacing';
import { Suspense } from 'react';

interface ProfilePageProps {
params: {
Expand All @@ -15,10 +16,11 @@ export default function Profile({ params: { lng } }: ProfilePageProps) {
return (
<>
<ProfileHeader />
<HydrationProvider queryFn={getProfile} queryKey={Keys.getProfile()}>
<ProfileDetail />
<Spacing size={70} />
</HydrationProvider>
<Suspense fallback={<Loading />}>
<HydrationProvider queryFn={getProfile} queryKey={Keys.getProfile()}>
<ProfileDetail />
</HydrationProvider>
</Suspense>
<Footer page="profile" isSpacing={false} lng={lng} />
</>
);
Expand Down
9 changes: 3 additions & 6 deletions src/components/Card/GroupingCard.client.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
'use client';

import { Icon } from '../Icon';
import ImageSample from '../Image/ImageSample';
import { NavLink } from '../NavLink';
import { useDeleteScrapMeeting } from '@/apis/groups';
import { useTranslation } from '@/app/i18n/client';
import { Flex } from '@/components/Layout';
import { Spacing } from '@/components/Spacing';
import useAppRouter from '@/hooks/useAppRouter';
import usePlaceDetails from '@/hooks/usePlaceDetails';
import cn from '@/utils/cn';
import { formatMeetingDate } from '@/utils/formatMeetingDate';
Expand Down Expand Up @@ -54,7 +52,6 @@ export default function GroupingCard({
} = groupingData;

const { lng } = useParams() as { lng: string };
const { push } = useAppRouter();
const { place } = usePlaceDetails({
placeId,
fields: ['formatted_address'],
Expand All @@ -66,7 +63,7 @@ export default function GroupingCard({

return (
<Flex className="bg-white px-20 py-16" direction="column">
<Flex onClick={onClick || (() => push(`/grouping/${groupId}`, false))} align="center">
<NavLink href={`/grouping/${groupId}`} className="flex items-center">
<section className="relative h-96 w-96 shrink-0">
{imageUrl ? (
<Image
Expand Down Expand Up @@ -106,7 +103,7 @@ export default function GroupingCard({
<p className="truncate">{formatMeetingDate(meetDate, startTime)}</p>
</Flex>
</section>
</Flex>
</NavLink>
{children}
</Flex>
);
Expand Down
5 changes: 3 additions & 2 deletions src/components/Footer/Footer.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Link from 'next/link';

import { ButtonAnimation } from '../Animation';
import { Icon } from '../Icon';
import { NavLink } from '../NavLink';
import { serverTranslation } from '@/app/i18n';
import cn from '@/utils/cn';

Expand Down Expand Up @@ -63,15 +64,15 @@ export default async function Footer({ lng, page, isSpacing = true, spacingColor
'text-sign-tertiary': !isSelected(tab),
})}
>
<Link replace href={tab.url} scroll={false}>
<NavLink isReplace href={tab.url} scroll={false}>
<Icon
id={`32-footer-${tab.name}${isSelected(tab) ? '_selected' : '_default'}`}
width={32}
height={32}
className="mx-auto"
/>
<p>{t(tab.name)}</p>
</Link>
</NavLink>
</ButtonAnimation>
))}
</footer>
Expand Down
7 changes: 5 additions & 2 deletions src/components/NavLink/NavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ interface NavLinkProps {
href: string;
className?: string;
scroll?: boolean;
isReplace?: boolean;
}

export default function NavLink({
children,
href,
className,
scroll,
isReplace = false,
...props
}: PropsWithChildren<NavLinkProps>) {
const { push } = useAppRouter();
const { push, replace } = useAppRouter();
const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
push(href);
if (isReplace) replace(href);
else push(href, scroll);
};

return (
Expand Down
7 changes: 3 additions & 4 deletions src/components/Provider/HydrationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ export default async function HydrationProvider({
queryMultipleFn,
isInfiniteQuery = false,
}: StrictPropsWithChildren<HydrationProviderProps>) {
const getQueryClient = cache(() => new QueryClient());

const queryClient = getQueryClient();
const queryClient = new QueryClient();

if (queryMultipleFn && queryMultipleKey) {
if (isInfiniteQuery)
Expand All @@ -43,7 +41,8 @@ export default async function HydrationProvider({
}

if (queryFn && queryKey) {
if (isInfiniteQuery) await queryClient.prefetchInfiniteQuery({ queryKey, initialPageParam: 0 });
if (isInfiniteQuery)
await queryClient.prefetchInfiniteQuery({ queryKey, queryFn, initialPageParam: 0 as never });
else await queryClient.prefetchQuery({ queryKey, queryFn });
}

Expand Down
36 changes: 21 additions & 15 deletions src/components/Provider/QueryProvider.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,34 @@
import { useToast } from '@/hooks/useModal';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';

import type { StrictPropsWithChildren } from '@/types';

export default function QueryProvider({ children }: StrictPropsWithChildren) {
const { openToast } = useToast();

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 0,
refetchOnWindowFocus: false,
staleTime: 1000 * 60,
},
mutations: {
onError: (error) => {
const errorMessage =
typeof error === 'string' ? error : '오류가 발생했습니다. 다시 시도해주세요.';
openToast(errorMessage);
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
retry: 0,
refetchOnWindowFocus: false,
staleTime: 1000 * 60,
},
mutations: {
onError: (error) => {
const errorMessage =
typeof error === 'string' ? error : '오류가 발생했습니다. 다시 시도해주세요.';
openToast(errorMessage);
},
},
},
},
},
});
})
);

// const queryClient = useQueryClient(queryClientInstance);

return (
<QueryClientProvider client={queryClient}>
Expand Down
6 changes: 2 additions & 4 deletions src/utils/getBase64.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import fs from 'node:fs/promises';
import path from 'node:path';

import { getBufferFromS3Url } from './getBufferFromS3Url';
import { getPlaiceholder } from 'plaiceholder';

const getBase64 = async (src: string) => {
const buffer = await fs.readFile(path.join('./public', src));
const buffer = await getBufferFromS3Url(src);

const {
metadata: { height, width },
Expand Down
27 changes: 27 additions & 0 deletions src/utils/getBufferFromS3Url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as https from 'https';

/**
* S3 URL에서 데이터를 가져와 Buffer 객체로 반환합니다.
* @param s3Url - S3 파일의 URL (문자열 타입)
* @returns Promise<Buffer> - 비동기적으로 Buffer 객체를 반환하는 Promise
*/
export const getBufferFromS3Url = (s3Url: string): Promise<Buffer> => {
return new Promise((resolve, reject) => {
https
.get(s3Url, (res) => {
const chunks: Buffer[] = [];

res.on('data', (chunk: Buffer) => {
chunks.push(chunk);
});

res.on('end', () => {
const buffer = Buffer.concat(chunks);
resolve(buffer);
});
})
.on('error', (error: Error) => {
reject(error);
});
});
};
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6640,6 +6640,7 @@ __metadata:
eslint-plugin-react-hooks: ^4.6.0
eslint-plugin-testing-library: ^5.11.1
framer-motion: ^10.16.4
https: ^1.0.0
husky: ^8.0.0
i18next: ^23.5.1
i18next-browser-languagedetector: ^7.1.0
Expand Down Expand Up @@ -6891,6 +6892,13 @@ __metadata:
languageName: node
linkType: hard

"https@npm:^1.0.0":
version: 1.0.0
resolution: "https@npm:1.0.0"
checksum: ccea8a8363a018d4b241db7748cff3a85c9f5b71bf80639e9c37dc6823f590f35dda098b80b726930e9f945387c8bfd6b1461df25cab5bf65a31903d81875b5d
languageName: node
linkType: hard

"human-signals@npm:^2.1.0":
version: 2.1.0
resolution: "human-signals@npm:2.1.0"
Expand Down

0 comments on commit 1c98996

Please sign in to comment.