- Published On
Next.js로 블로그 제작하기 1
블로그 템플릿을 사용하지 않고 Next.js로 직접 제작한 이유
블로그의 기획부터 배포까지 전 과정에 직접 참여함으로써 제 취향의 블로그를 생성해보고 싶었습니다.
맞춤형 레이아웃
, mdx 파일 파싱
, 자동 배포 구축
, SEO 최적화 및 검색 엔진 등록
등을 직접 해보며 생성하였습니다.
Next.js의 강력한 SEO 지원
과 효율적인 디렉토리 파싱
및 라우팅 기능
이 이러한 결정에 크게 기여하였습니다.
패키지 매니저로 pnpm을 선택한 이유
프로젝트에서 사용할 패키지 매니저 선택은 개발의 효율성과 안정성에 중대한 영향을 미칩니다.
초기에는 bun
을 고려했으나, bun이 npm의 최신 버전을 지원하지 않아 여러 호환성 문제가 발생했습니다.
이 문제를 해결하기 위해, 저는 pnpm
을 선택했습니다.
pnpm은 뛰어난 안정성과 호환성을 제공하며, 특히 프로젝트의 의존성을 효율적으로 관리할 수 있도록 도와줍니다.
mdx 파일 파싱으로 velite를 선택한 이유
기존에 가장 많이 사용하는 contentlayer
는 더 이상 업데이트가 없어서 다른 파싱 라이브러리를 찾다가 velite
를 선택하였습니다.
직접 mdx파일을 일일이 파싱하는것보다 효율적으로 파싱
을 해주고 타입 안전성
과 빌드 시간 최적화
, 유연한 콘텐츠 쿼리와 통합
콘텐츠 쿼리란?
콘텐츠 쿼리
는 데이터를 검색하고 추출하는 데 사용되는 요청입니다.
이는 일반적으로 데이터베이스에서 정보를 검색하거나, 컨텐츠 관리 시스템(CMS)에서 특정 조건에 맞는 컨텐츠를 불러올 때 사용됩니다.
// 예시
// ./.velite에서 데이터 검색하고 추출
import { posts } from "#site/content";
const tags = getAllTags(posts);
블로그 제작 프로세스
제가 프로젝트를 효과적으로 진행하기 위해 준비한 단계별 계획은 다음과 같습니다.
- 벤치마킹: 경쟁력 있는 블로그들을 조사하여, 우수한 디자인과 기능을 참고하였습니다.
- 디자인 초안: 블로그의 대략적인 디자인 및 레이아웃 구상을 피그마를 통해 진행하였습니다.
- 프로젝트 생성: pnpm을 사용하여 Next.js 프로젝트를 생성하였습니다.
- 코드 품질 관리: ESLint와 Prettier를 설정 및 vscode의 setting을 변경하고 적용하였습니다.
- 콘텐츠 파싱: velite를 활용해 MDX 혹은 마크다운 파일로부터 정적 HTML을 생성하였습니다.
- 설정 파일 수정: velite.config.ts 파일을 세부적으로 조정하여 컨텐츠 파싱을 최적화하였습니다.
- 스타일링: CSS를 수정하여 블로그의 시각적 요소와 스타일링 하였습니다.
- 메타데이터 설정: 페이지별 메타데이터를 설정하여 SEO 친화적인 블로그를 구축하였습니다.
- SEO 및 유틸리티 페이지 작성: 검색엔진 최적화를 위해 sitemap과 robots.txt를 생성하고, 사용자 경험을 개선하기 위해 404 Not Found 및 Error 페이지를 생성하였습니다.
환경 설정하기
pnpm
package manager를 이용하여 nextjs 생성
# nextjs 설치
pnpm create next-app
# dark모드를 위한 theme 라이브러리 설치
pnpm i next-themes
# tailwindcss의 3rd party로 사용할 `typography`, `forms`를 설치
pnpm i -D @tailwindcss/forms @tailwindcss/typography
# mdx를 html로 parsing할 라이브러리 설치
pnpm i -D velite
# html parsing할 rehype 라이브러리들 설치
pnpm i -D rehype-slug rehype-pretty-code rehype-autolink-headingspnpm rehype-prism-plus
# 기본 ui로 shadcn-ui 설치
pnpm dlx shadcn-ui@latest init\n
pnpm dlx shadcn-ui@latest add button sheet dropdown-menu badge pagination card
# 코드 문법을 위한 shiki, 경로를 설정을 위한 slugger 설치
pnpm i shiki github-slugger
- tailwind config 설정
forms 태그의 초기값의 일관화를 위해 forms를 설치하고, 블로그 상세페이지의 css를 위해 typography를 설치하였습니다.
plugins: [require("@tailwindcss/forms"), require('@tailwindcss/typography')],
...
// 기타 tailwind 설정
- velite를 사용하면 mdx파일을
./.velite
에 파싱해주기 때문에 자주 사용되는 paths를 설정했습니다.
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"#site/content": ["./.velite"],
}
- Next.js 프로젝트에서 Velite 웹팩 플러그인을 설정
import { build } from "velite";
class VeliteWebpackPlugin {
// 플러그인이 이미 시작되었는지 확인
static started = false;
constructor(options = {}) {
this.options = options;
}
apply(compiler) {
// executed three times in nextjs !!!
// twice for the server (nodejs / edge runtime) and once for the client
compiler.hooks.beforeCompile.tapPromise("VeliteWebpackPlugin", async () => {
if (VeliteWebpackPlugin.started) return;
VeliteWebpackPlugin.started = true;
const dev = compiler.options.mode === "development";
this.options.watch = this.options.watch ?? dev;
this.options.clean = this.options.clean ?? !dev;
await build(this.options); // start velite
});
}
}
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
// 웹팩 설정에 VeliteWebpackPlugin 플러그인을 추가
config.plugins.push(new VeliteWebpackPlugin());
return config;
},
};
export default nextConfig;
제가 설정한 .eslintrc.json
, tsconfig.json
, vscode setting
은 아래와 같습니다.
{
"extends": "next/core-web-vitals",
"rules": {
"semi": ["error", "always"], // 코드 끝에 세미콜론이 항상 있어야 함을 나타냅니다.
// "no-unused-vars": "warn", // 사용되지 않는 변수가 있을 경우 경고를 표시합니다.
"eqeqeq": ["error", "always"], // 항상 === 또는 !==를 사용하여 등가 비교를 수행하도록 강제합니다.
"indent": ["error", 2] // 들여쓰기를 스페이스 2개로 설정합니다.
}
}
{
"compilerOptions": {
// "baseUrl": ".",
"target": "ES6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"#site/content": ["./.velite"],
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
{
"mdx.validate.validateReferences": "ignore",
"typescript.updateImportsOnFileMove.enabled": "always", // mdx []링크 경고 무시
"[mdx]": {"editor.quickSuggestions": false }, //mdx 자동완성 비활성화
}
Velite
를 이용한 MDX에서 HTML로의 파싱 과정에 대해서는 다음 블로그 글에서 자세히 다루겠습니다.
이전 포스트
ReactQuery in Nextjs다음 포스트
Next.js로 블로그 제작하기 2연관된 포스트 구경가기
간략히