➜ header 시작
➜ header 완성
➜ intro
➜ About Me
➜ Projects
➜ Skills
➜ Experience
➜ Contact
➜ Footer
➜ Vercel 배포
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 컴포넌트를 렌더링한 결과를 이메일 내용으로 사용한다.
'Project > portfolio' 카테고리의 다른 글
포트폴리오 Vercel 배포하기 | No Next.js version could be detected in your project. Make sure `"next"` is installed in "dependencies" or "devDependencies" (0) | 2023.11.01 |
---|---|
포트폴리오 Footer 만들기 (0) | 2023.10.31 |
포트폴리오 Experience 만들기 | react-vertical-timeline-component (0) | 2023.10.28 |
포트폴리오 Skills 만들기 | Framer Motion 애니메이션 (0) | 2023.10.27 |
포트폴리오 header 완성하기 | clsx, useActiveSectionContext (0) | 2023.10.26 |