[Next 13] Data Fetching - Form 과 Mutations

Jeongho·2023년 9월 6일
0
  • form을 사용하면 웹 애플리케이션에서 데이터를 생성하고 업데이트할 수 있습니다. Next.js는 서버 작업을 사용하여 양식 제출 및 데이터 변형을 처리하는 강력한 방법을 제공합니다.

서버 작업의 작동 방식

  • 서버 작업을 사용하면 API 엔드포인트를 수동으로 생성할 필요가 없습니다. 대신 컴포넌트에서 직접 호출할 수 있는 비동기 서버 기능을 정의합니다.
  • 서버 작업은 서버 컴포넌트에서 정의하거나 클라이언트 컴포넌트에서 호출할 수 있습니다. 서버 컴포넌트에서 작업을 정의하면 양식이 JavaScript 없이 작동하여 점진적인 향상을 제공할 수 있습니다.
  • next.config.js 파일에서 서버 작업을 수행할 수 있습니다.
// next.config.js
module.exports = {
  experimental: {
    serverActions: true,
  },
}
  • 알아두면 좋은 점
    • 서버 컴포넌트에서 서버 작업을 호출하는 양식은 JavaScript 없이 작동할 수 있습니다.
    • 클라이언트 컴포넌트에서 서버 작업을 호출하는 양식은 JavaScript가 아직 로드되지 않은 경우 제출을 대기열에 넣어 클라이언트 하이드레이션의 우선 순위를 지정합니다.
    • 서버 작업은 사용되는 페이지나 레이아웃에서 런타임을 상속합니다.
    • 현재 경로가 서버 작업을 사용하는 경우 동적으로 렌더링해야 합니다.

캐시된 데이터 재검증

  • 서버 작업은 Next.js 캐싱 및 재검증 아키텍처와 긴밀하게 통합됩니다. form이 제출되면 서버 작업은 캐시된 데이터를 업데이트하고 변경해야 하는 모든 캐시 키의 유효성을 다시 검사할 수 있습니다.
  • 기존 애플리케이션처럼 경로당 단일 form으로 제한되는 대신 서버 작업을 사용하면 경로당 여러 작업을 수행할 수 있습니다. 또한 form 제출 시 브라우저를 새로 고칠 필요가 없습니다. 단일 네트워크 왕복에서 Next.js는 업데이트된 UI와 새로 고쳐진 데이터를 모두 반환할 수 있습니다.

Example

서버 전용 Forms

  • 서버 전용 form을 만들려면 서버 컴포넌트에서 서버 작업을 정의하세요. 작업은 함수 상단에 있는 "use server" 지시문을 사용하여 인라인으로 정의하거나 파일 상단에 지시문을 사용하여 별도의 파일에서 정의할 수 있습니다.
// app/page.tsx
export default function Page() {
  async function create(formData: FormData) {
    'use server'
 
    // mutate data
    // revalidate cache
  }
 
  return <form action={create}>...</form>
}
  • <form action={create}>는 FormData 데이터 유형을 사용합니다. 위의 예에서 HTML 양식을 통해 제출된 FormData는 서버 작업 만들기에서 액세스할 수 있습니다.

데이터 재검증

  • 서버 작업을 사용하면 요청 시 Next.js 캐시를 무효화할 수 있습니다. revalidatePath를 사용하여 전체 경로 세그먼트를 무효화할 수 있습니다.
// app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export default async function submit() {
  await submitForm()
  revalidatePath('/')
}
  • 또는 revalidateTag를 사용하여 캐시 태그로 특정 데이터 가져오기를 무효화합니다.
// app/actions.ts
'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function submit() {
  await addPost()
  revalidateTag('posts')
}

Redirecting

  • 서버 작업이 완료된 후 사용자를 다른 경로로 리디렉션하려면 리디렉션과 절대 또는 상대 URL을 사용할 수 있습니다.
// app/actions.ts
'use server'
 
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
 
export default async function submit() {
  const id = await addPost()
  revalidateTag('posts') // Update cached posts
  redirect(`/post/${id}`) // Navigate to new route
}

Form 유효성 검사

  • 기본 form 유효성 검사에는 필수 및 type="email"과 같은 HTML 유효성 검사를 사용하는 것이 좋습니다.
  • 고급 서버 측 유효성 검사를 위해 zod와 같은 스키마 유효성 검사 라이브러리를 사용하여 구문 분석된 양식 데이터의 구조를 유효성 검사합니다.
