[블로그 만들기 #1] Next.js & Vecel 이용해서 30분만에 블로그를 만들어보자

Cover Image for [블로그 만들기 #1] Next.js & Vecel 이용해서 30분만에 블로그를 만들어보자
swhan
· 7 min read

백엔드 개발자의 초간단 개인 블로그 구축기 시리즈. Next.js blog-starter-kit을 이용해서 일단 띄우고 하나씩 개선해 나가는 과정을 알아봅시다.

들어가며

왜 자체 블로그인가?

개발자라면 블로그 하나 정도는 있어야 한다는 생각으로 여러 플랫폼 고민하던 중...

  • Notion: 뭔가 안 끌림
  • Naver Blog: 개발자가...네이버 블로그...?
  • Tistory: fancy하지 않음
  • Medium: 국산품 애용해야지!

이런 저런 핑계를 대며 개발자의 낭만을 위해 직접 구축하기로 결심했습니다.

어떻게 만들지?

근데 또 직접 다 만들기에는 너무 귀찮고, 서버 돌리기에는 돈 아까워서 비용 들어가지 않고, (MD 파일만 잘 관리하면 나중에 옮기기 쉬울거라고 행복 회로를 돌리며) 사람들이 배포 쉽다고 말하는 Vercel에서 밀어주는 Next.js를 이용해서 만들어보기로 결정했습니다.

테마 선택

React, Next.js, Tailwind CSS등 백엔드 개발자에게 익숙하지 않은 부분들 다 공부하고 시작하면 평생 글 못쓰고, 하루 한명 올까말까한 블로그에 디자인보다는 내용이 우선이다라는 마음으로 그나마 봐줄만하고, 유지보수 편하게 폴더 구조 간단한 blog-starter-kit을 선택했습니다. (저희같은 작고 소중한 블로그는 일단 돌려보고 고쳐나가는게 가장 빠르고 효율적입니다.)

설치 및 테스트

npx create-next-app --example blog-starter blog-test --typescript

Need to install the following packages:
create-next-app@15.4.3
Ok to proceed? (y) y

cd blog-test

폴더 구조 확인 한번 해주고

blog-test
├── _posts #이 폴더 내부에 md 파일 작성하면 게시글이 됩니다.
├── next-env.d.ts
├── node_modules
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── README.md
├── src
├── tailwind.config.ts
└── tsconfig.json

package.json 파일에서 react, next.js 버전들 최신으로 바꿔서 설치 후 개발용으로 실행해서 테스트 해보겠습니다.

// packate.json
{
  "private": true,
  ...
  "dependencies": {
    ...
    "next": "15.3.0",
    "react": "19.1.0",
    "react-dom": "19.1.0",
  },
  "devDependencies": {
    ...
    "@types/node": "^22.13.0",
    "@types/react": "^19.1.0",
    "@types/react-dom": "^19.1.0",
  }
}
npm install && npm run dev

> dev
> next dev --turbopack

  ▲ Next.js 15.3.3 (Turbopack)
  - Local:        http://localhost:3000
  - Network:      http://192.168.0.69:3000

이제 브라우저에서 Local 주소인 http://localhost:3000 접속하면

블로그 로컬 다크모드 실행 화면

일단 뭔가 블로그답게 렌더링된 화면을 보실 수 있습니다.

다크모드 없애기

화면 왼쪽 아래에 error 표시는 다크모드 설정 오류 때문에 나오는건데요, 지금은 테마 설정에 신경쓸 단계가 아닙니다. 블로그 전체 레이아웃을 담당하는 src/layout.tsx 파일에서

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <head>
        ...
        <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
      </head>
      <body className={cn(inter.className, "dark:bg-slate-900 dark:text-slate-400")}>
        <ThemeSwitcher /> // 다크모드 테마 설정하는 친구
        <div className="min-h-screen">{children}</div>
        <Footer />
      </body>
    </html>
  );
}

다크모드 테마 설정해주는 ThemeSwitcher 컴포넌트를 없애버리면

블로그 다크모드 설정 제거 화면

이렇게 쓸데없이 관리 포인트만 많아지는 다크모드 꺼버릴 수 있습니다.

작동 구조 파악하기

저희같이 블로그 시작 단계에서는 함수 세부 구현 원리보다는 블로그에서 가장 중요한 포스팅 정보를 가지고 있는 md 파일들이 어떻게 생겼고, 어떻게 렌더링 되는지에 집중해야 합니다. 포스팅 파일들을 보관하는 _posts 폴더 보시면 이렇게 3개의 파일들이 보이실텐데요

_posts
├── dynamic-routing.md
├── hello-world.md
└── preview.md

preview.md 파일을 예시로 알아보겠습니다.

---
title: "preview.md 파일 제목입니다."
excerpt: "preview.md 파일 설명입니다."
coverImage: "/assets/blog/preview/cover.jpg"
date: "2025-01-01T05:35:07.322Z"
author:
  name: Joe Haddad
  picture: "/assets/blog/authors/joe.jpeg"
ogImage:
  url: "/assets/blog/preview/cover.jpg"
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...

