블로그가 어떻게 동작하는지 알아보기 - MDX
2024년 09월 01일이번 포스팅에서는 블로그에 사용한 라이브러리와 MDX 파일을 불러오는 방식을 살펴보겠습니다.
나의 니즈 파악하기
- 운영비용 0원
- 디자인 커스텀 쉽게
- SEO 최적화
- 페이지 빠른 로딩
- 데이터베이스 사용안하기
그럼 이제 자세히 알아보겠습니다
운영비용 0원
이는 Vercel을 이용하면 쉽게 해결할 수 있습니다.
Vercel란?
Vercel은 Serverless 환경을 지원하는 호스팅 플랫폼입니다. 라우트와 서버 측 코드가 Serverless Functions으로 배포되며, 무료 호스팅이 가능합니다. 단, 유료 버전으로 업그레이드할 경우 AWS보다 비쌀 수 있으니 주의가 필요합니다.
디자인 커스터마이징 용이
디자인은 아무래도 가장 중요한 부분입니다. 각 서비스에 맞는 디자인이 필요하지만, 저는 디자인에 능숙하지 않아서 토스 블로그를 레퍼런스로 삼았습니다. 앞으로 추가될 기능들은 별도로 계획하고, 우선 원하는 방향으로 디자인을 구현하고 덧붙일 예정입니다.
SEO 최적화 , 페이지 빠른 로딩
리액트 기반으로 이 두 가지를 충족할 수 있는 프레임워크로는 Gatsby.js , Next.js가 있습니다. 저는 Next.js를 선택했습니다.
선택한 이유
- 현 실무에서 사용하는 프레임워크이다.
- AppRouter 함수들을 더 학습할 필요가 있습니다.
데이터베이스 사용하지 않기
MDX를 사용하여 파일 기반의 콘텐츠 관리 방식을 선택했습니다. 이렇게 하면 데이터베이스의 복잡함을 피하고, 개발과 유지보수 측면에서 유리하게 콘텐츠를 관리할 수 있습니다.
어떤 라이브러를 사용했을까?
package.json
"dependencies": { "@next/third-parties": "^14.2.7", "@vercel/analytics": "^1.3.1", "next": "14.2.5", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.7", "@types/mdx": "^2.0.13", "@types/node": "^20.16.2", "@types/prismjs": "^1.26.4", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", "eslint": "^8.57.0", "eslint-config-next": "14.2.5", "gray-matter": "^4.0.3", "image-size": "^1.1.1", "next-mdx-remote": "^5.0.0", "sass": "^1.77.8", "sharp": "^0.33.5", "typescript": "^5.5.4" }
prismjs
MDX의 코드 문법을 강조해주는 라이브러리입니다.
@mdx
React 애플리케이션에서 MDX 파일을 렌더링할 수 있도록 도와주는 라이브러리로, 이 블로그에서 가장 중요한 역할을 합니다.
next-mdx-remote
MDX 라이브러리를 동적으로 사용할 수 있게 해주는 라이브러리입니다.
gray-matter
MDX 파일을 포함한 마크다운 파일에서 메타데이터와 콘텐츠 내용을 추출하는 데 사용되는 라이브러리입니다.
gray-matter 사용방법
import matter from 'gray-matter'; /* MDX --- title: 블로그는 어떻게 구성되어 있을까 description: 블로그가 어떻게 구성되었는지, 어떤 방식으로 동작하는지를 설명합니다. date: 2024.8.27 thumbnail: image/thumbnail/20240827.webp path: how to make series: 블로그 제작기 --- 안녕하세요 //matter types const content: string; const data: { [key: string]: any; } */ const { data, content } = matter(fileContents);
gray-matter result
console.log(data) =>{ title: '블로그는 어떻게 구성되어 있을까', description: '블로그가 어떻게 구성되었는지, 어떤 방식으로 동작하는지를 설명합니다.', date: '2024.8.27', thumbnail: 'image/thumbnail/20240827.webp', path: 'how to make', series: '블로그 제작기' } console.log(content) => 안녕하세요
data
는 객체 형태로 반환되며, content
는 문자열로 반환됩니다.image-size
로컬 이미지의 크기를 알려주는 라이브러리로, 이미지 최적화를 위해 사용했습니다.
sass
Sass는
CSS
전처리기로, CSS
기능을 확장하고 작성과 유지 보수를 더 쉽게 만들어줍니다. 제가 애용하는 라이브러리입니다.어떤 함수를 사용했을까
자세한 코드는 useReadMdx.tsx 확인하실 수 있습니다.
export const useReadMdx = async (pathUrl?: string): Promise<MDX.Metadata[] | MDX.DetailProps> => { const markdownDirectory = path.join(process.cwd(), '_markdown'); const filenames = await fs.readdir(markdownDirectory); let current_content = undefined; let current_tag: string[] = []; let current_series: string | undefined = undefined; const markdownMetaData: MDX.Metadata[] = await Promise.all( filenames.map(async (filename) => { const filePath = path.join(markdownDirectory, filename); const meta = await parseMarkdownFile(filePath); if (!!pathUrl && normalizePath(pathUrl) === meta.path) { const fileContents = await fs.readFile(filePath, 'utf8'); const { content, data } = matter(fileContents); current_content = content; current_tag = data.tags; current_series = data.series; } return { ...meta }; }) ).then((data) => data.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())); if (pathUrl && typeof current_content === 'string') { const currentMetaIdx = markdownMetaData.findIndex((data) => data.path === normalizePath(pathUrl)) as number; let tagsArray = []; for (const tag of current_tag) { const resultSearchData = markdownMetaData.filter((data) => data.path !== normalizePath(pathUrl) && data.tags.includes(tag)); resultSearchData.length && tagsArray.push({ [tag]: resultSearchData }); } return { content: current_content, meta: markdownMetaData[currentMetaIdx], series: !!current_series ? markdownMetaData.filter((data) => data.series === (current_series as string)).reverse() : null, tags: !!current_tag.length ? tagsArray : null, }; } else { return markdownMetaData; } };
코드 설명
전체적으로 보면
pathUrl
유무에 따라 로직이 달라집니다.공통
_markdown
폴더 안에 있는 파일을 모두 읽어온 후, 전체 파일을 메타데이터 기준으로 최신순으로 정렬합니다.pathUrl 있을떄
포스팅 상세 페이지를 보기 때문에, 해당
path
에 따른 메타데이터, 같은 태그의 게시물, 시리즈 게시물, 해당 게시물의 내용 등을 반환합니다.pathUrl 없을때
pathUrl
이 없다는 것은 메인 화면을 의미하므로, 전체 메타데이터만 반환합니다.마치면서
이번 포스팅에서는 사용한 라이브러리와 MDX 파일을 불러오는 방법을 알아보았고, 다음 포스팅에서는
Next.js
의 함수들을 활용하는 방법을 살펴보겠습니다.