diff --git a/package-lock.json b/package-lock.json index d4903c29..31b71d05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23207,7 +23207,8 @@ }, "node_modules/react-hot-toast": { "version": "2.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", "dependencies": { "goober": "^2.1.10" }, @@ -28384,6 +28385,7 @@ "react": "^18", "react-dom": "^18", "react-hook-form": "^7.52.1", + "react-hot-toast": "^2.4.1", "react-secure-storage": "^1.3.2", "react-slick": "^0.30.2", "slick-carousel": "^1.8.1" diff --git a/packages/client/app/(withLayout)/question/[id]/page.tsx b/packages/client/app/(withLayout)/question/[id]/page.tsx index 0b264351..d679068f 100644 --- a/packages/client/app/(withLayout)/question/[id]/page.tsx +++ b/packages/client/app/(withLayout)/question/[id]/page.tsx @@ -1,5 +1,20 @@ +import { fetchMemberInformation, ssrConfig } from '@shared/api' +import { QueryClient } from '@tanstack/react-query' +import { cookies } from 'next/headers' import { ClientQuestionDetailPage } from 'src/clientPages/questionDetail' -export default function QuestionDetailPage() { +export default async function QuestionDetailPage(d) { + const queryClient = new QueryClient() + const config = ssrConfig() + + try { + const user = await queryClient.fetchQuery({ + queryKey: [`/members/information`], + queryFn: () => fetchMemberInformation(config), + }) + } catch (e) { + console.log(e) + } + // TODO: give userData by props to ClientQuestionDetailPage return } diff --git a/packages/client/app/layout.tsx b/packages/client/app/layout.tsx index 4a66563b..cd9c9d68 100644 --- a/packages/client/app/layout.tsx +++ b/packages/client/app/layout.tsx @@ -4,6 +4,7 @@ import './globals.css' import localFont from 'next/font/local' import './design-system-globals.css' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { ToastPrepare } from '@gds/component' import ReactQueryClientProviders from '../src/apps/provider/reactQueryProviders' export const metadata: Metadata = { @@ -53,6 +54,7 @@ export default function RootLayout({
+ {children} {process.env.NODE_ENV !== 'production' && ( diff --git a/packages/client/next.config.mjs b/packages/client/next.config.mjs index 22efc9ba..37eaec12 100644 --- a/packages/client/next.config.mjs +++ b/packages/client/next.config.mjs @@ -7,10 +7,12 @@ const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const withVanillaExtract = createVanillaExtractPlugin() +const API_BASE_URL = process.env.API_BASE_URL /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + trailingSlash: true, images: { remotePatterns: [ { @@ -19,6 +21,14 @@ const nextConfig = { }, ], }, + async rewrites() { + return [ + { + source: '/api/:path*/', + destination: `${API_BASE_URL}/:path*/`, + }, + ] + }, } export default withVanillaExtract(nextConfig) diff --git a/packages/client/package.json b/packages/client/package.json index 374da341..73414e9d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,7 +21,7 @@ "react": "^18", "react-dom": "^18", "react-hook-form": "^7.52.1", - "react-secure-storage": "^1.3.2", + "react-hot-toast": "^2.4.1", "react-slick": "^0.30.2", "slick-carousel": "^1.8.1" }, diff --git a/packages/client/src/clientPages/questionCreate/ui/ClientQuestionCreatePage.tsx b/packages/client/src/clientPages/questionCreate/ui/ClientQuestionCreatePage.tsx index 7253f894..a61547d3 100644 --- a/packages/client/src/clientPages/questionCreate/ui/ClientQuestionCreatePage.tsx +++ b/packages/client/src/clientPages/questionCreate/ui/ClientQuestionCreatePage.tsx @@ -8,6 +8,7 @@ import { QuestionCreateInputs } from '@widgets/QuestionCreateInputs' import { MainLoader } from '@shared/ui' import { useState } from 'react' import { useRouter } from 'next/navigation' +import { Toast } from '@gds/component' import { usePostQuestion } from '../api/auth' export function ClientQuestionCreatePage() { @@ -37,6 +38,16 @@ export function ClientQuestionCreatePage() { form.reset() setImageUrls([]) router.push('/home') + Toast.success({ + title: '질문이 등록되었습니다.', + }) + }, + onError: (e) => { + Toast.error({ + title: + e.response?.data?.message || + '서버 오류가 발생했습니다. 다시 시도해주세요.', + }) }, }, ) diff --git a/packages/client/src/clientPages/questionDetail/ui/ClientQuestionDetailPage.tsx b/packages/client/src/clientPages/questionDetail/ui/ClientQuestionDetailPage.tsx index 980ea1e2..28346fae 100644 --- a/packages/client/src/clientPages/questionDetail/ui/ClientQuestionDetailPage.tsx +++ b/packages/client/src/clientPages/questionDetail/ui/ClientQuestionDetailPage.tsx @@ -9,7 +9,7 @@ import { import { useFetchMemberInformation } from '@shared/api' import { useParams, useRouter } from 'next/navigation' import { MainLoader } from '@shared/ui' -import { useState } from 'react' +import { Toast } from '@gds/component' import { pageWrapper } from './style.css' import { useFetchQuestionPost, @@ -22,10 +22,21 @@ export function ClientQuestionDetailPage() { const params = useParams() const router = useRouter() - const { data: userData } = useFetchMemberInformation() - const { data: questionData } = useFetchQuestionPost({ - questionPostId: Number(params.id), - }) + const { + data: userData, + isError: userDataIsError, + error: userDataError, + } = useFetchMemberInformation() + const { + data: questionData, + isError: questionDataIsError, + error: questionDataError, + } = useFetchQuestionPost( + { + questionPostId: Number(params.id), + }, + { enabled: !!params.id }, + ) const { mutate: createQuestionMeta } = usePostCreateQuestionMeta() const { mutate: cancelQuestionMeta } = usePostCancelQuestionMeta() const { mutate: createAnswer } = usePostAnswer() @@ -36,6 +47,17 @@ export function ClientQuestionDetailPage() { if (params.id === undefined) { router.push('/home') + Toast.error({ title: '잘못된 접근 입니다.' }) + } else if (userDataIsError) { + router.push('/home') + Toast.error({ + title: userDataError?.message || '서버 오류가 발생했습니다.', + }) + } else if (questionDataIsError) { + router.push('/home') + Toast.error({ + title: questionDataError?.message || '서버 오류가 발생했습니다.', + }) } return ( @@ -70,8 +92,9 @@ export function ClientQuestionDetailPage() { registerAnswerRequest: { content: answer }, }, { - onSuccess: () => { - refetch() + onSuccess: async () => { + await refetch() + Toast.success({ title: '답변이 등록되었습니다.' }) }, }, ) diff --git a/packages/client/src/clientPages/signup/api/mail.ts b/packages/client/src/clientPages/signup/api/mail.ts index b9cbfb27..2d810ab2 100644 --- a/packages/client/src/clientPages/signup/api/mail.ts +++ b/packages/client/src/clientPages/signup/api/mail.ts @@ -1,7 +1,7 @@ import { useMutation } from '@tanstack/react-query' import { AuthCodeRequest, MailAPIApi, SendMailRequest } from '@server-api/api' -import { getTmpAuthorizedConfig, config } from '@shared/api/config' +import { config } from '@shared/api/config' export const useSendVerificationCodeByEmail = () => { return useMutation({ diff --git a/packages/client/src/clientPages/signup/ui/ClientSignupPage.tsx b/packages/client/src/clientPages/signup/ui/ClientSignupPage.tsx index 3060d2d3..182ce5a0 100644 --- a/packages/client/src/clientPages/signup/ui/ClientSignupPage.tsx +++ b/packages/client/src/clientPages/signup/ui/ClientSignupPage.tsx @@ -8,6 +8,8 @@ import { SignupInputSection } from '@widgets/SignupInputs' import { usePostMember } from '@shared/api' import { useRouter } from 'next/navigation' import { PageURL } from '@shared/model' +import { useState } from 'react' +import { color, Typo } from '@gds/token' import * as styles from './style.css' export function ClientSignupPage() { @@ -22,6 +24,7 @@ export function ClientSignupPage() { } = form.watch() const { mutate: createMember, status } = usePostMember() const router = useRouter() + const [submitError, setSubmitError] = useState(null) return ( <> @@ -30,6 +33,14 @@ export function ClientSignupPage() {
+
+ {submitError} +