페이지 3: Next.js (App Router)에서 데이터 관리 (CRUD)

3. Next.js (App Router)에서 데이터 관리 (CRUD)

Next.js 애플리케이션에서 Supabase 데이터베이스와 상호 작용하는 것은 간단합니다. 이 섹션에서는 Supabase 클라이언트를 사용하여 기본적인 CRUD (생성, 읽기, 업데이트, 삭제) 작업을 수행하는 방법을 보여줍니다.

3.1. 데이터베이스 테이블 설정

먼저 Supabase SQL 편집기에서 간단한 테이블을 생성합니다. 예를 들어, todos 테이블:

create table todos (
  id uuid primary key default uuid_generate_v4(),
  user_id uuid references auth.users not null,
  task text,
  is_complete boolean default false,
  inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

3.2. 데이터 읽기 (서버 컴포넌트)

서버 컴포넌트에서 데이터를 가져옵니다. 이 방법은 렌더링 전에 서버에서 데이터를 가져오므로 효율적입니다.

// app/todos/page.tsx
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'

export default async function TodosPage() {
  const supabase = createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    redirect('/login')
  }

  const { data: todos, error } = await supabase
    .from('todos')
    .select('*')
    .eq('user_id', user.id) // 인증된 사용자별 필터링
    .order('inserted_at', { ascending: true })

  if (error) {
    console.error('할 일 가져오기 오류:', error.message)
    return <div>할 일을 로드하는 중 오류가 발생했습니다.</div>
  }

  return (
    <div>
      <h1>내 할 일</h1>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.task} ({todo.is_complete ? '완료' : '진행 중'})
          </li>
        ))}
      </ul>
    </div>
  )
}

3.3. 데이터 생성 (클라이언트 컴포넌트/서버 액션)

새 데이터를 생성하려면 클라이언트 컴포넌트 또는 서버 액션을 사용할 수 있습니다. App Router에서 변경 작업을 위해서는 서버 액션이 권장됩니다.

// app/todos/create-todo.ts (서버 액션)
'use server'

import { createClient } from '@/lib/supabase/server'
import { revalidatePath } from 'next/cache'

export async function createTodo(formData: FormData) {
  const supabase = createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    throw new Error('사용자가 인증되지 않았습니다')
  }

  const task = formData.get('task') as string

  const { error } = await supabase
    .from('todos')
    .insert({ user_id: user.id, task })

  if (error) {
    console.error('할 일 생성 오류:', error.message)
    throw error
  }

  revalidatePath('/todos') // 새 데이터를 표시하기 위해 할 일 페이지 재검증
}

// app/todos/page.tsx (클라이언트 컴포넌트 폼 내)
// ...
<form action={createTodo}>
  <input type="text" name="task" placeholder="새로운 할 일" required />
  <button type="submit">할 일 추가</button>
</form>
// ...

3.4. 데이터 업데이트 및 삭제 (클라이언트 컴포넌트/서버 액션)

생성과 마찬가지로 업데이트 및 삭제는 견고성과 보안을 위해 서버 액션으로 처리하는 것이 가장 좋습니다.

// 업데이트 예시 (서버 액션)
'use server'

import { createClient } from '@/lib/supabase/server'
import { revalidatePath } from 'next/cache'

export async function updateTodoStatus(todoId: string, isComplete: boolean) {
  const supabase = createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    throw new Error('사용자가 인증되지 않았습니다')
  }

  const { error } = await supabase
    .from('todos')
    .update({ is_complete: isComplete })
    .eq('id', todoId)
    .eq('user_id', user.id) // 소유자만 업데이트할 수 있도록 보장

  if (error) {
    console.error('할 일 업데이트 오류:', error.message)
    throw error
  }

  revalidatePath('/todos')
}

이 예시들은 Supabase를 사용하는 Next.js 애플리케이션에서 데이터를 관리하기 위한 견고한 기반을 제공합니다.