๐Ÿšค ์„ฑ์žฅ์ผ์ง€ 7.0

์ฑ… ํ–‰๋ณตํ•œ ์ด๊ธฐ์ฃผ์˜์ž(์›จ์ธ ๋‹ค์ด์–ด)์˜ ๋‚ด์šฉ์— ์ž๊ทน๋ฐ›์•„ ์‹œ์ž‘ํ•˜๋Š” ์†Œ๋ฐ•ํ•œ ์„ฑ์žฅ๊ธฐ๋ก

์‚ด์•„์žˆ๋Š” ๊ฝƒ๊ณผ ์ฃฝ์€ ๊ฝƒ์€ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ณ„ํ•˜๋Š”๊ฐ€?
์„ฑ์žฅํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์‚ด์•„ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.
์ƒ๋ช…์˜ ์œ ์ผํ•œ ์ฆ๊ฑฐ๋Š” ์„ฑ์žฅ์ด๋‹ค!

โš› (7.0)<์™„์ „ ๊ฐœํŽธ> ํŒŒ์ธ๋งŒ ํ•™์Šต๋ฒ•์„ ์•Œ๊ฒŒ ๋œ๋งŒํผ, ์„ฑ์žฅ์ผ์ง€๋Š” ์ •๋ง ๊ทธ ๋‚ ์˜ ํ‚ค์›Œ๋“œ ์ค‘์‹ฌ์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๋„๋ก ํ•œ๋‹ค.

์šด์˜์ฒด์ œ

์šด์˜์ฒด์ œ: ์ปดํ“จํ„ฐ ํ•˜๋“œ์›จ์–ด๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์‰ฝ๊ฒŒ ํ•ด์ฃผ๋Š” ์†Œํ”„ํŠธ์›จ์–ด. ๋‹จ์ˆœํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ์ปดํ“จํ„ฐ๋ผ๋Š” ํ•˜๋“œ์›จ์–ด๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋„ ๋œ๋‹ค.(๋ฌผ๋ก  ์ด์™ธ์—๋„ ์ •๋ง ๋งŽ์€ ๊ฒƒ๋“ค์„ ๊ด€๋ฆฌํ•˜์ง€๋งŒ!)

์šด์˜์ฒด์ œ์˜ ๋ชฉ์ 

  1. ์ฃผ์–ด์ง„ ํ•˜๋“œ์›จ์–ด๋ฅผ ์ตœ๋Œ€ํ•œ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ
  2. ์ปดํ“จํ„ฐ ์‹œ์Šคํ…œ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ์ œ๊ณต

*๋ฉ€ํ‹ฐํ…Œ์Šคํ‚น: ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋กœ๊ทธ๋žจ์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ => CPU๋ฅผ ๋น ๋ฅด๊ฒŒ ์ „ํ™˜ํ•˜๋ฉด์„œ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ฒƒ

์šด์˜์ฒด์ œ์˜ ๊ตฌ์„ฑ

  • CPU => CPU ์Šค์ผ€์ฅด๋ง: CPU๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด CPU๊ฐ€ ์–ด๋–ค ํ”„๋กœ์„ธ์Šค๋ฅผ ๋จผ์ € ์‹คํ–‰ํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ
  • ๋ฉ”๋ชจ๋ฆฌ => ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ: ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ• ๋‹นํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ
  • ๋””์Šคํฌ => ํŒŒ์ผ ์‹œ์Šคํ…œ(๊ด€๋ฆฌ): ๋””์Šคํฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋””์Šคํฌ์— ์–ด๋–ป๊ฒŒ ํŒŒ์ผ์„ ์ €์žฅํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ
  • I/O ์žฅ์น˜ => ์ž…์ถœ๋ ฅ ๊ด€๋ฆฌ: ์ž…์ถœ๋ ฅ ์žฅ์น˜๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ž…์ถœ๋ ฅ ์žฅ์น˜๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ
  • ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ: ํ”„๋กœ์„ธ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์„ธ์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ

Next.js์™€ contentlayer๋ฅผ ์ด์šฉํ•œ ๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ

์˜ค๋Š˜์€ ์ž‘์„ฑํ•œ ๋ธ”๋กœ๊ทธ ๊ธ€๋“ค์„ ๋ณด์—ฌ์ค„ posts ํŽ˜์ด์ง€์™€ ๊ฐ ๊ธ€์˜ ์ƒ์„ธํŽ˜์ด์ง€์— ํ•ด๋‹นํ•˜๋Š” posts/xxx/xxx... ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. ๊ธฐ์กด ๋ธ”๋กœ๊ทธ์—์„œ ์ž‘์„ฑํ–ˆ๋˜ md ํŒŒ์ผ๋“ค์„ ์–ด๋–ป๊ฒŒ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€์„œ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ domain์˜ path๋กœ ์ ์šฉํ•ด์„œ ์ •์ ์ธ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์„๊นŒ ๊ณ ๋ฏผํ•˜๋˜ ์ค‘ ์—ฌ๋Ÿฌ ๊ตฌ๊ธ€๋ง ๋์— contentlayer๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•Œ๊ฒŒ ๋˜์–ด ์ ์šฉํ•ด๋ณด์•˜๋‹ค.

์•„๋ž˜์˜ ๊ณต์‹๋ฌธ์„œ๋ฟ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๋ถ„๋“ค์˜ ๋ธ”๋กœ๊ทธ์— ์›Œ๋‚™ ์ž˜ ์ •๋ฆฌ๊ฐ€ ๋˜์–ด์žˆ์–ด์„œ ๋ฌธ์ œ์—†์ด posts ํŽ˜์ด์ง€์™€ ๊ฐ post ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.(๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์—๋Š” ๋”ฐ๋กœ ์ •๋ฆฌํ•˜์ง€ ์•Š์„ ์˜ˆ์ •!)

