ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

๐Ÿ“ Next.js Draft Mode ์‚ฌ์šฉ ๊ฐ€์ด๋“œ

Next.js์˜ Draft Mode๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ƒํƒœ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
Draft Mode๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด CMS์™€ ์—ฐ๋™ํ•˜์—ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํŽ˜์ด์ง€๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๊ฐ€์ด๋“œ๋ฅผ ํ†ตํ•ด Draft Mode ์„ค์ •๊ณผ ํ™œ์šฉ ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


โœ… 1. Draft Mode๋ž€?

Draft Mode๋Š” ์ •์  ํŽ˜์ด์ง€๋ฅผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜์—ฌ ์ฝ˜ํ…์ธ ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
์ •์  ์‚ฌ์ดํŠธ ์ƒ์„ฑ(SSG)์—์„œ ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ œ๊ณตํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.


โœ… 2. Draft Mode ํ™œ์„ฑํ™”

์„ค์ • ๋ฐฉ๋ฒ•

Draft Mode๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด Next.js์˜ res ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” API๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

// app/api/draft/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const response = NextResponse.json({ message: 'Draft mode ํ™œ์„ฑํ™”' });
  response.cookies.set('NEXT_PUBLIC_DRAFT_MODE', 'true', {
    httpOnly: true,
    path: '/',
  });
  return response;
}

๋น„ํ™œ์„ฑํ™” API

// app/api/exit-draft/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const response = NextResponse.json({ message: 'Draft mode ๋น„ํ™œ์„ฑํ™”' });
  response.cookies.set('NEXT_PUBLIC_DRAFT_MODE', '', {
    httpOnly: true,
    path: '/',
    maxAge: -1,
  });
  return response;
}

โœ… 3. Draft Mode ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ

ํŽ˜์ด์ง€ ๋‚ด์—์„œ Draft Mode ์ƒํƒœ ํ™•์ธ

// app/page.tsx
import { cookies } from 'next/headers';

export default function HomePage() {
  const draftMode = cookies().get('NEXT_PUBLIC_DRAFT_MODE');

  if (draftMode?.value) {
    return <h1>๐Ÿ“ Draft Mode ํ™œ์„ฑํ™” ์ค‘</h1>;
  }

  return <h1>์ •์ƒ ๋ชจ๋“œ ํŽ˜์ด์ง€</h1>;
}

ํ™œ์„ฑํ™” ๋ฐ ๋น„ํ™œ์„ฑํ™” ๋ฒ„ํŠผ

๋ฐ˜์‘ํ˜•
// app/components/DraftToggle.tsx
export default function DraftToggle() {
  const enableDraft = async () => {
    await fetch('/api/draft');
    window.location.reload();
  };

  const disableDraft = async () => {
    await fetch('/api/exit-draft');
    window.location.reload();
  };

  return (
    <div>
      <button onClick={enableDraft}>Draft ๋ชจ๋“œ ์ผœ๊ธฐ</button>
      <button onClick={disableDraft}>Draft ๋ชจ๋“œ ๋„๊ธฐ</button>
    </div>
  );
}

โœ… 4. Draft Mode๋ฅผ ํ™œ์šฉํ•œ ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

CMS์™€ ์—ฐ๊ฒฐํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ: Sanity์™€ ์—ฐ๋™

  1. Sanity์—์„œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ URL ์„ค์ •:
    • /api/draft?slug=my-page ํ˜•ํƒœ๋กœ ์„ค์ •
  2. Next.js API์—์„œ ์ฒ˜๋ฆฌ
// app/api/draft/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const slug = searchParams.get('slug');

  if (!slug) {
    return NextResponse.json({ message: '์ž˜๋ชป๋œ ์š”์ฒญ' }, { status: 400 });
  }

  const response = NextResponse.redirect(`/${slug}`);
  response.cookies.set('NEXT_PUBLIC_DRAFT_MODE', 'true', {
    httpOnly: true,
    path: '/',
  });
  return response;
}

ํŽ˜์ด์ง€์—์„œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ™•์ธ

// app/[slug]/page.tsx
import { cookies } from 'next/headers';

export default function Page({ params }: { params: { slug: string } }) {
  const draftMode = cookies().get('NEXT_PUBLIC_DRAFT_MODE');

  return (
    <div>
      <h1>{params.slug} ํŽ˜์ด์ง€</h1>
      {draftMode?.value && <p>Draft ๋ชจ๋“œ๊ฐ€ ํ™œ์„ฑํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.</p>}
    </div>
  );
}

โœ… 5. Draft Mode ์ฃผ์˜์‚ฌํ•ญ

  • SEO ์œ ์˜: Draft Mode ํŽ˜์ด์ง€๊ฐ€ ๊ฒ€์ƒ‰์—”์ง„์— ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์„ค์ • ํ•„์š”
  • ์บ์‹ฑ ๋ฌธ์ œ: ์บ์‹œ ๋ฌดํšจํ™”๋ฅผ ํ†ตํ•ด ์ตœ์‹  ์ฝ˜ํ…์ธ ๋ฅผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋กœ ์ œ๊ณต
  • ๋ณด์•ˆ ๊ณ ๋ ค: ๋ฌด๋ถ„๋ณ„ํ•œ ํ™œ์„ฑํ™”๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๊ด€๋ฆฌ์ž ์ „์šฉ์œผ๋กœ ์„ค์ •

โœ… 6. ๋ฐฐํฌ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ

Draft Mode๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋˜๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์„œ๋ฒ„์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ๊ฐ€ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •

NEXT_PUBLIC_DRAFT_MODE=false

์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง

if (process.env.NEXT_PUBLIC_DRAFT_MODE !== 'true') {
  return <h1>Draft Mode ์‚ฌ์šฉ ๋ถˆ๊ฐ€</h1>;
}

โœ… ์š”์•ฝ

๊ธฐ๋Šฅ ์„ค๋ช…

Draft Mode ํ™œ์„ฑํ™” /api/draft ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ํ™œ์„ฑํ™”
Draft Mode ๋น„ํ™œ์„ฑํ™” /api/exit-draft ์—”๋“œํฌ์ธํŠธ๋กœ ์ข…๋ฃŒ
์ƒํƒœ ํ™•์ธ ๋ฐ ๋ Œ๋”๋ง ์ฟ ํ‚ค๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ๋ชจ๋“œ ํ™•์ธ
์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ™œ์šฉ CMS ์—ฐ๋™ํ•˜์—ฌ ์ดˆ์•ˆ ์ฝ˜ํ…์ธ  ๋ฏธ๋ฆฌ๋ณด๊ธฐ
๋ฐฐํฌ ํ™˜๊ฒฝ ๊ณ ๋ ค์‚ฌํ•ญ ํ”„๋กœ๋•์…˜์—์„œ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š๋„๋ก ์„ค์ •

 

Next.js, Draft Mode, ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ, CMS ์—ฐ๋™, ์ฟ ํ‚ค ๊ด€๋ฆฌ, API ์„ค์ •, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ, ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ, ์ฝ˜ํ…์ธ  ๋ฏธ๋ฆฌ๋ณด๊ธฐ, SEO ๊ณ ๋ ค์‚ฌํ•ญ

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/05   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•