Skip to content
6 changes: 4 additions & 2 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<html lang="ko" suppressHydrationWarning>
<body
className={`${fontSans.variable} ${fontMono.variable} font-sans antialiased`}
>
<Providers>{children}</Providers>
<Providers>
<main className="mx-auto max-w-4xl p-6">{children}</main>
</Providers>
</body>
</html>
);
Expand Down
13 changes: 10 additions & 3 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ export default async function Home() {

return (
<>
<Button asChild>
<Link href="/posts/write">새 글 작성</Link>
</Button>
<div className="flex flex-col">
<h1 className="mx-8 my-4 text-center text-2xl font-bold">
게시글 목록
</h1>
<div className="flex justify-end">
<Button asChild className="mx-8">
<Link href="/posts/write">새 글 작성</Link>
</Button>
</div>
</div>

<PostInfiniteScroll
postList={data}
Expand Down
24 changes: 19 additions & 5 deletions apps/web/app/posts/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { Button } from '@workspace/ui/components/button';
import Link from 'next/link';

import { PostDetail } from '@/features/post/post-detail';

// 특정 게시글 페이지 - 게시글 생성 후 넘어가지는지 확인용으로 만든 임시 페이지
export default function PostPage() {
export default async function PostPage({ params }: { params: { id: string } }) {
const id = (await params).id;

return (
<main>
<PostDetail />
</main>
<>
<PostDetail params={params} />
<div className="flex flex-col items-end">
<div className="mx-8 flex gap-2">
<Button asChild>
<Link href={'/'}>목록</Link>
</Button>
<Button asChild>
<Link href={`/posts/edit/${id}`}>수정</Link>
</Button>
</div>
</div>
</>
);
}
6 changes: 1 addition & 5 deletions apps/web/app/posts/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,5 @@ export default async function PostEditPage({ params }: PostEditPageProps) {
notFound();
}

return (
<main className="mx-auto max-w-2xl p-6">
<PostForm post={post} />
</main>
);
return <PostForm post={post} />;
}
6 changes: 1 addition & 5 deletions apps/web/app/posts/write/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { PostForm } from '@/features/post/post-editor';

export default function NewPostPage() {
return (
<main className="mx-auto max-w-2xl p-6">
<PostForm />
</main>
);
return <PostForm />;
}
15 changes: 13 additions & 2 deletions apps/web/entities/post/model/post-view-model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { Post } from '@/entities/post';
import { formatToLocaleDate } from '@/shared/lib';

export const mapPostToViewModel = (post: Post) => ({
...post,
export interface PostViewModel {
id: string;
title: string;
content: string;
author: string;
localeCreatedAt: string;
}

export const mapPostToViewModel = (post: Post): PostViewModel => ({
id: post.id,
title: post.title,
content: post.content,
author: post.author,
localeCreatedAt: formatToLocaleDate(post.createdAt),
});
79 changes: 28 additions & 51 deletions apps/web/features/post/post-detail/ui/post-detail.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,38 @@
'use client';
import { getPostById, mapPostToViewModel } from '@/entities/post';
import { PostViewModel } from '@/entities/post/model/post-view-model';

import { Button } from '@workspace/ui/components/button';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';

import { getPostById } from '@/entities/post';
import { Post } from '@/entities/post';
import { formatToLocaleDate } from '@/shared/lib';
interface PostDetailProps {
params: { id: string };
}

// 특정 게시글 페이지 - 게시글 생성 후 넘어가지는지 확인용으로 만든 임시 컴포넌트
export function PostDetail() {
const { id } = useParams();
const router = useRouter();
const [post, setPost] = useState<Post | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
export async function PostDetail({ params }: PostDetailProps) {
let post: PostViewModel | null = null;
let error: string | null = null;

useEffect(() => {
if (!id) return;
const id = (await params).id;

async function fetchPost() {
try {
setIsLoading(true);
const data = await getPostById(id as string);
setPost(data);
} catch (err) {
setError('게시글을 불러오는 중 오류가 발생했습니다.');
console.error(err);
} finally {
setIsLoading(false);
}
}
fetchPost();
}, [id]);
try {
const data = await getPostById(id);
post = mapPostToViewModel(data);
} catch (err) {
console.error(err);
error = '게시글을 불러오는 중 오류가 발생했습니다.';
}

if (isLoading) return <p className="text-center text-gray-500">로딩 중...</p>;
if (error) return <p className="text-center text-red-500">{error}</p>;
if (!post)
return (
<p className="text-center text-gray-500">게시글을 찾을 수 없습니다.</p>
);
if (error) return <p>{error}</p>;
if (!post) return <p>게시글을 찾을 수 없습니다.</p>;

return (
<div className="mx-auto max-w-2xl space-y-4 rounded-lg bg-white p-6 shadow-md">
<h1 className="text-2xl font-bold text-gray-900">{post.title}</h1>
<p className="whitespace-pre-line text-gray-700">{post.content}</p>
<div className="text-sm text-gray-500">
<p>작성자: {post.author}</p>
<p>작성일: {formatToLocaleDate(post.createdAt)}</p>
</div>
<div className="mt-4 flex justify-between">
<Button variant="outline" onClick={() => router.push('/')}>
목록으로
</Button>
<Button onClick={() => router.push(`/posts/edit/${post.id}`)}>
수정하기
</Button>
<div className="mx-8 my-4 rounded border border-gray-400 p-4">
<h3 className="pb-4 font-semibold">{post.title}</h3>
<hr />
<p className="min-h-[30vh] whitespace-pre-wrap pt-4">{post.content}</p>
<br />
<div className="flex flex-col gap-2">
<span className="rounded border border-gray-300 bg-gray-300 px-2 py-0.5 italic">
{post.author}
</span>
<time>{post.localeCreatedAt}</time>
</div>
</div>
);
Expand Down
29 changes: 16 additions & 13 deletions apps/web/features/post/post-editor/ui/post-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export function PostForm({ post }: PostFormProps) {
);

return (
<form action={formAction}>
<h1 className="mb-4 text-xl font-bold">
<form action={formAction} className="mx-8 flex flex-col">
<h1 className="mb-4 text-center text-xl font-bold">
{isEditMode ? '게시글 수정' : '새 게시글 작성'}
</h1>

Expand All @@ -43,6 +43,7 @@ export function PostForm({ post }: PostFormProps) {
disabled={isPending}
isTextArea
defaultValue={post?.content || ''}
className="min-h-[30vh]"
/>
<TextField
name="author"
Expand All @@ -57,17 +58,19 @@ export function PostForm({ post }: PostFormProps) {
<p className="mt-2 text-sm text-red-500">{actionResult.error}</p>
)}

<Button
type="button"
variant="outline"
onClick={() => router.back()}
disabled={isPending}
>
취소
</Button>
<Button type="submit" disabled={isPending}>
{isPending ? '저장 중...' : '작성하기'}
</Button>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
onClick={() => router.back()}
disabled={isPending}
>
취소
</Button>
<Button type="submit" disabled={isPending}>
{isPending ? '저장 중...' : '작성하기'}
</Button>
</div>
</form>
);
}