๐
์ธ์ ์ฒซ ๋๋ง์ ๋ธ๋ก๊ทธ๋ฅผ ๋ง๋ ๊ฑด์ ๋ํ์ฌ
blog
2025-04-01
blog
2025-04-01
์๋ ๋ถํฐ ํด์ผ์ง ํด์ผ์ง ํ์์ง๋ง, ๊ทธ๋งํผ ๋ฏธ๋ฃจ๊ณ ๋ฏธ๋ค๋ โ๋๋ง์ ๋ธ๋ก๊ทธโ ๋ง๋ค๊ธฐ!
์ด๋ฒ์ ์ง์์๋ฅผ ์์ฑํ๋ฉด์, ๋๋ง์ ๊ฐ์ ์ด ๋ญ๊น์ ๋ํด์ ๋ง์ด ๊ณ ๋ฏผํ๊ณ ์์ผ๋ก ์ด๋ค ๊ฒฝ์๋ ฅ์ ๊ฐ์ถฐ์ผ ํ ๊น?๋ฅผ ๋ง์ด ์๊ฐํ๋ค.
๊ธฐ๋ก์ ๊ฑฐ์ง๋ง์ ํ์ง ์๋๋ค.
์ด์ ๊ป ๋ด๊ฐ ๊ธฐ๋กํ ๊ฒ๋ค, ์์ผ๋ก ๋ด๊ฐ ๊ธฐ๋กํ ๊ฒ๋ค์ ๋ชจ๋ ๋์ ํฐ ์์ฐ์ด ๋ ๊ฒ์ด๋ค. ์ทจ์ง์ ํ ๋์ ์์์ ๋ช์ฒ์ค ๋ณด๋จ ๋ด๊ฐ ์ฌํ ์์ฑํ ๋ธ๋ก๊ทธ ๊ธ๋ค์ด ๋๋ผ๋ ์ฌ๋์ ๋ํด์ ๋ ์์ธํ ์๋ ค์ค ๊ฒ์ด๊ณ , ์ธ์์ ์์ด์ ๋ฐฉํฉ์ ํ ๋์๋ ๊ณผ๊ฑฐ์ ๋ด ์์ ์ด ์ด ๊ธ๋ค์ด ๋๋ฅผ ๊ตฌํด์ค ๊ฒ์ด๋ค.
๊ทธ๋ฅผ ์ํด์ ๋๋์ฑ ๋๋ง์ ๋ธ๋ก๊ทธ ํ๋ก์ ํธ๋ฅผ ์์ฑํด์ผ๋ง ํ๋ค.
์ฌ์ค ์ด์ ์๋ ๋ฒจ๋ก๊ทธ์ ๊ธ์ ์ฃผ๋ก ์ผ๋ค. ๋ฒจ๋ก๊ทธ๋ ํ์คํ ๊ฐ๋ณ๊ฒ ๊ธ์ ์ฐ๊ธฐ์ ์ข์ ํ๋ซํผ์ด๋ค. ๊ทธ๋ฌ๋ ๋ฑ ๊ฑฐ๊ธฐ๊น์ง๋ค. ๊ฐ์ฅ ํฐ ๋ถํธํจ์ ์ด๋ฏธ์ง์ ํฌ๊ธฐ ๋ฐ ์คํ์ผ ์กฐ์ ์ด ๋ฒ๊ฑฐ๋กญ๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ธ๋ก๊ทธ ๊พธ๋ฏธ๊ธฐ๊ฐ ๋ถ๊ฐ๋ฅํ๋ค(์ด๊ฒ ๊ฐ์ฅ ํฐ ์ด์ ์).
์ด๋์ ๊ฐ ๊ทธ๋ฐ ๊ธ์ ๋ณธ ์ ์ด ์๋ค. โํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ผ๋ฉด, ์๊ธฐ ๋ธ๋ก๊ทธ ์ ๋ ๋ง๋ค์ด์ผ ํ์ง ์๊ฒ ๋๊ฐ?โ
๋๋ ์ด ๊ธ์ ๊ณต๊ฐํ๋ฉฐ ๋๋ง์ ๋ธ๋ก๊ทธ๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ๋ ธ๋ผ ๋ค์ง์ ํ์ง ์ด์ธ 1๋ . . . .
์ฌ์ค ๊ทธ ๋์ ์ข ๋ฐ๋นด๊ธฐ๋ ํ๊ณ ๋ฌด์๋ณด๋ค ๋ธ๋ก๊ทธ๋ฅผ ๋ง๋ค ๋๊ธฐ๊ฐ ๋ถ์กฑํ๋ค. ์ง๊ธ ๋ฒจ๋ก๊ทธ๋ก๋ ์ถฉ๋ถํ ์ ์ฐ๊ณ ์๊ณ , ๋ ธ์ ์๋ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ๊ฒช์ ํธ๋ฌ๋ธ ์ํ ๊ธ์ ์์ฑํด๋๊ณ ์์๊ธฐ ๋๋ฌธ์ โ๊ธฐ๋ก๋ง ์ ํด๋๋ฉด ๋์ง~โ ๋ผ๋ ์๊ฐ์ผ๋ก ์ง๋ด๊ณ ์์๋ค.
๊ทธ๋ฌ๋ 25๋ ์ด ๋ฐ์์๊ณ ๋๋ ์ฌ์ฌ ์ทจ์ค์ ๋ฐ์ ๋ค์๋ค. ์ทจ์ค์ ์ํด์๋ ํฌํธํด๋ฆฌ์ค๋ฅผ ๋ง๋ค์ด์ผ ํ๋๋ฐ, ํธ๋ฌ๋ธ ์ํ ๊ธ์ด ์ฌ๊ธฐ ์ ๊ธฐ์ ๋ถ์ฐ๋์ด ์์๋ค. ํ๋ก์ ํธ A์ ํธ๋ฌ๋ธ ์ํ ๊ธ์ ํ๋ก์ ํธ A ๋ ธ์ ์ ์์๊ณ , ํ๋ก์ ํธ B์ ๊ธ์ B ๋ ธ์ ์ ์์ฑ๋์ด ์์๋ค. ๋ฐ๋ผ์ ๋๋ ํด๋น ๊ธ๋ค์ ๋ชจ๋ ๋ณต์ฌํด์ ๋ด ๋ ธ์ ์ ๋ถ์ฌ๋ฃ๊ธฐ ํ ํํ๋ก ๊ด๋ฆฌํ๊ณ ์์๋ค.
์์ ๊ฐ์ ํํ์ด๋ค ๋ณด๋, ์๋ณธ ๊ธ์ด ์ ๋ฐ์ดํธ ๋๋ฉด ๋ด๊ฐ ์์ ๋ด ๋ ธ์ ์ ์๋ ๋ณต์ฌ๋ณธ๋ ์ ๋ฐ์ดํธ๋ฅผ ํด์ค์ผ ํ๋ค. ์ฌ์ค ์ด๊ฑด ์์ฒญ๋๊ฒ ํฐ ๋ฌธ์ ๊ฐ ์๋์๋ค. ๊ฐ์ฅ ํฐ ๋ฌธ์ ๋ โ๋ธ๋ก๊ทธ์ ์ ์ฒด์ฑโ์ด์๋ค.
๋ด ๋ฒจ๋ก๊ทธ(Velog)๋ ์๊ณ ๋ฆฌ์ฆ + ์ผ์ ํ๊ณ ๋ก์ผ๋ก ์ฌ์ฉ๋๊ณ ์์๋ค. ๊ทธ๋ฌ๋ ํธ๋ฌ๋ธ ์ํ ๊ณผ ๊ฐ์ด ๊ธฐ์ ๊ด๋ จ๋ ๊ธ๋ ์ด๋๊ฐ์ ์ ์ฅ์ ํด์ผ ํ๋๋ฐ, ๋๋ ๋ ธ์ , ๋ฒจ๋ก๊ทธ ๋ ๋ค ๋ด ๋ง์ ์ฉ ๋ค์ง ์์๋ค. ๋ฒจ๋ก๊ทธ๋ ์์์ ๋งํ๋ฏ์ด ์์ ๋ก์ด ์ปค์คํฐ๋ง์ด์ง์ด ๋ถ๊ฐ๋ฅํ๊ณ , ๋ ธ์ ์ ๊ธ๋ค์ ๊ด๋ฆฌํ๊ธฐ์ ๋ฐ์ด๋์ง๋ง ๊ทธ ๊ธ๋ค์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ์์ด์๋ ๋๋ฌด โ๋์ค์ โ์ด์๋ค.
๋ค๋ฅธ ์ฌ๋๋ค๋ ๋ฒจ๋ก๊ทธ, ๋ ธ์ , ํฐ์คํ ๋ฆฌ, ๋ฏธ๋์ ๋ฑ ๋ค์ํ ํ๋ซํผ์ ์ด์ฉํ์ฌ ์์ ๋ง์ ์ด์ผ๊ธฐ๋ฅผ ๊ธฐ๋กํด ๋๊ฐ๊ณ ์์ ๊ฒ์ด๋ค. ๋ด๊ฐ ๋ฉด์ ๊ด์ด๋ผ๋ฉด ์ข ์ง๊ฒจ์ธ ์ ์๊ฒ ๋ค. ๋ผ๋ ์๊ฐ์ด ๋ค์๋ค.
๋ ธ์ , ๋ฒจ๋ก๊ทธ, ํฐ์คํ ๋ฆฌ . . . ๋งจ๋ ๋๊ฐ๋ค ์ด๋ ์๋ก์ด ๊ฑฐ ์๋?
ํ๋ฐฑ์๋ฆฌ์ฌ์์ ๋ํด๋ฆฌ ๋งํผ์๊ฐ ํจ์๋ถํ์ ์์ ๋ง์ ์๊ทน์ ์ธ ์์๋ค ์์์ ๋์ ํธ๋ฅผ ๋ด๋์ ์ด์๋จ๊ณ ๋น๋นํ ์ฐ์น์ ์ฐจ์งํ ๊ฒ์ฒ๋ผ, ๋๋ ๋ฉด์ ๊ด๋ค์๊ฒ ์์ง๋ง ์ ์ ํจ์ ์ฃผ๊ณ ์ถ์๊ธฐ์ โ๋๋ง์ ๋ธ๋ก๊ทธโ ํ๋ก์ ํธ๋ฅผ ์ถ์งํ๊ฒ ๋์๋ค.
๋จ์ฐ์ฝ NextJS์๋ค. NextJS๊ฐ ReactJS ํ๋ ์์ํฌ๋ก์ ์ ์ ํ๋ก ํธ๊ฐ๋ฐ์์ ๊ธฐ๋ณธ ์์ ์ค ํ๋๋ก ์๋ฆฌ ์ก๊ณ ์์ผ๋ฉฐ, SSR, SSG, ISR, Server Component, App router . . . ๋ฑ๋ฑ ๋ฐฐ์ธ ๊ฒ์ด ์ฐธ ๋ง์ ๋์๊ฒ๋ ๋์ ํฌ์ฑ์ด์ธ ํ๋ ์์ํฌ์๊ธฐ์ NextJS๋ฅผ ์ ํํ๋ค.
๋ธ๋ก๊ทธ๊ฐ ์ฌ์ค NextJS์ ๋ชจ๋ ๊ธฐ๋ฅ๋ค์ ํ์ฉํ๋ ค๋ฉด ์ ๋ง ๋ณผ๋ฅจ์ด ์ปค์ ธ์ผ ํ๋ค. ๊ทธ๋ฌ๋ ๋๋ ๊ทธ๋ด ์ญ๋์ด ์์ ๋ฟ๋๋ฌ, ์ง๊ธ ๋น์ฅ ๊ธ์ ์์ฑํ ๋ธ๋ก๊ทธ๊ฐ ํ์ํ๋ค. ๊ทธ๋์ ์ต๋ํ ์์ ๋ณผ๋ฅจ์ ์งํฅํ๋ฉฐ ์ฐจ์ฐจ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐํด ๋๊ฐ์! ๋ผ๋ ์๊ฐ์ผ๋ก ์ผ๋จ CRN ์ปค๋งจ๋๋ฅผ ๋๋ฌ๋ณด์๋ค.
์คํ์ผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ธฐ์กด์ ์ฌ์ฉํ๋ ์คํ์ผ๋ ์ปดํฌ๋ํธ ๋์ ์ ํ ์ผ์๋๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค. ์์ฆ ๋งค์ฐ ํซํ๊ธฐ๋ ํ๊ณ ํ๋ฒ์ฏค์ ์ฌ์ฉํ๊ณ ์ถ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ค์ ํ ์ผ์๋๋ก ํ๋ก์ ํธ๋ฅผ ํ๊ฒ ๋์ ๋ ๋๋ฌด ํค๋งค์ง ์๋๋ก ์ง๊ธ ๋จผ์ ๋งค๋ฅผ ๋ง์๋์. ๋ผ๋ ์๊ฐ์ด์๋ค.
๊ทธ๋ฐ๋ฐ ์ต๊ทผ(25/01/22)์ ํ ์ผ์๋ 4.0์ด ์ถ์ํ๋ค.
์ด์ ํ ๊ฑฐ ์ต์ ๋ฒ์ ผ์ ์ฌ์ฉํด๋ณด์! ๋ผ๋ ์ผ์์ ๊ฐ๊ณ ํ ์ผ์๋ 4.0์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์์ํ๊ฒ ๋๋ค.(์ด๋ฌ์ง ๋ง์์ด์ผ ํ๋๋ฐ. . . )
๋ง๋งํ๋ค. ๋ค๋ฅธ ์ฌ๋๋ค์ ์๋ง ๋ง๋ค๋๋ฐ, ๋ ํผ์ ๋๋๊ฐ ์ค๋ฆฌ์ ์ฒ๋ผ ๋ฅ๋ฅ ๋ ๋ค๋๋ฉฐ ๋ฌด์์ ํด์ผํ ์ง ๊ฐํผ๋ฅผ ์ก์ง ๋ชปํ๋ค. ์ฐ์ ์ธํฐ๋ท์ โNextJS ๋ธ๋ก๊ทธ ๋ง๋ค๊ธฐโ๋ฅผ ๊ฒ์ํด๋ณด๋ ์ ๋ง ๋ง์ ์์๋ค์ด ๋์๋ค. ๊ทธ๋์ ์ผ๋จ ๊ทธ ์์๋ค์ ๋ฐ๋ผํด ๋ณด์๋ค.
๋ฌด์ํ ๋ง์ ์๋ฌ๋ค์ด ๋๋ฅผ ๋ฐ๊ฒจ์คฌ๋ค.
JUST ๋ณต๋ถ์ ์ด๋์ ์ํํ๋ค๋ ๊ฒ์ ๋ค์ ํ๋ฒ ๊นจ๋ฌ์๋ค. ๊ทธ๋์ ์๋ง์ ์ฝ์ง์ ํ๋ฉฐ, ๋ง์ ์๊ฐ๋ค์ ๋ณด๋ธ ๊ฒฐ๊ณผ!! ๋ฌด์ผ ํด์ผํ ์ง ์ด๋์ ๋ ์ ์ ์์๋ค.
์ฐ์ ๋ธ๋ก๊ทธ ๊ธ์ ๋งํฌ๋ค์ด์ผ๋ก ์์ฑํด์ ์ด๋ฅผ HTML๋ก ํ์ฑํ๋ ์์ ์ด ํ์ํ๋ค. ์ด๋ฅผ ์ํด์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋์์ ๋ฐ์์ผ ํ๋ค.
๋ํ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ 2๊ฐ๋ฅผ ๋ถ์ํด๋ณด์๋ค.
NextJS์์ ๊ณต์์ผ๋ก ์ง์ํ๋ ๋ก์ปฌ์์์ ๋งํฌ๋ค์ด ํ์ผ์ HTML๋ก ํ์ฑํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. MDX ํ์ผ์ ํ๋ก์ ํธ ๋ด์ ๊ทธ๋๋ก importํด์ ์ฌ์ฉํ๋, ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ด ์๋์ผ๋ก ์ง์๋์ด ๋ณ๋์ ๋ณต์กํ ์ค์ ์์ด๋ ์ฝ๊ฒ ์ธ ์ ์๋ค. ๋น๋ ํ์์ MDX ํ์ผ์ด ํ์ฑ๋์ด ๋ฐํ์์์๋ ํ์ฑ ์ค๋ฒํค๋ ์์ด ๋น ๋ฅธ ๋ ๋๋ง์ ๋ณด์ฅํด์ค๋ค.
์๋ฒ์ getStaticProps๋ getServerProps์์ MDX ์ฝํ ์ธ ๋ฅผ fetchํ๊ฑฐ๋ ๋ก์ปฌ ํ์ผ์ ์ฝ์ด ์ฒ๋ฆฌํ ์ ์์ด, CMS๋ API ๋ฑ ์ธ๋ถ ์์ค์์ ์ฐ๋์ด ์ฉ์ดํ๋ค. ๊ทธ๋ฌ๋ ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ ์๋์ผ๋ก ์ ๊ณตํ์ง ์๊ธฐ ๋๋ฌธ์, MDX ์ฝํ ์ธ ๋ฅผ props๋ก ์ ๋ฌํ๋ ๋ฑ์ ์ถ๊ฐ ์์ ์ด ํ์ํ๋ค. ๊ฑฐ๊ธฐ์ ๋๋ถ์ด MDX ํ์ผ ๋ด import/export ๋ฌธ๋ฒ ์ง์์ด ์ ํ์ ์ด๋ค. ๋ง์ฝ ๊ธ๋ค์ ์ธ๋ถ ์์ค๋ก๋ถํฐ ๋ฐ์์ค๋ ๋ฐฉ์์ด์๋ค๋ฉด, ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ ๊ฒ์ด๋ค.
์ ์์๋ค์ ๊ณ ๋ คํ์ฌ ๋๋ ์ค์ ์ด ๋จ์ํ๊ณ ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ ์๋์ผ๋ก ์ง์ํ๋ฉฐ, ๋ก์ปฌ MDX ์ฝํ ์ธ ๋ฅผ ํ์ฉํ๋ @next/mdx ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค!
์์ธํ ๋ด์ฉ์ ๊ณต์ ๋ฌธ์์์ ๋ค๋ค์ฃผ๊ณ ์๋ค. (์ฌ์ค ๊ทธ๋ฆฌ ์์ธํ์ง ์์ ๊ฒ ๊ฐ๋คใ )
์ปค๋งจ๋๋ก ์ค์นํด์ฃผ๊ณ
1npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
next.config ํ์ผ์ ์ถ๊ฐํด์ฃผ๊ณ
1import createMDX from "@next/mdx";23/** @type {import('next').NextConfig} */4const nextConfig = {5 // Configure `pageExtensions` to include markdown and MDX files6 pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],7 // Optionally, add any other Next.js config below8};910const withMDX = createMDX({11 // Add markdown plugins here, as desired12});1314// Merge MDX config with Next.js config15export default withMDX(nextConfig);
srcํด๋ ๋ฐ๋ก ์๋ ๋๋ ํ๋ก์ ํธ ์ต์๋จ์ mdx-components.tsx ํ์ผ์ ๋ง๋ค์ด์ค์ผ ํ๋ค. ์ฐธ๊ณ ๋ก @next/mdx๋ App Router๋ง ์ฌ์ฉํด์ผ ํ๋ค!
1import type { MDXComponents } from "mdx/types";23export function useMDXComponents(components: MDXComponents): MDXComponents {4 return {5 ...components,6 };7}
๋ด App Router ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
1src/app2โโโ (contents)3โ โโโ layout.tsx4โ โโโ markdownTest5โ โ โโโ page.mdx6โ โโโ notion7โ โโโ page.mdx8โโโ about9โ โโโ page.tsx10โโโ layout.tsx11โโโ page.tsx12โโโ posts13โ โโโ page.tsx14โโโ projects15 โโโ page.tsx
ํด๋น mdx ํ์ผ๋ค์ ์ด๋ป๊ฒ ๊ฐ์ ธ์๋? ์์์๋ ๋งํ๋ฏ์ด @next/mdx๋ src/app/(contents) ๋๋ ํ ๋ฆฌ์ ์์นํ MDX ํ์ผ๋ค์ ์ง์ importํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
1import { PostData } from "@/types";2import { readdir } from "fs/promises";3import path from "path";45export async function getPosts(): Promise<PostData[]> {6 // MDX ํ์ผ๋ค์ด ์์นํ ๋๋ ํ ๋ฆฌ7 const postsDir = path.join(process.cwd(), "src", "app", "(contents)");89 // ๊ฐ ํฌ์คํธ ๋๋ ํ ๋ฆฌ ์กฐํ10 const slugs = (await readdir(postsDir, { withFileTypes: true })).filter(11 (dirent) => dirent.isDirectory()12 );1314 // ๊ฐ MDX ํ์ผ์์ metadata๋ฅผ ์ถ์ถํ ํ PostData ํํ๋ก ๋งคํ15 const posts = await Promise.all(16 slugs.map(async (dirent) => {17 const { meta } = await import(18 `../app/(contents)/${dirent.name}/page.mdx`19 );2021 return {22 slug: dirent.name,23 emoji: meta.emoji || "",24 title: meta.title || "",25 date: meta.date || meta.publishDate || "",26 preview: meta.preview || "",27 tag: meta.tag || "",28 } as PostData;29 })30 );3132 posts.sort((a, b) => +new Date(b.date) - +new Date(a.date));3334 return posts;35}
์ดํ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํด์ฃผ๋ฉด ๋๋ค.
1export default async function Home() {2 const posts = await getPosts();3 const slicedPosts = posts.slice(0, 5);45 return (6 <div className="flex flex-col justify-between items-center gap-[10px] w-full">7 <Intro />8 <PostList posts={slicedPosts} />9 </div>10 );11}
์ด์ธ์๋ ํ๋ ์ฝ์ง๋ค์ด ์ ๋ง ๋ง๋ค. ๊ทธ๊ฑด ๋ค๋ฅธ ํฌ์คํธ์์ ๋ค๋ฃจ๋๋ก ํ๊ฒ ๋ค. ์ผ๋จ ์ค๋์ ์ฌ๊ธฐ๊น์ง! ๋ค๋ค ๋์ ์ฒซ ๋ธ๋ก๊ทธ ๊ฐ์ค์ ์ถํํด์ค์ ์ ๋ง ๊ณ ๋ง๋ค! Thank you guys~~~~~๐