Skip to content

Commit 69dbdda

Browse files
authored
[IMPROVE] prefetchQuery를 이용한 초기 렌더링 속도 개선 (#38)
* feat: 라이브러리 페이지 이동 전, 카테고리 상세 이동 전 prefetchQuery 적용 * fix: 카테고리 내부에 있는 콘텐츠는 드래그가 안되도록 수정 * feat: 콘텐츠 상세 조회 전 prefetchQuery 적용 * refactor: 분을 나타내는 상수 추가 및 적용 * chore: metaBase 추가 및 extension에서 사용하지 않는 host_permissions 제거 * fix: 모달 닫기 시 전역 상태 초기화가 되도록 수정 * feat: 토픽 보드 이동 시 prefetchQuery 적용
1 parent 2e05abe commit 69dbdda

16 files changed

Lines changed: 83 additions & 12 deletions

File tree

apps/extension/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"background": {
1414
"service_worker": "service-worker.js"
1515
},
16-
"host_permissions": ["https://api.linkyboard.com/*", "http://localhost:3000/*"],
16+
"host_permissions": ["https://api.linkyboard.com/*"],
1717
"icons": {
1818
"128": "public/icon128.png"
1919
}

apps/web/src/app/(with-side-bar)/topic/[id]/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import TopicStickerDetailModal from "@/components/(with-side-bar)/topic/sticker/topic-sticker-detail-modal";
22

3-
interface TopicStickerDetailLayoutProps {
3+
interface TopicBoardLayoutProps {
44
children: React.ReactNode;
55
}
66

7-
export default function TopicStickerDetailLayout({ children }: TopicStickerDetailLayoutProps) {
7+
export default function TopicBoardLayout({ children }: TopicBoardLayoutProps) {
88
return (
99
<>
1010
{children}

apps/web/src/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const jetbrainsMono = JetBrains_Mono({
2626
});
2727

2828
export const metadata: Metadata = {
29+
metadataBase: new URL("https://www.linkyboard.com"),
2930
title: "LinkyBoard - 지식을 연결하는 스마트 지식 관리 서비스",
3031
description:
3132
"흩어진 정보를 하나로 연결하는 지식 관리 플랫폼. 웹 콘텐츠를 수집하고 시각화하여 나만의 지식 보드를 만들어 보세요.",

apps/web/src/components/(with-side-bar)/layout/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import Link from "next/link";
44
import { usePathname } from "next/navigation";
55

66
import Logo from "@/assets/logo.svg";
7+
import { CATEGORY } from "@/constants/category";
8+
import { MINUTE } from "@/constants/time";
9+
import { queryClient } from "@/lib/tanstack";
710
import { useMobileMenuStore } from "@/lib/zustand/mobile-menu";
11+
import { getCategories } from "@/services/category";
812
import { cn } from "@linkyboard/utils";
913

1014
import type { LucideIcon } from "lucide-react";
@@ -30,6 +34,16 @@ export default function Sidebar() {
3034

3135
const { isOpen, close } = useMobileMenuStore();
3236

37+
const onMouseEnter = (href: string) => {
38+
if (href === "/library") {
39+
queryClient.prefetchQuery({
40+
queryKey: [CATEGORY.GET_CATEGORIES],
41+
queryFn: getCategories,
42+
staleTime: MINUTE,
43+
});
44+
}
45+
};
46+
3347
return (
3448
<>
3549
{/* Sidebar */}
@@ -55,6 +69,7 @@ export default function Sidebar() {
5569
<Link
5670
key={item.label}
5771
href={item.href}
72+
onMouseEnter={() => onMouseEnter(item.href)}
5873
className={cn(
5974
"mb-2 flex items-center gap-3 rounded-md px-4 py-3 transition-all duration-300",
6075
pathname.startsWith(item.href)

apps/web/src/components/(with-side-bar)/layout/recent-topic-item.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import RemoveTopicDialog from "@/components/(with-side-bar)/topic/remove-topic-dialog";
2+
import { MINUTE } from "@/constants/time";
3+
import { TOPIC } from "@/constants/topic";
4+
import { queryClient } from "@/lib/tanstack";
25
import type { TopicDTO } from "@/models/topic";
6+
import { getTopicBoardById } from "@/services/topic";
37
import { Dialog, DialogTrigger } from "@linkyboard/components";
48
import { cn } from "@linkyboard/utils";
59

@@ -34,13 +38,22 @@ const getTopicColor = (topicId: number) => {
3438
export default function RecentTopicItem({ isSelected, topic, onTopicClick }: RecentTopicItemProps) {
3539
const color = getTopicColor(topic.id);
3640

41+
const onMouseEnter = () => {
42+
queryClient.prefetchQuery({
43+
queryKey: [TOPIC.GET_TOPIC_BOARD_BY_ID, topic.id.toString()],
44+
queryFn: async () => await getTopicBoardById(topic.id.toString()),
45+
staleTime: MINUTE,
46+
});
47+
};
48+
3749
return (
3850
<div
3951
className={cn(
4052
"group mb-2 flex cursor-pointer items-center justify-between gap-3 rounded-md p-2 transition-all duration-300",
4153
isSelected ? "bg-sidebar-primary text-sidebar-primary-foreground" : "hover:bg-accent"
4254
)}
4355
onClick={onTopicClick}
56+
onMouseEnter={onMouseEnter}
4457
>
4558
<div className="flex items-center gap-2">
4659
<div className={cn("h-2 w-2 rounded-full", color)} />

apps/web/src/components/(with-side-bar)/library/category-list.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import { useRouter } from "next/navigation";
44

55
import { CATEGORY } from "@/constants/category";
6-
import { invalidateQueries } from "@/lib/tanstack";
6+
import { CONTENT } from "@/constants/content";
7+
import { MINUTE } from "@/constants/time";
8+
import { invalidateQueries, queryClient } from "@/lib/tanstack";
79
import { useDeleteCategory } from "@/lib/tanstack/mutation/category";
810
import { useGetCategories } from "@/lib/tanstack/query/category";
911
import type { CategoryDTO } from "@/models/category";
12+
import { getCategoryContentById } from "@/services/content";
1013
import {
1114
Button,
1215
Dialog,
@@ -60,8 +63,19 @@ function CategoryItem(props: CategoryDTO) {
6063
router.push(`/library?category=${props.id},${props.name}`);
6164
};
6265

66+
const onMouseEnter = () => {
67+
queryClient.prefetchQuery({
68+
queryKey: [CONTENT.GET_CATEGORY_CONTENT_BY_ID, props.id.toString()],
69+
queryFn: async () => getCategoryContentById(props.id.toString()),
70+
staleTime: MINUTE,
71+
});
72+
};
73+
6374
return (
64-
<div className="bg-card border-border hover:border-primary relative cursor-pointer rounded-lg border p-6 transition-all duration-300 hover:-translate-y-1 hover:transform hover:shadow-lg">
75+
<div
76+
className="bg-card border-border hover:border-primary relative cursor-pointer rounded-lg border p-6 transition-all duration-300 hover:-translate-y-1 hover:transform hover:shadow-lg"
77+
onMouseEnter={onMouseEnter}
78+
>
6579
<Dialog>
6680
<DialogTrigger
6781
className="absolute right-2 top-2 rounded-md p-1 text-red-500 transition-colors hover:bg-red-100 hover:text-red-700 disabled:opacity-50"

apps/web/src/components/(with-side-bar)/library/content-item.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import { CONTENT } from "@/constants/content";
2+
import { MINUTE } from "@/constants/time";
3+
import { queryClient } from "@/lib/tanstack";
4+
import { getContentById } from "@/services/content";
15
import type { CategoryContentDTO } from "@linkyboard/types";
26
import { cn } from "@linkyboard/utils";
37

48
import { FileText, Globe, Youtube } from "lucide-react";
59

610
interface ContentItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
711
item: CategoryContentDTO;
12+
draggable?: boolean;
813
}
914

1015
const contentType = {
@@ -22,7 +27,7 @@ const contentType = {
2227
},
2328
};
2429

25-
export default function ContentItem({ item, ...props }: ContentItemProps) {
30+
export default function ContentItem({ item, draggable = false, ...props }: ContentItemProps) {
2631
const { className, ...restProps } = props;
2732

2833
const onDragStart = (e: React.DragEvent<HTMLButtonElement>) => {
@@ -37,16 +42,27 @@ export default function ContentItem({ item, ...props }: ContentItemProps) {
3742
e.currentTarget.style.opacity = "1";
3843
};
3944

45+
const onMouseEnter = () => {
46+
if (draggable) return;
47+
48+
queryClient.prefetchQuery({
49+
queryKey: [CONTENT.GET_CONTENT_BY_ID, item.id],
50+
queryFn: async () => getContentById(item.id),
51+
staleTime: MINUTE,
52+
});
53+
};
54+
4055
return (
4156
<button
4257
className={cn(
4358
"bg-card border-border hover:border-primary group cursor-pointer rounded-lg border p-6 text-start transition-all duration-300 hover:-translate-y-1 hover:transform hover:shadow-lg",
4459
className
4560
)}
4661
aria-label={`${item.title} 콘텐츠 상세보기`}
47-
draggable
62+
draggable={draggable}
4863
onDragStart={onDragStart}
4964
onDragEnd={onDragEnd}
65+
onMouseEnter={onMouseEnter}
5066
{...restProps}
5167
>
5268
<div className="mb-4 flex items-center gap-4">

apps/web/src/components/(with-side-bar)/topic/add-content-list.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export default function AddContentList({ type, id, isTopicLoading, nodes }: AddC
130130
) : (
131131
filteredContents?.map((item) => (
132132
<ContentItem
133+
draggable
133134
key={`${item.id}-content-item`}
134135
item={item}
135136
onClick={() => onAddContent(item)}

apps/web/src/components/(with-side-bar)/topic/sticker/topic-sticker-detail-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default function TopicStickerDetailModal() {
4848
<button onClick={onRouteToDetail}>
4949
<MoveDiagonal2 size={20} />
5050
</button>
51-
<button onClick={() => router.back()}>
51+
<button onClick={() => topicStore.reset()}>
5252
<X size={20} />
5353
</button>
5454
</div>

apps/web/src/components/(with-side-bar)/topic/sticker/topic-sticker.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MINUTE } from "@/constants/time";
12
import { TOPIC } from "@/constants/topic";
23
import { queryClient } from "@/lib/tanstack";
34
import { useTopicStore } from "@/lib/zustand/topic";
@@ -20,7 +21,7 @@ export default function TopicSticker({ item }: { item: TopicDTO }) {
2021
queryClient.prefetchQuery({
2122
queryKey: [TOPIC.GET_TOPIC_BY_ID, item.id.toString(), null],
2223
queryFn: () => getTopicById(item.id.toString()),
23-
staleTime: 1000 * 60,
24+
staleTime: MINUTE,
2425
});
2526
};
2627

0 commit comments

Comments
 (0)