본문 바로가기

Project/portfolio

포트폴리오 Contact 만들기 | Resend, toast

반응형

 

 

 

 

토글

 

 

Contact 컴포넌트

    <SectionHeading>Contact me</SectionHeading>

    <p className="text-gray-700 -mt-6 dark:text-white/80">
      직접 메일을 보내고 싶으시다면 {""}
      <a className="underline" href="mailto:@naver.com">
        @naver.com
      </a>{" "}
      혹은 아래의 폼을 이용해주세요!
    </p>

    <form
      className="mt-10 flex flex-col dark:text-black"
      action={async (formData) => {
        const { data, error } = await sendEmail(formData);

        if (error) {
          toast.error(error);
          return;
        }

        toast.success("Email sent successfully!");
      }}
    >

 

 

섹션 안에 직접 메일보내는 링크와 안내문구를 추가한다.

 

 

      <input
        className="h-14 px-4 rounded-lg borderBlack dark:bg-white dark:bg-opacity-80 dark:focus:bg-opacity-100 transition-all dark:outline-none"
        name="senderEmail"
        type="email"
        required
        maxLength={500}
        placeholder="보내시는 분의 메일 주소"
      />
      <textarea
        className="h-52 my-3 rounded-lg borderBlack p-4 dark:bg-white dark:bg-opacity-80 dark:focus:bg-opacity-100 transition-all dark:outline-none"
        name="message"
        placeholder="보내는 메시지"
        required
        maxLength={5000}
      />
      <SubmitBtn />
    </form>
  </motion.section>
);

 

 

input과 textarea를 포함한 폼을 구성한다.

메일 전송 버튼(SubmitBtn)을 추가하여 사용자가 메일을 보낼 수 있도록 구현한다.
sendEmail 함수를 사용하여 폼 데이터를 서버로 전송하고, 그에 따른 성공 및 오류 메시지를 토스트로 출력한다.

 

 

 

 

SubmitBtn 컴포넌트

export default function SubmitBtn() {
  const { pending } = useFormStatus();

 

useFormStatus 훅을 사용하여 현재 폼 상태를 확인한다.

 

  <button
    type="submit"
    className="group flex items-center justify-center gap-2 h-[3rem] w-[8rem] bg-gray-900 text-white rounded-full outline-none transition-all focus:scale-110 hover:scale-110 hover:bg-gray-950 active:scale-105 dark:bg-white dark:bg-opacity-10 disabled:scale-100 disabled:bg-opacity-65"
    disabled={pending}
  >

 

폼이 처리 중일 때 버튼을 비활성화한다.

 

 

    {pending ? (
      <div className="h-5 w-5 animate-spin rounded-full border-b-2 border-white"></div>
    ) : (
      <>
        보내기{" "}
        <FaPaperPlane className="text-xs opacity-70 transition-all group-hover:translate-x-1 group-hover:-translate-y-1" />{" "}
      </>
    )}
  </button>
);

 

메일 전송이 진행 중이라면 스피닝 애니메이션을 나타내는 div가 표시되고, 

그렇지 않으면 "보내기" 텍스트와 종이 비행기 아이콘이 표시된다.

반응형

 

Server

"use server"; 를 사용한다.

const resend = new Resend('API_KEY');

 

resend 라이브러리를 사용해서 resend 인스턴스를 생성한다.

 

export const sendEmail = async (formData: FormData) => {
  const senderEmail = formData.get("senderEmail");
  const message = formData.get("message");

 

sendEmail 함수는 이메일을 전송하는 함수로, 폼 데이터에서 발신자 이메일과 메시지를 추출한다.

 

 

  if (!validateString(senderEmail, 500)) {
    return {
      error: "Invalid sender email",
    };
  }
  if (!validateString(message, 5000)) {
    return {
      error: "Invalid message",
    };
  }

 

추출한 데이터를 유효성 검사한다.

validateString 함수를 사용하여 이메일과 메시지가 특정 길이 제한 내에 있는지 확인한다.

 

 


  let data;
  try {
    data = await resend.emails.send({
      from: "Contact Form <onboarding@resend.dev>",
      to: "@naver.com",
      subject: "Message from contact form",
      reply_to: senderEmail,
      react: React.createElement(ContactFormEmail, {
        message: message,
        senderEmail: senderEmail,
      }),
    });
  } catch (error: unknown) {
    return {
      error: getErrorMessage(error),
    };
  }

  return {
    data,
  };
};

 

유효성 검사를 통과한 경우, 

resend.emails.send 메소드를 사용하여 이메일을 전송한다.

이때 이메일의 여러 속성(보내는 사람, 받는 사람, 제목 등)을 설정한다.
React.createElement를 사용하여 ContactFormEmail 컴포넌트를 렌더링한 결과를 이메일 내용으로 사용한다.

 

 

 

 

 

반응형