diff --git a/app/(route)/boards/[boardType]/detail/[articleId]/page.tsx b/app/(route)/boards/[boardType]/detail/[articleId]/page.tsx
index 145d633..4e4499c 100644
--- a/app/(route)/boards/[boardType]/detail/[articleId]/page.tsx
+++ b/app/(route)/boards/[boardType]/detail/[articleId]/page.tsx
@@ -1,6 +1,11 @@
+import {
+ dehydrate,
+ HydrationBoundary,
+ QueryClient,
+} from '@tanstack/react-query';
+import { articleDeatilQueryOption } from '@app/_hooks/apis/boards/useArticleDetailQuery';
import { formatDistanceToNow } from 'date-fns';
import { ko } from 'date-fns/locale';
-import { getArticleDetail } from '@app/_service/article';
import { Separator } from '@app/_shadcn/components/ui/separator';
import {
ButtonSection,
@@ -17,19 +22,23 @@ export default async function ArticleDetailPage({
}: {
params: { articleId: string };
}) {
+ const queryClient = new QueryClient();
+ const options = articleDeatilQueryOption(Number(articleId));
const {
- article: { title, userId, content, createdAt },
+ article: { title, userId, createdAt },
files,
- } = await getArticleDetail(articleId);
+ } = await queryClient.fetchQuery(options);
return (
-
-
+
+
{title}
+
+
{formatDistanceToNow(new Date(createdAt), {
@@ -39,15 +48,12 @@ export default async function ArticleDetailPage({
{files && files.length > 0 && }
-
-
-
-
-
+
+
+
+
-
-
+
+
);
}
diff --git a/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardBody/index.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardBody/index.tsx
new file mode 100644
index 0000000..ae69bc2
--- /dev/null
+++ b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardBody/index.tsx
@@ -0,0 +1,128 @@
+'use client';
+
+import Link from 'next/link';
+import { useParams } from 'next/navigation';
+
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from '@app/_shadcn/components/ui/table';
+import { BoardListParams } from '@app/_types/boardTypes';
+import formateDate from '@app/_utils/formatDate';
+import { BookOpen, MessageCircle, File } from 'lucide-react';
+import { Tooltip, TooltipContent } from '@app/_shadcn/components/ui/tooltip';
+import { TooltipTrigger } from '@app/_shadcn/components/plate-ui/tooltip';
+import { Badge } from '@app/_shadcn/components/ui/badge';
+import useArticleListQuery from '@app/_hooks/apis/boards/useArticleListQuery';
+
+function BoardBody() {
+ const params = useParams
();
+ const { data } = useArticleListQuery({
+ boardType: params.boardType,
+ page: Number(params.page),
+ category: params.category,
+ });
+
+ if (!data || data.articleList.length === 0)
+ return 게시글이 없어요. 😭
;
+
+ const { articleList } = data;
+ return (
+
+
+
+ 번호
+ 제목
+ 작성자
+
+ 날짜
+
+
+ 조회수
+
+
+
+
+
+ {articleList?.map(
+ ({
+ articleId,
+ title,
+ view,
+ nickname,
+ createdAt,
+ hide,
+ numOfComments,
+ file,
+ category,
+ }) => {
+ const formattedDate = formateDate(createdAt);
+ return (
+
+
+ {articleId}
+
+ {category !== 'all' && (
+
+ {category}
+
+ )}
+
+
+ {hide && (
+
+
+
+
+
+ 해당 글은 정회원만 열람 가능해요.
+
+
+ )}
+
+ {title}
+
+ {numOfComments > 0 && (
+
+
+ {numOfComments}
+
+ )}
+ {file && (
+
+
+
+ )}
+
+
+
+ {nickname}
+
+
+ {formattedDate}
+
+
+ {view}
+
+
+
+ );
+ },
+ )}
+
+
+ );
+}
+
+export default BoardBody;
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/CategorySelect.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/CategorySelect.tsx
similarity index 67%
rename from app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/CategorySelect.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/CategorySelect.tsx
index 003f8f4..6bf4f7d 100644
--- a/app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/CategorySelect.tsx
+++ b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/CategorySelect.tsx
@@ -1,5 +1,6 @@
'use client';
+import { NEW_PATH } from '@app/_constants/urls';
import {
Select,
SelectContent,
@@ -7,7 +8,8 @@ import {
SelectTrigger,
SelectValue,
} from '@app/_shadcn/components/ui/select';
-import { useRouter } from 'next/navigation';
+import { BoardListParams } from '@app/_types/boardTypes';
+import { useParams, useRouter } from 'next/navigation';
interface Props {
categories: string[];
@@ -15,9 +17,17 @@ interface Props {
function CategorySelect({ categories }: Props) {
const { push } = useRouter();
+ const { boardType, page } = useParams();
const handleSelectionChange = (value: string) => {
- if (value) push(`?category=${value}`);
+ if (value)
+ push(
+ NEW_PATH.boardList.url({
+ boardType,
+ page: Number(page),
+ category: value,
+ }),
+ );
};
return (
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/SearchInput.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/SearchInput.tsx
similarity index 100%
rename from app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/SearchInput.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/SearchInput.tsx
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/index.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/index.tsx
similarity index 100%
rename from app/(route)/boards/[boardType]/list/[page]/_components/BoardHeader/index.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/_components/BoardHeader/index.tsx
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/PageNav.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/PageNav.tsx
similarity index 81%
rename from app/(route)/boards/[boardType]/list/[page]/_components/PageNav.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/_components/PageNav.tsx
index 3c03b47..660fdc5 100644
--- a/app/(route)/boards/[boardType]/list/[page]/_components/PageNav.tsx
+++ b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/PageNav.tsx
@@ -10,14 +10,22 @@ import {
PaginationPrevious,
} from '@app/_shadcn/components/ui/pagination';
import { BoardListParams } from '@app/_types/boardTypes';
+import useArticleListQuery from '@app/_hooks/apis/boards/useArticleListQuery';
-interface Props {
- maxPage: number;
-}
-
-function PageNav({ maxPage }: Props) {
+function PageNav() {
const INTERVAL = 7;
- const { page: curPageParam, boardType } = useParams();
+ const {
+ page: curPageParam,
+ boardType,
+ category,
+ } = useParams();
+ const { data } = useArticleListQuery({
+ page: Number(curPageParam),
+ boardType,
+ category,
+ });
+ if (!data) return null;
+ const { maxPageNum: maxPage } = data;
const curPage = Number(curPageParam);
const prevPage = curPage - 1 < 1 ? 1 : curPage - 1;
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/PostLink.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/_components/PostLink.tsx
similarity index 100%
rename from app/(route)/boards/[boardType]/list/[page]/_components/PostLink.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/_components/PostLink.tsx
diff --git a/app/(route)/boards/[boardType]/list/[page]/error.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/error.tsx
similarity index 100%
rename from app/(route)/boards/[boardType]/list/[page]/error.tsx
rename to app/(route)/boards/[boardType]/list/[category]/[page]/error.tsx
diff --git a/app/(route)/boards/[boardType]/list/[category]/[page]/page.tsx b/app/(route)/boards/[boardType]/list/[category]/[page]/page.tsx
new file mode 100644
index 0000000..55b1f18
--- /dev/null
+++ b/app/(route)/boards/[boardType]/list/[category]/[page]/page.tsx
@@ -0,0 +1,38 @@
+import {
+ dehydrate,
+ HydrationBoundary,
+ QueryClient,
+} from '@tanstack/react-query';
+import { articleListQueryOption } from '@app/_hooks/apis/boards/useArticleListQuery';
+import { BoardListParams } from '@app/_types/boardTypes';
+import BoardHeader from './_components/BoardHeader';
+import BoardBody from './_components/BoardBody';
+import PostLink from './_components/PostLink';
+import PageNav from './_components/PageNav';
+
+interface Props {
+ params: BoardListParams;
+}
+
+async function BoardPage({ params: { boardType, page, category } }: Props) {
+ const queryClient = new QueryClient();
+ const options = articleListQueryOption({
+ page: Number(page),
+ boardType,
+ category,
+ });
+ await queryClient.prefetchQuery(options);
+
+ return (
+
+
+
+ );
+}
+
+export default BoardPage;
diff --git a/app/(route)/boards/[boardType]/list/[page]/_components/BoardBody/index.tsx b/app/(route)/boards/[boardType]/list/[page]/_components/BoardBody/index.tsx
deleted file mode 100644
index 818d5b3..0000000
--- a/app/(route)/boards/[boardType]/list/[page]/_components/BoardBody/index.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-'use client';
-
-import Link from 'next/link';
-import { useParams } from 'next/navigation';
-
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from '@app/_shadcn/components/ui/table';
-import { ArticleData, BoardListParams } from '@app/_types/boardTypes';
-import formateDate from '@app/_utils/formatDate';
-import { BookOpen, MessageCircle, File } from 'lucide-react';
-import { Tooltip, TooltipContent } from '@app/_shadcn/components/ui/tooltip';
-import { TooltipTrigger } from '@app/_shadcn/components/plate-ui/tooltip';
-import { Badge } from '@app/_shadcn/components/ui/badge';
-
-interface Props {
- articleList?: ArticleData[];
-}
-
-function BoardBody({ articleList }: Props) {
- const { boardType } = useParams();
-
- return (
- <>
-
-
-
- 번호
- 제목
- 작성자
-
- 날짜
-
-
- 조회수
-
-
-
-
-
- {articleList?.map(
- ({
- articleId,
- title,
- view,
- nickname,
- createdAt,
- hide,
- numOfComments,
- file,
- category,
- }) => {
- const formattedDate = formateDate(createdAt);
- return (
-
-
- {articleId}
-
- {category !== 'all' && (
-
- {category}
-
- )}
-
-
- {hide && (
-
-
-
-
-
- 해당 글은 정회원만 열람 가능해요.
-
-
- )}
-
- {title}
-
- {numOfComments > 0 && (
-
-
- {numOfComments}
-
- )}
- {file && (
-
-
-
- )}
-
-
-
- {nickname}
-
-
- {formattedDate}
-
-
- {view}
-
-
-
- );
- },
- )}
-
-
- {articleList?.length === 0 && (
- 게시글이 없어요. 😭
- )}
- >
- );
-}
-
-export default BoardBody;
diff --git a/app/(route)/boards/[boardType]/list/[page]/page.tsx b/app/(route)/boards/[boardType]/list/[page]/page.tsx
deleted file mode 100644
index c28c425..0000000
--- a/app/(route)/boards/[boardType]/list/[page]/page.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { BoardListParams } from '@app/_types/boardTypes';
-import { getOnePageArticleList } from '@app/_service/article';
-import PageNav from './_components/PageNav';
-import BoardHeader from './_components/BoardHeader';
-import BoardBody from './_components/BoardBody';
-import PostLink from './_components/PostLink';
-
-interface Props {
- params: BoardListParams;
- searchParams: { category: string };
-}
-
-async function BoardPage({
- params: { boardType, page },
- searchParams: { category },
-}: Props) {
- const { maxPageNum: maxPage, articleList } = await getOnePageArticleList({
- boardType,
- page,
- category,
- });
-
- return (
-
- );
-}
-
-export default BoardPage;
diff --git a/app/_components/molecules/Header/NavSection.tsx b/app/_components/molecules/Header/NavSection.tsx
index 76df70b..d6b50ea 100644
--- a/app/_components/molecules/Header/NavSection.tsx
+++ b/app/_components/molecules/Header/NavSection.tsx
@@ -8,12 +8,13 @@ import {
NavigationMenuTrigger,
} from '@app/_shadcn/components/ui/navigation-menu';
-import { ADMIN_PATH, PATH } from '@app/_constants/urls';
+import { ADMIN_PATH, NEW_PATH, PATH } from '@app/_constants/urls';
import { BOARD_TABS, MEMBER_TABS } from '@app/_constants/menu';
import { Button } from '@app/_shadcn/components/ui/button';
import { useRecoilValue } from 'recoil';
import { roleState } from '@app/_store/permissionAtoms';
import Link from 'next/link';
+import { BOARD_TYPE } from '@app/_constants/mock';
const MENU_ITEMS = [
{
@@ -33,7 +34,11 @@ const MENU_ITEMS = [
{
startWith: '/boards',
title: 'Boards',
- href: `${PATH.boards.notice.url}/list/1`,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.notice,
+ page: 1,
+ category: 'all',
+ }),
tabs: BOARD_TABS,
desc: 'Casper 회원들의 소통을 위한 공간이에요.',
accessibleRoles: ['관리자', '정회원', '준회원', '손님'],
diff --git a/app/_components/providers/QueryWrapper.tsx b/app/_components/providers/QueryWrapper.tsx
index fac3309..1f76d76 100644
--- a/app/_components/providers/QueryWrapper.tsx
+++ b/app/_components/providers/QueryWrapper.tsx
@@ -1,24 +1,34 @@
-'use client';
-
-import { ReactNode, useState } from 'react';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import {
+ isServer,
+ QueryClient,
+ QueryClientProvider,
+} from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { PropsWithChildren } from 'react';
-interface Props {
- children: ReactNode;
+function makeQueryClient() {
+ return new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000,
+ },
+ },
+ });
}
-function QueryWrapper({ children }: Props) {
- const [queryClient] = useState(
- () =>
- new QueryClient({
- defaultOptions: {
- queries: {
- staleTime: 60 * 1000,
- },
- },
- }),
- );
+let browserQueryClient: QueryClient | undefined;
+
+function getQueryClient() {
+ if (isServer) {
+ return makeQueryClient();
+ }
+
+ if (!browserQueryClient) browserQueryClient = makeQueryClient();
+ return browserQueryClient;
+}
+
+function QueryWrapper({ children }: PropsWithChildren) {
+ const queryClient = getQueryClient();
return (
diff --git a/app/_constants/menu.ts b/app/_constants/menu.ts
index cbb206d..5d85604 100644
--- a/app/_constants/menu.ts
+++ b/app/_constants/menu.ts
@@ -1,5 +1,5 @@
import { BOARD_TYPE, MEMBER_TYPE } from './mock';
-import { PATH } from './urls';
+import { NEW_PATH, PATH } from './urls';
export const MEMBER_TABS = [
{
@@ -34,38 +34,88 @@ export const MEMBER_TABS = [
export const BOARD_TABS = [
{
- key: BOARD_TYPE.notice,
- href: `${PATH.boards.notice.url}/list/1`,
- name: PATH.boards.notice.name,
- startWith: PATH.boards.notice.url,
+ name: '공지사항',
accessibleRoles: ['관리자', '정회원', '준회원', '손님'],
+ key: BOARD_TYPE.notice,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.notice,
+ category: 'all',
+ page: 1,
+ }),
+ startWith: NEW_PATH.boardList
+ .url({
+ boardType: BOARD_TYPE.notice,
+ category: 'all',
+ page: 1,
+ })
+ .split('/list')[0],
},
{
- key: BOARD_TYPE.full,
- href: `${PATH.boards.full.url}/list/1`,
- name: PATH.boards.full.name,
- startWith: PATH.boards.full.url,
+ name: '정회원 게시판',
accessibleRoles: ['관리자', '정회원'],
+ key: BOARD_TYPE.full,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.full,
+ category: 'all',
+ page: 1,
+ }),
+ startWith: NEW_PATH.boardList
+ .url({
+ boardType: BOARD_TYPE.full,
+ category: 'all',
+ page: 1,
+ })
+ .split('/list')[0],
},
{
- key: BOARD_TYPE.graduate,
- href: `${PATH.boards.graduate.url}/list/1`,
- name: PATH.boards.graduate.name,
- startWith: PATH.boards.graduate.url,
+ name: '졸업생 게시판',
accessibleRoles: ['관리자', '정회원'],
+ key: BOARD_TYPE.graduate,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.graduate,
+ category: 'all',
+ page: 1,
+ }),
+ startWith: NEW_PATH.boardList
+ .url({
+ boardType: BOARD_TYPE.graduate,
+ category: 'all',
+ page: 1,
+ })
+ .split('/list')[0],
},
{
- key: BOARD_TYPE.associate,
- href: `${PATH.boards.associate.url}/list/1`,
- name: PATH.boards.associate.name,
- startWith: PATH.boards.associate.url,
+ name: '준회원 게시판',
accessibleRoles: ['관리자', '정회원', '준회원'],
+ key: BOARD_TYPE.associate,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.associate,
+ category: 'all',
+ page: 1,
+ }),
+ startWith: NEW_PATH.boardList
+ .url({
+ boardType: BOARD_TYPE.associate,
+ category: 'all',
+ page: 1,
+ })
+ .split('/list')[0],
},
{
- key: BOARD_TYPE.freedom,
- href: `${PATH.boards.free.url}/list/1`,
- name: PATH.boards.free.name,
- startWith: PATH.boards.free.url,
+ name: '지유게시판',
accessibleRoles: ['관리자', '정회원', '준회원', '손님'],
+ key: BOARD_TYPE.freedom,
+ href: NEW_PATH.boardList.url({
+ boardType: BOARD_TYPE.freedom,
+ category: 'all',
+ page: 1,
+ }),
+ startWith: NEW_PATH.boardList
+ .url({
+ boardType: BOARD_TYPE.freedom,
+ category: 'all',
+ page: 1,
+ })
+ .split('/list')[0],
},
];
diff --git a/app/_constants/urls.ts b/app/_constants/urls.ts
index 1090dc9..50149e8 100644
--- a/app/_constants/urls.ts
+++ b/app/_constants/urls.ts
@@ -1,4 +1,3 @@
-import { BoardType } from '@app/_types/boardTypes';
import { BOARD_TYPE } from './mock';
const ADMIN_PATH = {
@@ -8,8 +7,15 @@ const ADMIN_PATH = {
const NEW_PATH = {
boardList: {
- url: ({ boardType, page }: { boardType: BoardType; page: number }) =>
- `/boards/${boardType}/list/${page}`,
+ url: ({
+ boardType,
+ page,
+ category,
+ }: {
+ boardType: string;
+ page: number;
+ category: string;
+ }) => `/boards/${boardType}/list/${category}/${page}`,
},
};
diff --git a/app/_hooks/apis/boards/useArticleDetailQuery.ts b/app/_hooks/apis/boards/useArticleDetailQuery.ts
new file mode 100644
index 0000000..c97428c
--- /dev/null
+++ b/app/_hooks/apis/boards/useArticleDetailQuery.ts
@@ -0,0 +1,14 @@
+import boardService from '@app/_service/boardService';
+import { useQuery } from '@tanstack/react-query';
+import { boardQueryKey } from '../queryKey';
+
+export const articleDeatilQueryOption = (id: number) => ({
+ queryKey: boardQueryKey.detail(id),
+ queryFn: () => boardService.getArticleDetail(id),
+});
+
+function useArticleDetailQuery(id: number) {
+ return useQuery(articleDeatilQueryOption(id));
+}
+
+export default useArticleDetailQuery;
diff --git a/app/_hooks/apis/boards/useArticleListQuery.ts b/app/_hooks/apis/boards/useArticleListQuery.ts
new file mode 100644
index 0000000..b54c801
--- /dev/null
+++ b/app/_hooks/apis/boards/useArticleListQuery.ts
@@ -0,0 +1,19 @@
+import boardService from '@app/_service/boardService';
+import { ArticleListParams } from '@app/_types/boardTypes';
+import { useQuery } from '@tanstack/react-query';
+import { boardQueryKey } from '../queryKey';
+
+export const articleListQueryOption = ({
+ page,
+ boardType,
+ category,
+}: ArticleListParams) => ({
+ queryKey: boardQueryKey.list({ boardType, category, page }),
+ queryFn: () => boardService.getArticleList({ boardType, category, page }),
+});
+
+function useArticleListQuery(params: ArticleListParams) {
+ return useQuery(articleListQueryOption(params));
+}
+
+export default useArticleListQuery;
diff --git a/app/_hooks/apis/boards/useCommentDelete.ts b/app/_hooks/apis/boards/useCommentDelete.ts
index f1a3f28..a50ee80 100644
--- a/app/_hooks/apis/boards/useCommentDelete.ts
+++ b/app/_hooks/apis/boards/useCommentDelete.ts
@@ -2,6 +2,7 @@ import { POPUP_MESSAGE, TOAST_TITLE } from '@app/_constants/message';
import boardService from '@app/_service/boardService';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { commentQueryKey } from '../queryKey';
function useCommentDelete(articleId: number) {
const { toast } = useToast();
@@ -14,7 +15,9 @@ function useCommentDelete(articleId: number) {
toast({
description: POPUP_MESSAGE.deleteCommentSuccess,
});
- queryClient.invalidateQueries({ queryKey: ['comment', articleId] });
+ queryClient.invalidateQueries({
+ queryKey: commentQueryKey.list(articleId),
+ });
};
const onError = () => {
diff --git a/app/_hooks/apis/boards/useCommentMutation.ts b/app/_hooks/apis/boards/useCommentMutation.ts
index f27720b..3e49012 100644
--- a/app/_hooks/apis/boards/useCommentMutation.ts
+++ b/app/_hooks/apis/boards/useCommentMutation.ts
@@ -3,6 +3,7 @@ import boardService from '@app/_service/boardService';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
import { CommentWriteRequest } from '@app/_types/boardTypes';
import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { commentQueryKey } from '../queryKey';
function useCommentMutation(articleId: number) {
const queryCache = useQueryClient();
@@ -16,7 +17,7 @@ function useCommentMutation(articleId: number) {
toast({
description: '댓글이 작성 되었어요.',
});
- queryCache.invalidateQueries({ queryKey: ['comment', articleId] });
+ queryCache.invalidateQueries({ queryKey: commentQueryKey.list(articleId) });
};
const onError = () => {
diff --git a/app/_hooks/apis/boards/useCommentUpdate.ts b/app/_hooks/apis/boards/useCommentUpdate.ts
index e0d8732..2be26ca 100644
--- a/app/_hooks/apis/boards/useCommentUpdate.ts
+++ b/app/_hooks/apis/boards/useCommentUpdate.ts
@@ -3,6 +3,7 @@ import boardService from '@app/_service/boardService';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
import { CommentModifyRequest } from '@app/_types/boardTypes';
import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { commentQueryKey } from '../queryKey';
function useCommentUpdate(articleId: number) {
const { toast } = useToast();
@@ -17,7 +18,9 @@ function useCommentUpdate(articleId: number) {
toast({
description: '댓글이 수정되었어요.',
});
- queryClient.invalidateQueries({ queryKey: ['comment', articleId] });
+ queryClient.invalidateQueries({
+ queryKey: commentQueryKey.list(articleId),
+ });
};
const onError = () => {
diff --git a/app/_hooks/apis/boards/useComments.ts b/app/_hooks/apis/boards/useComments.ts
index 58ded81..8e4e10a 100644
--- a/app/_hooks/apis/boards/useComments.ts
+++ b/app/_hooks/apis/boards/useComments.ts
@@ -1,8 +1,9 @@
import boardService from '@app/_service/boardService';
import { useQuery } from '@tanstack/react-query';
+import { commentQueryKey } from '../queryKey';
function useComments(articleId: number) {
- const queryKey = ['comment', articleId];
+ const queryKey = commentQueryKey.list(articleId);
const queryFn = async () => boardService.getComments(articleId);
return useQuery({
diff --git a/app/_hooks/apis/boards/useDeleteArticleMutation.ts b/app/_hooks/apis/boards/useDeleteArticleMutation.ts
index cd77670..97c4ebb 100644
--- a/app/_hooks/apis/boards/useDeleteArticleMutation.ts
+++ b/app/_hooks/apis/boards/useDeleteArticleMutation.ts
@@ -1,35 +1,33 @@
-import { DELETE_ARTICLE_API } from '@app/_constants/apiUrl';
import {
ERROR_MESSAGE,
POPUP_MESSAGE,
TOAST_TITLE,
} from '@app/_constants/message';
-import { PATH } from '@app/_constants/urls';
+import { NEW_PATH } from '@app/_constants/urls';
+import boardService from '@app/_service/boardService';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
-import { bearerTokenState } from '@app/_store/permissionAtoms';
import { ErrorResponse } from '@app/_types/errorTypes';
-import { useMutation } from '@tanstack/react-query';
-import axios, { AxiosError } from 'axios';
-import { useRouter } from 'next/navigation';
-import { useRecoilValue } from 'recoil';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { AxiosError } from 'axios';
+import { useParams, useRouter } from 'next/navigation';
+import { boardQueryKey } from '../queryKey';
function useDeleteArticleMutation(id: number) {
const { push } = useRouter();
const { toast } = useToast();
- const bearerToken = useRecoilValue(bearerTokenState);
+ const { boardType } = useParams<{ boardType: string }>();
+ const queryClient = useQueryClient();
- const mutationFn = () =>
- axios.delete(`/proxy${DELETE_ARTICLE_API}/${id}`, {
- headers: {
- Authorization: bearerToken,
- },
- });
+ const mutationFn = () => boardService.deleteArticle(id);
const onSuccess = () => {
toast({
description: POPUP_MESSAGE.deleteSuccess,
});
- push(`${PATH.boards.notice.url}/list/1`);
+ queryClient.invalidateQueries({
+ queryKey: boardQueryKey.list({ boardType }),
+ });
+ push(NEW_PATH.boardList.url({ boardType, category: 'all', page: 1 }));
};
const onError = (error: AxiosError) => {
diff --git a/app/_hooks/apis/boards/usePostArticleMutation.ts b/app/_hooks/apis/boards/usePostArticleMutation.ts
index 40826e1..7d1bfe2 100644
--- a/app/_hooks/apis/boards/usePostArticleMutation.ts
+++ b/app/_hooks/apis/boards/usePostArticleMutation.ts
@@ -1,6 +1,6 @@
import { AxiosError } from 'axios';
import { useParams, useRouter } from 'next/navigation';
-import { useMutation } from '@tanstack/react-query';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
import { CreateArticleForm } from '@app/_types/PostTypes';
import { NEW_PATH, PATH } from '@app/_constants/urls';
import {
@@ -10,13 +10,13 @@ import {
} from '@app/_constants/message';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
import boardService from '@app/_service/boardService';
-import { BoardType } from '@app/_types/boardTypes';
-import { revalidatePath } from '@app/_actions';
+import { boardQueryKey } from '../queryKey';
export default function usePostArticleMutation() {
const { toast } = useToast();
const { push } = useRouter();
- const { boardType } = useParams<{ boardType: BoardType }>();
+ const { boardType } = useParams<{ boardType: string }>();
+ const queryClient = useQueryClient();
const mutationFn = ({ files, uploadedFiles, ...rest }: CreateArticleForm) =>
boardService.createArticle({
@@ -28,8 +28,15 @@ export default function usePostArticleMutation() {
toast({
description: POPUP_MESSAGE.postSuccess,
});
- const boardListPath = NEW_PATH.boardList.url({ boardType, page: 1 });
- await revalidatePath(boardListPath.split('/list')[0]);
+ const boardListPath = NEW_PATH.boardList.url({
+ boardType,
+ category: 'all',
+ page: 1,
+ });
+
+ queryClient.invalidateQueries({
+ queryKey: boardQueryKey.list({ boardType }),
+ });
push(boardListPath);
};
diff --git a/app/_hooks/apis/boards/useUpdateArticleMutation.ts b/app/_hooks/apis/boards/useUpdateArticleMutation.ts
index a2e9363..9fb4bb6 100644
--- a/app/_hooks/apis/boards/useUpdateArticleMutation.ts
+++ b/app/_hooks/apis/boards/useUpdateArticleMutation.ts
@@ -1,27 +1,22 @@
-import axios from 'axios';
-import { useMutation } from '@tanstack/react-query';
-import { UPDATE_ARTICLE_API } from '@app/_constants/apiUrl';
-import { useRecoilValue } from 'recoil';
-import { accessTokenState } from '@app/_store/permissionAtoms';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
import { UpdateReqData } from '@app/_types/PostTypes';
import { POPUP_MESSAGE, TOAST_TITLE } from '@app/_constants/message';
import { useToast } from '@app/_shadcn/components/ui/use-toast';
+import boardService from '@app/_service/boardService';
+import { boardQueryKey } from '../queryKey';
-function useUpdateArticleMutation(id: string) {
- const accessToken = useRecoilValue(accessTokenState);
+function useUpdateArticleMutation(id: number) {
const { toast } = useToast();
+ const queryClient = useQueryClient();
const mutationFn = (data: UpdateReqData) =>
- axios.patch(`/proxy${UPDATE_ARTICLE_API}/${id}`, data, {
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- });
+ boardService.updateArticle({ id, updateData: data });
const onSuccess = () => {
toast({
description: POPUP_MESSAGE.updateSuccess,
});
+ queryClient.invalidateQueries({ queryKey: boardQueryKey.detail(id) });
};
const onError = () => {
diff --git a/app/_hooks/apis/queryKey.ts b/app/_hooks/apis/queryKey.ts
new file mode 100644
index 0000000..fab6455
--- /dev/null
+++ b/app/_hooks/apis/queryKey.ts
@@ -0,0 +1,13 @@
+export const boardQueryKey = {
+ all: ['board'],
+ details: () => [...boardQueryKey.all, 'detail'],
+ detail: (id: number) => [...boardQueryKey.details(), id],
+ lists: () => [...boardQueryKey.all, 'list'],
+ list: (filter: object) => [...boardQueryKey.lists(), filter],
+};
+
+export const commentQueryKey = {
+ all: ['comment'],
+ lists: () => [...commentQueryKey.all, 'list'],
+ list: (id: number) => [...commentQueryKey.lists(), id],
+};
diff --git a/app/_service/boardService.ts b/app/_service/boardService.ts
index 67ef2c7..4572dda 100644
--- a/app/_service/boardService.ts
+++ b/app/_service/boardService.ts
@@ -1,5 +1,9 @@
-import { CreateArticleRequest } from '@app/_types/PostTypes';
-import { CommentResponse } from '@app/_types/boardTypes';
+import { CreateArticleRequest, UpdateReqData } from '@app/_types/PostTypes';
+import {
+ ArticleDetail,
+ CommentResponse,
+ OnePageOfArticleList,
+} from '@app/_types/boardTypes';
import Service from './service';
class BoardService extends Service {
@@ -72,6 +76,49 @@ class BoardService extends Service {
const blobs = await Promise.all(blobPromiseList);
return blobs;
}
+
+ async getArticleList({
+ page,
+ boardType,
+ category = 'all',
+ }: {
+ boardType: string;
+ page: number;
+ category?: string;
+ }) {
+ const { data } = await this.axiosExtend.get(
+ `/api/article/${boardType}/${category}/${page}`,
+ );
+ return data;
+ }
+
+ async getArticleDetail(id: number) {
+ const { data } = await this.axiosExtend.get(
+ `/api/article/view/${id}`,
+ );
+ return data;
+ }
+
+ async deleteArticle(id: number) {
+ const { data } = await this.axiosExtend.delete(
+ `/api/article/delete/${id}`,
+ );
+ return data;
+ }
+
+ async updateArticle({
+ id,
+ updateData,
+ }: {
+ id: number;
+ updateData: UpdateReqData;
+ }) {
+ const { data } = await this.axiosExtend.patch(
+ `/api/article/update/${id}`,
+ updateData,
+ );
+ return data;
+ }
}
const boardService = new BoardService();
diff --git a/app/_types/boardTypes.ts b/app/_types/boardTypes.ts
index ec0e3e4..892f699 100644
--- a/app/_types/boardTypes.ts
+++ b/app/_types/boardTypes.ts
@@ -48,6 +48,7 @@ export type BoardType =
export interface BoardListParams extends Params {
boardType: BoardType;
page: string;
+ category: string;
}
export interface BoardDetailParams extends Params {
@@ -76,3 +77,9 @@ export interface CommentResponse {
export interface Category {
categories: string[];
}
+
+export interface ArticleListParams {
+ page: number;
+ boardType: BoardType;
+ category: string;
+}