diff --git a/cspell.json b/cspell.json index 8efc873d..5f0dbe32 100644 --- a/cspell.json +++ b/cspell.json @@ -4,29 +4,29 @@ "ignorePaths": ["node_modules", ".next", "*.js", "*.json", "*.html", "*.css", "*.md", "*.svg"], "files": ["src/**/*.tsx", "src/**/*.ts"], "words": [ + "autodocs", "classname", + "customise", + "hanmail", "kakao", "KEYPOINT", "kisa", "lotties", + "lucide", "lukemorales", "metadatas", + "naver", "nextauth", + "noti", + "partialize", "picktoss", "remirror", "svgs", + "swipeable", "Tanstack", "Uncapitalize", "vaul", "yxxx", - "swipeable", - "lucide", - "hanmail", - "naver", - "noti", - - // storybook - "autodocs", - "customise" + "zustand" ] } diff --git a/package-lock.json b/package-lock.json index 8b97a0fb..cf746795 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,8 @@ "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.1", - "zod": "^3.23.8" + "zod": "^3.23.8", + "zustand": "^5.0.2" }, "devDependencies": { "@chromatic-com/storybook": "^1.9.0", @@ -25447,6 +25448,34 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zustand": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz", + "integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 458117cb..b1249cea 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,8 @@ "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.1", - "zod": "^3.23.8" + "zod": "^3.23.8", + "zustand": "^5.0.2" }, "devDependencies": { "@chromatic-com/storybook": "^1.9.0", diff --git a/src/app/(routes)/document/[id]/(main)/page.tsx b/src/app/(routes)/document/[id]/(main)/page.tsx index f17893c1..32306f71 100644 --- a/src/app/(routes)/document/[id]/(main)/page.tsx +++ b/src/app/(routes)/document/[id]/(main)/page.tsx @@ -3,7 +3,7 @@ import DocumentFloatingButton from '@/features/document/components/document-floa import { QuizListProvider } from '@/features/document/contexts/quiz-list-context' import DocumentContent from '@/features/document/screens/document-content' import Quiz from '@/features/document/screens/quiz' -import { fetchDocumentDetail } from '@/requests/document' +import { fetchDocumentDetail } from '@/requests/document/client' interface Props { params: { diff --git a/src/app/(routes)/main/@header/default.tsx b/src/app/(routes)/main/@header/default.tsx index b7d4a462..6c62988f 100644 --- a/src/app/(routes)/main/@header/default.tsx +++ b/src/app/(routes)/main/@header/default.tsx @@ -4,23 +4,23 @@ import Icon from '@/shared/components/custom/icon' import Text from '@/shared/components/ui/text' import { useMessaging } from '@/shared/hooks/use-messaging' import usePreviousPath from '@/shared/hooks/use-previous-path' -import { useSession } from 'next-auth/react' +// import { useSession } from 'next-auth/react' import Link from 'next/link' -import { useRouter } from 'next/navigation' -import { useEffect } from 'react' +// import { useRouter } from 'next/navigation' +// import { useEffect } from 'react' const Header = () => { - const router = useRouter() - const { data: session, status } = useSession() + // const router = useRouter() + // const { data: session, status } = useSession() usePreviousPath() useMessaging() - useEffect(() => { - // 첫 로그인 회원일 경우 온보딩 화면(관심분야선택)으로 보냅니다 - if (status === 'authenticated' && session?.user.isNewUser) { - router.push('/on-boarding') - } - }, [session, status, router]) + // useEffect(() => { + // // 첫 로그인 회원일 경우 온보딩 화면(관심분야선택)으로 보냅니다 + // if (status === 'authenticated' && session?.user.isNewUser) { + // router.push('/on-boarding') + // } + // }, [session, status, router]) return (
diff --git a/src/app/(routes)/main/page.tsx b/src/app/(routes)/main/page.tsx index 72a8ddd3..6a798ec9 100644 --- a/src/app/(routes)/main/page.tsx +++ b/src/app/(routes)/main/page.tsx @@ -7,14 +7,14 @@ import Icon from '@/shared/components/custom/icon' import MainTodayQuizArea from '@/features/quiz/components/main-today-quiz-area' import ReviewTop5Container from '@/features/document/components/review-top5-container' import InterestedCategoryCollections from '@/features/collection/components/interested-category-collections' -import { fetchTodayQuizSetId } from '@/requests/quiz' -import { fetchDocuments } from '@/requests/document' import RandomQuizLottie from '@/features/quiz/components/random-quiz-lottie' import BombQuizLottie from '@/features/quiz/components/bomb-quiz-lottie' +import { fetchTodayQuizSetId } from '@/requests/quiz/server' +import { fetchDocumentsServer } from '@/requests/document/server' const Home = async () => { const { quizSetId, createdAt, type } = await fetchTodayQuizSetId() - const { documents } = await fetchDocuments() + const { documents } = await fetchDocumentsServer() const isEmpty = !documents || documents.length === 0 const todayQuizState = isEmpty ? 'EMPTY' : type === 'READY' ? 'ARRIVED' : 'NOT_ARRIVED' diff --git a/src/app/(routes)/profile/account/page.tsx b/src/app/(routes)/profile/account/page.tsx index af62adb1..58876d0b 100644 --- a/src/app/(routes)/profile/account/page.tsx +++ b/src/app/(routes)/profile/account/page.tsx @@ -4,12 +4,14 @@ import { cn } from '@/shared/lib/utils' import CategoryDrawer from '@/features/user/components/category-drawer' import SetNameDialog from '@/features/user/components/set-name-dialog' import Link from 'next/link' -import { fetchUserInfo } from '@/requests/user' +import { fetchUserInfo } from '@/requests/user/server' const AccountPage = async () => { const user = await fetchUserInfo() - const interestFields = user.interestField?.length ? user.interestField : ['관심 분야 없음'] + const interestCategories = user.interestCategories?.length + ? user.interestCategories + : ['관심 분야 없음'] return (
@@ -31,7 +33,9 @@ const AccountPage = async () => { diff --git a/src/app/(routes)/quiz/[id]/page.tsx b/src/app/(routes)/quiz/[id]/page.tsx index 4a2e7aa1..ecf4d19b 100644 --- a/src/app/(routes)/quiz/[id]/page.tsx +++ b/src/app/(routes)/quiz/[id]/page.tsx @@ -1,6 +1,6 @@ import IntroAndQuizView from '@/features/quiz/screen/intro-and-quiz-view' import { getQuizSetTypeEnum } from '@/features/quiz/utils' -import { fetchQuizSetById } from '@/requests/quiz' +import { fetchQuizSetById } from '@/requests/quiz/client' import { notFound } from 'next/navigation' interface Props { diff --git a/src/app/(routes)/quiz/random/page.tsx b/src/app/(routes)/quiz/random/page.tsx index 2e959841..6a2b9cef 100644 --- a/src/app/(routes)/quiz/random/page.tsx +++ b/src/app/(routes)/quiz/random/page.tsx @@ -1,5 +1,5 @@ import RandomQuizView from '@/features/quiz/screen/random-quiz-view' -import { getBookmarkedCollections } from '@/requests/collection' +import { getBookmarkedCollections } from '@/requests/collection/server' const RandomQuiz = async () => { const bookmarkedCollections = await getBookmarkedCollections() diff --git a/src/features/category/components/category-select-area/index.tsx b/src/features/category/components/category-select-area/index.tsx index 46f7f6a8..edfb0d32 100644 --- a/src/features/category/components/category-select-area/index.tsx +++ b/src/features/category/components/category-select-area/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { useUpdateCollectionFields } from '@/requests/user/hooks' +import { useUpdateCollectionCategories } from '@/requests/user/hooks' import Loading from '@/shared/components/custom/loading' import { Button } from '@/shared/components/ui/button' import { Form, FormField, FormItem } from '@/shared/components/ui/form' @@ -22,12 +22,12 @@ const MAX_CATEGORY = 2 const CategorySelectArea = () => { const router = useRouter() - const { mutate, isPending } = useUpdateCollectionFields() + const { mutate, isPending } = useUpdateCollectionCategories() const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { - categories: [] as interestedCategory[], + categories: [] as User.InterestedCategory[], }, }) const { watch } = form @@ -43,11 +43,11 @@ const CategorySelectArea = () => { > ) => { const target = e.target as HTMLElement - const category = target.id as interestedCategory + const category = target.id as User.InterestedCategory if (!category) return // 버튼이 아닌 다른 영역 클릭 시 무시 - const currentCategories = (field.value || []) as interestedCategory[] + const currentCategories = (field.value || []) as User.InterestedCategory[] if (currentCategories.includes(category)) { // 이미 선택된 경우 제거 const updatedCategories = currentCategories.filter((item) => item !== category) @@ -61,7 +61,7 @@ const CategorySelectArea = () => { const onSubmit = (values: FormValues) => { mutate( - { interestCollectionFields: values.categories as interestedCategory[] }, + { interestCollectionCategories: values.categories as NonNullable[] }, { onSuccess: () => { router.replace('/main') diff --git a/src/features/quiz/components/ox-choice/index.tsx b/src/features/quiz/components/ox-choice/index.tsx index 66915059..3c83a51d 100644 --- a/src/features/quiz/components/ox-choice/index.tsx +++ b/src/features/quiz/components/ox-choice/index.tsx @@ -3,15 +3,15 @@ import { SVGProps } from 'react' interface OXChoiceProps { - condition: Exclude - userAnswer?: OXQuizAnswer - onSelect: (userAnswer: OXQuizAnswer) => void + condition: Exclude + userAnswer?: Quiz.OXAnswer + onSelect: (userAnswer: Quiz.OXAnswer) => void } const OXChoice = ({ condition, userAnswer, onSelect }: OXChoiceProps) => { const disabled = condition === 'RIGHT' || condition === 'WRONG' - const getIconColors = (type: OXQuizAnswer) => { + const getIconColors = (type: Quiz.OXAnswer) => { const defaultColors = { bg: type === 'correct' ? '#4D7BF9' : '#FB8320', // 기본 배경색 fill: 'white', // 기본 채우기색 diff --git a/src/features/quiz/config/index.tsx b/src/features/quiz/config/index.tsx index 72013961..c8ba35fd 100644 --- a/src/features/quiz/config/index.tsx +++ b/src/features/quiz/config/index.tsx @@ -1,4 +1,4 @@ -export const quizzes: Quiz.ItemWithCategory[] = [ +export const quizzes: Quiz.ItemWithMetadata[] = [ { document: { id: 1, @@ -8,10 +8,6 @@ export const quizzes: Quiz.ItemWithCategory[] = [ id: 1, name: '전공 공부', }, - category: { - id: 1, - name: 'IT', - }, id: 1, quizType: 'MULTIPLE_CHOICE', question: '식물기반 단백질 시장에서 대기업의 참여가 늘어나는 이유는 무엇인가요?', @@ -33,10 +29,6 @@ export const quizzes: Quiz.ItemWithCategory[] = [ id: 1, name: '전공 공부', }, - category: { - id: 1, - name: 'IT', - }, id: 2, quizType: 'MIX_UP', question: '식물기반 단백질 시장에서 대기업의 참여가 늘어나는 이유는 무엇인가요?', @@ -53,10 +45,6 @@ export const quizzes: Quiz.ItemWithCategory[] = [ id: 1, name: '전공 공부', }, - category: { - id: 1, - name: 'IT', - }, id: 3, quizType: 'MULTIPLE_CHOICE', question: '식물기반 단백질 시장에서 대기업의 참여가 늘어나는 이유는 무엇인가요?', @@ -78,10 +66,6 @@ export const quizzes: Quiz.ItemWithCategory[] = [ id: 1, name: '전공 공부', }, - category: { - id: 1, - name: 'IT', - }, id: 5, quizType: 'MIX_UP', question: '식물기반 단백질 시장에서 대기업의 참여가 늘어나는 이유는 무엇인가요?', @@ -97,10 +81,6 @@ export const quizzes: Quiz.ItemWithCategory[] = [ id: 1, name: '전공 공부', }, - category: { - id: 1, - name: 'IT', - }, id: 4, quizType: 'MIX_UP', question: '식물기반 단백질 시장에서 대기업의 참여가 늘어나는 이유는 무엇인가요?', diff --git a/src/features/quiz/screen/quiz-view/components/quiz-option.tsx b/src/features/quiz/screen/quiz-view/components/quiz-option.tsx index f18ea0f5..d7b0dadf 100644 --- a/src/features/quiz/screen/quiz-view/components/quiz-option.tsx +++ b/src/features/quiz/screen/quiz-view/components/quiz-option.tsx @@ -72,8 +72,8 @@ const QuizOptions = ({ quiz, currentResult, onAnswer, className }: QuizOptionsPr > { + userAnswer={currentResult?.choseAnswer as Quiz.OXQuizAnswer} + onSelect={(userAnswer: Quiz.OXQuizAnswer) => { onAnswer({ id: quiz.id, isRight: quiz.answer === userAnswer ? true : false, diff --git a/src/features/quiz/screen/quiz-view/index.tsx b/src/features/quiz/screen/quiz-view/index.tsx index cb014a96..c4ffda8b 100644 --- a/src/features/quiz/screen/quiz-view/index.tsx +++ b/src/features/quiz/screen/quiz-view/index.tsx @@ -33,8 +33,8 @@ const QuizView = ({ quizzes, isFirst }: Props) => { const [exitDialogOpen, setExitDialogOpen] = useState(false) - const currentQuiz = quizzes[currentIndex] - const currentResult = quizResults[currentIndex] + const currentQuiz = quizzes[currentIndex] ?? ({} as Quiz.ItemWithMetadata) + const currentResult = quizResults[currentIndex] ?? null const onNext = () => { const hasNextQuiz = handleNext(currentIndex, quizzes.length) @@ -127,9 +127,7 @@ const QuizView = ({ quizzes, isFirst }: Props) => { /> )} - {isQuizSolved(quizResults[currentIndex]) && ( - - )} + {isQuizSolved(currentResult) && } diff --git a/src/features/quiz/utils/index.ts b/src/features/quiz/utils/index.ts index c014ff18..2d68ef03 100644 --- a/src/features/quiz/utils/index.ts +++ b/src/features/quiz/utils/index.ts @@ -22,7 +22,7 @@ export const isQuizSolved = (result: Quiz.Result | null): result is Quiz.Result } export const getAnswerText = (answer: string) => { - const OXType = ['correct', 'incorrect'] as OXQuizAnswer[] + const OXType = ['correct', 'incorrect'] as Quiz.OXAnswer[] const isOXType = OXType.find((oxType) => oxType === answer) if (isOXType) { @@ -34,7 +34,7 @@ export const getAnswerText = (answer: string) => { } export const getQuizSetTypeEnum = (quizSetType: 'today' | 'document' | 'collection' | 'create') => { - let enumQuizType: QuizSetType + let enumQuizType: Quiz.SetType switch (quizSetType) { case 'today': diff --git a/src/features/user/components/category-drawer/index.tsx b/src/features/user/components/category-drawer/index.tsx index 106f4737..4ba1f186 100644 --- a/src/features/user/components/category-drawer/index.tsx +++ b/src/features/user/components/category-drawer/index.tsx @@ -16,7 +16,7 @@ import { Button } from '@/shared/components/ui/button' import SetCategoryCompleteDialog from '@/features/category/components/set-category-complete-dialog' import { useState } from 'react' import * as z from 'zod' -import { useUpdateCollectionFields } from '@/requests/user/hooks' +import { useUpdateCollectionCategories } from '@/requests/user/hooks' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useRouter } from 'next/navigation' @@ -34,13 +34,13 @@ type FormValues = z.infer const CategoryDrawer = ({ interestedCategories, }: { - interestedCategories?: (interestedCategory | '관심 분야 없음')[] + interestedCategories?: (User.InterestedCategory | '관심 분야 없음')[] }) => { const router = useRouter() const [open, setOpen] = useState(false) const [completeOpen, setCompleteOpen] = useState(false) - const { mutate, isPending } = useUpdateCollectionFields() + const { mutate, isPending } = useUpdateCollectionCategories() const form = useForm({ resolver: zodResolver(formSchema), @@ -51,7 +51,7 @@ const CategoryDrawer = ({ const onSubmit = (values: FormValues) => { mutate( - { interestCollectionFields: values.categories as interestedCategory[] }, + { interestCollectionCategories: values.categories as NonNullable[] }, { onSuccess: () => { setCompleteOpen(true) @@ -79,7 +79,7 @@ const CategoryDrawer = ({ 관심분야 - {interestedCategories ? ( + {interestedCategories && interestedCategories[0] ? ( ) : ( diff --git a/src/providers/index.tsx b/src/providers/index.tsx index 75624403..d79ac044 100644 --- a/src/providers/index.tsx +++ b/src/providers/index.tsx @@ -5,6 +5,7 @@ import { QueryClientProvider } from '@tanstack/react-query' import { getQueryClient } from '@/shared/lib/tanstack-query/client' import { SessionProvider } from 'next-auth/react' import { AmplitudeContextProvider } from '@/shared/hooks/use-amplitude-context' +import { AuthProvider } from '@/shared/provider/auth-provider' export function Providers({ children }: { children: ReactNode }) { const [queryClient] = useState(() => getQueryClient()) @@ -12,7 +13,9 @@ export function Providers({ children }: { children: ReactNode }) { return ( - {children} + + {children} + ) diff --git a/src/requests/collection/index.ts b/src/requests/collection/client.ts similarity index 52% rename from src/requests/collection/index.ts rename to src/requests/collection/client.ts index d4472bf1..f0b3baaf 100644 --- a/src/requests/collection/index.ts +++ b/src/requests/collection/client.ts @@ -1,20 +1,12 @@ -'use server' +'use client' -import { auth } from '@/app/api/auth/[...nextauth]/auth' import { API_ENDPOINTS } from '@/shared/configs/endpoint' import { http } from '@/shared/lib/axios/http' export const getAllCollections = async () => { try { - const session = await auth() - const { data } = await http.get( - API_ENDPOINTS.COLLECTION.GET.ALL, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.COLLECTION.GET.ALL ) return data } catch (error) { @@ -24,15 +16,8 @@ export const getAllCollections = async () => { export const getMyCollections = async () => { try { - const session = await auth() - const { data } = await http.get( - API_ENDPOINTS.COLLECTION.GET.MY_COLLECTIONS, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.COLLECTION.GET.MY_COLLECTIONS ) return data } catch (error) { @@ -42,15 +27,8 @@ export const getMyCollections = async () => { export const getBookmarkedCollections = async () => { try { - const session = await auth() - const { data } = await http.get( - API_ENDPOINTS.COLLECTION.GET.BOOKMARKED, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.COLLECTION.GET.BOOKMARKED ) return data } catch (error) { @@ -60,16 +38,9 @@ export const getBookmarkedCollections = async () => { export const createCollection = async (payload: Collection.Request.CreateCollection) => { try { - const session = await auth() - const { data } = await http.post( API_ENDPOINTS.COLLECTION.POST.CREATE_COLLECTION, - payload, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + payload ) return data } catch (error) { @@ -79,17 +50,7 @@ export const createCollection = async (payload: Collection.Request.CreateCollect export const createBookmark = async (collectionId: number) => { try { - const session = await auth() - - await http.post( - API_ENDPOINTS.COLLECTION.POST.CREATE_BOOKMARK(collectionId), - {}, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) + await http.post(API_ENDPOINTS.COLLECTION.POST.CREATE_BOOKMARK(collectionId)) } catch (error) { throw error } @@ -97,13 +58,7 @@ export const createBookmark = async (collectionId: number) => { export const deleteBookmark = async (collectionId: number) => { try { - const session = await auth() - - await http.delete(API_ENDPOINTS.COLLECTION.DELETE.BOOKMARK(collectionId), { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) + await http.delete(API_ENDPOINTS.COLLECTION.DELETE.BOOKMARK(collectionId)) } catch (error) { throw error } @@ -111,15 +66,8 @@ export const deleteBookmark = async (collectionId: number) => { export const getCollectionInfo = async ({ collectionId }: { collectionId: number }) => { try { - const session = await auth() - const { data } = await http.get( - API_ENDPOINTS.COLLECTION.GET.INFO(collectionId), - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.COLLECTION.GET.INFO(collectionId) ) return data } catch (error) { diff --git a/src/requests/collection/hooks.ts b/src/requests/collection/hooks.ts index ae8aed35..77f22e47 100644 --- a/src/requests/collection/hooks.ts +++ b/src/requests/collection/hooks.ts @@ -8,7 +8,7 @@ import { createBookmark, getMyCollections, getCollectionInfo, -} from '.' +} from './client' export const useCollections = () => { return useQuery({ diff --git a/src/requests/collection/server.ts b/src/requests/collection/server.ts new file mode 100644 index 00000000..fdd1a545 --- /dev/null +++ b/src/requests/collection/server.ts @@ -0,0 +1,15 @@ +'use server' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { httpServer } from '@/shared/lib/axios/http-server' + +export const getBookmarkedCollections = async () => { + try { + const { data } = await httpServer.get( + API_ENDPOINTS.COLLECTION.GET.BOOKMARKED + ) + return data + } catch (error) { + throw error + } +} diff --git a/src/requests/directory/index.tsx b/src/requests/directory/client.tsx similarity index 61% rename from src/requests/directory/index.tsx rename to src/requests/directory/client.tsx index 7b27a115..9034393e 100644 --- a/src/requests/directory/index.tsx +++ b/src/requests/directory/client.tsx @@ -1,6 +1,5 @@ -'use server' +'use client' -import { auth } from '@/app/api/auth/[...nextauth]/auth' import { API_ENDPOINTS } from '@/shared/configs/endpoint' import { http } from '@/shared/lib/axios/http' @@ -8,16 +7,9 @@ import { http } from '@/shared/lib/axios/http' * 모든 디렉토리 가져오기 */ export const fetchDirectories = async () => { - const session = await auth() - try { const { data } = await http.get( - API_ENDPOINTS.DIRECTORY.GET.ALL, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.DIRECTORY.GET.ALL ) return data } catch (error: unknown) { @@ -30,16 +22,9 @@ export const fetchDirectories = async () => { * directory_id로 디렉토리 가져오기 */ export const fetchDirectory = async (directoryId: Directory.Item['id']) => { - const session = await auth() - try { const { data } = await http.get( - API_ENDPOINTS.DIRECTORY.GET.BY_ID(directoryId), - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.DIRECTORY.GET.BY_ID(directoryId) ) return data } catch (error: unknown) { @@ -52,17 +37,10 @@ export const fetchDirectory = async (directoryId: Directory.Item['id']) => { * 디렉토리 생성 */ export const createDirectory = async ({ name, emoji }: Directory.Request.CreateDirectory) => { - const session = await auth() - try { const { data } = await http.post( API_ENDPOINTS.DIRECTORY.POST.CREATE, - { name, emoji }, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + { name, emoji } ) return data } catch (error: unknown) { @@ -81,18 +59,8 @@ export const updateDirectoryInfo = async ({ }: { directoryId: Directory.Item['id'] } & Directory.Request.UpdateDirectoryInfo) => { - const session = await auth() - try { - await http.patch( - API_ENDPOINTS.DIRECTORY.PATCH.UPDATE_INFO(directoryId), - { name, emoji }, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) + await http.patch(API_ENDPOINTS.DIRECTORY.PATCH.UPDATE_INFO(directoryId), { name, emoji }) } catch (error: unknown) { console.error(error) throw error @@ -103,14 +71,8 @@ export const updateDirectoryInfo = async ({ * 디렉토리 삭제 */ export const deleteDirectory = async (directoryId: Directory.Item['id']) => { - const session = await auth() - try { - await http.delete(API_ENDPOINTS.DIRECTORY.DELETE.BY_ID(directoryId), { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) + await http.delete(API_ENDPOINTS.DIRECTORY.DELETE.BY_ID(directoryId)) } catch (error: unknown) { console.error(error) throw error diff --git a/src/requests/directory/hooks.ts b/src/requests/directory/hooks.ts index e4354bb4..cbede863 100644 --- a/src/requests/directory/hooks.ts +++ b/src/requests/directory/hooks.ts @@ -7,7 +7,7 @@ import { fetchDirectories, fetchDirectory, updateDirectoryInfo, -} from '.' +} from './client' import { getQueryClient } from '@/shared/lib/tanstack-query/client' import { queries } from '@/shared/lib/tanstack-query/query-keys' diff --git a/src/requests/document/index.tsx b/src/requests/document/client.tsx similarity index 68% rename from src/requests/document/index.tsx rename to src/requests/document/client.tsx index 96aa961c..376caeac 100644 --- a/src/requests/document/index.tsx +++ b/src/requests/document/client.tsx @@ -1,10 +1,9 @@ -'use server' +'use client' -import { auth } from '@/app/api/auth/[...nextauth]/auth' import { API_ENDPOINTS } from '@/shared/configs/endpoint' import { http } from '@/shared/lib/axios/http' -interface GetDocumentsParams { +type GetDocumentsParams = { directoryId?: string sortOption?: Document.Sort } @@ -21,12 +20,7 @@ export const fetchDocuments = async (params?: GetDocumentsParams) => { } try { - const session = await auth() - const { data } = await http.get<{ documents: Document.List }>(API_ENDPOINTS.DOCUMENT.GET.ALL, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, params: DocsParams, }) return data @@ -36,19 +30,13 @@ export const fetchDocuments = async (params?: GetDocumentsParams) => { } } +// document id page 컴포넌트에서도 사용됨 수정 필요 export const fetchDocumentDetail = async (documentId?: number) => { if (documentId === null || documentId === undefined) return try { - const session = await auth() - const { data } = await http.get( - API_ENDPOINTS.DOCUMENT.GET.BY_ID(documentId), - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } + API_ENDPOINTS.DOCUMENT.GET.BY_ID(documentId) ) return data } catch (error: unknown) { @@ -59,13 +47,7 @@ export const fetchDocumentDetail = async (documentId?: number) => { export const moveDocument = async (requestBody: Document.Request.MoveDocument) => { try { - const session = await auth() - - const response = await http.patch(API_ENDPOINTS.DOCUMENT.PATCH.MOVE, requestBody, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) + const response = await http.patch(API_ENDPOINTS.DOCUMENT.PATCH.MOVE, requestBody) // eslint-disable-next-line no-console console.log(response) // 디버깅용 @@ -75,16 +57,11 @@ export const moveDocument = async (requestBody: Document.Request.MoveDocument) = } } +/** delete 메서드로 body를 받는 api입니다 (여러 문서 id를 리스트로 보냄) */ export const deleteDocument = async (requestBody: Document.Request.DeleteDocuments) => { try { - const session = await auth() - - // delete 메서드로 body를 받는 api입니다 (여러 문서 id를 리스트로 보냄) const response = await http.delete(API_ENDPOINTS.DOCUMENT.DELETE.DOCUMENTS, { data: requestBody, - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, }) // eslint-disable-next-line no-console diff --git a/src/requests/document/hooks.ts b/src/requests/document/hooks.ts index 3e5e254e..7c0a6a2b 100644 --- a/src/requests/document/hooks.ts +++ b/src/requests/document/hooks.ts @@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query' import { useSession } from 'next-auth/react' import { createDocument } from './create-document' -import { deleteDocument, fetchDocumentDetail, moveDocument } from '.' +import { deleteDocument, fetchDocumentDetail, moveDocument } from './client' import { queries } from '@/shared/lib/tanstack-query/query-keys' import { getQueryClient } from '@/shared/lib/tanstack-query/client' import { updateDocument } from './update-document' diff --git a/src/requests/document/server.ts b/src/requests/document/server.ts new file mode 100644 index 00000000..cb12aa07 --- /dev/null +++ b/src/requests/document/server.ts @@ -0,0 +1,34 @@ +'use server' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { httpServer } from '@/shared/lib/axios/http-server' + +type GetDocumentsParams = { + directoryId?: string + sortOption?: Document.Sort +} + +export const fetchDocumentsServer = async (params?: GetDocumentsParams) => { + const defaultSortOption = 'CREATED_AT' + + const DocsParams = + params?.directoryId == null + ? { 'sort-option': params?.sortOption ?? defaultSortOption } + : { + 'directory-id': params.directoryId, + 'sort-option': params.sortOption ?? defaultSortOption, + } + + try { + const { data } = await httpServer.get<{ documents: Document.List }>( + API_ENDPOINTS.DOCUMENT.GET.ALL, + { + params: DocsParams, + } + ) + return data + } catch (error: unknown) { + console.error(error) + throw error + } +} diff --git a/src/requests/fcm/index.tsx b/src/requests/fcm/index.tsx index dee0fb9d..bfef0bbd 100644 --- a/src/requests/fcm/index.tsx +++ b/src/requests/fcm/index.tsx @@ -2,13 +2,13 @@ import { auth } from '@/app/api/auth/[...nextauth]/auth' import { API_ENDPOINTS } from '@/shared/configs/endpoint' -import { http } from '@/shared/lib/axios/http' +import { httpServer } from '@/shared/lib/axios/http-server' export const postFcmToken = async (requestBody: { fcmToken: string }) => { try { const session = await auth() - const response = await http.post(API_ENDPOINTS.FCM.POST.TOKEN, requestBody, { + const response = await httpServer.post(API_ENDPOINTS.FCM.POST.TOKEN, requestBody, { headers: { Authorization: `Bearer ${session?.user.accessToken}`, }, diff --git a/src/requests/index.ts b/src/requests/index.ts index 239ec8bb..88e0c799 100644 --- a/src/requests/index.ts +++ b/src/requests/index.ts @@ -1,13 +1,17 @@ -import * as directory from './directory' -import * as document from './document' -import * as quiz from './quiz' -import * as collection from './collection' -import * as user from './user' +import * as directory from './directory/client' +import * as document from './document/client' +import * as documentServer from './document/server' +import * as quiz from './quiz/client' +import * as quizServer from './quiz/server' +import * as collection from './collection/client' +import * as user from './user/client' export const REQUEST = { directory, document, + documentServer, quiz, + quizServer, collection, user, } diff --git a/src/requests/quiz/client.tsx b/src/requests/quiz/client.tsx new file mode 100644 index 00000000..9d31b0ff --- /dev/null +++ b/src/requests/quiz/client.tsx @@ -0,0 +1,120 @@ +'use client' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { http } from '@/shared/lib/axios/http' + +export const fetchDirectoryQuizzes = async ({ directoryId }: { directoryId: number }) => { + try { + const { data } = await http.get( + API_ENDPOINTS.QUIZ.GET.BY_DIRECTORY(directoryId) + ) + return data + } catch (error: unknown) { + throw error + } +} + +// query-keys.ts에서 사용 중 +export const fetchDocumentQuizzes = async ({ + documentId, + quizType, +}: { + documentId: number + quizType?: Quiz.Type +}) => { + const params = quizType ? { 'quiz-type': quizType } : null + + try { + const { data } = await http.get( + API_ENDPOINTS.QUIZ.GET.BY_DOCUMENT(documentId), + { + params, + } + ) + return data + } catch (error: unknown) { + throw error + } +} + +// query-keys.ts에서 사용 중 +export const fetchWrongAnswerQuizzes = async () => { + try { + const { data } = await http.get( + API_ENDPOINTS.QUIZ.GET.WRONG_ANSWER + ) + return data + } catch (error: unknown) { + throw error + } +} + +// query-keys.ts에서 사용 중 +export const fetchQuizSetRecord = async ({ + quizSetId, + quizSetType, +}: { + quizSetId: string + quizSetType: Quiz.SetType +}) => { + try { + const { data } = await http.get( + API_ENDPOINTS.QUIZ.GET.RECORD(quizSetId, quizSetType) + ) + return data + } catch (error: unknown) { + throw error + } +} + +export const createQuizSetForCheck = async ({ documentId }: { documentId: number }) => { + try { + const { data } = await http.post( + API_ENDPOINTS.QUIZ.POST.CHECK_QUIZ_SET(documentId), + null + ) + return data + } catch (error: unknown) { + throw error + } +} + +export const createReplayDocumentQuizSet = async ({ + documentId, + requestBody, +}: { + documentId: number + requestBody: Quiz.Request.CreateReplayQuizSet +}) => { + try { + const { data } = await http.post( + API_ENDPOINTS.QUIZ.POST.REPLAY(documentId), + requestBody + ) + return data + } catch (error: unknown) { + throw error + } +} + +export const updateQuizResult = async (requestBody: Quiz.Request.UpdateQuizResult) => { + try { + const { data } = await http.patch( + API_ENDPOINTS.QUIZ.PATCH.UPDATE_RESULT, + requestBody + ) + return data + } catch (error: unknown) { + throw error + } +} + +export const updateWrongQuizResult = async (requestBody: Quiz.Request.UpdateWrongQuizResult) => { + try { + const response = await http.patch(API_ENDPOINTS.QUIZ.PATCH.UPDATE_WRONG_RESULT, requestBody) + // eslint-disable-next-line no-console + console.log(response) // 디버깅용 + } catch (error: unknown) { + throw error + } +} diff --git a/src/requests/quiz/hooks.ts b/src/requests/quiz/hooks.ts index fd343e8c..659bd4ce 100644 --- a/src/requests/quiz/hooks.ts +++ b/src/requests/quiz/hooks.ts @@ -5,19 +5,19 @@ import { createQuizSetForCheck, createReplayDocumentQuizSet, fetchDirectoryQuizzes, - fetchTodayQuizSetId, updateQuizResult, updateWrongQuizResult, -} from '.' +} from './client' import { getQueryClient } from '@/shared/lib/tanstack-query/client' import { queries } from '@/shared/lib/tanstack-query/query-keys' -export const useTodayQuizSetId = () => { - return useQuery({ - queryKey: ['todayQuizSetId'], - queryFn: async () => fetchTodayQuizSetId(), - }) -} +// 사용되는 곳 없음 (검토 후 삭제) +// export const useTodayQuizSetId = () => { +// return useQuery({ +// queryKey: ['todayQuizSetId'], +// queryFn: async () => fetchTodayQuizSetId(), +// }) +// } export const useDirectoryQuizzes = (directoryId: number | null) => { return useQuery({ diff --git a/src/requests/quiz/index.tsx b/src/requests/quiz/index.tsx deleted file mode 100644 index a3dadecb..00000000 --- a/src/requests/quiz/index.tsx +++ /dev/null @@ -1,220 +0,0 @@ -'use server' - -import { auth } from '@/app/api/auth/[...nextauth]/auth' -import { API_ENDPOINTS } from '@/shared/configs/endpoint' -import { http } from '@/shared/lib/axios/http' - -export const fetchTodayQuizSetId = async () => { - try { - const session = await auth() - - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.TODAY_SET, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const fetchQuizSetById = async ({ - quizSetId, - collectionId, - quizSetType, -}: { - quizSetId: string - collectionId?: number - quizSetType: QuizSetType -}) => { - const session = await auth() - - const params = collectionId - ? { 'collection-id': collectionId, 'quiz-set-type': quizSetType } - : { 'quiz-set-type': quizSetType } - - try { - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.BY_SET_ID(quizSetId), - { - params, - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const fetchDirectoryQuizzes = async ({ directoryId }: { directoryId: number }) => { - const session = await auth() - - try { - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.BY_DIRECTORY(directoryId), - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const fetchDocumentQuizzes = async ({ - documentId, - quizType, -}: { - documentId: number - quizType?: Quiz.Type -}) => { - const session = await auth() - - const params = quizType ? { 'quiz-type': quizType } : null - - try { - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.BY_DOCUMENT(documentId), - { - params, - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const fetchWrongAnswerQuizzes = async () => { - const session = await auth() - - try { - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.WRONG_ANSWER, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const fetchQuizSetRecord = async ({ - quizSetId, - quizSetType, -}: { - quizSetId: string - quizSetType: QuizSetType -}) => { - const session = await auth() - - try { - const { data } = await http.get( - API_ENDPOINTS.QUIZ.GET.RECORD(quizSetId, quizSetType), - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const createQuizSetForCheck = async ({ documentId }: { documentId: number }) => { - const session = await auth() - - try { - const { data } = await http.post( - API_ENDPOINTS.QUIZ.POST.CHECK_QUIZ_SET(documentId), - null, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const createReplayDocumentQuizSet = async ({ - documentId, - requestBody, -}: { - documentId: number - requestBody: Quiz.Request.CreateReplayQuizSet -}) => { - const session = await auth() - - try { - const { data } = await http.post( - API_ENDPOINTS.QUIZ.POST.REPLAY(documentId), - requestBody, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const updateQuizResult = async (requestBody: Quiz.Request.UpdateQuizResult) => { - const session = await auth() - - try { - const { data } = await http.patch( - API_ENDPOINTS.QUIZ.PATCH.UPDATE_RESULT, - requestBody, - { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - } - ) - return data - } catch (error: unknown) { - throw error - } -} - -export const updateWrongQuizResult = async (requestBody: Quiz.Request.UpdateWrongQuizResult) => { - const session = await auth() - - try { - const response = await http.patch(API_ENDPOINTS.QUIZ.PATCH.UPDATE_WRONG_RESULT, requestBody, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - // eslint-disable-next-line no-console - console.log(response) // 디버깅용 - } catch (error: unknown) { - throw error - } -} diff --git a/src/requests/quiz/server.tsx b/src/requests/quiz/server.tsx new file mode 100644 index 00000000..66d94071 --- /dev/null +++ b/src/requests/quiz/server.tsx @@ -0,0 +1,41 @@ +'use server' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { httpServer } from '@/shared/lib/axios/http-server' + +export const fetchTodayQuizSetId = async () => { + try { + const { data } = await httpServer.get( + API_ENDPOINTS.QUIZ.GET.TODAY_SET + ) + return data + } catch (error: unknown) { + throw error + } +} + +export const fetchQuizSetById = async ({ + quizSetId, + collectionId, + quizSetType, +}: { + quizSetId: string + collectionId?: number + quizSetType: Quiz.SetType +}) => { + const params = collectionId + ? { 'collection-id': collectionId, 'quiz-set-type': quizSetType } + : { 'quiz-set-type': quizSetType } + + try { + const { data } = await httpServer.get( + API_ENDPOINTS.QUIZ.GET.BY_SET_ID(quizSetId), + { + params, + } + ) + return data + } catch (error: unknown) { + throw error + } +} diff --git a/src/requests/user/client.ts b/src/requests/user/client.ts new file mode 100644 index 00000000..bb130d19 --- /dev/null +++ b/src/requests/user/client.ts @@ -0,0 +1,38 @@ +'use client' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { http } from '@/shared/lib/axios/http' + +export const updateTodayQuizCount = async (payload: User.Request.UpdateTodayQuizCount) => { + try { + await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_QUIZ_COUNT, payload) + } catch (error: unknown) { + throw error + } +} + +export const updateQuizNotification = async (payload: User.Request.UpdateQuizNotification) => { + try { + await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_NOTIFICATION, payload) + } catch (error: unknown) { + throw error + } +} + +export const updateUserName = async (payload: User.Request.UpdateName) => { + try { + await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_NAME, payload) + } catch (error: unknown) { + throw error + } +} + +export const updateCollectionCategories = async ( + payload: User.Request.UpdateCollectionCategories +) => { + try { + await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_COLLECTION_CATEGORIES, payload) + } catch (error: unknown) { + throw error + } +} diff --git a/src/requests/user/hooks.ts b/src/requests/user/hooks.ts index 56926bed..edc2d1a7 100644 --- a/src/requests/user/hooks.ts +++ b/src/requests/user/hooks.ts @@ -1,20 +1,20 @@ 'use client' -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { useMutation, useQueryClient } from '@tanstack/react-query' import { - fetchUserInfo, updateTodayQuizCount, updateQuizNotification, updateUserName, - updateCollectionFields, -} from '.' + updateCollectionCategories, +} from './client' -export const useUserInfo = () => { - return useQuery({ - queryKey: ['userInfo'], - queryFn: async () => fetchUserInfo(), - }) -} +// 아직 사용처 없음 - 검토 필요 +// export const useUserInfo = () => { +// return useQuery({ +// queryKey: ['userInfo'], +// queryFn: async () => fetchUserInfo(), +// }) +// } export const useUpdateTodayQuizCount = () => { const queryClient = useQueryClient() @@ -44,12 +44,12 @@ export const useUpdateUserName = () => { }) } -export const useUpdateCollectionFields = () => { +export const useUpdateCollectionCategories = () => { const queryClient = useQueryClient() return useMutation({ - mutationFn: async (payload: User.Request.UpdateCollectionFields) => - updateCollectionFields(payload), + mutationFn: async (payload: User.Request.UpdateCollectionCategories) => + updateCollectionCategories(payload), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['userInfo'] }), }) } diff --git a/src/requests/user/index.ts b/src/requests/user/index.ts deleted file mode 100644 index 9b98ae80..00000000 --- a/src/requests/user/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -'use server' - -import { auth } from '@/app/api/auth/[...nextauth]/auth' -import { API_ENDPOINTS } from '@/shared/configs/endpoint' -import { http } from '@/shared/lib/axios/http' - -export const fetchUserInfo = async () => { - const session = await auth() - - try { - const { data } = await http.get(API_ENDPOINTS.USER.GET.INFO, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - return data - } catch (error: unknown) { - throw error - } -} - -export const updateTodayQuizCount = async (payload: User.Request.UpdateTodayQuizCount) => { - const session = await auth() - - try { - await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_QUIZ_COUNT, payload, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - } catch (error: unknown) { - throw error - } -} - -export const updateQuizNotification = async (payload: User.Request.UpdateQuizNotification) => { - const session = await auth() - - try { - await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_NOTIFICATION, payload, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - } catch (error: unknown) { - throw error - } -} - -export const updateUserName = async (payload: User.Request.UpdateName) => { - const session = await auth() - - try { - await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_NAME, payload, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - } catch (error: unknown) { - throw error - } -} - -export const updateCollectionFields = async (payload: User.Request.UpdateCollectionFields) => { - const session = await auth() - - try { - await http.patch(API_ENDPOINTS.USER.PATCH.UPDATE_COLLECTION_FIELDS, payload, { - headers: { - Authorization: `Bearer ${session?.user.accessToken}`, - }, - }) - } catch (error: unknown) { - throw error - } -} diff --git a/src/requests/user/server.ts b/src/requests/user/server.ts new file mode 100644 index 00000000..ff29174d --- /dev/null +++ b/src/requests/user/server.ts @@ -0,0 +1,13 @@ +'use server' + +import { API_ENDPOINTS } from '@/shared/configs/endpoint' +import { httpServer } from '@/shared/lib/axios/http-server' + +export const fetchUserInfo = async () => { + try { + const { data } = await httpServer.get(API_ENDPOINTS.USER.GET.INFO) + return data + } catch (error: unknown) { + throw error + } +} diff --git a/src/shared/configs/endpoint.ts b/src/shared/configs/endpoint.ts index 0d9a397c..9f4b2913 100644 --- a/src/shared/configs/endpoint.ts +++ b/src/shared/configs/endpoint.ts @@ -133,7 +133,7 @@ export const API_ENDPOINTS = { /** PATCH /members/update-name - Update member name */ UPDATE_NAME: '/members/update-name', /** PATCH /members/update-collection-fields - 관심분야 태그 설정 */ - UPDATE_COLLECTION_FIELDS: '/members/update-collection-fields', + UPDATE_COLLECTION_CATEGORIES: '/members/update-collection-categories', }, }, @@ -152,7 +152,7 @@ export const API_ENDPOINTS = { /** GET /quizzes - 생성된 모든 퀴즈 가져오기(전체 문서) */ ALL: '/quizzes', /** GET /quizzes/{quiz_set_id}/{quiz_set_type}/quiz-record - 퀴즈 세트에 대한 상세 기록 */ - RECORD: (quizSetId: string, quizSetType: QuizSetType) => + RECORD: (quizSetId: string, quizSetType: Quiz.SetType) => `/quizzes/${quizSetId}/${quizSetType}/quiz-record`, /** GET /quizzes/quiz-records - 전체 퀴즈 기록 */ ALL_RECORDS: '/quizzes/quiz-records', diff --git a/src/shared/lib/axios/http-server.ts b/src/shared/lib/axios/http-server.ts new file mode 100644 index 00000000..34026b40 --- /dev/null +++ b/src/shared/lib/axios/http-server.ts @@ -0,0 +1,41 @@ +import { ServerEnv } from '@/actions/api-client/server-env' +import { auth } from '@/app/api/auth/[...nextauth]/auth' +import axios, { isAxiosError } from 'axios' + +export const httpServer = axios.create({ + baseURL: ServerEnv.apiUrl(), + headers: { + 'Content-Type': 'application/json', + }, +}) + +httpServer.interceptors.request.use( + async (config) => { + if (typeof window !== 'undefined') { + throw new Error('httpServer should only be used in server-side code.') + } + + const session = await auth() + const token = session?.user?.accessToken + + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + return Promise.reject(error) + } +) + +httpServer.interceptors.response.use( + (response) => { + return response + }, + (error) => { + if (isAxiosError(error)) { + console.error(error.response?.data) + } + return Promise.reject(error) + } +) diff --git a/src/shared/lib/axios/http.ts b/src/shared/lib/axios/http.ts index 46b0226a..fd692cb7 100644 --- a/src/shared/lib/axios/http.ts +++ b/src/shared/lib/axios/http.ts @@ -1,4 +1,7 @@ +'use client' + import { ServerEnv } from '@/actions/api-client/server-env' +import { useAuthStore } from '@/store/auth' import axios, { isAxiosError } from 'axios' export const http = axios.create({ @@ -8,6 +11,19 @@ export const http = axios.create({ }, }) +http.interceptors.request.use( + (config) => { + const accessToken = useAuthStore.getState().accessToken + if (accessToken) { + config.headers.Authorization = `Bearer ${accessToken}` + } + return config + }, + (error) => { + return Promise.reject(error) + } +) + http.interceptors.response.use( (response) => { return response diff --git a/src/shared/lib/tanstack-query/query-keys.ts b/src/shared/lib/tanstack-query/query-keys.ts index c991a56f..b4164e05 100644 --- a/src/shared/lib/tanstack-query/query-keys.ts +++ b/src/shared/lib/tanstack-query/query-keys.ts @@ -32,7 +32,7 @@ export const queries = createQueryKeyStore({ queryFn: () => REQUEST.quiz.fetchDocumentQuizzes(params), enabled: !!params.documentId, }), - setRecord: (params: { quizSetId: string; quizSetType: QuizSetType }) => ({ + setRecord: (params: { quizSetId: string; quizSetType: Quiz.SetType }) => ({ queryKey: [params], queryFn: () => REQUEST.quiz.fetchQuizSetRecord(params), enabled: !!params.quizSetId, @@ -46,7 +46,7 @@ export const queries = createQueryKeyStore({ collection: { info: (collectionId: number) => ({ queryKey: [collectionId], - queryFn: () => REQUEST.collection.fetchCollectionInfo({ collectionId }), + queryFn: () => REQUEST.collection.getCollectionInfo({ collectionId }), enabled: !!collectionId, }), }, diff --git a/src/shared/provider/auth-provider.tsx b/src/shared/provider/auth-provider.tsx new file mode 100644 index 00000000..9601bdc2 --- /dev/null +++ b/src/shared/provider/auth-provider.tsx @@ -0,0 +1,21 @@ +'use client' + +import { useEffect } from 'react' +import { useSession } from 'next-auth/react' +import { useAuthStore } from '@/store/auth' + +/** 사용할 때 useToken */ +export const AuthProvider = ({ children }: { children: React.ReactNode }) => { + const { data: session } = useSession() + const setAccessToken = useAuthStore.getState().setAccessToken + + useEffect(() => { + if (session?.user?.accessToken) { + setAccessToken(session.user.accessToken) + } else { + setAccessToken(null) + } + }, [session, setAccessToken]) + + return <>{children} +} diff --git a/src/store/auth.ts b/src/store/auth.ts new file mode 100644 index 00000000..8275902f --- /dev/null +++ b/src/store/auth.ts @@ -0,0 +1,22 @@ +import { create } from 'zustand' +import { persist } from 'zustand/middleware' + +// 추가로 auth관련 데이터를 저장할 수 있음 + +interface AuthStore { + accessToken: string | null + setAccessToken: (token: string | null) => void +} + +export const useAuthStore = create( + persist( + (set) => ({ + accessToken: null, + setAccessToken: (token) => set({ accessToken: token }), + }), + { + name: 'auth-storage', // localStorage용 + partialize: (state) => ({ ...state, accessToken: state.accessToken }), + } + ) +) diff --git a/src/types/quiz.d.ts b/src/types/quiz.d.ts index 07d2dfc4..9fb3d924 100644 --- a/src/types/quiz.d.ts +++ b/src/types/quiz.d.ts @@ -1,7 +1,7 @@ import { DeepRequired } from 'react-hook-form' -import { paths } from './schema' +import { components, paths } from './schema' -type QuizSetType = 'DOCUMENT_QUIZ_SET' | 'TODAY_QUIZ_SET' | 'COLLECTION_QUIZ_SET' | 'FIRST_QUIZ_SET' +type ReplayQuizType = 'RANDOM' | 'MIX_UP' | 'MULTIPLE_CHOICE' type QuizItem = { id: number @@ -13,50 +13,18 @@ type QuizItem = { answer?: 'correct' | 'incorrect' } -type QuizType = 'MIX_UP' | 'MULTIPLE_CHOICE' -type ReplayQuizType = 'RANDOM' | 'MIX_UP' | 'MULTIPLE_CHOICE' - -type QuizCondition = 'IDLE' | 'DISABLED' | 'RIGHT' | 'WRONG' - -type Category = { - id: number - name: string -} - -type DocumentInQuiz = Pick -type DirectoryInQuiz = Pick - -type ConsecutiveDays = { - currentConsecutiveDays: number - maxConsecutiveDays: number -} +type DocumentInQuiz = DeepRequired +type DirectoryInQuiz = DeepRequired type QuizWithMetadata = { document: DocumentInQuiz directory: DirectoryInQuiz } & QuizItem -type QuizWithCategory = { - category: Category -} & QuizWithMetadata - -type QuizRecord = { - question: string - answer: string - explanation: string - options: string[] - choseAnswer: string - documentName: string - directoryName: string -} - -type QuizSetRecord = { - quizSetId: string - quizSetType: QuizSetType - name: string - quizCount: number - score: number - solvedDate: string +// 처리 필요 +type ConsecutiveDays = { + currentConsecutiveDays: number + maxConsecutiveDays: number } declare global { @@ -64,10 +32,20 @@ declare global { type Item = QuizItem type List = QuizItem[] type ItemWithMetadata = QuizWithMetadata - type ItemWithCategory = QuizWithCategory - type Type = QuizType - type Record = QuizRecord - type Result = UpdateQuizResultPayload['quizzes'][number] + + type Type = Exclude, undefined> + type OXAnswer = 'correct' | 'incorrect' + + type SetRecord = DeepRequired + type Record = DeepRequired + type Result = Quiz.Request.UpdateQuizResult['quizzes'][number] + + type SetType = Exclude< + DeepRequired, + undefined + > + + type Condition = 'IDLE' | 'DISABLED' | 'RIGHT' | 'WRONG' declare namespace Request { /** PATCH /api/v2/quiz/result diff --git a/src/types/user.d.ts b/src/types/user.d.ts index c2ff3adc..2789bc3d 100644 --- a/src/types/user.d.ts +++ b/src/types/user.d.ts @@ -1,36 +1,12 @@ import { DeepRequired } from 'react-hook-form' -import { paths } from './schema' - -type interestedCategory = - | 'IT' - | 'LAW' - | 'ART' - | 'BUSINESS_ECONOMY' - | 'HISTORY_PHILOSOPHY' - | 'LANGUAGE' - | 'SOCIETY_POLITICS' - | 'MEDICINE_PHARMACY' - | 'SCIENCE_ENGINEERING' - | 'OTHER' - -interface UserInfo { - id: number - name: string - email: string - socialPlatform: 'KAKAO' | 'GOOGLE' - role: 'ROLE_USER' | 'ROLE_ADMIN' - interestField: interestedCategory[] - documentUsage: { - possessDocumentCount: number - maxPossessDocumentCount: number - } - star: number - quizNotificationEnabled: boolean -} +import { components, paths } from './schema' declare global { declare namespace User { - type Info = UserInfo + type Info = DeepRequired + type InterestedCategory = DeepRequired< + components['schemas']['GetCollectionCategoriesDto']['collectionCategory'] + > declare namespace Request { /** PATCH /api/v2/members/update-today-quiz-count @@ -57,7 +33,7 @@ declare global { /** PATCH /api/v2/members/update-collection-fields * 관심분야 태그 설정 */ - type UpdateCollectionFields = DeepRequired< + type UpdateCollectionCategories = DeepRequired< paths['/api/v2/members/update-collection-categories']['patch']['requestBody']['content']['application/json;charset=UTF-8'] > }