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

๋ฐ˜์‘ํ˜•

๐Ÿ”„ Next.js App Router์—์„œ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์™„์ „ ๊ฐ€์ด๋“œ

Next.js App Router์—์„œ๋Š” ๊ธฐ์กด REST API๋‚˜ API Route๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ ๋„, Server Actions๋ฅผ ์‚ฌ์šฉํ•ด ์„œ๋ฒ„์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๋ฐฉ์‹์€ ์ฝ”๋“œ๋Ÿ‰์„ ์ค„์ด๊ณ  ๋ณด์•ˆ์„ฑ์„ ๋†’์ด๋ฉฐ, ์„œ๋ฒ„์—์„œ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ์ฆ‰์‹œ UI์— ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.


โœ… Server Action์ด๋ž€?

'use server' ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ํ˜ธ์ถœ๋˜๋”๋ผ๋„ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

// app/lib/actions.ts
'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  const content = formData.get('content');

  // ์—ฌ๊ธฐ์— DB INSERT ๋กœ์ง ์ถ”๊ฐ€
}

๋˜๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ํ•จ์ˆ˜๋กœ๋„ ์„ ์–ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

// app/page.tsx
export default function Page() {
  async function submit(formData: FormData) {
    'use server';
    // DB ์ €์žฅ
  }

  return <form action={submit}>...</form>;
}

๐Ÿงพ HTML <form>์—์„œ Server Action ํ˜ธ์ถœํ•˜๊ธฐ

// app/posts/create-form.tsx
import { createPost } from '@/app/lib/actions';

export default function CreateForm() {
  return (
    <form action={createPost}>
      <input type="text" name="title" placeholder="์ œ๋ชฉ" />
      <textarea name="content" placeholder="๋‚ด์šฉ" />
      <button type="submit">์ž‘์„ฑ</button>
    </form>
  );
}
  • form์˜ action ์†์„ฑ์— Server Action์„ ์—ฐ๊ฒฐ
  • form ๋ฐ์ดํ„ฐ๋ฅผ FormData ํ˜•ํƒœ๋กœ ์ž๋™ ์ „๋‹ฌ

๐Ÿ–ฑ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ Server Action ํ˜ธ์ถœ

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” formAction ์†์„ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

'use client';
import { createPost } from '@/app/lib/actions';

export function SubmitButton() {
  return <button formAction={createPost}>๋“ฑ๋ก</button>;
}
  • formAction์€ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ง€์ •๋œ Server Action์„ ํ˜ธ์ถœ
  • type="submit" ๋ฒ„ํŠผ๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ ํผ ์™ธ๋ถ€์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

โ™ป๏ธ ์บ์‹œ ๋ฌดํšจํ™” + ๋ฆฌ๋””๋ ‰์…˜ ์ฒ˜๋ฆฌ

๋ฐ˜์‘ํ˜•

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋œ ํ›„ ํŠน์ • ํŽ˜์ด์ง€์˜ ์บ์‹œ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์‚ฌ์šฉ์ž ๊ฒฝ๋กœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

'use server';

import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

export async function createPost(formData: FormData) {
  // DB ์ €์žฅ ๋กœ์ง ์‹คํ–‰

  revalidatePath('/posts'); // ๋ชฉ๋ก ํŽ˜์ด์ง€ ์บ์‹œ ๋ฌดํšจํ™”
  redirect('/posts');       // /posts ํŽ˜์ด์ง€๋กœ ์ด๋™
}
  • revalidatePath: ์ง€์ • ๊ฒฝ๋กœ์˜ ์„œ๋ฒ„ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜์—ฌ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ์žฌ์š”์ฒญ
  • redirect: ์„œ๋ฒ„์—์„œ ๋ฆฌ๋””๋ ‰์…˜ ์ˆ˜ํ–‰

โš ๏ธ ์—๋Ÿฌ ํ•ธ๋“ค๋ง๋„ ํ•„์ˆ˜

Server Action ๋‚ด๋ถ€์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ try/catch ์ฒ˜๋ฆฌ๋กœ ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

'use server';

export async function createPost(formData: FormData) {
  try {
    const title = formData.get('title');
    // DB ์ €์žฅ ๋˜๋Š” ์™ธ๋ถ€ ์š”์ฒญ
  } catch (error) {
    console.error('์—๋Ÿฌ ๋ฐœ์ƒ:', error);
  }
}

๐Ÿงช ๊ฐ„๋‹จํ•œ ์‹ค์ „ ๊ตฌ์กฐ ์˜ˆ์‹œ

app/
โ”œโ”€โ”€ lib/
โ”‚   โ””โ”€โ”€ actions.ts      # Server Action ์ •์˜
โ”œโ”€โ”€ posts/
โ”‚   โ”œโ”€โ”€ create-form.tsx # <form> ์ •์˜
โ”‚   โ””โ”€โ”€ page.tsx        # ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ

โœ… ์š”์•ฝ ์ •๋ฆฌ

๊ธฐ๋Šฅ ์‚ฌ์šฉ๋ฒ•

Server Action ์ƒ์„ฑ 'use server' ๋””๋ ‰ํ‹ฐ๋ธŒ ์‚ฌ์šฉ
form์—์„œ ํ˜ธ์ถœ <form action={ํ•จ์ˆ˜}> ์‚ฌ์šฉ
ํด๋ผ์ด์–ธํŠธ ๋ฒ„ํŠผ์—์„œ ํ˜ธ์ถœ <button formAction={ํ•จ์ˆ˜}> ์‚ฌ์šฉ
์บ์‹œ ๋ฌดํšจํ™” revalidatePath('/url')
๋ฆฌ๋””๋ ‰์…˜ redirect('/target')
์—๋Ÿฌ ์ฒ˜๋ฆฌ try/catch ๊ตฌ์กฐ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ

 

Next.js, Server Actions, ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ, App Router, FormData, ์บ์‹œ ๋ฌดํšจํ™”, ๋ฆฌ๋””๋ ‰์…˜, ํด๋ผ์ด์–ธํŠธ ํ˜ธ์ถœ, ์„œ๋ฒ„ ํ•จ์ˆ˜, SEO ์ตœ์ ํ™” 10๊ฐœ

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/04   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
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
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•