Google AdSense Banner Ad (728x90 or Responsive)

Next.js로 블로그 만들기 #3

By Adminblogging

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>&copy; 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)

시리즈

Next.js로 블로그 만들기

3 / 3

다음 →
다음 포스트 없음

Google AdSense Banner Ad (728x90 or Responsive)