입력폼 이메일로 보내기(Next.js+nodemailer)

ㅇㅖㅈㅣ·2024년 5월 1일
1

Today I Learned

목록 보기
88/93
post-thumbnail

개인 포트폴리오 웹페이지를 만들어보면서 contact페이지에 사용자가 입력하는 내용을 나의 이메일로 전달받을 수 있는 방법에 대해 찾아보았다.

검색해보니 Nodemailer를 이용하여 메일전송 기능을 구현할 수 있다는 것을 알게되었다.

Nodemailer 란?

Node.js 기반 모듈로 Email Engine에 등록된 이메일 계정을 활용하여 이메일을 송수신 할 수 있도록 해준다.

설치

npm i nodemailer
yarn add nodemailer

문서

기본세팅

  • Next.jsTypescript 로 환경구축
  • react-hook-form 을 활용하여 간단한 유효성 검사

Gmail 세팅

  • 사용하는 계정 Gmail홈 → 설정 → 모든 설정 보기 → 전달 및 POP/IMAP → IMAP 사용 체크
  • 사용하는 계정 Google 계정관리 → 보안 → 2단계 인증 사용 → 앱 비밀번호 생성

앱 비밀번호 생성탭이 안보이면 검색해보기!

앱 이름 등록후 생성된 비밀번호를 넣어주어야 함..

(한참 헤매다가 겨우 설정했다)

참고한 유튜브

이렇게 앱 비밀번호 1개가 뜨면 생성된 것이다!

.env 파일 생성해주기

NEXT_PUBLIC_AUTH_USER=abc123@gmail.com
NEXT_PUBLIC_AUTH_PASS=abcdef

기능구현

// components/Contact.tsx
const {
    register,
    handleSubmit,
    getValues,
    reset,
    formState: { errors },
  } = useForm<Inputs>({ mode: "onChange" });

const onSubmitHandler = async () => {
    const formData = new FormData();
    formData.append("name", getValues().name);
    formData.append("email", getValues().email);
    formData.append("title", getValues().title);
    formData.append("content", getValues().content);

    try {
      const response = await fetch("/api/contact", {
        method: "POST",
        body: formData,
      });

      if (!response.ok) {
        throw new Error(`response status: ${response.status}`);
      }
      await response.json();
      toast.success("이메일이 성공적으로 전송되었습니다");
      reset();
    } catch (error) {
      console.error(error);
      toast.error("이메일 전송에 실패하였습니다");
    }
  };

react-hook-form을 함께 사용해주었기 때문에 이름, 이메일, 제목, 내용 입력값을 getValues()로 가져와서 formData.append() 메서드를 사용하여 FormData에 담아주었다.

그리고 fetch() 함수를 사용하여 지정된 URL(/api/contact)에 POST 요청을 보내고 성공과 실패여부에 따라 알림이 표시될 수 있도록 하였다.

// contact/route.ts
import { NextResponse, NextRequest } from "next/server";
const nodemailer = require("nodemailer");

export const POST = async (request: NextRequest) => {
  try {
    const username = process.env.NEXT_PUBLIC_USERNAME;
    const password = process.env.NEXT_PUBLIC_AUTH_PASS;
    const authEmail = process.env.NEXT_PUBLIC_AUTH_USER;

    if (!username || !password || !authEmail) {
      throw new Error("정보에 오류가 발생하였습니다");
    }

    const formData = await request.formData();
    const name = formData.get("name");
    const email = formData.get("email");
    const title = formData.get("title");
    const content = formData.get("content");

    const transporter = nodemailer.createTransport({
      service: "gmail",
      host: "smtp.gmail.com",
      secure: false,
      port: 587,
      auth: {
        user: authEmail,
        pass: password,
      },
    });

    const mailOptions = {
      from: username,
      to: authEmail,
      subject: `[Portfolio] ${title}`,
      html: `
      <p>이름/소속: ${name} </p>
      <p>이메일: ${email} </p>
      <p>문의 내용: ${content} </p>
      `,
    };

    await transporter.sendMail(mailOptions);
    return NextResponse.json({ message: "Success" }, { status: 200 });
  } catch (error) {
    console.log(error);
    NextResponse.json({ message: "fail" }, { status: 500 });
  }
};

전달받은 데이터를 처리해주는 부분인데 먼저 POST 함수로 클라이언트로부터의 요청(request) 객체를 매개변수로 받는다.

그리고 아까 전달한 입력값들을 각각 변수에 담아준 후에 nodemailer.createTransport를 사용하여 Gmail 서버에 연결해주었다.
Gmail을 사용하여 이메일을 보내기 위해 Gmail SMTP 서버를 설정하였는데 이 부분은 참고자료에서 많이 도움받았다.

마지막으로 subject에는 메일 제목을 어떻게 설정할지 정해주고 html로 메일 내용을 담아주었다.

transporter.sendMai로 메일을 전송해주고 성공과 실패여부 처리!

참고
Nodemailer 문서
블로그1
블로그2
여기는 깃허브로 전체 코드도 참고할 수 있어서 아주 도움이 많이 되었다


트러블슈팅

작업하면서 계속 null값으로 메일전송이 되는 문제를 겪었었다.

그때 작성했던 코드는 아래와 같았다.

const onSubmitHandler: SubmitHandler<Inputs> = async (
    event: React.FormEvent<HTMLFormElement>
  ) => {
    const formData = new FormData(event.currentTarget);

hook-form을 사용하였기 때문에 currentTarget 으로는 값을 받아오지 못했던 것이다.

따라서 getValues() 라는 hook-form의 메서드를 사용하여 위에 기록한 코드로 수정하여 오류를 해결할 수 있었다.


회고

Nodemailer 라는 기능을 처음 구현해보면서 허우적거리기도 하였지만 알아두면 잘 사용한 기능이라고 생각이 되었다.

중요하다고 생각된 과정은 Gmail의 앱 비밀번호를 설정하는 것과 꼭 환경변수로!! 처리해주는 부분, 그리고 전송실패시에 대한 오류처리인 것 같다.

profile
웰씽킹_나는 경쟁력을 갖춘 FE개발자로 성장할 것이다.

0개의 댓글