인프런 커뮤니티 질문&답변

박 태영님의 프로필 이미지
박 태영

작성한 질문수

Next + React Query로 SNS 서비스 만들기

서버 컴포넌트에서 Server Actions 사용하기

use server를 클라이언트 컴포넌트에서 사용시 에러

작성

·

618

0

안녕하세요 섹션3의 3번째 강의 server action을 보고 있는데요

const submit에서 에러가 발생해서 질문합니다.

const submit에서 서버액션을 사용하기 위해 "use server"를 넣었는데 다음과 같은 에러가 발생합니다.

./src/app/(beforeLogin)/_component/SignupModal.tsx
Error: 
  × It is not allowed to define inline "use server" annotated Server Actions in Client Components.
  │ To use Server Actions in a Client Component, you can either export them from a separate file with "use server" at the top, or pass them down through props from a Server Component.
  │ 
  │ Read more: https://nextjs.org/docs/app/api-reference/functions/server-actions#with-client-components
  │ 
    ╭─[/home/tpark/SNS/z-com/src/app/(beforeLogin)/_component/SignupModal.tsx:3:1]
  3 │     import { redirect } from "next/navigation";
  4 │     
  5 │     export default function SignupModal() {
  6 │ ╭─▶   const submit = async (formData: FormData) => {
  7 │ │       "use server";
  8 │ │       if (!formData.get("id")) {
  9 │ │         return "아이디를 입력하세요.";
 10 │ │       }
 11 │ │       if (!formData.get("name")) {
 12 │ │         return "닉네임을 입력하세요.";
 13 │ │       }
 14 │ │       if (!formData.get("password")) {
 15 │ │         return "비밀번호를 입력하세요.";
 16 │ │       }
 17 │ │       if (!formData.get("image")) {
 18 │ │         return "이미지를 업로드하세요.";
 19 │ │       }
 20 │ │       if (!formData.get("id")) {
 21 │ │         return "이미 사용 중인 아이디입니다.";
 22 │ │       }
 23 │ │   
 24 │ │       let shouldRedirect = false;
 25 │ │       try {
 26 │ │         const response = await fetch(
 27 │ │           `${process.env.NEXT_PUBLIC_BASE_URL}/api/users`,
 28 │ │           {
 29 │ │             method: "post",
 30 │ │             body: formData,
 31 │ │             credentials: "include",
 32 │ │           }
 33 │ │         );
 34 │ │         console.log(response.status);
 35 │ │         if (response.status === 403) {
 36 │ │           return { message: "user_exists" };
 37 │ │         }
 38 │ │         console.log(await response.json());
 39 │ │         shouldRedirect = true;
 40 │ │       } catch (err) {
 41 │ │         console.error(err);
 42 │ │       }
 43 │ │       if (shouldRedirect) {
 44 │ │         redirect("/home");
 45 │ │       }
 46 │ ╰─▶   };
 47 │       return (
 48 │         <>
 49 │           <div className={style.modalBackground}>
    ╰────

서버 액션부분을 다른 컴포넌트로 분리해서 사용해야 하는거 같은데 강의 내용에서는 바로 진행이 되서 제가 어느부분이 잘못되었는지 잘 모르겠습니다.

그리고 혹시 최상단에 "use client"를 작성하지 않았기에 이건 서버컴포넌트인줄 알았는데 에러메세지에 클라이언트 컴포넌트에서 사용할 수 없다는건 현재 컴포넌트가 클라이언트 컴포넌트인건가요...?

 

아래는 제가 작성한 코드입니다.

import style from "./signup.module.css";
import BackButton from "./BackButton";
import { redirect } from "next/navigation";

export default function SignupModal() {
  const submit = async (formData: FormData) => {
    "use server";
    if (!formData.get("id")) {
      return "아이디를 입력하세요.";
    }
    if (!formData.get("name")) {
      return "닉네임을 입력하세요.";
    }
    if (!formData.get("password")) {
      return "비밀번호를 입력하세요.";
    }
    if (!formData.get("image")) {
      return "이미지를 업로드하세요.";
    }
    if (!formData.get("id")) {
      return "이미 사용 중인 아이디입니다.";
    }

    let shouldRedirect = false;
    try {
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_BASE_URL}/api/users`,
        {
          method: "post",
          body: formData,
          credentials: "include",
        }
      );
      console.log(response.status);
      if (response.status === 403) {
        return { message: "user_exists" };
      }
      console.log(await response.json());
      shouldRedirect = true;
    } catch (err) {
      console.error(err);
    }
    if (shouldRedirect) {
      redirect("/home");
    }
  };
  return (
    <>
      <div className={style.modalBackground}>
        <div className={style.modal}>
          <div className={style.modalHeader}>
            <BackButton />
            <div>계정을 생성하세요.</div>
          </div>
          <form action={submit}>
            <div className={style.modalBody}>
              <div className={style.inputDiv}>
                <label className={style.inputLabel} htmlFor="id">
                  아이디
                </label>
                <input
                  id="id"
                  name="id"
                  className={style.input}
                  type="text"
                  placeholder=""
                  required
                />
              </div>
              <div className={style.inputDiv}>
                <label className={style.inputLabel} htmlFor="name">
                  닉네임
                </label>
                <input
                  id="name"
                  name="name"
                  className={style.input}
                  type="text"
                  placeholder=""
                  required
                />
              </div>
              <div className={style.inputDiv}>
                <label className={style.inputLabel} htmlFor="password">
                  비밀번호
                </label>
                <input
                  id="password"
                  name="password"
                  className={style.input}
                  type="password"
                  placeholder=""
                  required
                />
              </div>
              <div className={style.inputDiv}>
                <label className={style.inputLabel} htmlFor="image">
                  프로필
                </label>
                <input
                  id="image"
                  name="image"
                  className={style.input}
                  type="file"
                  accept="image/*"
                  required
                />
              </div>
            </div>
            <div className={style.modalFooter}>
              <button type="submit" className={style.actionButton}>
                가입하기
              </button>
            </div>
          </form>
        </div>
      </div>
    </>
  );
}

답변 2

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

강의 조금 더 보시면 onSubmit 부분을 별도의 파일로 분리할 겁니다.

해당 컴포넌트가 클라이언트 컴포넌트인 이유는 상위 컴포넌트가 클라이언트 컴포넌트이고 거기서 import했기에 클라이언트 컴포넌트로 전환된 것입니다.

0

안녕하세요, 인프런 AI 인턴입니다. 박 태영님의 질문에 답변드립니다.

박 태영님께서 직면하신 문제는 Server Actions를 클라이언트 컴포넌트 내부에서 직접 사용하려 하시면서 발생한 것으로 보입니다. Server Actions는 기본적으로 서버 컴포넌트에서만 사용될 수 있으며, 클라이언트 컴포넌트에서는 사용할 수 없습니다. 에러 메시지에서는 이를 해결하기 위해 두 가지 방법을 제시하고 있습니다:

  1. Server Actions를 별도의 파일에 정의하고 “use server”를 파일 최상단에 추가한 후, 해당 액션을 필요한 클라이언트 컴포넌트로 가져와 사용하기.
  2. 서버 컴포넌트에서 Server Actions를 정의하고, 이를 props를 통해 클라이언트 컴포넌트로 전달해서 사용하기.

Next.js 공식 문서에서 제공하는 Server Actions 관련 가이드를 참조하시면 이와 관련하여 더 자세한 정보와 사용 방법을 확인하실 수 있습니다.

질문 내용을 통해 이해한 바로는, 박 태영님께서 클라이언트 컴포넌트에서 바로 Server Actions를 사용하려 하시는 것으로 파악됩니다. 모범 사례는 공식 문서에 나와 있는 대로 Server Actions를 별도의 서버 컴포넌트나 파일에 분리하여 정의하고, 이를 클라이언트 컴포넌트에서 사용할 수 있도록 하는 것입니다. 따라서 submit 함수와 같은 서버 액션을 별도의 파일로 분리하여 처리하는 방식을 고려해 보시는 것이 좋을 것 같습니다.

이번 상황에 대한 구체적인 해결 방안이나 예제 코드는 공식 문서나 해당 강의의 추가 설명을 참고하시는 것이 도움이 될 것입니다. Next.js와 같은 현대적인 프레임워크에서 서버와 클라이언트 간의 상호작용을 구현하는 방법은 다양하므로, 강의 내용과 공식 문서를 통해 다양한 시나리오에 대한 이해를 넓히시는 것을 추천드립니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏

추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

박 태영님의 프로필 이미지
박 태영

작성한 질문수

질문하기