--- 구분선을 기준으로 위쪽에 메타데이터들을 Key-Value 구조로 표시하고, 아래쪽에는 포스팅 본문 내용들이 들어가 있는데요, 루트 페이지 렌더링 담당하는 src/page.tsx 파일을 보시면

// src/page.tsx
...
import { getAllPosts } from "@/lib/api";

export default function Index() {
  // 파싱된 포스팅 정보들을 작성일 기준 내림차순으로 가져오는 함수
  const allPosts = getAllPosts();

  const heroPost = allPosts[0]; // 가장 최신 포스팅 1개
  const morePosts = allPosts.slice(1); // 나머지 포스팅 목록들

  return (
    <main>
      <Container>
        <Intro />
        <HeroPost
          title={heroPost.title}
          coverImage={heroPost.coverImage}
          date={heroPost.date}
          author={heroPost.author}
          slug={heroPost.slug}
          excerpt={heroPost.excerpt}
        />
        {morePosts.length > 0 && <MoreStories posts={morePosts} />}
      </Container>
    </main>
  );
}
  • getAllPosts: 파싱된 포스팅 정보들을 작성일 기준 내림차순으로 가져오고
  • heroPost: 최신 포스팅 1개
  • morePosts: 나머지 포스팅들

이것들을 렌더링 담당하는 각각의 컴포넌트에 전달해주면 아래 화면처럼 나오는데요

md파일 루트 페이지 렌더링 화면 설명

루트 페이지가 어떻게 렌더링 되는지 직관적으로 알 수 있습니다.

이제 각 포스팅 페이지 렌더링 담당하는 posts/[slug]/page.tsx 파일도 확인해 보겠습니다.

...
export default async function Post(props: Params) {
  const params = await props.params;
  const post = getPostBySlug(params.slug);

  if (!post) {
    return notFound();
  }

  // md 파일의 포스팅 본문을 html 문법으로 바꿔주는 함수
  const content = await markdownToHtml(post.content || "");

  return (
    <main>
      <Alert preview={post.preview} />
      <Container>
        <Header />
        <article className="mb-32">
          <PostHeader title={post.title} coverImage={post.coverImage} date={post.date} author={post.author} />
          <PostBody content={content} />
        </article>
      </Container>
    </main>
  );
}
...

md파일 포스팅 페이지 렌더링 화면 설명

이것 또한 Post 컴포넌트에 메타데이터, 본문 내용들이 어떻게 연결되어서 렌더링 되는지 쉽게 확인하실 수 있습니다.

참고사항

Next.js에서 public 폴더는 이미지나 JS 파일 같은 정적 자산을 서비스하기 위한 특수한 예약 폴더입니다. 이 폴더 내부의 파일들은 **루트 URL("/")**를 기준 경로로 삼아 접근할 수 있는데요

예를 들어, preview.md 파일에서 coverImage로 제공할 public/assets/blog/preview/cover.jpg 라는 파일이 있다면, 실제 웹에서 접근할 경로는 /assets/blog/preview/cover.jpg가 됩니다. 즉, public 폴더는 실제 URL 경로에서는 드러나지 않고, 마치 루트 디렉토리에 있는 것처럼 동작한다는 것을 알 수 있습니다.

배포하기

이제 md 파일에 포스팅 작성했다고 가정하고, 누구나 저희 블로그에 접근할 수 있도록 전 세계에 배포해 보도록 하겠습니다.

Github

github create repo screenshot

깃허브에 repository 하나 만들어주고 기존 프로젝트 깔끔하게 초기화 후 push 해주시면 됩니다. (저는 private으로 진행했습니다.)

rm -rf .git && \
git init && \
git add . && \
git commit -m "first commit" && \
git remote add origin https://github.com/YOUR_ACCOUNT/YOUR_REPO.git && \
git push origin main

Vercel

vercel github app 설치 화면

vercel에 회원가입 & 로그인 후 저희 github repo에 접근할 수 있도록 Github App 설치해주시고

github app 접근 권한 설정 화면

vercel github repo import 화면

방금 만든 repo 선택, vercel에 import

vercel next.js 프로젝트 설정

프로젝트 Framework Preset 부분에 Next.js 설정해주시고 deploy 버튼 클릭해주시면

vercel deployment detail

알아서 저장소 클론해서 빌드 후, 웹에서 접근 가능한 도메인들을 발급해 줍니다.

vercel 도메인 접속 화면

도메인 아무거나 하나 클릭해보면 정상적으로 접속 가능한 것을 보실 수 있습니다.

마무리

이렇게 아주 심플하고 빠르게 md 파일만으로 개인 블로그를 운영할 수 있도록 전체적인 뼈대를 만들고 작동 구조를 살펴보았습니다.

테마도 뭔가 애매하고, 도메인 주소도 마음에 들진 않지만, 일단 배포하고 고쳐나가는게 뭔가를 시작할 땐 빠르고 효율적인 것 같습니다.

다음 포스팅들을 통해서 개인 도메인 연결, 테마 변경 등 그나마 봐줄만한 수준으로 블로그 고쳐나가는 방법들을 알아보도록 하겠습니다.