[NextJS] 서버액션 개념 정리

최하영·2024년 11월 23일
2

NextJS

목록 보기
3/3
post-thumbnail

서버액션이란?

이번에 next.js로 프로젝트를 만들었는데 (wowmazon) 토큰 관리에서 꽤나 어려움을 느꼈다. 이때 마주한 next.js의 서버액션에 대해서 알아보려고한다.

서버액션 : next.js 서버측에서 실행되는 비동기함수.

  • 데이터 변경과 폼 제출 관리하는데 사용되며, 서버액션을 통해 서버에서 데이터를 처리하고 결과를 클라이언트에 전달한다.

서버액션의 기능

서버액션은 비동기적으로 실행 되기때문에 서버와 클라이언트 간의 통신 동안 사용자 인터페이스를 멈추지않고 사용자가 계속 사용할 수 있도록 해준다. 그외에도,,

  • 폼 제출 처리 :<form>태그와 연동되어 서버측에서 사용자 입력을 받아 처리할 수 있음.
  • 데이터 관리 : 데이터를 수정, 생성, 삭제 하는 등의 변형 작업을 처리해준다. (이 과정에서 데이터의 무결성을 보장하고 필요한 경우 캐시를 재검증하여 최신 데이터를 제공한다고한다.)
  • 서버와 클라이언트 에서 모두 사용 : 클라이언트 컴포넌트와 서버컴포넌트에서 모두 사용할 수 있고, "use server"라는 키워드를 통해서 서버액션을 설정할 수 있다.

서버액션 특징들

"use server" 키워드 설정

위에서 말했듯 저 키워드 하나로 서버액션을 간단히 정의할 수 있다. 함수나 파일 상단에 배치하여 해당 함수 또는 파일을 서버에서만 실행되도록 지정할 수 있다.

"use server";

export async function addNumbers(a, b) {
    return a + b;
}

서버컴포넌트와 클라이언트 컴포넌트에서 모두 사용가능한 서버액션

서버액션은 클라이언트와 서버컴포넌트 모두에서 사용가능하다.
위에 서버액션 파일 안에있는 addNumber() 함수를 클라이언트 컴포넌트에서 불러올 수 있다.

"use client";

import { useState } from "react";
import { addNumbers } from "./actions";

export default function HomePage() {
    const [result, setResult] = useState(null);

    async function handleAddition() {
        const sum = await addNumbers(5, 10); // 서버에서 계산
        setResult(sum); // 결과를 상태에 저장
    }

    return (
        <div>
            <h1>Simple Server Action Example</h1>
            <button onClick={handleAddition}>Add 5 + 10</button>
            {result !== null && <p>Result: {result}</p>}
        </div>
    );
}

이런 처리 방식은 next.js 어플리케이션에서 비동기적으로 작업을 관리할 수 있으며, 데이터처리가 서버 중앙에서 관리되고 클라이언트와 서버간의 일관된 데이터 흐름을 유지할 수 있다.

서버액션 사용

  • form 태그에서의 사용
// 페이지 컴포넌트
export default function Page() {
  async function handleSubmit(Data) {
    'use server';
    // 폼 데이터 처리 로직
    console.log(formData.get('name'));
  }

  return (
    <form action={handleSubmit}>
      <input name="username" type="text" placeholder="이름" />
      <button type="submit">제출</button>
    </form>
  );
}

서버액션은 form 태그와 함께 연동해서 사용할 수 있다. form 태그 안에 action이라는 속성이 존재하는데 이걸 사용하면 폼이 제출될때 자동으로 서버액션이 호출된다고 한다.

(아쉽게도 나는 form 태그 처리시에 사용하지않았다. 예시는 간단히 이렇게만 하고 넘어가겠다.)

  • 데이터처리와 같은 비동기 함수 실행
    서버액션은 클라이언트와 서버 간의 상호작용을 간소화하며, 서버에서 데이터를 처리하거나 반환할 때 사용된다. 이 기능은 React의 서버 컴포넌트와 비동기 함수 실행을 기반으로 동작한다. 따라서 다음과 같은 동작을 처리할 수 있다.
    • 데이터베이스 쿼리
    • API 호출
    • 데이터 검증 및 처리

그중 우리가 사용한 동작은 api 호출이 되겠다.

