Next.js 캐럿마켓 클론코딩(SERVER ACTIONS)

짜스의 하루 ·2024년 8월 2일

Server Actions

Next.js의 Server Actions는 Next.js 13에서 도입된 새로운 기능으로, 주로 서버에서 실행되는 코드를 간편하게 작성하고 관리할 수 있도록 돕는 기능이다. Server Actions는 React 컴포넌트 내에서 서버 사이드 로직을 쉽게 실행할 수 있게 해주며, 서버와 클라이언트 간의 데이터 흐름을 단순화해준다.

Server Actions의 주요 특징
1 . 서버에서 실행: Server Actions는 서버에서 실행되며, 클라이언트로 전달되지 않는다. 즉, 민감한 데이터나 비즈니스 로직을 안전하게 처리할 수 있다.

2 . React 컴포넌트와의 통합: React 컴포넌트 내부에서 Server Action을 정의하고 사용할 수 있다. 이를 통해 컴포넌트 내에서 바로 서버 상의 작업(예: 데이터베이스 쿼리, API 호출)을 수행할 수 있다.

3 . Form Handling: Server Actions는 기본적으로 폼 제출을 처리하는 데 유용하다. 클라이언트에서 폼을 제출하면, 서버에서 Action이 실행되고, 그 결과가 클라이언트로 다시 전달된다.

4 . 간단한 API: Server Actions는 함수로 정의되며, 그 함수가 React 컴포넌트 내에서 호출된다. 함수 내에서는 서버 사이드 로직을 작성할 수 있으며, React는 이를 클라이언트와 서버 간의 상태 동기화와 관련된 처리를 자동으로 관리한다.

이렇게 설명만 해놓으면 뭔지 모르기 때문에
login.tsx 에서 로그인 처리를 Server Actions를 사용해서 해보도록 하겠다

  • 'use server' 는 Next.js의 Server Actions에서 사용되는 지시자이다.
    이 지시자를 추가하면 해당 함수가 클라이언트에서 실행되는 것이 아니라 서버에서만 실행된다는 것을 명시하게 된다.
  • 함수의 매개변수 data는 FormData 타입으로 선언
    --> FormData는 HTML 폼 데이터를 쉽게 다룰 수 있도록 도와주는 내장 객체로, 폼 필드와 그 값을 쌍으로 저장된다.

여기서, <FormInput/> 를 자세하게 살펴보면, name이 생긴 것을 확인할 수 있다.

name 속성폼 데이터를 서버로 전송할 때, 해당 데이터의 "키"로 사용된다.
예를 들어, <input type="email" name="email" />와 같은 필드가 있으면, 사용자가 입력한 이메일 주소는 email이라는 이름의 키에 저장되게 된다.

각각의 type에 맞게 name을 지정해두게 되면,
이렇게 email, password에 각각의 데이터가 저장되는 것을 확인할 수 있다.

console.log(data.get('email'), data.get('password')); 를 통해서 서버에서 정상적으로 작동하는 것 또한 확인할 수 있었다!


useFormStatus

그럼 이제, 로그인 form 을 제출하고, 5초 뒤에 로그인을 실행시켜보도록 하겠다.
(보통 사이트에서 로그인을 하게 될 때, 바로 이루어지지 않고 2~3초 정도 기다리는 경험을 해본 적이 있을 것이다)

new Promise((resolve) => setTimeout(resolve, 5000)) : 새로운 Promise 객체를 생성한다.
Promise자바스크립트의 비동기 작업을 처리하기 위한 객체로, 보통 비동기 작업이 성공했을 때 resolve, 실패했을 때 reject를 호출

setTimeout을 사용하여 5초(5000ms) 후에 resolve를 호출한다.
setTimeout은 지정된 시간 후에 주어진 콜백 함수를 실행하는 함수를 의미한다.

이렇게 처리를 시켜두고 로그인을 시도해보면

이 화면 상태 그대로 5초를 유지하게 된다.
이 화면을 본 사용자라면, '로그인 처리가 제대로 안된건가?'하면서 "Create account"버튼을 다시 누르거나, 뒤로 나가버리는 경우가 발생한다.

그래서, 사용자에게 로그인 처리 중임을 UI로 보여주어야 한다.
이때 사용되는 것이 useFormStatus()함수이다.

useFormStatus()함수는 Next.js 13에서 도입된 기능으로, 서버 액션(Server Actions)을 사용하여 폼을 제출할 때, 폼의 상태(예: 제출 중, 완료, 오류 등)를 추적하고 UI에 반영하는 데 사용된다.

