diff --git a/src/app/wines/AddWineModalProvider.tsx b/src/app/(user-wine)/wines/AddWineModalProvider.tsx similarity index 100% rename from src/app/wines/AddWineModalProvider.tsx rename to src/app/(user-wine)/wines/AddWineModalProvider.tsx diff --git a/src/app/wines/[wineid]/page.tsx b/src/app/(user-wine)/wines/[wineid]/page.tsx similarity index 100% rename from src/app/wines/[wineid]/page.tsx rename to src/app/(user-wine)/wines/[wineid]/page.tsx diff --git a/src/app/wines/page.tsx b/src/app/(user-wine)/wines/page.tsx similarity index 98% rename from src/app/wines/page.tsx rename to src/app/(user-wine)/wines/page.tsx index eafda6d..bf504b1 100644 --- a/src/app/wines/page.tsx +++ b/src/app/(user-wine)/wines/page.tsx @@ -1,3 +1,4 @@ +//wines/page.tsx 와인목록페이지 "use client"; import { useState, useCallback, useEffect } from "react"; @@ -16,7 +17,7 @@ import Search from "@/components/wines/search"; import RecommendCard from "@/components/wines/recommend-card"; import EntireCard from "@/components/wines/entire-card"; import FilterModal from "@/components/wines/modal/filter-modal"; -import arrowRight from "../../../public/icons/right.svg"; +import arrowRight from "../../../../public/icons/right.svg"; import "swiper/css"; import "swiper/css/navigation"; import { useAddWineModal } from "./AddWineModalProvider"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8eabfb0..9dbb5fd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,8 +1,10 @@ +//app/layout.tsx + import type { Metadata } from "next"; import "@/styles/globals.css"; import { AuthProvider } from "@/context/auth-provider"; import ClientWrapper from "@/components/common/nav-wrapper"; -import { AddWineModalProvider } from "@/app/wines/AddWineModalProvider"; +import { AddWineModalProvider } from "@/app/(user-wine)/wines/AddWineModalProvider"; import ReviewProvider from "@/provider/usereviewmodals"; import DarkThemeProvider from "@/components/common/theme-provider"; import DarkMode from "@/components/common/dark-mode-button"; diff --git a/src/components/common/Chips.tsx b/src/components/common/chips.tsx similarity index 100% rename from src/components/common/Chips.tsx rename to src/components/common/chips.tsx diff --git a/src/components/common/Slider.tsx b/src/components/common/slider.tsx similarity index 100% rename from src/components/common/Slider.tsx rename to src/components/common/slider.tsx diff --git a/src/components/common/StarRating.tsx b/src/components/common/star-rating.tsx similarity index 100% rename from src/components/common/StarRating.tsx rename to src/components/common/star-rating.tsx diff --git a/src/components/common/TextArea.tsx b/src/components/common/text-area.tsx similarity index 100% rename from src/components/common/TextArea.tsx rename to src/components/common/text-area.tsx diff --git a/src/components/modal-review/modal-review-edit.tsx b/src/components/modal-review/modal-review-edit.tsx index 70cb5fa..25ebff4 100644 --- a/src/components/modal-review/modal-review-edit.tsx +++ b/src/components/modal-review/modal-review-edit.tsx @@ -4,9 +4,9 @@ import { useReviewModalStore } from "@/provider/usereviewmodals"; import { FormEvent, useEffect } from "react"; import Button from "@/components/common/Button"; import Modalv from "@/components/common/modal-container-review"; -import ReviewInput from "@/components/modal-review/ReviewInput"; -import TagSelector from "@/components/modal-review/TagSelector"; -import TasteSlider from "@/components/modal-review/TasteSlider"; +import ReviewInput from "@/components/modal-review/review-input"; +import TagSelector from "@/components/modal-review/tag-selector"; +import TasteSlider from "@/components/modal-review/taste-slider"; import { convertToAroma } from "@/utils/translate-aroma"; import { useAddReview, diff --git a/src/components/modal-review/ReviewInput.tsx b/src/components/modal-review/review-input.tsx similarity index 61% rename from src/components/modal-review/ReviewInput.tsx rename to src/components/modal-review/review-input.tsx index fa41d25..fb90058 100644 --- a/src/components/modal-review/ReviewInput.tsx +++ b/src/components/modal-review/review-input.tsx @@ -1,11 +1,11 @@ "use client"; -import StarRating from "@/components/common/StarRating"; -import TextArea from "@/components/common/TextArea"; +import StarRating from "@/components/common/star-rating"; +import TextArea from "@/components/common/text-area"; import { useReviewModalStore } from "@/provider/usereviewmodals"; import Image from "next/image"; import wine from "../../../public/icons/wine.svg"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback } from "react"; import instance from "@/api/api"; interface WineDetails { @@ -17,10 +17,33 @@ interface WineIdProps { content?: string; } +const MAX_CHARS = 200; +const DEBOUNCE_DELAY = 300; + +function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timer = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + clearTimeout(timer); + }; + }, [value, delay]); + + return debouncedValue; +} + export default function ReviewInput({ id, content }: WineIdProps) { const { setContent, setRating } = useReviewModalStore(); const [wineName, setWineName] = useState(null); const [error, setError] = useState(""); + const [localContent, setLocalContent] = useState(content || ""); + const [charCount, setCharCount] = useState(content?.length || 0); + + const debouncedContent = useDebounce(localContent, DEBOUNCE_DELAY); useEffect(() => { if (id) { @@ -36,16 +59,26 @@ export default function ReviewInput({ id, content }: WineIdProps) { } }, [id]); - const handleContentChange = (e: React.ChangeEvent) => { - const newContent = e.target.value; - setContent(newContent); - - if (newContent.length < 10) { + useEffect(() => { + setContent(debouncedContent); + + if (debouncedContent.length < 10) { setError("10자 이상 작성해주세요"); } else { setError(""); } - }; + }, [debouncedContent, setContent]); + + const handleContentChange = useCallback((e: React.ChangeEvent) => { + const newContent = e.target.value; + + if (newContent.length > MAX_CHARS) { + return; + } + + setLocalContent(newContent); + setCharCount(newContent.length); + }, []); const wineNameText = wineName ? wineName.name : "와인 이름 로딩 중..."; @@ -62,7 +95,7 @@ export default function ReviewInput({ id, content }: WineIdProps) { />
-

+

{wineNameText}

@@ -76,11 +109,15 @@ export default function ReviewInput({ id, content }: WineIdProps) {