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
1 change: 1 addition & 0 deletions src/app/(user-page)/my-meeting/my/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import NotYet from '@/components/common/NotYet';

Check warning on line 1 in src/app/(user-page)/my-meeting/my/page.tsx

View workflow job for this annotation

GitHub Actions / check

'NotYet' is defined but never used. Allowed unused vars must match /^_/u
import { myMeetingKeys } from '@/hooks/queries/useMyMeetingQueries';
import { QUERY_KEYS } from '@/hooks/queries/useMyPageQueries';
import {
Expand Down
13 changes: 13 additions & 0 deletions src/app/(user-page)/mypage/MyPageClient.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { prefetchProfileData } from '@/hooks/queries/useMyPageQueries';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter, useSearchParams } from 'next/navigation';
import { useCallback, useEffect, useRef, useState } from 'react';

Expand All @@ -18,6 +20,7 @@ import TechStackInfo from './_features/TechStackInfo';
const MyPageClient = () => {
const router = useRouter();
const searchParams = useSearchParams();
const queryClient = useQueryClient();

// URL에서 탭 값만 가져오기
const tabFromUrl = searchParams.get('tab') || TAB_TYPES.BASIC;
Expand All @@ -37,6 +40,11 @@ const MyPageClient = () => {
[TAB_TYPES.PASSWORD]: null,
});

// 컴포넌트 마운트 시 프로필 데이터 prefetch
useEffect(() => {
prefetchProfileData(queryClient);
}, [queryClient]);

