icon안동민 개발노트

서버 액션 (Server Actions) 이해하기


 서버 액션은 Next.js 13.4에서 도입된 강력한 기능으로, 서버 사이드 로직을 클라이언트 컴포넌트에서 직접 호출할 수 있게 해줍니다.

 이 절에서는 서버 액션의 개념, 사용 방법, 그리고 장점에 대해 자세히 알아보겠습니다.

서버 액션의 정의

 서버 액션은 서버에서 실행되는 비동기 함수로, 클라이언트에서 직접 호출할 수 있습니다.

 이를 통해 데이터베이스 조작, 파일 시스템 접근 등 서버 사이드 작업을 수행할 수 있습니다.

클라이언트-서버 상호작용 모델

 서버 액션은 기존의 API 라우트 방식과는 다른 새로운 상호작용 모델을 제공합니다.

  1. 클라이언트에서 서버 액션 호출
  2. 서버에서 액션 실행
  3. 서버에서 결과 반환 및 UI 업데이트

 이 모델은 클라이언트와 서버 간의 통신을 추상화하여 개발자가 더 직관적으로 서버 로직을 작성할 수 있게 해줍니다.

서버 액션 사용 방법

app/actions.js
 
export async function addTodo(formData) {
  const todo = formData.get('todo')
  // 데이터베이스에 todo 추가 로직
  console.log('Added todo:', todo)
  return { message: 'Todo added successfully' }
}

 클라이언트 컴포넌트에서 서버 액션 사용

'use client'
 
import { addTodo } from './actions'
 
export default function TodoForm() {
  return (
    <form action={addTodo}>
      <input type="text" name="todo" required />
      <button type="submit">Add Todo</button>
    </form>
  )
}

폼 제출 처리에서의 활용

 서버 액션은 폼 제출 처리에 특히 유용합니다.

 <form> 요소의 action 속성에 서버 액션을 직접 지정할 수 있습니다.

'use client'
 
import { addTodo } from './actions'
import { useRef } from 'react'
 
export default function TodoForm() {
  const formRef = useRef()
 
  async function handleSubmit(formData) {
    await addTodo(formData)
    formRef.current.reset()
  }
 
  return (
    <form action={handleSubmit} ref={formRef}>
      <input type="text" name="todo" required />
      <button type="submit">Add Todo</button>
    </form>
  )
}

서버 액션의 장점

  1. 보안 : 민감한 로직을 서버에서 실행하여 클라이언트에 노출되지 않습니다.
  2. 성능 : 서버에서 직접 데이터를 조작하여 불필요한 네트워크 요청을 줄입니다.
  3. 개발 경험 : 클라이언트와 서버 로직을 더 자연스럽게 통합할 수 있습니다.
  4. 프로그레시브 인핸스먼트 : JavaScript가 비활성화된 환경에서도 기본적인 기능이 작동합니다.

6장의 데이터 페칭과의 비교

 6장에서 다룬 데이터 페칭이 주로 데이터를 읽어오는 데 초점을 맞췄다면, 서버 액션은 데이터 변경(생성, 수정, 삭제)에 더 적합합니다.

  • 데이터 페칭 : 주로 GET 요청, 읽기 전용 작업
  • 서버 액션 : POST, PUT, DELETE 등 데이터 변경 작업

 서버 액션은 폼 제출이나 사용자 상호작용에 따른 서버 사이드 로직 실행에 더 적합합니다.

실습 : 서버 액션을 사용한 DB 갱신

 다음 요구사항을 만족하는 간단한 사용자 프로필 업데이트 기능을 구현해보세요.

  1. 사용자 이름 업데이트 폼 구현
  2. 서버 액션을 사용하여 데이터베이스 업데이트
  3. 업데이트 후 UI 즉시 갱신

 구현 예시

app/actions.js
import { db } from '@/lib/db'  // 가상의 데이터베이스 연결
 
export async function updateUsername(formData) {
  const newUsername = formData.get('username')
  
  // 데이터베이스 업데이트 (예시)
  await db.user.update({
    where: { id: 'current-user-id' },
    data: { username: newUsername },
  })
 
  return { success: true, username: newUsername }
}
app/profile/page.js
'use client'
 
import { useRef, useState } from 'react'
import { updateUsername } from '../actions'
 
export default function ProfilePage() {
  const formRef = useRef()
  const [username, setUsername] = useState('Current Username')
 
  async function handleSubmit(formData) {
    const result = await updateUsername(formData)
    if (result.success) {
      setUsername(result.username)
      formRef.current.reset()
    }
  }
 
  return (
    <div>
      <h1>Profile</h1>
      <p>Current username: {username}</p>
      <form action={handleSubmit} ref={formRef}>
        <input type="text" name="username" placeholder="New username" required />
        <button type="submit">Update Username</button>
      </form>
    </div>
  )
}

 이 실습을 통해 서버 액션을 사용하여 데이터베이스를 업데이트하고, 그 결과를 즉시 UI에 반영하는 과정을 경험할 수 있습니다.

 이는 실제 애플리케이션에서 자주 사용되는 패턴으로, Next.js의 서버 액션 기능을 활용하여 효율적인 클라이언트-서버 상호작용을 구현하는 방법을 보여줍니다.

 서버 액션은 Next.js 애플리케이션에서 서버 사이드 로직을 더 쉽고 효율적으로 통합할 수 있게 해주는 강력한 기능입니다.

 이를 통해 개발자는 더 안전하고 성능이 뛰어난 웹 애플리케이션을 구축할 수 있습니다.