태그
목차

'use server'

생성일: 2024-02-04

수정일: 2024-02-04

Canary

'use server' 는 React 서버 컴포넌트를 지원하는 라이브러리를 사용하는 경우에만 필요하다.

'use server' 는 클라이언트 사이드 코드에서 호출할 수 있는 서버 사이드 함수를 표시한다.

레퍼런스

'use server'

비동기 함수 본문 상단에 'use server' 를 추가하여 함수를 클라이언트에서 호출 가능한 것으로 표시한다.

이러한 함수를 서버 액션이라고 부른다.

async function addToCart(data) {
  'use server';
  // ...
}

클라이언트에서 서버 액션을 호출하면 서버 액션 함수에 전달한 인자를 직렬화하여 네트워크 요청을 서버에 보낸다. 서버 액션이 값을 반환하면 해당 값이 직렬화되어 클라이언트에 반환된다.

함수에 'use server' 를 개별적으로 표시하는 대신 파일 상단에 지시문을 추가하여 해당 파일 내의 모든 내보내기(export)를 클라이언트 코드에서 가져오는 것(import)을 포함하여 어디에서나 사용할 수 있는 서버 액션으로 표시할 수 있다.

주의사항

보안 고려 사항

서버 액션에 대한 인자는 완전히 클라이언트가 제어한다. 보안을 위해 항상 신뢰할 수 없는 입력으로 취급하고 인자의 유효성을 검사하고 적절하게 이스케이프 처리해야 한다.

모든 서버 액션에서 로그인한 사용자가 해당 작업을 수행할 수 있는지 확인해야 한다.

Warning

서버 액션에서 민감한 데이터를 전송하는 것을 방지하기 위해 특정 값과 객체가 클라이언트 코드에 전달되지 않도록 하는 실험적인 테인트 API가 있다.

experimental_taintUniqueValueexperimental_taintObjectReference를 살펴보도록 한다.

직렬화 가능한 인자 및 리턴 값

클라이언트 코드가 네트워크를 통해 서버 액션을 호출할 때 전달되는 모든 인자는 직렬화할 수 있어야 한다.

다음은 서버 액션 인자로 지원되는 유형이다:

특히, 다음은 지원되지 않는다:

지원되는 직렬화 가능 반환 값은 바운더리 클라이언트 컴포넌트의 직렬화 가능 프로퍼티와 동일하다.

사용법

폼에서 서버 액션

서버 액션의 가장 일반적인 사용 사례는 데이터를 변경하는 서버 함수를 호출하는 것이다. 브라우저에서 HTML 폼 엘리먼트는 사용자가 뮤테이션을 제출하는 전통적인 접근 방식이다. React 서버 컴포넌트를 통해 React는 폼에서 서버 액션에 대한 최고 수준의 지원을 도입했다.

다음은 사용자가 username을 입력할 수 있는 폼이다.

// App.js

async function requestUsername(formData) {
  'use server';
  const username = formData.get('username');
  // ...
}

export default function App() {
  return (
    <form action={requestUsername}>
      <input type="text" name="username" />
      <button type="submit">Request</button>
    </form>
  );
}

이 예제에서 requestUsername<form> 에 전달된 서버 액션이다. 사용자가 이 폼을 제출하면 서버 함수 requestUsername 에 대한 네트워크 요청이 이루어진다. 폼에서 서버 액션을 호출할 때, React는 폼의 FormData 를 서버 액션의 첫 번째 인자로 제공한다.

서버 액션을 폼 action 에 전달하면 React는 폼을 점진적 향상시킬 수 있다. 즉, 자바스크립트 번들이 로드되기 전에 폼을 제출할 수 있다.

폼에서 리턴 값 처리하기

username 요청 폼에서는 사용 가능한 username이 없을 수 있다. requestUsername 은 실패 여부를 알려주어야 한다.

점진적 향상을 지원하면서 서버 액션의 결과에 따라 UI를 업데이트하려면 useFormState 를 사용한다.

// requestUsername.js

'use server';

export default async function requestUsername(formData) {
  const username = formData.get('username');
  if (canRequest(username)) {
    // ...
    return 'successful';
  }
  return 'failed';
}
// UsernameForm.js

'use client';

import { useFormState } from 'react-dom';
import requestUsername from './requestUsername';

function UsernameForm() {
  const [returnValue, action] = useFormState(requestUsername, 'n/a');

  return (
    <>
      <form action={action}>
        <input type="text" name="username" />
        <button type="submit">Request</button>
      </form>
      <p>Last submission request returned: {returnValue}</p>
    </>
  );
}

대부분의 Hook과 마찬가지로, useFormState 는 클라이언트 코드에서만 호출할 수 있다.

<form> 외부에서 서버 액션 호출하기

서버 액션은 노출된 서버 엔드포인트이며 클라이언트 코드의 어느 곳에서나 호출할 수 있다.

외부에서 서버 액션을 사용하는 경우 트랜지션에서 서버 액션을 호출하면 로딩 표시기를 표시하고, 낙관적 상태 업데이트를 표시하고, 예기치 않은 오류를 처리할 수 있다. 폼은 트랜지션에서 서버 액션을 자동으로 래핑한다.

import incrementLike from './actions';
import { useState, useTransition } from 'react';

function LikeButton() {
  const [isPending, startTransition] = useTransition();
  const [likeCount, setLikeCount] = useState(0);

  const onClick = () => {
    startTransition(async () => {
      const currentCount = await incrementLike();
      setLikeCount(currentCount);
    });
  };

  return (
    <>
      <p>Total Likes: {likeCount}</p>
      <button onClick={onClick} disabled={isPending}>
        Like
      </button>;
    </>
  );
}
// actions.js
'use server';

let likeCount = 0;
export default async function incrementLike() {
  likeCount++;
  return likeCount;
}

서버 액션 리턴 값을 읽으려면 프로미스가 반환될 때까지 await 해야 한다.