저는 블로그 포스트를 각 마크다운(MDX) 파일로 관리하고, 빌드 시점(혹은 ISR) 또는 요청 시점(SSR)에 해당 MDX 파일을 파싱해주도록 설정했습니다. 이를 통해 새로운 글을 작성하거나 수정할 때의 프로세스를 간소화할 수 있었습니다.
bash1npx create-next-app my-blog
bash1touch tsconfig.json2npm install --save-dev typescript @types/react @types/node
bash1npm install next-mdx-remote gray-matter remark remark-html
'libraries/PostManager.ts' 같은 곳에, 아래와 같은 함수를 만들어서 마크다운을 파싱합니다.
tsx1import matter from 'gray-matter';2import { serialize } from 'next-mdx-remote/serialize';34export async function getPostBySlug(slug: string): Promise<PostData | null> {5 ...6 // 3) MDX 변환7 const source = await serialize(content, {8 mdxOptions: {9 remarkPlugins: [remarkGfm],10 rehypePlugins: [],11 format: 'mdx',12 },13 });14 ...15}
tsx1import components from '@/components';2import { Box, Container } from '@mui/material';3import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote';4import React from 'react';56import { PostData } from '@/libraries/PostManager';78const ArticleContainer = ({ post }: { post: PostData }) => {9 const source = post.source as MDXRemoteSerializeResult;10 return (11 <Container maxWidth='xl'>12 <Box py={4}>13 <MDXRemote {...source} components={components} />14 </Box>15 </Container>16 );17};1819export default ArticleContainer;
bash1npm install @mui/material @emotion/react @emotion/styled
tsx1// theme.ts2export default responsiveFontSizes(3 createTheme({4 palette: {5 primary: {6 light: brand[200],7 main: brand[400],8 dark: brand[700],9 contrastText: brand[50],10 },11 },12 typography: {13 fontFamily: robotoFont.style.fontFamily,14 },15 shadows: customShadows,16 components: {17 MuiLink: {18 styleOverrides: {19 root: {20 textDecoration: 'none', // 기본적으로 밑줄 제거21 color: 'palette.link.main', // 테마 링크 색상 적용22 '&:hover': {23 color: 'palette.link.hover', // 호버 시 색상 변경24 textDecoration: 'underline', // 호버 시 밑줄 추가25 },26 '&:visited': {27 color: 'palette.link.visited', // 방문한 링크 색상28 },29 },30 },31 },32 },33 colorSchemes: {34 light: true,35 dark: true,36 },37 cssVariables: {38 colorSchemeSelector: 'class',39 },40 }),41);
tsx1const RootLayout = async ({2 children,3}: Readonly<{4 children: React.ReactNode;5}>) => {6 const gaId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID as string;78 return (9 <html lang='en' suppressHydrationWarning>10 <body>11 <InitColorSchemeScript attribute='class' />12 <AppRouterCacheProvider>13 <ThemeProvider theme={theme} defaultMode={'system'}>14 <CssBaseline enableColorScheme />15 {children}16 </ThemeProvider>17 </AppRouterCacheProvider>18 </body>19 <GoogleAnalytics gaId={gaId} />20 </html>21 );22};
감사합니다.