[Next.js] 서버 액션 쓰면서 form 초기화하기

련지·2024년 7월 12일

Next.js

목록 보기
2/3

서버 액션을 써서 form을 제출하고 싶었다.
그리고 정상 제출되면 form의 모든 필드를 비우고 싶었다.

...'싶었다'는 말은 생각처럼 되지 않았다는 뜻이다.

정답은 결국 클라이언트 컴포넌트 로 바꾸기
서버 컴포넌트 좋은 거 맞아...? 라고 할뻔

아무리 검색해도 서버 컴포넌트에서 폼 제출 후 초기화 방법이 없었다.
영어영어 글들을 열심히 영어영어해도 다 "use client" 가 붙어 있었다.
아, 클라이언트 컴포넌트로 만드는 수밖에 없구나!

그래서 나도 붙였는데 클라이언트 컴포넌트에서는 "use server"를 못 쓴다고 에러가 떴다.
... 그래서 action에 걸어놓은 함수를 따로 파일로 뺐다.

src/component/comment/InputForm.tsx


import { postComment } from "@/app/comment/_apis/postComment";
import { useRef } from "react";
import { revalidatePath } from "next/cache";

export default function InputForm() {
  const formRef = useRef<HTMLFormElement>(null);

  return (
    <form
      ref={formRef}
      action={async (formData: FormData) => {
        const result = await postComment({ formData });
        if (result === 200) {
          formRef.current?.reset();
          revalidatePath("/comment", "page");
        }
      }}
    >
    // ~~~
    </form>
  );
};

여기서 봐줘야 하는 건 아래 두 가지, 역할은 다음과 같다.

  1. ref : 제출 후 초기화할 대상으로 설정한다.
  2. action : 폼이 제출될 때 수행할 동작을 설정한다.

1. useRef

이 친구가 하는 일을 간단하게 말하자면 찜>.<이다.
위의 코드를 예로 들면, 폼 태그에 ref={formRef} 를 써줬기 때문에formRef 라는 변수가 폼 태그를 찜>.<한 것이다.
그래서 앞으로 폼 태그에 이것저것 해주고 싶다면 formRef 에 이것저것 해주면 된다.
그럼 찜콩해준 태그에 실제로 적용이 된다.
그래서 제출 완료 후 폼 태그 값을 다 갈아버리고 싶을 때 formRef.current?.reset(); 을 쓰면 실제로 리셋된다.

2. action

기존 "use server" 를 쓰던 함수를 저 postComment 로 뺀 것이다.
폼 데이터 어떻게 넘기지?!...는 이벤트 객체 e 넘길 때 처럼 그냥 formData 로 넘기면 된다.

src/app/comment/_apis/postComment.ts

"use server";

import { $Fetch } from "@/util/fetch";

export const postComment = async ({ formData }: { formData: FormData }) => {
  const title = formData.get("title") as string;
  const name = formData.get("name") as string;
  const content = formData.get("content") as string;

  const data = {
    title: title,
    name: name,
    content: content,
  };

  const now = new Date();
  const write_date = new Date(
    now.toLocaleString("en-US", { timeZone: "Asia/Seoul" })
  )
    .toISOString()
    .slice(0, 19)
    .replace("T", " ");
  const dataWithDate = {
    ...data,
    write_date: write_date,
  };

  const result = await $Fetch({
    method: "POST",
    url: "comment",
    cache: "default",
    data: dataWithDate,
  });

  return result.status;
};

이렇게 해주면 클라이언트 컴포넌트 안에 "use server" 를 쓸 수 있다.
$Fetch 는 뭐지? 싶다면 여기로...
문제 없이 POST 요청이 완료됐다면 데이터 캐싱도 지워주고 폼도 지워줘야 하니까 결과의 status 를 반환해준다.


서버 컴포넌트로 해보고 싶었지만... 실패했다!
그래도 클라이언트 컴포넌트 안에 "use server" 쓰는 법을 알았다!

profile
기술 블로그도 재미있을 수 있잖아요

0개의 댓글