Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion app/(main)/connect/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getPostDetailServer } from "@/features/connect/apis/getPostDetailServer
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { getQueryClient } from "@/libs/getQueryClient";
import PostDetailContainer from "@/features/connect/containers/PostDetailContainer";
import { connectQueryKeys } from "@/features/connect/queries";
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";
import QueryErrorBoundary from "@/components/common/QueryErrorBoundary";
import { Metadata } from "next";

Expand Down
2 changes: 1 addition & 1 deletion app/(main)/connect/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { getQueryClient } from "@/libs/getQueryClient";
import EditPostContainer from "@/features/connect/containers/EditPostContainer";
import { getPostDetailServer } from "@/features/connect/apis/getPostDetailServer";
import { connectQueryKeys } from "@/features/connect/queries";
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";
import QueryErrorBoundary from "@/components/common/QueryErrorBoundary";
import { Metadata } from "next";

Expand Down
4 changes: 2 additions & 2 deletions app/(main)/connect/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getQueryClient } from "@/libs/getQueryClient";
import { Suspense } from "react";
import { serverFetch } from "@/libs/serverFetch";
import IntroSection from "@/features/connect/components/IntroSection";
import { connectQueryKeys } from "@/features/connect/queries";
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";
import QueryErrorBoundary from "@/components/common/QueryErrorBoundary";
import { Metadata } from "next";

