diff --git a/apps/blog/src/api/instance.client.ts b/apps/blog/src/api/instance.client.ts index eaaa4c7d..103dabe4 100644 --- a/apps/blog/src/api/instance.client.ts +++ b/apps/blog/src/api/instance.client.ts @@ -1,6 +1,7 @@ "use client"; import httpClient, { HTTPClient } from "#utils/httpClient"; +import { to } from "@marshallku/utils"; export const request: HTTPClient = httpClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL, @@ -11,13 +12,21 @@ export const request: HTTPClient = httpClient({ cache: "no-store", interceptors: { async response(response) { - try { - return await response.json(); - } catch { + const [error, body] = await to(response.json()); + + if (error) { // eslint-disable-next-line no-console console.error("Failed to parse response body as JSON."); return null; } + + if (!response.ok) { + // eslint-disable-next-line no-console + console.error(body); + throw new Error(body.message); + } + + return body; }, }, }); diff --git a/apps/blog/src/components/CommentForm/index.tsx b/apps/blog/src/components/CommentForm/index.tsx index 763544cb..560e10de 100644 --- a/apps/blog/src/components/CommentForm/index.tsx +++ b/apps/blog/src/components/CommentForm/index.tsx @@ -1,6 +1,7 @@ "use client"; import { FormEventHandler, useCallback, useRef, useState } from "react"; +import { MutateOptions } from "@tanstack/react-query"; import Typography from "@marshallku/ui/Typography"; import Input from "@marshallku/ui/Input"; import Textarea from "@marshallku/ui/Textarea"; @@ -9,19 +10,27 @@ import { classNames } from "@marshallku/utils"; import { type CommentRequest } from "#api"; import CommentAvatar from "#components/CommentAvatar"; import styles from "./index.module.scss"; +import { toast } from "@marshallku/toast"; interface CommentFormProps { slug: string; - submit(data: CommentRequest): void; + submit(data: CommentRequest, options?: MutateOptions): void; + isClientSide?: boolean; } const cx = classNames(styles, "comment-form"); -function CommentForm({ slug, submit }: CommentFormProps) { +function CommentForm({ slug, submit, isClientSide = false }: CommentFormProps) { const [name, setName] = useState(""); const [body, setBody] = useState(""); const formRef = useRef(null); + const reset = useCallback(() => { + formRef.current?.reset(); + setName(""); + setBody(""); + }, []); + const handleSubmit: FormEventHandler = useCallback( (event) => { event.preventDefault(); @@ -30,17 +39,15 @@ function CommentForm({ slug, submit }: CommentFormProps) { const body = formData.get("body"); if (!body || typeof body !== "string" || body.trim() === "") { - return { - message: "내용을 입력해 주세요.", - }; + toast("내용을 입력해 주세요."); + return; } const korean = /[\u3131-\uD79D]/giu; if (!korean.test(body)) { - return { - message: "한글을 입력해 주세요.", - }; + toast("한글을 입력해 주세요."); + return; } const data = { @@ -53,12 +60,19 @@ function CommentForm({ slug, submit }: CommentFormProps) { parentCommentId: formData.get("parentCommentId") as string, }; - submit(data); - formRef.current?.reset(); - setName(""); - setBody(""); + if (isClientSide) { + submit(data, { + onSuccess: reset, + onError: (error) => { + toast(error.message); + }, + }); + } else { + submit(data); + reset(); + } }, - [slug, submit], + [slug, isClientSide, submit, reset], ); return ( diff --git a/apps/blog/src/components/PostCommentForm/index.tsx b/apps/blog/src/components/PostCommentForm/index.tsx index 13a41782..0fe71bde 100644 --- a/apps/blog/src/components/PostCommentForm/index.tsx +++ b/apps/blog/src/components/PostCommentForm/index.tsx @@ -7,7 +7,7 @@ export interface PostCommentFormProps { function PostCommentForm({ slug }: PostCommentFormProps) { const { mutate } = usePostComment(slug); - return ; + return ; } export default PostCommentForm;