// app/actions.ts
import { z } from 'zod'
 
const schema = z.object({
  // ...
})
 
export default async function submit(formData: FormData) {
  const parsed = schema.parse({
    id: formData.get('id'),
  })
  // ...
}

로딩 상태 표시

  • form이 서버에 제출될 때 로딩 상태를 표시하려면 useFormStatus 후크를 사용하십시오.
// app/page.tsx
'use client'
 
import { experimental_useFormStatus as useFormStatus } from 'react-dom'
 
function SubmitButton() {
  const { pending } = useFormStatus()
 
  return (
    <button disabled={pending}>{pending ? 'Submitting...' : 'Submit'}</button>
  )
}
  • 알아두면 좋은 점
    • 현재 로드 또는 오류 상태를 표시하려면 클라이언트 컴포넌트를 사용해야 합니다. 서버 작업의 안정성이 향상됨에 따라 이러한 값을 검색하는 서버 측 함수에 대한 옵션을 탐색하고 있습니다.

에러 처리

  • 서버 작업은 직렬화 가능한 개체를 반환할 수도 있습니다. 예를 들어 서버 작업은 새 항목 생성 시 발생하는 에러를 처리하고 성공 또는 오류 메시지를 반환할 수 있습니다.
// app/actions.ts
'use server'
 
export async function create(formData: FormData) {
  try {
    await createItem(formData.get('item'))
    revalidatePath('/')
    return { message: 'Success!' }
  } catch (e) {
    return { message: 'There was an error.' }
  }
}
  • 그런 다음 클라이언트 컴포넌트에서 이 값을 읽고 상태에 저장하면 컴포넌트가 서버 작업의 결과를 뷰어에 표시할 수 있습니다.
// app/page.tsx
'use client'
 
import { create } from './actions'
import { useState } from 'react'
 
export default function Page() {
  const [message, setMessage] = useState<string>('')
 
  async function onCreate(formData: FormData) {
    const res = await create(formData)
    setMessage(res.message)
  }
 
  return (
    <form action={onCreate}>
      <input type="text" name="item" />
      <button type="submit">Add</button>
      <p>{message}</p>
    </form>
  )
}
  • 알아두면 좋은 점
    • 현재 로드 또는 에러 상태를 표시하려면 클라이언트 컴포넌트를 사용해야 합니다. 서버 작업의 안정성이 향상됨에 따라 이러한 값을 검색하는 서버 측 함수에 대한 옵션을 탐색하고 있습니다.

낙관적인 업데이트

  • 응답을 기다리지 않고 서버 작업이 완료되기 전에 useOptimistic을 사용하여 UI를 낙관적으로 업데이트합니다.
  • 서버에서 응답을 받기 전에 UI를 업데이트!
// app/page.tsx
'use client'
 
import { experimental_useOptimistic as useOptimistic } from 'react'
import { send } from './actions'
 
type Message = {
  message: string
}
 
export function Thread({ messages }: { messages: Message[] }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic<Message[]>(
    messages,
    (state: Message[], newMessage: string) => [
      ...state,
      { message: newMessage },
    ]
  )
 
  return (
    <div>
      {optimisticMessages.map((m) => (
        <div>{m.message}</div>
      ))}
      <form
        action={async (formData: FormData) => {
          const message = formData.get('message')
          addOptimisticMessage(message)
          await send(message)
        }}
      >
        <input type="text" name="message" />
        <button type="submit">Send</button>
      </form>
    </div>
  )
}

쿠기 설정

  • 쿠키 기능을 사용하여 서버 작업 내에서 쿠키를 설정할 수 있습니다.
// app/actions.ts
'use server'
 
import { cookies } from 'next/headers'
 
export async function create() {
  const cart = await createCart()
  cookies().set('cartId', cart.id)
}

쿠키 읽기

  • 쿠키 기능을 사용하여 서버 작업 내에서 쿠키를 읽을 수 있습니다.
// app/actions.ts
'use server'
 
import { cookies } from 'next/headers'
 
export async function read() {
  const auth = cookies().get('authorization')?.value
  // ...
}

쿠키 삭제

  • 쿠키 기능을 사용하여 서버 작업 내에서 쿠키를 삭제할 수 있습니다.
// app/actions.ts
'use server'
 
import { cookies } from 'next/headers'
 
export async function delete() {
  cookies().delete('name')
  // ...
}

Reference

profile
주도적으로 문제를 정의하고 코드를 통해 해결합니다.

0개의 댓글