Server Actions

Odyssey·2025년 2월 7일
0

Next.js_study

목록 보기
27/58
post-thumbnail

2025.2.7 금요일의 공부기록

Next.js에서 폼(Form) 제출을 처리하는 최신 방식으로 Server Actions을 활용하면 fetch 없이 서버에서 직접 데이터를 처리할 수 있다.
이 글에서는 Server Actions을 이용한 로그인 폼 처리 실습 코드를 분석하고, 기존 방식과의 차이점을 정리한다.


Server Actions란?

  • 서버에서 실행되는 비동기 함수.
  • 클라이언트 및 서버 컴포넌트에서 호출 가능.
  • fetch() 없이 데이터를 변형하거나 서버와 직접 상호작용할 수 있음.
  • 데이터 업데이트 후 자동으로 페이지를 갱신(Revalidate) 가능.

🔗 공식 문서:


Server Actions을 활용한 Form 제출

기존 fetch()를 사용하던 방식과 달리, Next.js에서는 폼의 action 속성에 서버 액션을 직접 연결하여 데이터를 처리할 수 있다.

📌 기존 방식 (fetch 사용)

const handleSubmit = async () => {
  const response = await fetch("/api/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });
};

📌 단점:

  • API 요청(fetch())을 사용해야 하므로 코드가 길어짐.
  • 클라이언트에서 JSON 변환(JSON.stringify())이 필요함.

최신 방식 (Server Actions 활용)

Server Actions을 사용하면 API 없이 폼 데이터를 직접 서버에서 처리할 수 있다.
👉 fetch() 없이 바로 서버 함수 실행 가능!

1️⃣ 로그인 폼 컴포넌트 (login.tsx)

import FormButton from "@/components/form-btn";
import FormInput from "@/components/form-input";
import SocialLogin from "@/components/social-login";

export default function LogIn() {
  // Server Action: Form 데이터 처리
  const handleForm = async (formData: FormData) => {
    "use server"; // 이 함수는 서버에서 실행됨
    console.log(formData.get("email"), formData.get("password"));
    console.log("form submitted");
  };

  return (
    <div className="flex flex-col gap-10 py-8 px-6">
      <div className="flex flex-col gap-2 *:font-medium">
        <h1 className="text-2xl">안녕하세요!</h1>
        <h2 className="text-xl">Log in with email and password.</h2>
      </div>
      {/* Next.js Server Actions 활용 */}
      <form action={handleForm} className="flex flex-col gap-3">
        <FormInput
          name="email"
          type="email"
          placeholder="Email"
          required={true}
          errors={[]}
        />
        <FormInput
          name="password"
          type="password"
          placeholder="Password"
          required={true}
          errors={[]}
        />
        <FormButton loading={false} text="Log In" />
      </form>
      <SocialLogin />
    </div>
  );
}

📌 설명:

  • handleForm 함수는 "use server";를 선언하여 서버에서 실행됨.
  • formaction 속성에 handleForm을 직접 연결하여 fetch 없이 서버 액션 실행 가능.
  • formData.get("email"), formData.get("password")를 사용해 폼 데이터 추출.

기존 방식 vs 최신 방식 비교

방식기존 방식 (fetch)최신 방식 (Server Actions)
요청 방식fetch("/api")로 요청form action={handleForm} 직접 실행
API 필요 여부필요 (/api/login.ts)불필요 (서버 액션에서 직접 실행)
데이터 변환JSON.stringify() 필요formData.get()로 바로 사용 가능
보안API 엔드포인트 노출됨내부적으로 서버에서 실행됨

📌 결론:
Server Actions을 사용하면 API 없이도 클라이언트에서 서버로 데이터를 전송할 수 있어 더 깔끔하고 보안성이 높은 코드 작성이 가능하다. 🎯


Server Actions에서 데이터 검증 및 응답 추가

실제 서비스에서는 입력값 검증 및 응답 처리가 필요하다.
아래 코드에서는 폼 데이터 검증 후 메시지를 반환하는 방식을 추가했다.

2️⃣ 서버 액션에서 폼 데이터 검증

"use server";

export async function handleForm(formData: FormData) {
  const email = formData.get("email")?.toString();
  const password = formData.get("password")?.toString();

  if (!email || !password) {
    return { success: false, message: "이메일과 비밀번호를 입력하세요." };
  }

  if (email === "test@example.com" && password === "1234") {
    return { success: true, message: "로그인 성공" };
  } else {
    return { success: false, message: "이메일 또는 비밀번호가 올바르지 않습니다." };
  }
}

3️⃣ 클라이언트에서 서버 응답 처리

"use client";

import { useState } from "react";
import { handleForm } from "@/server-actions";

export default function LogIn() {
  const [message, setMessage] = useState("");

  const handleSubmit = async (formData: FormData) => {
    const response = await handleForm(formData);
    setMessage(response.message);
  };

  return (
    <div className="flex flex-col gap-10 py-8 px-6">
      <form action={handleSubmit} className="flex flex-col gap-3">
        <input name="email" type="email" placeholder="Email" className="border p-2" />
        <input name="password" type="password" placeholder="Password" className="border p-2" />
        <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded-md">
          Log In
        </button>
      </form>
      {message && <p className="text-red-500">{message}</p>}
    </div>
  );
}

📌 설명:

  • handleForm(formData)를 호출하여 서버에서 로그인 검증 실행.
  • 서버에서 반환한 메시지를 setMessage(response.message)로 상태 업데이트.
  • 오류 메시지를 화면에 출력.

Revalidating Data (데이터 갱신)

서버에서 데이터가 변경되면 자동으로 페이지를 리렌더링(Revalidate) 할 수도 있다.

📌 데이터 변경 후 자동 리렌더링 (revalidatePath())

import { revalidatePath } from "next/cache";

export async function handleForm(formData: FormData) {
  // 데이터 처리 후 자동으로 페이지 갱신
  revalidatePath("/dashboard");
}

📌 설명:

  • revalidatePath("/dashboard")를 호출하면 /dashboard 페이지가 자동으로 최신 데이터로 업데이트됨.

Server Actions을 언제 사용하면 좋을까?

  1. 폼 제출(Form Submission)

    • 로그인, 회원가입, 댓글 작성 등 양식 제출 처리.
  2. 데이터 저장 및 수정 (Mutations)

    • 데이터베이스에 새로운 항목 추가, 수정, 삭제.
  3. 서버 상태 업데이트

    • 특정 경로의 데이터를 변경하고, 자동으로 UI 갱신.
  4. API 요청 없이 서버와 상호작용

    • 기존 fetch() 호출 없이, 서버 액션을 함수처럼 호출할 수 있음.

그러면 Route Handlers는 필요없나?

⚠️ 그러지 않다!

언제 Route Handlers를 사용할까?

Server Actions가 더 간편한 방식이긴 하지만, 다음과 같은 경우에는 여전히 Route Handlers가 필요하다.

  1. 타사 API와 연동할 때

    • 예: 클라이언트가 fetch("/api/auth")를 호출하면, Next.js 서버에서 외부 API(fetch("https://external.com/auth"))를 호출해야 할 때.
  2. 파일 업로드 및 대용량 데이터 처리

    • 예: 클라이언트에서 파일을 업로드하고, Next.js 서버에서 이를 클라우드에 저장하는 경우.
  3. 완전한 REST API를 제공해야 할 때

    • 예: 모바일 앱 또는 다른 서비스에서 Next.js의 API를 호출해야 하는 경우.

최종 정리

방법사용 예시추천 상황
Server Actions ("use server";)await loginUser()API 없이 서버 코드를 직접 실행할 때
Route Handlers (api/route.ts)fetch("/api/users")REST API가 필요할 때, 타사 API 연동 시

Server Action 정리

✅ 기능🛠 설명
서버 액션 실행action={handleForm} 사용하여 직접 서버 함수 실행
API 없이 서버 함수 호출fetch("/api") 없이 직접 실행 가능
데이터 변경 후 자동 갱신revalidatePath("/users") 사용
서버에서 폼 제출 처리<form action={handleForm}>
  • fetch() 없이 서버와 직접 통신 가능 ✅
  • API 엔드포인트 필요 없음 ✅
  • 자동으로 최신 데이터 반영 ✅
  • 보안성이 높고 유지보수 용이 ✅

Next.js Server Actions을 활용하여 더 깔끔하고 효율적인 로그인 시스템을 만들어보자! 🚀
자세한 내용은 Next.js 공식 문서를 참고하자.

0개의 댓글