Expand Down Expand Up @@ -43,7 +43,7 @@ export default async function ConnectPage({
staleTime: 1000 * 60,
}),
queryClient.prefetchQuery({
queryKey: connectQueryKeys.hotPosts(),
queryKey: connectQueryKeys.hotPosts,
queryFn: async () => {
const res = await serverFetch(`/posts?type=best&limit=20`);
if (!res.ok) throw new Error("HOT 게시글 조회 실패");
Expand Down
8 changes: 4 additions & 4 deletions app/(main)/meetup/[meetupId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import {
getRelatedMeetingsServer,
getReviewsServer,
} from "@/features/meetupDetail/apis/apis.server";
import { meetupDetailQueryKeys } from "@/features/meetupDetail/queries";
import MeetupDetailClient from "@/features/meetupDetail/containers/meetupContainer";
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { getQueryClient } from "@/libs/getQueryClient";
import { meetupDetailQueryKeys } from "@/features/shared/queryKeys/meetupDetail";

interface PageProps {
params: Promise<{ meetupId: string }>;
Expand Down Expand Up @@ -47,18 +47,18 @@ export default async function MeetupDetailPage({ params }: PageProps) {

await Promise.all([
queryClient.prefetchQuery({
queryKey: meetupDetailQueryKeys.meeting(meetingId),
queryKey: meetupDetailQueryKeys.meeting.detail(meetingId),
queryFn: () => meeting,
staleTime: 1000 * 60 * 5,
}),
queryClient.prefetchInfiniteQuery({
queryKey: meetupDetailQueryKeys.participants(meetingId),
queryKey: meetupDetailQueryKeys.participants.detail(meetingId),
queryFn: () => getParticipantsServer(meetingId),
initialPageParam: undefined,
staleTime: 1000 * 60 * 3,
}),
queryClient.prefetchQuery({
queryKey: meetupDetailQueryKeys.reviews(meetingId, undefined),
queryKey: meetupDetailQueryKeys.reviews.detail(meetingId, undefined),
queryFn: () => getReviewsServer(meetingId),
staleTime: 1000 * 60 * 10,
}),
Expand Down
3 changes: 2 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getMeServer } from "@/features/auth/queries.server";
import { cookies } from "next/headers";
import { getQueryClient } from "@/libs/getQueryClient";
import Script from "next/script";
import { authQueryKeys } from "@/features/shared/queryKeys/auth";

const pretendard = localFont({
src: "../public/assets/fonts/PretendardVariable.woff2",
Expand Down Expand Up @@ -101,7 +102,7 @@ export default async function RootLayout({
if (accessToken) {
await queryClient
.prefetchQuery({
queryKey: ["me"],
queryKey: authQueryKeys.me,
queryFn: getMeServer,
})
.catch(() => {});
Expand Down
7 changes: 4 additions & 3 deletions features/auth/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { postLogin, postSignUp, postLogout, postOAuthLogin } from "@/features/auth/apis";
import { useToast } from "@/providers/toast-provider";
import { useRouter } from "next/navigation";
import { authQueryKeys } from "@/features/shared/queryKeys/auth";

export function useLogin(onSuccess: () => void) {
const queryClient = useQueryClient();
Expand All @@ -11,7 +12,7 @@ export function useLogin(onSuccess: () => void) {
return useMutation({
mutationFn: postLogin,
onSuccess: (data) => {
queryClient.setQueryData(["me"], data.user);
queryClient.setQueryData(authQueryKeys.me, data.user);
handleShowToast({ message: "로그인이 완료됐습니다.", status: "success" });
onSuccess();
router.refresh();
Expand All @@ -34,7 +35,7 @@ export function useSignUp(onSuccess: () => void, onAutoLoginFail?: () => void) {
email: variables.email,
password: variables.password,
});
queryClient.setQueryData(["me"], loginResult.user);
queryClient.setQueryData(authQueryKeys.me, loginResult.user);

handleShowToast({ message: "회원가입이 완료됐습니다.", status: "success" });
onSuccess();
Expand Down Expand Up @@ -81,7 +82,7 @@ export function useOAuthLogin(onSuccess: () => void) {
mutationFn: ({ provider, token }: { provider: "google" | "kakao"; token: string }) =>
postOAuthLogin(provider, token),
onSuccess: (data) => {
queryClient.setQueryData(["me"], data.user);
queryClient.setQueryData(authQueryKeys.me, data.user);
handleShowToast({ message: "로그인이 완료됐습니다.", status: "success" });
onSuccess();
router.refresh();
Expand Down
4 changes: 3 additions & 1 deletion features/auth/queries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useQuery } from "@tanstack/react-query";
import { clientFetch } from "@/libs/clientFetch";
import { User } from "@/features/auth/types";
import { authQueryKeys } from "@/features/shared/queryKeys/auth";

async function getMe(): Promise<User> {
const response = await clientFetch("/users/me");

Expand All @@ -11,7 +13,7 @@ async function getMe(): Promise<User> {

export function useGetMe() {
return useQuery({
queryKey: ["me"],
queryKey: authQueryKeys.me,
queryFn: getMe,
retry: false,
staleTime: 1000 * 60 * 5,
Expand Down
6 changes: 3 additions & 3 deletions features/connect/components/Banner/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/nextjs-vite";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Suspense } from "react";
import ConnectBanner from ".";
import { connectQueryKeys } from "@/features/connect/queries";
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";

const MOCK_HOT_POSTS = {
data: [
Expand Down Expand Up @@ -49,7 +49,7 @@ export const Default: Story = {
decorators: [
(Story) => {
const queryClient = createQueryClient();
queryClient.setQueryData(connectQueryKeys.hotPosts(), MOCK_HOT_POSTS);
queryClient.setQueryData(connectQueryKeys.hotPosts, MOCK_HOT_POSTS);

return (
<QueryClientProvider client={queryClient}>
Expand All @@ -69,7 +69,7 @@ export const Empty: Story = {
decorators: [
(Story) => {
const queryClient = createQueryClient();
queryClient.setQueryData(connectQueryKeys.hotPosts(), { data: [] });
queryClient.setQueryData(connectQueryKeys.hotPosts, { data: [] });

return (
<QueryClientProvider client={queryClient}>
Expand Down
15 changes: 7 additions & 8 deletions features/connect/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import { createComment } from "@/features/connect/apis/createComment";
import { updateComment } from "@/features/connect/apis/updateComment";
import { useToast } from "@/providers/toast-provider";
import { deleteComment } from "@/features/connect/apis/deleteComment";
import { connectQueryKeys } from "@/features/connect/queries";
import { headerQueryKeys } from "@/features/header/queries";
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";
import { useUser } from "@/hooks/useUser";
import { headerQueryKeys } from "@/features/shared/queryKeys/header";

// 댓글/좋아요 뮤테이션 후 공통으로 무효화할 헤더 관련 쿼리
function invalidateHeaderQueries(queryClient: ReturnType<typeof useQueryClient>) {
queryClient.invalidateQueries({ queryKey: headerQueryKeys.notifications }); // 알림 목록
queryClient.invalidateQueries({ queryKey: headerQueryKeys.notificationsCount }); // 읽지 않은 알림 수
queryClient.invalidateQueries({ queryKey: headerQueryKeys.notifications.all }); // 알림 목록
}

// 게시글 좋아요 토글 (Optimistic Update)
Expand Down Expand Up @@ -58,7 +57,7 @@ export function useToggleConnectLike(postId: number) {
onSuccess: () => {
// lists만 stale 표시 → 목록으로 돌아갈 때 자동 갱신
queryClient.invalidateQueries({
queryKey: connectQueryKeys.lists(),
queryKey: connectQueryKeys.lists,
refetchType: "none", // 즉시 refetch 안 함
});
},
Expand All @@ -82,7 +81,7 @@ export function useCreatePost() {
mutationFn: createPost,

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists() }); // 목록 전체 무효화
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists }); // 목록 전체 무효화
},

onError: (err) => {
Expand All @@ -102,7 +101,7 @@ export function useDeletePost(postId: number) {
mutationFn: () => deletePost(postId),

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists() }); // 목록 전체 무효화
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists }); // 목록 전체 무효화
router.replace("/connect?deleted=true");
},

Expand All @@ -122,7 +121,7 @@ export function useUpdatePost(postId: number) {
mutationFn: (data: { title: string; content: string }) => updatePost(postId, data),

onSuccess: () => {
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists() }); // 목록 전체
queryClient.invalidateQueries({ queryKey: connectQueryKeys.lists }); // 목록 전체
},

onError: (err) => {
Expand Down
24 changes: 2 additions & 22 deletions features/connect/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,7 @@ import { fetchPostsClient } from "@/features/connect/apis/fetchPostsClient";
import { getPostDetailClient } from "@/features/connect/apis/getPostDetailClient";
import { useSuspenseQuery, useQuery, keepPreviousData } from "@tanstack/react-query";
import { getUserProfile } from "@/apis/UserProfile";

export const connectQueryKeys = {
// 게시글 관련 전체 키 (invalidateQueries 범위 제어용)
all: () => ["connect"] as const,

// 게시글 목록 관련 키
lists: () => [...connectQueryKeys.all(), "list"] as const,

// 게시글 목록 필터 조합 키
list: (page: number, sortBy: string, limit: number, keyword: string) =>
[...connectQueryKeys.lists(), { page, sortBy, limit, keyword }] as const,

// 인기 게시글 키
hotPosts: () => [...connectQueryKeys.all(), "hotPosts"] as const,

// 게시글 상세 키
detail: (postId: number) => [...connectQueryKeys.all(), "detail", postId] as const,

// 유저 프로필 키
userProfile: (userId: number) => ["user", "profile", userId] as const,
};
import { connectQueryKeys } from "@/features/shared/queryKeys/connect";

// 게시글 목록 조회 (페이지네이션 + 정렬 + 검색)
export function useGetPosts({
Expand Down Expand Up @@ -54,7 +34,7 @@ export function useGetPosts({
// 인기 게시글 목록 조회 (상위 20개, Suspense 기반)
export function useGetHotPosts() {
return useSuspenseQuery({
queryKey: connectQueryKeys.hotPosts(),
queryKey: connectQueryKeys.hotPosts,
queryFn: () => fetchPostsClient({ type: "best", limit: 20 }),
staleTime: 1000 * 60 * 5,
retry: 1,
Expand Down
35 changes: 12 additions & 23 deletions features/favorites/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,17 @@ import {
postMeetingsFavorite,
postMeetingsJoin,
} from "@/apis/meetings";
import { headerQueryKeys } from "@/features/header/queries";
import { meetupDetailQueryKeys } from "@/features/meetupDetail/queries";
import { mypageQueryKeys } from "@/features/mypage/queries";
import { MeetupListRequest } from "../meetup/types";
import { queryKeys } from "./queries/queryKeys";
import { meetupDetailQueryKeys } from "@/features/shared/queryKeys/meetupDetail";
import { headerQueryKeys } from "@/features/shared/queryKeys/header";
import { mypageQueryKeys } from "@/features/shared/queryKeys/mypage";
import { meetupQueryKeys } from "@/features/shared/queryKeys/meetup";
import { favoritesQueryKeys } from "../shared/queryKeys/favorites";

type MutationCallbacks<TData, TVariables = void> = Omit<
UseMutationOptions<TData, Error, TVariables>,
"mutationKey" | "mutationFn"
>;

export const meetupQueryKeys = {
list: ["meetup", "list"] as const,
listWithParams: (params: MeetupListRequest) => [...meetupQueryKeys.list, params] as const,
};

export const meetupMutationKeys = {
postMeetup: ["meetup", "post"] as const,
uploadImage: ["meetup", "image", "upload"] as const,
Expand All @@ -33,7 +28,7 @@ export const meetupMutationKeys = {
async function invalidateMeetupAndFavoritesQueries(queryClient: ReturnType<typeof useQueryClient>) {
await Promise.all([
queryClient.invalidateQueries({
queryKey: queryKeys.favorites.all,
queryKey: favoritesQueryKeys.favorites.all,
}),
queryClient.invalidateQueries({
queryKey: meetupQueryKeys.list,
Expand All @@ -51,10 +46,10 @@ async function invalidateFavoriteRelatedQueries(
queryKey: headerQueryKeys.favorites,
}),
queryClient.invalidateQueries({
queryKey: meetupDetailQueryKeys.meeting(meetingId),
queryKey: meetupDetailQueryKeys.meeting.detail(meetingId),
}),
queryClient.invalidateQueries({
queryKey: meetupDetailQueryKeys.related.all(),
queryKey: meetupDetailQueryKeys.related.all,
}),
]);
}
Expand All @@ -66,22 +61,16 @@ async function invalidateJoinRelatedQueries(
await Promise.all([
invalidateMeetupAndFavoritesQueries(queryClient),
queryClient.invalidateQueries({
queryKey: meetupDetailQueryKeys.meeting(meetingId),
}),
queryClient.invalidateQueries({
queryKey: meetupDetailQueryKeys.participants(meetingId),
}),
queryClient.invalidateQueries({
queryKey: mypageQueryKeys.meetups,
queryKey: meetupDetailQueryKeys.meeting.detail(meetingId),
}),
queryClient.invalidateQueries({
queryKey: mypageQueryKeys.created,
queryKey: meetupDetailQueryKeys.participants.detail(meetingId),
}),
queryClient.invalidateQueries({
queryKey: headerQueryKeys.notifications,
queryKey: mypageQueryKeys.meetups.all,
}),
queryClient.invalidateQueries({
queryKey: headerQueryKeys.notificationsCount,
queryKey: headerQueryKeys.notifications.all,
}),
]);
}
Expand Down
4 changes: 2 additions & 2 deletions features/favorites/queries/queryOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FavoritesListRequest, FavoritesListResponse } from "../types";
import { queryKeys } from "./queryKeys";
import { favoritesQueryKeys } from "@/features/shared/queryKeys/favorites";

export function favoritesInfiniteOptions(
params: FavoritesListRequest,
Expand All @@ -9,7 +9,7 @@ export function favoritesInfiniteOptions(
const querykeyParams = { ...rest };

return {
queryKey: queryKeys.favorites.list(querykeyParams),
queryKey: favoritesQueryKeys.favorites.list(querykeyParams),
queryFn: ({ pageParam }: { pageParam: string | undefined }) =>
getFavorites({
...params,
Expand Down
4 changes: 2 additions & 2 deletions features/header/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
putNotificationsReadAll,
} from "./apis";
import { useToast } from "@/providers/toast-provider";
import { headerQueryKeys } from "./queries";
import { CursorPageResponse, NotificationCardList } from "./types";
import { headerQueryKeys } from "@/features/shared/queryKeys/header";

const notificationQueryKey = headerQueryKeys.notification.all;
const notificationQueryKey = headerQueryKeys.notifications.all;

export function usePutNotificationsReadAll() {
const queryClient = useQueryClient();
Expand Down
Loading
Loading