말 못할 고민을 익명으로 털어놓을 수 있는 게시판으로, 글을 작성할 때 시간을 지정하여 해당 시간이 경과하면 자동으로 삭제된다.

친구들에게 말 못할 고민을 털어놓자는 콘셉트로 추가된 페이지다.
비밀일기장 같은 느낌을 위한 일러스트 이미지를 메인에 삽입했다.
일러스트디자인은 디자이너 지인이 도와주었습니다 https://www.youtube.com/@티거-r9c


고민을 작성하는 시점에 시간을 지정할 수 있으며, 기본 시간은 24시간이다.
작성을 완료하면 해당 작성글로 바로 redirect 되고, 삭제까지 남은 시간이 실시간으로 줄어들게 설정했다.
첫 번째 댓글은 서버가 반환한 '모각봇' 의 AI 위로 답변이다.
"use client";
import { useEffect, useState } from "react";
import { AiFillHeart, AiOutlineHeart } from "react-icons/ai";
import {
timeArrayToSeconds,
secondsToTimeArray,
formatTimeArray,
} from "@/utils/shared/time.util";
import { AdviceDetailResponse } from "@/types/advice.type";
import AdviceCommentsCard from "./advice-comments-card";
import { AdviceEmpathyPost } from "@/lib/client/advice.client.api";
import AlertModal from "@/app/components/custom-modal";
import { useRouter } from "next/navigation";
import { useAuthStore } from "@/store/authStore";
export default function AdviceContents({
id,
data,
}: {
id: string;
data: AdviceDetailResponse;
}) {
const [showModal, setShowModal] = useState(false);
const [empathyCount, setEmpathyCount] = useState(data.empathyCount);
const [hasEmpathized, setHasEmpathized] = useState(data.hasEmpathized);
const [restSeconds, setRestSeconds] = useState(
timeArrayToSeconds(data.restTime)
);
const jwt = useAuthStore((state) => state.jwt);
useEffect(() => {
const timer = setInterval(() => {
setRestSeconds((prev) => (prev > 0 ? prev - 1 : 0));
}, 1000);
return () => clearInterval(timer);
}, []);
const handleEmpathy = async () => {
if (!jwt) {
setShowModal(true);
return;
}
try {
const res = await AdviceEmpathyPost(id);
const { empathyCount, hasEmpathized } = res;
setEmpathyCount(empathyCount);
setHasEmpathized(hasEmpathized);
} catch (err) {
console.log("공감 실패 : " + err);
}
};
const router = useRouter();
const toLogin = () => {
router.push(`/login`);
};
return (
<div className="flex flex-col lg:flex-row gap-4 max-w-screen-xl mx-auto px-4 sm:px-6 py-10">
<div className="lg:w-[65%] w-full flex flex-col gap-6">
<div className="flex items-center justify-between border-borders dark:border-border-dark border-b pb-2">
<div>
<h2 className="text-lg font-bold text-primary dark:text-primary-dark">
{data.title}
</h2>
<p className="text-sm text-border-dark dark:text-borders">
{formatTimeArray(secondsToTimeArray(restSeconds))} 남음
</p>
</div>
</div>
<p className="text-sm text-text dark:text-text-dark whitespace-pre-line">
{data.body}
</p>
<button
className="flex items-center gap-1 text-error dark:text-error-dark text-sm"
onClick={handleEmpathy}
>
{hasEmpathized ? (
<AiFillHeart size={18} />
) : (
<AiOutlineHeart size={18} />
)}
공감 {empathyCount}개
</button>
</div>
<div className="w-full lg:w-[35%]">
<AdviceCommentsCard worryId={id} />
</div>
{showModal && (
<AlertModal
message="로그인 후 이용해 주세요."
moveMsg="로그인하기"
onMove={toLogin}
confirmMsg="닫기"
onConfirm={() => setShowModal(false)}
/>
)}
</div>
);
}
advice 도 동일하게 page.tsx 는 server component 로 유지하며 wrapper(contents) 를 작성하여 import 하는 방식을 사용했다.
사실 여러 번 구현해 보았던 게시판과 크게 다른 부분은 '삭제까지의 시간 출력' 부분밖에 없었고
남은 시간 출력 후 클라이언트단에서 1초씩 감소하는 방식을 사용하여 크게 어렵지 않았다.
'Dazz6 > ABOUT' 카테고리의 다른 글
| 모각 프로젝트 - #2-6. 관리자 페이지 (3) | 2025.08.06 |
|---|---|
| 모각 프로젝트 - #2-5. 로그인/로그아웃 (0) | 2025.08.05 |
| 모각 프로젝트 - #2-3. 모각챌 (0) | 2025.08.03 |
| 모각 프로젝트 - #2-2. 모각존 (0) | 2025.08.02 |
| 모각 프로젝트 - #2-1. 홈 화면 (4) | 2025.08.01 |