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 src/apis/council/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface CouncilResponse {
location: string;
personalColor: string;
operatingHour: string;
instagramUrl: string;
instagramUserName: string;
imageUrl: string;
}

Expand Down
7 changes: 6 additions & 1 deletion src/pages/Auth/SignUp/hooks/useUniversity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getUniversityList } from '@/apis/university';

export const universityQueryKeys = {
all: ['university'],
list: () => [...universityQueryKeys.all, 'list'],
};

export const useGetUniversityList = () => {
return useSuspenseQuery({
queryKey: ['universityList'],
queryKey: universityQueryKeys.list(),
queryFn: () => getUniversityList(),
});
};
16 changes: 11 additions & 5 deletions src/pages/Chat/hooks/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { useMutation, useSuspenseQuery, useInfiniteQuery, useQueryClient } from
import { getChatMessages, getChatRooms, postChatMessage, postChatRooms } from '@/apis/chat';
import type { ChatRoomsResponse } from '@/apis/chat/entity';

export const chatQueryKeys = {
all: ['chat'],
rooms: () => [...chatQueryKeys.all, 'rooms'],
messages: (chatRoomId: number) => [...chatQueryKeys.all, 'messages', chatRoomId],
};

const useChat = (chatRoomId?: number) => {
const queryClient = useQueryClient();

const { data: chatRoomList } = useSuspenseQuery({
queryKey: ['chatList'],
queryKey: chatQueryKeys.rooms(),
queryFn: () => getChatRooms(),
refetchInterval: 5000,
});
Expand All @@ -24,7 +30,7 @@ const useChat = (chatRoomId?: number) => {
isFetchingNextPage,
isSuccess,
} = useInfiniteQuery({
queryKey: ['chatMessages', chatRoomId],
queryKey: chatQueryKeys.messages(chatRoomId!),
queryFn: ({ pageParam }) =>
getChatMessages({
chatRoomId: chatRoomId!,
Expand All @@ -50,7 +56,7 @@ const useChat = (chatRoomId?: number) => {
if (markedReadRoomRef.current === chatRoomId) return;
markedReadRoomRef.current = chatRoomId;

queryClient.setQueryData<ChatRoomsResponse>(['chatList'], (old) => {
queryClient.setQueryData<ChatRoomsResponse>(chatQueryKeys.rooms(), (old) => {
if (!old) return old;

const nextRooms = old.chatRooms.map((room) =>
Expand All @@ -66,8 +72,8 @@ const useChat = (chatRoomId?: number) => {
mutationFn: ({ chatRoomId, content }: { chatRoomId: number; content: string }) =>
postChatMessage(chatRoomId, content),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['chatMessages', chatRoomId] });
queryClient.invalidateQueries({ queryKey: ['chatList'] });
queryClient.invalidateQueries({ queryKey: chatQueryKeys.messages(chatRoomId!) });
queryClient.invalidateQueries({ queryKey: chatQueryKeys.rooms() });
},
});

Expand Down
5 changes: 3 additions & 2 deletions src/pages/Club/Application/hooks/useClubApply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-q
import { useNavigate } from 'react-router-dom';
import { applyClub, getClubQuestions } from '@/apis/club';
import type { ClubApplyRequest } from '@/apis/club/entity';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

const useClubApply = (clubId: number) => {
const navigate = useNavigate();
const queryClient = useQueryClient();

const { data: clubQuestions } = useSuspenseQuery({
queryKey: ['clubQuestions', clubId],
queryKey: clubQueryKeys.questions(clubId),
queryFn: () => getClubQuestions(clubId),
});

const { mutateAsync: applyToClub } = useMutation({
mutationKey: ['applyToClub', clubId],
mutationFn: (answers: ClubApplyRequest) => applyClub(clubId, answers),
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ['clubDetail', clubId] });
queryClient.invalidateQueries({ queryKey: clubQueryKeys.detail(clubId) });
if (data.amount !== null) {
navigate(`/clubs/${clubId}/fee`);
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/pages/Club/Application/hooks/useGetClubFee.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getClubFee } from '@/apis/club';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

export const useGetClubFee = (clubId: number) => {
return useSuspenseQuery({
queryKey: ['clubFee', clubId],
queryKey: clubQueryKeys.fee(clubId),
queryFn: () => getClubFee(clubId),
});
};
3 changes: 2 additions & 1 deletion src/pages/Club/ClubDetail/hooks/useCouncilNotices.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { getCouncilNotice } from '@/apis/council';
import { councilQueryKeys } from '@/pages/Council/CouncilDetail/hooks/useGetCouncilInfo';

interface UseCouncilNoticeParams {
limit?: number;
}

export const useCouncilNotice = ({ limit = 10 }: UseCouncilNoticeParams = {}) => {
return useSuspenseInfiniteQuery({
queryKey: ['councilNotice', limit],
queryKey: councilQueryKeys.notices(limit),
queryFn: ({ pageParam }) => getCouncilNotice({ page: pageParam, limit }),
initialPageParam: 1,
getNextPageParam: (lastPage) => {
Expand Down
3 changes: 2 additions & 1 deletion src/pages/Club/ClubDetail/hooks/useGetClubDetail.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getClubDetail } from '@/apis/club';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

export const useGetClubDetail = (clubId: number) => {
return useSuspenseQuery({
queryKey: ['clubDetail', clubId],
queryKey: clubQueryKeys.detail(clubId),
queryFn: () => getClubDetail(clubId),
});
};
3 changes: 2 additions & 1 deletion src/pages/Club/ClubDetail/hooks/useGetClubMembers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getClubMembers } from '@/apis/club';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

export const useGetClubMembers = (clubId: number) => {
return useSuspenseQuery({
queryKey: ['clubMembers', clubId],
queryKey: clubQueryKeys.members(clubId),
queryFn: () => getClubMembers(clubId),
});
};
3 changes: 2 additions & 1 deletion src/pages/Club/ClubDetail/hooks/useGetClubRecruitment.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getClubRecruitment } from '@/apis/club';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

const useGetClubRecruitment = (clubId: number) => {
return useSuspenseQuery({
queryKey: ['clubRecruitment', clubId],
queryKey: clubQueryKeys.recruitment(clubId),
queryFn: () => getClubRecruitment(clubId),
});
};
Expand Down
19 changes: 18 additions & 1 deletion src/pages/Club/ClubList/hooks/useGetClubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@ import { useInfiniteQuery } from '@tanstack/react-query';
import { getClubs } from '@/apis/club';
import type { ClubResponse } from '@/apis/club/entity';

export const clubQueryKeys = {
all: ['clubs'],
list: (params: { limit: number; query?: string; isRecruiting: boolean }) => [
...clubQueryKeys.all,
'list',
params.limit,
params.query,
params.isRecruiting,
],
detail: (clubId: number) => [...clubQueryKeys.all, 'detail', clubId],
members: (clubId: number) => [...clubQueryKeys.all, 'members', clubId],
recruitment: (clubId: number) => [...clubQueryKeys.all, 'recruitment', clubId],
fee: (clubId: number) => [...clubQueryKeys.all, 'fee', clubId],
questions: (clubId: number) => [...clubQueryKeys.all, 'questions', clubId],
joined: () => [...clubQueryKeys.all, 'joined'],
};

interface UseGetClubsParams {
limit?: number;
query?: string;
Expand All @@ -11,7 +28,7 @@ interface UseGetClubsParams {

export const useGetClubs = ({ limit = 10, query, enabled = true, isRecruiting = false }: UseGetClubsParams = {}) => {
return useInfiniteQuery({
queryKey: ['clubs', limit, query, isRecruiting],
queryKey: clubQueryKeys.list({ limit, query, isRecruiting }),
queryFn: ({ pageParam = 1 }) =>
getClubs({
page: pageParam,
Expand Down
7 changes: 5 additions & 2 deletions src/pages/Council/CouncilDetail/components/CouncilIntro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ function CouncilIntro({ councilDetail }: CouncilIntroProps) {
</div>
<div className="flex flex-col gap-1">
<div className="text-[10px] leading-3 font-medium text-indigo-300">인스타그램</div>
<Link to={councilDetail.instagramUrl} className="text-sm leading-3.5 font-semibold text-indigo-700">
@{councilDetail.instagramUrl.split('/').pop()}
<Link
to={`https://instagram.com/${councilDetail.instagramUserName}`}
className="text-sm leading-3.5 font-semibold text-indigo-700"
>
@{councilDetail.instagramUserName}
</Link>
</div>
</div>
Expand Down
9 changes: 8 additions & 1 deletion src/pages/Council/CouncilDetail/hooks/useGetCouncilInfo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getCouncilInfo } from '@/apis/council';

export const councilQueryKeys = {
all: ['council'],
info: () => [...councilQueryKeys.all, 'info'],
notices: (limit: number) => [...councilQueryKeys.all, 'notices', limit],
noticeDetail: (noticeId: number) => [...councilQueryKeys.all, 'noticeDetail', noticeId],
};

export const useGetCouncilInfo = () => {
return useSuspenseQuery({
queryKey: ['councilInfo'],
queryKey: councilQueryKeys.info(),
queryFn: () => getCouncilInfo(),
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useEffect, useRef } from 'react';
import { useQueryClient, useSuspenseQuery, type InfiniteData } from '@tanstack/react-query';
import { getCouncilNoticeDetail } from '@/apis/council';
import type { NoticeResponse } from '@/apis/council/entity';
import { councilQueryKeys } from '@/pages/Council/CouncilDetail/hooks/useGetCouncilInfo';
import { userQueryKeys } from '@/pages/User/Profile/hooks/useMyInfo';

interface UseGetCouncilNoticeDetailParams {
noticeId: number;
Expand All @@ -11,7 +13,7 @@ export const useGetCouncilNoticeDetail = ({ noticeId }: UseGetCouncilNoticeDetai
const queryClient = useQueryClient();

const query = useSuspenseQuery({
queryKey: ['councilNoticeDetail', noticeId],
queryKey: councilQueryKeys.noticeDetail(noticeId),
queryFn: () => getCouncilNoticeDetail(noticeId),
});

Expand All @@ -23,7 +25,7 @@ export const useGetCouncilNoticeDetail = ({ noticeId }: UseGetCouncilNoticeDetai
if (patchedRef.current === noticeId) return;
patchedRef.current = noticeId;

queryClient.setQueriesData<InfiniteData<NoticeResponse>>({ queryKey: ['councilNotice'] }, (old) => {
queryClient.setQueriesData<InfiniteData<NoticeResponse>>({ queryKey: councilQueryKeys.all }, (old) => {
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query key pattern is inconsistent here. The queryKey should be councilQueryKeys.notices() or similar, not councilQueryKeys.all. The setQueriesData method is meant to match multiple queries that start with ['council', 'notices'] to update cached notice data, but councilQueryKeys.all only returns ['council'] which would match all council-related queries including info and notice details, not just the notice list queries. This could lead to unintended cache updates.

Suggested change
queryClient.setQueriesData<InfiniteData<NoticeResponse>>({ queryKey: councilQueryKeys.all }, (old) => {
queryClient.setQueriesData<InfiniteData<NoticeResponse>>({ queryKey: councilQueryKeys.notices() }, (old) => {

Copilot uses AI. Check for mistakes.
if (!old) return old;

let changed = false;
Expand All @@ -45,7 +47,7 @@ export const useGetCouncilNoticeDetail = ({ noticeId }: UseGetCouncilNoticeDetai
return changed ? next : old;
});

queryClient.invalidateQueries({ queryKey: ['myInfo'], refetchType: 'all' });
queryClient.invalidateQueries({ queryKey: userQueryKeys.myInfo(), refetchType: 'all' });
}, [noticeId, query.data, queryClient]);

return query;
Expand Down
3 changes: 2 additions & 1 deletion src/pages/Home/hooks/useGetJoinedClubs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getJoinedClubs } from '@/apis/club';
import { clubQueryKeys } from '@/pages/Club/ClubList/hooks/useGetClubs';

export const useGetJoinedClubs = () => {
return useSuspenseQuery({
queryKey: ['joinedClubs'],
queryKey: clubQueryKeys.joined(),
queryFn: () => getJoinedClubs(),
});
};
3 changes: 2 additions & 1 deletion src/pages/Home/hooks/useGetScheduleList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getScheduleList } from '@/apis/schedule';
import type { ScheduleRequestParams } from '@/apis/schedule/entity';
import { scheduleQueryKeys } from '@/pages/Schedule/hooks/useGetSchedules';

export const useGetScheduleList = (params: ScheduleRequestParams) => {
return useSuspenseQuery({
queryKey: ['scheduleList', params.year, params.month],
queryKey: scheduleQueryKeys.monthly(params),
queryFn: () => getScheduleList(params),
});
};
3 changes: 2 additions & 1 deletion src/pages/Home/hooks/useGetUpComingSchedule.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getUpComingScheduleList } from '@/apis/schedule';
import { scheduleQueryKeys } from '@/pages/Schedule/hooks/useGetSchedules';

export const useGetUpComingScheduleList = () => {
return useSuspenseQuery({
queryKey: ['scheduleList'],
queryKey: scheduleQueryKeys.upcoming(),
queryFn: () => getUpComingScheduleList(),
});
};
1 change: 1 addition & 0 deletions src/pages/Schedule/hooks/useGetSchedules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getScheduleList } from '@/apis/schedule/index';
export const scheduleQueryKeys = {
all: ['schedules'],
monthly: (params: ScheduleRequestParams) => [...scheduleQueryKeys.all, 'monthly', params.year, params.month],
upcoming: () => [...scheduleQueryKeys.all, 'upcoming'],
};

export const useScheduleList = (params: ScheduleRequestParams) => {
Expand Down
22 changes: 19 additions & 3 deletions src/pages/Timer/hooks/useStudyTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { getStudyTimeSummary, startStudyTimer, stopStudyTimer } from '@/apis/studyTime';
import { API_ERROR_CODES, isApiError } from '@/interface/error';

export const STUDY_TIME_SUMMARY_QUERY_KEY = ['studyTimeSummary'] as const;
export const studyTimeQueryKeys = {
all: ['studyTime'],
summary: () => [...studyTimeQueryKeys.all, 'summary'],
ranking: (params: { limit: number; sort: string; type: string }) => [
...studyTimeQueryKeys.all,
'ranking',
params.limit,
params.sort,
params.type,
],
myRanking: (params: { sort: string; type: string }) => [
...studyTimeQueryKeys.all,
'myRanking',
params.sort,
params.type,
],
};

export const useStudyTime = () => {
const queryClient = useQueryClient();

const { data: studyTime } = useQuery({
queryKey: STUDY_TIME_SUMMARY_QUERY_KEY,
queryKey: studyTimeQueryKeys.summary(),
queryFn: getStudyTimeSummary,
});

Expand All @@ -26,7 +42,7 @@ export const useStudyTime = () => {
// 이미 실행 중인 타이머가 있으면 정리 후 재시도
if (error.apiError?.code === API_ERROR_CODES.ALREADY_RUNNING_STUDY_TIMER) {
await stopMutation.mutateAsync({ totalSeconds: 0 });
queryClient.invalidateQueries({ queryKey: STUDY_TIME_SUMMARY_QUERY_KEY });
queryClient.invalidateQueries({ queryKey: studyTimeQueryKeys.summary() });
}

throw error;
Expand Down
5 changes: 3 additions & 2 deletions src/pages/Timer/hooks/useStudyTimeRanking.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useInfiniteQuery, useSuspenseQuery } from '@tanstack/react-query';
import { getMyStudyTimeRanking, getStudyTimeRanking } from '@/apis/studyTime';
import type { StudyRankingParams } from '@/apis/studyTime/entity';
import { studyTimeQueryKeys } from './useStudyTime';

interface UseStudyTimeRankingParams {
limit?: number;
Expand All @@ -14,7 +15,7 @@ export const useStudyTimeRanking = ({
type = 'PERSONAL',
}: UseStudyTimeRankingParams = {}) => {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ['studyTimeRanking', limit, sort, type],
queryKey: studyTimeQueryKeys.ranking({ limit, sort, type }),
queryFn: ({ pageParam }) =>
getStudyTimeRanking({
page: pageParam,
Expand All @@ -29,7 +30,7 @@ export const useStudyTimeRanking = ({
});

const { data: myRankingData } = useSuspenseQuery({
queryKey: ['myStudyTimeRanking', sort, type],
queryKey: studyTimeQueryKeys.myRanking({ sort, type }),
queryFn: () => getMyStudyTimeRanking({ sort }),
select: (data) => {
if (type === 'CLUB') return data.clubRankings;
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Timer/hooks/useStudyTimer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useEffectEvent, useRef, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useStudyTime, STUDY_TIME_SUMMARY_QUERY_KEY } from './useStudyTime';
import { useStudyTime, studyTimeQueryKeys } from './useStudyTime';

export const useStudyTimer = () => {
const queryClient = useQueryClient();
Expand Down Expand Up @@ -42,7 +42,7 @@ export const useStudyTimer = () => {
setSessionStartMs(null);
setIsRunning(false);

queryClient.invalidateQueries({ queryKey: STUDY_TIME_SUMMARY_QUERY_KEY });
queryClient.invalidateQueries({ queryKey: studyTimeQueryKeys.summary() });
} catch (error) {
console.error('타이머 정지 실패:', error);
setSessionStartMs(null);
Expand Down
Loading