๋‹ค๋งŒ, ์•„๋ž˜ ๋ ˆํผ๋Ÿฐ์Šค์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ๋ธ”๋กœ๊ทธ ๊ธ€๋“ค์˜ ๋””๋ ‰ํ† ๋ฆฌ path๋ฅผ ๊ทธ๋Œ€๋กœ domain path์— ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ฒช์—ˆ๋˜ ๋‚ด์šฉ๋งŒ ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๋จผ์ € app/src/posts/[...slug]/page.tsx ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.

import { format, parseISO } from 'date-fns';
import { allPosts } from 'contentlayer/generated';

export const generateStaticParams = async () => {
  // allPosts์˜ ๊ฐ post๋Š” 'a/b/c/d' ํ˜•ํƒœ๋กœ md ํŒŒ์ผ์˜ ๋””๋ ‰ํ† ๋ฆฌ path๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ split('/')์„ ํ†ตํ•ด ๊ฐ path๋ฅผ ๋‚˜๋ˆ„์–ด slug๋กœ ์‚ฌ์šฉํ•˜์˜€๋‹ค.
  return allPosts.map((post) => ({ slug: post._raw.flattenedPath.split('/') }));
};

export const generateMetadata = ({ params }: { params: { slug: string[] } }) => {
  // ์œ„์™€ ๋ฐ˜๋Œ€๋กœ params.slug๋ฅผ join('/')์„ ํ†ตํ•ด 'a/b/c/d' ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์ด๋ฅผ ํ†ตํ•ด post๋ฅผ ์ฐพ์•„์„œ title์„ ๋ฐ˜ํ™˜ํ•˜์˜€๋‹ค.
  const post = allPosts.find((post) => post._raw.flattenedPath === params.slug.join('/'));
  if (!post) throw new Error(`Post not found for slug: ${params.slug.join('/')}`);
  return { title: post.title };
};

export default function PostPage({ params }: { params: { slug: string[] } }) {
  // ์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ params.slug๋ฅผ join('/')์„ ํ†ตํ•ด 'a/b/c/d' ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์ด๋ฅผ ํ†ตํ•ด post๋ฅผ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜ํ•˜์˜€๋‹ค.
  const post = allPosts.find((post) => post._raw.flattenedPath === params.slug.join('/'));
  if (!post) throw new Error(`Post not found for slug: ${params.slug.join('/')}`);

  return (
    <article className="mx-auto max-w-xl py-8">
      <div className="mb-8 text-center">
        <time dateTime={post.date} className="mb-1 text-xs text-gray-600">
          {format(parseISO(post.date), 'LLLL d, yyyy')}
        </time>
        <h1 className="text-3xl font-bold">{post.title}</h1>
      </div>
      <div
        className="[&>*:last-child]:mb-0 [&>*]:mb-3"
        dangerouslySetInnerHTML={{ __html: post.body.html }}
      />
    </article>
  );
}

์•„๋งˆ ์•„๋ž˜ ์ฐธ๊ณ  ๊ธ€์„ ๋”ฐ๋ผ์„œ ์ง„ํ–‰ํ•˜๊ณ  ์œ„์˜ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์†”์งํžˆ ์˜ˆ์ „์— Next.js ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฝ์œผ๋ฉด์„œ Catch-all Segments ํŒŒํŠธ๋Š” ๋„๋Œ€์ฒด ์–ธ์ œ ์“ฐ๋ ค๋‚˜ ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ์–ผํ• ๋ดค๋˜ ๋‚ด์šฉ์ด ๊ธฐ์–ต๋‚œ ๋•๋ถ„์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๐Ÿ“ ํšŒ๊ณ 

ํ™•์‹คํžˆ ๋‚˜๋Š” ๋‚˜๋ฌด ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ๋ณด๋ฉด์„œ ๊ณต๋ถ€ํ•˜๊ธฐ๋ณด๋‹จ ๋จผ์ € ์ˆฒ์„ ํ•œ ๋ฒˆ ๋ณด๊ณ  ๋‚˜์„œ ๋‚˜๋ฌด๋ฅผ ๋ณด๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ธ ๊ฒƒ ๊ฐ™๋‹ค. ์šฐ์„  ์ด๋Ÿฐ ์‹์œผ๋กœ CS์— ๋Œ€ํ•œ ์ „์ฒด์ ์ธ ์‹œ์•ผ๋ฅผ ๋„“ํžˆ๊ณ  ์ข€๋” ๊นŠ๊ฒŒ ๋“ค์–ด๊ฐ€๊ณ  ์‹ถ์€ ์ฃผ์ œ๋ฅผ ์„ ์ •ํ•ด์„œ ๊ณต๋ถ€ํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. Next.js๋กœ ๋ธ”๋กœ๊ทธ ๋งŒ๋“œ๋Š” ํŒŒํŠธ์˜ ๊ฒฝ์šฐ, ์‚ฌ์‹ค posts ๋ถ€๋ถ„์ด ๋ง‰๋ง‰ํ–ˆ๋Š”๋ฐโ€ฆ ์ƒ๊ฐ๋ณด๋‹ค ๊ธˆ๋ฐฉ ํ•ด๊ฒฐ์ด ๋˜์—ˆ๋‹ค. ์—ญ์‹œ ๊ฐœ๋ฐœ์€ ๋ญ˜ ๋งŒ๋“ค๋ฉด์„œ ๋ฐฐ์šฐ๋Š” ๊ฒŒ ๊ฐ€์žฅ ๋น ๋ฅด๋‹ค๋Š” ๊ฒƒ์„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๋Š๊ผˆ๋‹ค.

์ฐธ๊ณ