const { } = useFormStatus() 이렇게 사용되는데, {} 괄호 안에 다양한 기능을 제공해주지만, pending을 주로 사용하게 된다 ! ( 폼이 제출 중이면 true가 된다 )

  • 이 hook은 action을 실행하는 form과 같은 곳에서 사용할 수 없음.
  • 이 hook은 form의 자식에서만 사용이 가능함.

즉, 이 hook은 form의 상태에 따라서 변경하고자 하는 component내부에서 사용되어야 한다.
이게 무슨 소리인가? 로그인을 처리하는 login/page.tsx를 살펴보면,
<form/> 태그 내에서 action = {handleForm}을 실행하고 있다. 고로 이 login/page.tsx에서는 useFormStatus() 를 사용하지 못한다(규율이다)

그럼 우리는 어디서 useFormStatus()를 사용할 수 있는가?
--> <FormButton/>에서 사용할 수 있다 (form의 자식)

로그인을 했을 때, 이 버튼의 상태로 로그인을 처리하고 있음을 보여주면 된다.

form-btn.tsx

여기서 useFormStatus()의 특징을 살펴보면
폼 상태 확인: const { pending } = useFormStatus() 를 통해 pending 상태를 가져온다.
pending이 true일 때는 폼이 서버에 제출된 상태로, 폼 처리가 진행 중임을 의미한다.

버튼 상태 관리:
disabled={pending} : 폼이 제출 중(pending이 true) 이면 버튼을 비활성화시킨다.
--> 이는 사용자가 폼이 처리 중일 때 중복으로 버튼을 누르지 않도록 하기 위함이다.
--> pending일 때는 "로딩 중..."이라는 텍스트가 표시되고, pending이 아닐 때는 text 속성에서 전달된 기본 텍스트가 표시된다.

useFormStatus()를 통해서 -> 요청의 상태를 알 수 있음
이 hook은 자동으로 부모 form을 찾게 되며,
그리고 부모 form의 action 이 pending 상태인지를 자동으로 알아내게 된다.

이렇게 "로딩 중 ..." 메시지와 함께 로딩중임을 사용자에게 보여주면서,disabled={pending}을 통해 버튼을 비활성화 시켜두면서 중복 로그인을 방지할 수 있었다.


useFormState

useFormStatus() 는 진행중인 상태를 보여주는거라면,
useFormState() 는 이 훅은 폼 제출 후의 결과 상태를 추적하는 데 사용된다.
예를 들어, 사용자가 비밀번호나 이메일을 잘못 입력했을 때, 서버에서 반환된 오류 메시지를 UI에 표시하고 싶을 때 사용된다.
폼이 성공적으로 처리되었는지, 아니면 오류가 발생했는지를 확인하고 그에 맞는 메시지나 처리를 UI에 반영할 수 있다.

어떻게 사용하는지 한 번 살펴보러 가보자

const [state, action] = useFormState(handleForm, null);
useFormState(handleForm)을 넘겨주면, handleForm action을 실행하고 return 값을 반환하게 된다.
(여기서 handleForm 이외에 null 값 초기값을 넘겨주는데, action은 formdata와 함께 호출이 되는데, 초기에는 state와 함께 호출되고 그리고 다음부터는 이전 action에서 return 된 state와 함께 호출될 것임.)

  • state -> action의 return 값
  • action -> ()괄호 안에 들어가는 action을 실행시킬것임 (트리거라고 함)

이 hook을 쓴다는 건 UI를 interactive하게 만들겠다는 의미를 뜻함
폼 제출 후에 결과 상태에 따라서 redirect를 하거나, error메시지를 보내는 등, interactive하게 대응하기 때문이다!

그러기 때문에 , "use client" 를 꼭 기입을 해주어야 한다.
그럼 이러한 오류가 발생하게 되는데, 이 오류를 해석하면, "use serveruse client" 에서 사용이 가능하지 않다를 의미한다.

그럼 어떻게 해야할 까?
use server를 코드를 분리하면 된다. /login/action.ts라는 폴더를 만들어서 상단에 'use server' 를 작성해두면 서버에서 실행되게 된다!

그럼 이제 useFormState에서 제공한 action을 <form/> 태그 안에 action에 넘겨주면 된다, 어 ? 왜 action을 넘기지?
useFormState()에서 제공하는 action은 괄호에 들어가는 함수를 실행시키게 된다.
즉, handleForm을 실행시키게 된다. 그렇게 실행된 후 return 값을 state에 받아오게 된다.

현재, return 값에서 errors를 반환하고 있기 때문에, 이 errors를 state?.errors ?? []로 넘겨주게되면,
이렇게 handleForm에 반환한 return 값이 UI에 뜨는 것을 확인할 수 있다.

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글