// URL 변경 감지 및 상태 업데이트 (중요: 뒤로가기/앞으로가기 처리)
useEffect(() => {
const currentTabFromUrl = searchParams.get('tab') || TAB_TYPES.BASIC;
Expand All @@ -59,6 +67,9 @@ const MyPageClient = () => {

// 탭 변경 핸들러
const handleTabChange = (tab: string) => {
// 탭 변경 전에 해당 탭에 필요한 데이터 prefetch
prefetchProfileData(queryClient);

setActiveTab(tab);
updateUrl(tab);
};
Expand All @@ -69,6 +80,8 @@ const MyPageClient = () => {
};

const handleEnableEdit = () => {
// 편집 모드로 전환하기 전에 프로필 데이터 다시 prefetch하여 최신 데이터 확보
prefetchProfileData(queryClient);
setEditModeSection(activeTab);
};

Expand Down
6 changes: 2 additions & 4 deletions src/app/(user-page)/mypage/_features/BasicEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import Dropdown from '@/components/common/Dropdown';
import { Button } from '@/components/ui/Button';
import {
useProfileQuery,
useUpdateProfileMutation,
} from '@/hooks/queries/useMyPageQueries';
import { useUpdateProfileMutation } from '@/hooks/mutations/useMyPageMutation';
import { useProfileQuery } from '@/hooks/queries/useMyPageQueries';
import { useCallback, useEffect, useState } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';

Expand Down
6 changes: 2 additions & 4 deletions src/app/(user-page)/mypage/_features/ContactEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import {
useProfileQuery,
useUpdateContactInfoMutation,
} from '@/hooks/queries/useMyPageQueries';
import { useUpdateContactInfoMutation } from '@/hooks/mutations/useMyPageMutation';
import { useProfileQuery } from '@/hooks/queries/useMyPageQueries';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';

Expand Down
2 changes: 1 addition & 1 deletion src/app/(user-page)/mypage/_features/PasswordEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useToast } from '@/components/common/ToastContext';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { useUpdatePasswordMutation } from '@/hooks/queries/useMyPageQueries';
import { useUpdatePasswordMutation } from '@/hooks/mutations/useMyPageMutation';
import { useForm } from 'react-hook-form';

interface PasswordFormData {
Expand Down
6 changes: 2 additions & 4 deletions src/app/(user-page)/mypage/_features/ProfileImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import EditLogo from '@/assets/icon/editLogo.svg';
import { useToast } from '@/components/common/ToastContext';
import { Button } from '@/components/ui/Button';
import Modal from '@/components/ui/modal/Modal';
import {
useProfileQuery,
useUpdateProfileImageMutation,
} from '@/hooks/queries/useMyPageQueries';
import { useUpdateProfileImageMutation } from '@/hooks/mutations/useMyPageMutation';
import { useProfileQuery } from '@/hooks/queries/useMyPageQueries';
import { useQueryClient } from '@tanstack/react-query';
import { Pencil } from 'lucide-react';
import Image from 'next/image';
Expand Down
6 changes: 2 additions & 4 deletions src/app/(user-page)/mypage/_features/TechStackEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
'use client';

import { Button } from '@/components/ui/Button';
import { useUpdateSkillsMutation } from '@/hooks/mutations/useMyPageMutation';
// Button 컴포넌트 import 추가
import {
useProfileQuery,
useUpdateSkillsMutation,
} from '@/hooks/queries/useMyPageQueries';
import { useProfileQuery } from '@/hooks/queries/useMyPageQueries';
import useTechSelection from '@/hooks/useTechSelection';
import { getIconColor, getIconsByCategory } from '@/util/getIconDetail';
import { useQueryClient } from '@tanstack/react-query';
Expand Down
25 changes: 18 additions & 7 deletions src/app/(user-page)/mypage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
// page.tsx (서버 컴포넌트)
import { prefetchProfileData } from '@/hooks/queries/useMyPageQueries';
import { QueryClient, dehydrate } from '@tanstack/react-query';
import { HydrationBoundary } from '@tanstack/react-query';
import { Suspense } from 'react';

import MyPageClient from './MyPageClient';
import ProfileImage from './_features/ProfileImage';
import SkeletonBasicInfo from './_features/skeletons/SkeletonBasicInfo';

export default function MyPage() {
export default async function MyPage() {
// 서버 컴포넌트에서 QueryClient 생성
const queryClient = new QueryClient();

// 필요한 데이터 프리페치
await prefetchProfileData(queryClient);

return (
<div className="flex flex-col px-[24px] pb-[100px]">
<div className="md:mb-8">
<ProfileImage />
</div>
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="md:mb-8">
<ProfileImage />
</div>

<Suspense fallback={<SkeletonBasicInfo />}>
<MyPageClient />
</Suspense>
<Suspense fallback={<SkeletonBasicInfo />}>
<MyPageClient />
</Suspense>
</HydrationBoundary>
</div>
);
}
70 changes: 70 additions & 0 deletions src/hooks/mutations/useMyPageMutation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import {
updateContactInfo,
updatePassword,
updateProfile,
updateProfileImage,
updateSkills,
} from '../../service/api/mypageProfile';
import {
IContactInfoUpdateRequest,
IPasswordUpdateRequest,
IProfileUpdateRequest,
} from '../../types/mypageTypes';
import { QUERY_KEYS } from '../queries/useMyPageQueries';

// 프로필 정보 업데이트 커스텀 훅
export const useUpdateProfileMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: IProfileUpdateRequest) => updateProfile(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 연락처 정보 업데이트 커스텀 훅
export const useUpdateContactInfoMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: IContactInfoUpdateRequest) => updateContactInfo(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 프로필 이미지 업데이트 커스텀 훅
export const useUpdateProfileImageMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (file: File) => updateProfileImage(file),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 비밀번호 업데이트 커스텀 훅
export const useUpdatePasswordMutation = () => {
return useMutation({
mutationFn: (data: IPasswordUpdateRequest) => updatePassword(data),
});
};

// 기술 스택 업데이트 커스텀 훅
export const useUpdateSkillsMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (skillArray: string[]) => updateSkills(skillArray),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};
99 changes: 24 additions & 75 deletions src/hooks/queries/useMyPageQueries.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { QueryClient, useQuery } from '@tanstack/react-query';

import {
getBanner,
getProfile,
updateContactInfo,
updatePassword,
updateProfile,
updateProfileImage,
updateSkills,
} from '../../service/api/mypageProfile';
import {
IContactInfoUpdateRequest,
IPasswordUpdateRequest,
IProfileUpdateRequest,
} from '../../types/mypageTypes';
import { getBanner, getProfile } from '../../service/api/mypageProfile';

// 마이페이지 관련 쿼리 키 정의
export const QUERY_KEYS = {
Expand All @@ -24,78 +11,40 @@ export const QUERY_KEYS = {
banner: () => [...QUERY_KEYS.all, 'banner'] as const,
};

// 프로필 정보 조회 커스텀 훅
export const useProfileQuery = () => {
return useQuery({
queryKey: QUERY_KEYS.profile(), // 기존 코드에서 사용하던 쿼리 키와 일치시킴
// 프로필 정보 prefetch 함수
export const prefetchProfileData = async (queryClient: QueryClient) => {
await queryClient.prefetchQuery({
queryKey: QUERY_KEYS.profile(),
queryFn: getProfile,
staleTime: 5 * 60 * 1000, // 5분 동안 fresh 상태 유지
});
};

// 프로필 정보 업데이트 커스텀 훅
export const useUpdateProfileMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: IProfileUpdateRequest) => updateProfile(data),
onSuccess: () => {
// 성공 시 프로필 데이터 캐시 무효화
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 연락처 정보 업데이트 커스텀 훅
export const useUpdateContactInfoMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (data: IContactInfoUpdateRequest) => updateContactInfo(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 프로필 이미지 업데이트 커스텀 훅
export const useUpdateProfileImageMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (file: File) => updateProfileImage(file),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
},
});
};

// 비밀번호 업데이트 커스텀 훅
export const useUpdatePasswordMutation = () => {
return useMutation({
mutationFn: (data: IPasswordUpdateRequest) => updatePassword(data),
// 배너 정보 prefetch 함수
export const prefetchBannerData = async (queryClient: QueryClient) => {
await queryClient.prefetchQuery({
queryKey: QUERY_KEYS.banner(),
queryFn: getBanner,
staleTime: 5 * 60 * 1000, // 5분 동안 fresh 상태 유지
});
};

// 기술 스택 업데이트 커스텀 훅
export const useUpdateSkillsMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (skillArray: string[]) => updateSkills(skillArray),
onSuccess: () => {
// 명시적으로 캐시 무효화 강화
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.profile() });
queryClient.refetchQueries({ queryKey: ['profile'] });
},
// 프로필 정보 조회 커스텀 훅 - staleTime 추가
export const useProfileQuery = (options = {}) => {
return useQuery({
queryKey: QUERY_KEYS.profile(),
queryFn: getProfile,
staleTime: 5 * 60 * 1000, // 5분 동안 fresh 상태 유지
...options,
});
};

// banner 정보 불러오기
export const useBannerQueries = () => {
const { data, error, isLoading } = useQuery({
export const useBannerQueries = (options = {}) => {
return useQuery({
queryKey: QUERY_KEYS.banner(),
queryFn: () => getBanner(),
staleTime: 5 * 60 * 1000, // 5분 동안 fresh 상태 유지
...options,
});

return { data, error, isLoading };
};
Loading