티스토리 뷰

 

웹 개발의 흐름은 늘 '복잡함'에서 '단순함'으로 회귀하곤 합니다. 과거에는 HTML Form을 던지면 서버가 받는 단순한 구조였다가, SPA(Single Page Application) 시대에 접어들며 클라이언트에서 데이터를 가공하고 API 엔드포인트를 호출하는 복잡한 과정을 거치게 되었죠.

Next.js에서 도입된 Server Actions는 이 복잡한 중간 단계를 제거합니다. 별도의 API Route를 만들지 않고도 클라이언트의 이벤트를 서버 함수와 직접 연결하는 이 기술이 왜 현대 웹 생태계의 '게임 체인저'로 불리는지 심도 있게 살펴보겠습니다.


1. 핵심 개념 설명 (Deep Dive)

작동 원리: 브릿지 없는 데이터 전송

Server Actions는 클라이언트에서 호출하지만 실제로는 서버에서 실행되는 비동기 함수입니다. 이전에는 폼 제출을 위해 fetch나 axios를 사용해 특정 URL로 POST 요청을 보내고 리스폰스를 기다려야 했습니다. 하지만 Server Actions를 사용하면 함수 자체가 하나의 엔드포인트 역할을 수행합니다.

일상적인 비유: 무전기와 유선 전화

  • 기존 API 방식 (유선 전화): 전화를 걸고, 상대방이 받을 때까지 기다린 뒤, 내 신원을 밝히고 용건을 말해야 합니다. (엔드포인트 설정, JSON 직렬화, 페칭 로직 필요)
  • Server Actions (무전기): 버튼 하나만 누르면 서버(상대방)와 즉시 연결되어 메시지가 전달됩니다. 복잡한 연결 과정은 시스템 내부에서 알아서 처리해주죠.

2. 풍부한 실전 예제 (Hands-on)

단순한 이름 입력 폼이 아닌, 실제 이커머스에서 활용될 법한 "제품 재고 업데이트 시스템" 예제를 통해 구현 방법을 알아보겠습니다.

단계 1: 서버 액션 정의하기

먼저 서버에서만 실행될 로직을 분리하여 작성합니다. use server 지시어가 이 함수의 실행 환경을 결정합니다.

JavaScript
 
// actions/inventory.js
"use server";

import { revalidatePath } from "next/cache";

/**
 * 재고 수량을 업데이트하는 서버 액션
 * @param {FormData} formData - 폼에서 전달된 데이터
 */
export async function updateStock(formData) {
  const productId = formData.get("productId");
  const quantity = formData.get("quantity");

  // 1. 비즈니스 로직: 실제 데이터베이스 업데이트 (예시)
  console.log(`DB 작업 시작: 제품 ID ${productId}의 재고를 ${quantity}개로 변경합니다.`);
  
  // 데이터베이스 연결 및 업데이트 로직이 여기에 위치합니다.
  // await db.product.update({ where: { id: productId }, data: { stock: quantity } });

  // 2. 캐시 갱신: 데이터가 변했으므로 페이지를 새로 고침하여 사용자에게 최신 상태를 보여줍니다.
  revalidatePath("/inventory");

  return { success: true, message: "재고가 성공적으로 업데이트되었습니다." };
}

단계 2: 클라이언트 컴포넌트 구성

별도의 onSubmit 핸들러나 상태 관리가 필요 없습니다. 폼의 action 속성에 위에서 만든 함수를 연결하기만 하면 됩니다.

JavaScript
 
// components/StockForm.js
import { updateStock } from "@/actions/inventory";

export default function StockForm({ productId }) {
  return (
    <form action={updateStock} className="stock-form">
      {/* 제품 ID는 사용자에게 보이지 않게 hidden 필드로 처리 */}
      <input type="hidden" name="productId" value={productId} />
      
      <div className="input-group">
        <label htmlFor="quantity">조정 수량</label>
        <input 
          type="number" 
          id="quantity" 
          name="quantity" 
          required 
          placeholder="수량을 입력하세요"
        />
      </div>

      <button type="submit">재고 업데이트 반영</button>
    </form>
  );
}

트러블슈팅 팁

  • 데이터 타입 문제: formData.get()으로 가져오는 값은 기본적으로 **문자열(String)**입니다. 숫자가 필요한 로직이라면 Number()를 통해 반드시 형변환을 거쳐야 에러를 방지할 수 있습니다.
  • 로딩 상태 처리: 폼 제출 중 버튼을 비활성화하고 싶다면 React의 useFormStatus 훅을 활용하세요. 이 훅은 해당 폼의 상태를 자동으로 감지합니다.

3. 장단점 및 고려사항 (Trade-offs)

Server Actions는 강력하지만 모든 상황에서 정답은 아닙니다.

장점

  • 코드 감소: API Route를 정의하는 보일러플레이트 코드가 사라집니다.
  • 점진적 향상: 자바스크립트가 로드되기 전에도 브라우저 기본 기능을 통해 폼 전송이 가능해집니다.
  • 강력한 타입 안전성: 서버와 클라이언트 간의 데이터 규약을 맞추기가 훨씬 수월합니다.

단점 및 한계

  • 서버 리소스 점유: 모든 액션이 서버에서 실행되므로, 단순한 UI 변경 로직까지 서버 액션으로 처리하면 불필요한 서버 부하를 줄 수 있습니다.
  • Next.js 종속성: 프레임워크에 강하게 결합되므로 나중에 다른 환경으로 이전할 때 코드 수정이 많이 필요할 수 있습니다.

4. 결론 및 요약

Server Actions는 클라이언트와 서버 사이의 벽을 허물어 개발 생산성을 비약적으로 높여줍니다. 특히 데이터 쓰기(Write) 작업과 페이지 갱신(Revalidation)이 동시에 일어나는 폼 처리에서 그 진가를 발휘합니다.

단순히 "기술이 새로우니까" 쓰는 것이 아니라, 내 프로젝트에서 API 레이어를 관리하는 비용이 얼마나 큰지를 먼저 따져보세요. 만약 단순 CRUD가 반복되는 프로젝트라면 Server Actions는 여러분의 퇴근 시간을 앞당겨줄 최고의 도구가 될 것입니다.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/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
글 보관함