"use server"
//카테고리 불어오는 함수
export const getCategoryId = async (
  queryParams?: ProductCategoryParamsType
) => {
  let stringRecord: Record<string, string> = {};
  if (queryParams) {
    stringRecord = createQueryString(queryParams);
  }
  const data = await fetchWithToken<GetProductCategoryResponse>(
    "category/",
    {},
    stringRecord
  );
  return data;
};

위 서버액션 함수로 카테고리를 불러오는 infiniteQuery 훅 함수를 만들었고 이를 상품 목록 클라이언트 컴포넌트에서 불러왔다.

//카테고리 훅함수
export const useInfiniteCategory = () => {
  return useInfiniteQuery({
    queryKey: [INFINITE_CATEGORY],
    queryFn: ({ pageParam = "" }) => {
      const params: ProductCategoryParamsType = pageParam
        ? { cursor: pageParam }
        : {};
      return getCategoryId(params);
    },
    initialPageParam: "",
    // 생략
  • CORS 에러 문제해결
    이건 사용하고 나서 알게되었는데 , 서버액션을 사용해서 외부 api 에 요청하게 되면 CORS 에러는 일어나지 않는다.

동작 방식은 이러하다.

  1. 클라이언트는 서버 액션을 호출. 이 요청은 브라우저에서 Next.js 서버로만 전달되므로 CORS 문제가 발생하지 않는다.

  2. Next.js 서버에서 외부 API를 호출한다. 여기서의 통신은 서버와 서버간의 통신이므로 브라우저 정책인 CORS 정책에 영향을 받지 않는다.

  3. Next.js 서버가 외부 API의 응답을 클라이언트로 반환.

"use server";

import { createURLWithParams } from "@/utils/apis/create-URL-Params";
import { getValidAccessToken } from "@/auth/token";

const NITO_BASE_URL = process.env.NEXT_PUBLIC_NITO_URL;

export const fetchWithToken = async <T>(
  endpoint: string,
  options: RequestInit = {},
  queryParams?: Record<string, string> // 쿼리 파라미터를 위한 인수 추가
): Promise<T> => {
  const url = createURLWithParams(`${NITO_BASE_URL}`, endpoint, queryParams);

  const token = await getValidAccessToken();

  if (!token) {
    throw new Error();
  }
  const response = await fetch(url, {
    method: options.method || "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
      ...options.headers, // 추가 헤더 병합
    },
    credentials: "include", // 자격증명 포함
    ...options,
  });
  if (!response.ok) {
    throw new Error(`API 요청 실패: ${response.statusText}`);
  }
  return response.json() as T;
};

프로젝트에서는 헤더에 토큰값을 싣는 이 함수를 통해 모든 api를 작성했는데 , 이렇게 작성한 함수는 서버에서 실행되며, 브라우저와 직접 통신하지 않고, 서버 간 통신을 통해 외부 API에 접근하기 때문에,
얼레벌레 CORS 에러를 해결하는 데 직접적인 역할을 해버렸다...

(다시 돌아와서)

또한 서버액션은 서버와 클라이언트 간의 데이터 전송시 올바르게 처리되고 전송되도록 보장하기 위해 인자와 반환값이 직렬화가 가능해야한다고 한다.

🖐🏻 여기서 잠깐
React에서 직렬화 가능 데이터란?
React가 데이터를 직렬화하려면 JSON 형태로 변환 가능한 데이터 타입이어야 한다.

[일반적으로 허용되는 데이터 타입]

  • 기본 타입: string, number, boolean, null
  • 객체: JSON 직렬화가 가능한 단순 객체
  • 배열: JSON 직렬화가 가능한 배열
  • Date: JSON 문자열로 변환됨

[허용되지않는 데이터 타입]

  • 함수
  • undefined
  • Symbol
  • Map, Set, WeakMap, WeakSet
  • 순환 참조가 포함된 객체

이처럼 서버액션을 잘 활용하면 next.js 애플리케이션에서 폼처리, 데이터 관리 및 비동기 작업을 효율적으로 수행 할 수있다.
(해당 기능은 next.js 14버전 이상부터는 디폴트값으로 추가가되어 따로 nextconfig 파일에서 설정하지 않아도 된다.)

2개의 댓글

comment-user-thumbnail
7일 전

굉장 한데요~~>??? 잘 읽고갑니다

1개의 답글