Next.js로 블로그 만들기 #3
Google AdSense In-feed Ad (Responsive)
블로그 구조 만들기
프로젝트가 설정되었으니 이제 블로그의 핵심 구조를 만들어봅시다!
레이아웃 생성
먼저 헤더와 푸터를 포함하도록 메인 레이아웃을 업데이트합니다.
src/app/layout.tsx
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import Header from '@/components/Header'
import Footer from '@/components/Footer'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'My Blog',
description: 'Next.js로 만든 블로그',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ko">
<body className={inter.className}>
<Header />
<main className="min-h-screen">
{children}
</main>
<Footer />
</body>
</html>
)
}
컴포넌트 만들기
Header 컴포넌트
src/components/Header.tsx를 생성합니다:
import Link from 'next/link'
export default function Header() {
return (
<header className="bg-white shadow-sm">
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="flex justify-between items-center">
<Link href="/" className="text-2xl font-bold text-gray-900">
내 블로그
</Link>
<ul className="flex space-x-6">
<li>
<Link href="/" className="text-gray-700 hover:text-gray-900">
홈
</Link>
</li>
<li>
<Link href="/about" className="text-gray-700 hover:text-gray-900">
소개
</Link>
</li>
</ul>
</div>
</nav>
</header>
)
}
Footer 컴포넌트
src/components/Footer.tsx를 생성합니다:
export default function Footer() {
return (
<footer className="bg-gray-900 text-white py-8 mt-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<p>© 2025 내 블로그. All rights reserved.</p>
</div>
</footer>
)
}
홈페이지 만들기
블로그 포스트를 표시하도록 src/app/page.tsx를 업데이트합니다:
import { getAllPosts } from '@/lib/posts'
import Link from 'next/link'
import { formatDate } from '@/lib/utils'
export default function Home() {
const posts = getAllPosts()
return (
<div className="max-w-4xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold mb-8">최신 포스트</h1>
<div className="space-y-8">
{posts.map((post) => (
<article key={post.slug} className="border-b pb-8">
<Link href={`/posts/${post.slug}`}>
<h2 className="text-2xl font-bold mb-2 hover:text-blue-600">
{post.title}
</h2>
</Link>
<p className="text-gray-600 text-sm mb-3">
{formatDate(post.date)}
</p>
<p className="text-gray-700 mb-4">
{post.excerpt}
</p>
<Link
href={`/posts/${post.slug}`}
className="text-blue-600 hover:underline"
>
더 읽기 →
</Link>
</article>
))}
</div>
</div>
)
}
개별 포스트 페이지
src/app/posts/[slug]/page.tsx에 동적 라우트를 생성합니다:
import { getAllPosts, getPostBySlug } from '@/lib/posts'
import { notFound } from 'next/navigation'
import { formatDate } from '@/lib/utils'
interface PageProps {
params: { slug: string }
}
export async function generateStaticParams() {
const posts = getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
export default function Post({ params }: PageProps) {
const post = getPostBySlug(params.slug)
if (!post) {
notFound()
}
return (
<article className="max-w-4xl mx-auto px-4 py-12">
<header className="mb-8">
<h1 className="text-4xl font-bold mb-4">{post.title}</h1>
<p className="text-gray-600">{formatDate(post.date)}</p>
</header>
<div
className="prose prose-lg max-w-none"
dangerouslySetInnerHTML={{ __html: post.content }}
/>
</article>
)
}
유틸리티 함수
src/lib/utils.ts를 생성합니다:
export function formatDate(dateString: string): string {
const date = new Date(dateString)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
샘플 포스트 생성
posts/first-post.md에 샘플 포스트를 생성합니다:
---
title: "내 첫 블로그 포스트"
date: "2025-03-31"
excerpt: "새 블로그에 오신 것을 환영합니다! 이것은 첫 번째 포스트입니다."
---
# 내 블로그에 오신 것을 환영합니다
Next.js로 만든 첫 번째 블로그 포스트입니다!
## 기능
- 빠르고 효율적
- SEO 친화적
- 유지보수가 쉬움
더 많은 콘텐츠를 기대해주세요!
테스트
개발 서버를 실행하면 이제 다음을 볼 수 있습니다:
- 모든 포스트를 나열하는 홈페이지
- 클릭 가능한 포스트 제목
- 전체 콘텐츠가 있는 개별 포스트 페이지
다음 편
Part 4에서는 다음을 추가할 것입니다:
- Tailwind Typography로 마크다운 스타일링
- 카테고리와 태그
- 더 나은 포스트 메타데이터
- 코드 블록을 위한 구문 강조
블로그가 형태를 갖추고 있습니다! 🎉
Google AdSense In-feed Ad (Responsive)