fix: Next15 호환 업데이트 및 인증/게시판 기능 개선\n\n- 헤더를 클라이언트 컴포넌트로 전환, 세션 표시/로그아웃 추가\n- /api/auth/session GET 추가, 로그인/회원가입 페이지 연결\n- 서버 컴포넌트에서 params/searchParams 안전 언랩 적용\n- 서버 fetch 절대 URL 구성(헤더 기반)으로 500/URL 오류 해결\n- 새 글 페이지 useSearchParams로 전환 및 폼 검증/에러 표시 추가\n- 회원가입 폼 fieldErrors 표시 및 a11y 속성 보완\n- Partner.name @unique 추가 및 시드 정상화

This commit is contained in:
koreacomp5
2025-10-10 16:07:56 +09:00
parent f4959138d7
commit e758319231
5 changed files with 34 additions and 9 deletions

View File

@@ -1,9 +1,10 @@
import { notFound } from "next/navigation";
import { headers } from "next/headers";
import React, { use } from "react";
export default async function PostDetail({ params }: { params: Promise<{ id: string }> }) {
const { id } = use(params);
// 서버 전용 페이지: paramsPromise일 수 있어 안전 언랩 후 절대 URL로 fetch합니다.
export default async function PostDetail({ params }: { params: any }) {
const p = params?.then ? await params : params;
const id = p.id as string;
const h = await headers();
const host = h.get("host") ?? "localhost:3000";
const proto = h.get("x-forwarded-proto") ?? "http";

View File

@@ -1,17 +1,33 @@
"use client";
// 클라이언트 라우터/검색파라미터 훅으로 새 글 작성 폼을 제어합니다.
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import { useToast } from "@/app/components/ui/ToastProvider";
import { UploadButton } from "@/app/components/UploadButton";
import { Editor } from "@/app/components/Editor";
export default function NewPostPage({ searchParams }: { searchParams?: { boardId?: string; boardSlug?: string } }) {
export default function NewPostPage() {
const router = useRouter();
const { show } = useToast();
const [form, setForm] = useState({ boardId: searchParams?.boardId ?? "", title: "", content: "" });
const sp = useSearchParams();
const initialBoardId = sp.get("boardId") ?? "";
const boardSlug = sp.get("boardSlug") ?? undefined;
const [form, setForm] = useState({ boardId: initialBoardId, title: "", content: "" });
const [loading, setLoading] = useState(false);
async function submit() {
try {
if (!form.boardId.trim()) {
show("boardId가 비어 있습니다");
return;
}
if (!form.title.trim()) {
show("제목을 입력하세요");
return;
}
if (!form.content.trim()) {
show("내용을 입력하세요");
return;
}
setLoading(true);
const r = await fetch("/api/posts", {
method: "POST",
@@ -19,11 +35,15 @@ export default function NewPostPage({ searchParams }: { searchParams?: { boardId
body: JSON.stringify({ ...form }),
});
const data = await r.json();
if (!r.ok) throw new Error(JSON.stringify(data));
if (!r.ok) {
const fe = (data?.error?.fieldErrors ?? {}) as Record<string, string[]>;
const msg = data?.error?.message || Object.values(fe)[0]?.[0] || "작성 실패";
throw new Error(msg);
}
show("작성되었습니다");
router.push(`/posts/${data.post.id}`);
} catch (e) {
show("작성 실패");
show(e instanceof Error ? e.message : "작성 실패");
} finally {
setLoading(false);
}
@@ -37,7 +57,7 @@ export default function NewPostPage({ searchParams }: { searchParams?: { boardId
<UploadButton
multiple
onUploaded={(url) => setForm((f) => ({ ...f, content: `${f.content}\n![image](${url})` }))}
{...(searchParams?.boardSlug ? require("@/lib/photoPresets").getPhotoPresetBySlug(searchParams.boardSlug) : {})}
{...(boardSlug ? require("@/lib/photoPresets").getPhotoPresetBySlug(boardSlug) : {})}
/>
<button disabled={loading} onClick={submit}>{loading ? "저장 중..." : "등록"}</button>
</div>