Next.js 14 와 Toast Library의 오류
원래는 toast 라이브러리로 만들 생각이었는데 이상하게 자꾸만 toasat css 쪽에서 에러가 발생했다.
경고문구는 아래와 같았다.
/_next/static/css/app/ReactToastify.css.map 404
새 라이브러리를 찾아서 Next.js app router 버전과 잘 맞는지 등등 따져보기엔 시간도 부족하고 주 기능도 아니라 해결법이 없다면 tailwind 로 만들어야겠다고 생각했다.
구글링해보니까 나와 비슷한 문제를 겪는 사람들이 많았다. 세가지 정도를 해봤는데 결과적으로 다 실패해버렸다...🥹
출처 : https://stackoverflow.com/questions/77152572/reacttoastify-css-map-not-found-next-13-app-folder
나와 똑같은 Next.js 14 (App Router) 로 작업하던 사람이 라이브러리 토스트를 import 할 때 위치를 바꿔봤더니 해결되었다고 해서 나도 동일하게 옮겨주었다. 하지만 여전히 똑같은 문제가 발생했다. 🤯
출처 : https://github.com/fkhadra/react-toastify/issues/1099
이번에는 Toast Library의 위치도 여기저기 옮겨봤는데 여전히 동일한 에러가 뜨거나 esm.mjs.map 404
에러가 났다.
rm -rf node_modules
yarn
yarn add react-toastify@7.0.3
라이브러리 버전을 낮췄더니 해결되었다는 글도 봐서 기존에 설치했던 라이브러리 버전도 지우고 새로 설치해보았지만 역시 문제는 동일하게 발생했다..ㅠㅠ
tailwind 로 토스트 알림창 구현하기
결국 일반 컴포넌트처럼 만들어서 구현하기로 하고 빠르게 작업을 실행했다.
복잡할 줄 알았는데 다행히 금방 만들 수 있었다.
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
interface CustomToastProps {
message: string;
onMove: () => void;
}
const CustomToast: React.FC<CustomToastProps> = ({ message, onMove }) => {
const router = useRouter();
// 일정 시간이 지나면 자동으로 닫히도록 설정 ⭐️
useEffect(() => {
const timer = setTimeout(onMove, 3000); // 3초 뒤에 사라짐
return () => clearTimeout(timer);
}, [onMove]);
const handleRedirect = () => {
onMove();
router.push("/scraps");
};
return (
<div className="fixed bottom-5 right-5 z-50 flex w-1/2 items-center justify-between space-x-4 rounded bg-gray-800 p-4 text-white shadow-md">
<p>{message}</p>
<button onClick={handleRedirect} className="font-semibold text-orange-500 hover:underline">
스크랩 이동
</button>
</div>
);
};
export default CustomToast;
"use client";
import { useState, useEffect } from "react";
import CustomToast from "@/components/scraps/CustomToast"; // ✅ 토스트 컴포넌트 import
const ScrapButton = ({ postId }: { postId: string }) => {
const [showToast, setShowToast] = useState(false); // ✅ 토스트 알림창 상태관리
const { userId, folderName, setFolderName, isSaving, setIsSaving } = useScrapStore();
const { existingFolders, saveScrap, useFetchScrapCount, isAlreadyScrapped } = useScrapData();
// 저장 성공 시 토스트 표시
const handleSaveComplete = async () => {
setIsSaving(true);
try {
const savedSuccessfully = await saveScrap({ recipeId: postId, folderName });
if (savedSuccessfully) {
setIsScrapped(true);
setShowToast(true); // ✅ 스크랩 성공하면 토스트 등장
}
} catch (error) {
console.error("스크랩 저장 오류:", error);
} finally {
setIsSaving(false);
setIsModalOpen(false);
setTimeout(() => setShowToast(false), 3000);
}
};
return (
<div>
<div className="flex cursor-pointer" onClick={handleMarkClick}>
<Image src={isScrapped ? scrapFill : scrapEmpty} alt="스크랩 버튼" width={18} height={18} />
<span className="ml-2 text-sm font-medium text-gray-700">{scrapCount || 0}</span>
</div>
{/* 모달 컴포넌트 */}
{isModalOpen && (
<ScrapModal
isSaving={isSaving}
folderName={folderName}
existingFolders={existingFolders || []}
onFolderNameChange={setFolderName}
onSave={handleSaveComplete}
onClose={() => setIsModalOpen(false)}
onFolderClick={handleFolderClick}
/>
)}
{isLoginModal && <LoginCheckModal onClose={() => setIsLoginModal(false)} />}
{showToast && <CustomToast message="스크랩 되었습니다." onMove={() => setShowToast(false)} />}
</div>
);
};
export default ScrapButton;
일부 로직은 삭제했지만 아무튼 북마크를 클릭했을 때 성공하면 해당 토스트 알림창이 나오도록 적용해주었다.
스크랩 성공하면 이렇게 알림창이 나오고 스크랩 이동 버튼을 눌렀을 때 페이지이동도 잘 되었다 👏