nodeMailer와 Oauth2.0으로 메일을 보내기 [B]

캡틴 노드랭크·2021년 8월 26일
0

NodeJS

목록 보기
5/12

Google OAuth 2.0을 활성화 해주었으니 클라이언트와 서버의 코드를 작성해주면 된다.

우선 서버부터 작성한다.

Server

필요한 패키지를 설치해준다.

npm i nodemailer

환경 변수를 설정하려면 dotenv를 설치해준다.

npm i dotenv --save-dev

nodemailer는 메일을 보내기 위한 무료 서비스를 제공하며, 정말 다양한 장점들을 갖추었다.

몇가지 예시를 알아보자

  • OAuth2 지원
  • JavaScript ES6 코드 지원
  • 자동 생성 된 이메일 테스트 계정 생성가능
  • TLS/STARTTLS 사용한 안전한 메일 전송 등 여러가지 특징이 있다.

요구 사항은 Node.js v6.0.0 만 갖춰주면 된다.

자세한 내용은 nodemailer에서 알아보자.

메일 전용 서버 프로토콜로 사용되는 SMTP로 작성해주었다.

Server파일 생성

프로젝트 루트 디렉토리에 .env파일을 생성해주고 아래 내용을 작성해준다.

.env

MAILER_EMAIL= your Email
MAILER_CLIENT_ID= OAuth2.0 클라이언트 ID 복붙
MAILER_CLIENT_PWD= OAuth2.0 클라이언트 시크릿 키 복붙
MAILER_ACCTKN=  OAuth Playground 에서 복사한 ACCTOKEN을 복붙
MAILER_REFTKN= OAuth Playground 에서 복사한 RefTOKEN을 복붙

mailerController.js

const nodemailer = require("nodemailer");
require("dotenv").config();

모듈 최상단에 필요한 라이브러리를 가져온다.

공식 페이지에서 제공된 3LO 인증 설정의 코드를 작성하면

Google OAuth playground 에서 복사한 AccessToken은 3500초가 지나면 만료되는데, 제공된 refreshToken으로 새 accToken을 자동으로 생성한다고 공식홈페이지에서 알려준다.

3. Set up 3LO authentication
This example uses an existing Access Token. If the token is not accepted or current time is past the expires value, then refreshToken is used to automatically generate a new accessToken

이번엔 다른 파일로 보내기 위해 모듈로 코드를 작성한다.

module.exports = {
  mailerModule: async (req, res) => {
  },
}

async를 작성해주는 이유는 nodemailer에서 메일을 보낼때 쓰는 함수sendMail을 비동기적으로 작동하기 위함이다.

싱글톤 패턴(singleton pattern)(?) 으로 작성해줬다.

가장 먼저 트랜스포터(transporter)을 작성해주는데 사용방법은 다음과 같다.

let transporter = nodemailer.createTransport(options[, defaults])

자세한 내용은 nodemailer에서 알아보자.

이름 그대로 인증받은 수신자를 작성해주는데, 여기서 이전에 Google OAuth2.0 에서 생성한 것들을 총동원해준다.

  const mailTransporter = nodemailer.createTransport({
      host: "smtp.gmail.com", //smpt 전용 gail 주소
      port: 465, // or 587, 465는 SMTP의 대표로쓰이는 포트
      service: "gmail", 
      secure: true, //true인 경우 TLS를 사용, TLS는 이후에 알아보자.
      auth: {
        type: "OAuth2", // TYPE는 당연히 OAuth2
        user: process.env.MAILER_EMAIL,
        clientId: process.env.MAILER_CLIENT_ID,
        clientSecret: process.env.MAILER_CLIENT_PWD,
        accessToken: process.env.MAILER_ACCTKN,
        refreshToken: process.env.MAILER_REFTKN,
        expires: 1484314697598,
      },
    });

다음

//이 부분은 클라이언트에서 넘어온 요청에 담긴 body의 각각 요소를 가져온다.
    let { name, email, subject, message } = req.body;

    console.log(req.body);

    let mailOption = {
      from: email,  // 보내는사람이메일
      to: `받는사람 이메일`,
      subject: subject, // 제목
      //   text: `name:${name}, email: ${email} message:${message}`,
      html: `
      <div>
       <h2>Contact Us</h2>
       <ul>
         <li>Name: ${name}</li>
         <li>Email: ${email}</li>
         <li>Subject: ${subject}</li>
         <article>${message}</article>
       </ul>
      </div>`,
    };

사용자 이메일을 보여주는 부분에서 text와 html 외 다양한 메세지를 보낼 수 있다. 자세한 사항은

Nodemailer/Message 참조

text보단 html로 보내지도록 작성해줬다.

   await mailTransporter.verify((error, success) => {
      if (error) {
        res.json({ message: error });
      } else {
        res.json({ message: `서버가 메세지를 전송할 수 있음.` });
      }
    });

SMTP 연결 구성을 확인하는 코드인데, 잘못되면 오류를 반환하고, 잘되면 다음으로 넘어간다.

이 코드는 연결 및 인증만 테스트하고, 특정 메일을 보낸 메세지의 사용 여부는 확인하지 않는다.

success를 결과로 받아보면 true만 반환한다.

await mailTransporter.sendMail(mailOption, (err, res) => {
      if (err) {
        res.status(400).json({
          status: "Failed",
          code: 400,
          message: "메일 전송에 실패함",
        });
      } else {
        res.status(200).json({
          status: "Success",
          code: 200,
          message: "메일 전송에 성공함",
        });
      }
    });
    mailTransporter.close();
  },
};

마지막 코드이다. 수신자를 셋팅했으므로

메일 옵션을 첫번째 parameter로 두번째는 콜백으로 작성해줬다.

mailer.js

const router = require("express").Router();
const { mailerModule } = require("../controller/userController/mailController");

router.post("/contact", mailerModule);

module.exports = router;

라우터로 따로 분리하여 작성해줬다.. 굳이 이렇게 안해도된다.

app.use(cors(corsOption));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

app.use(mailerRoute);

let server;

if (fs.existsSync("./cert/key.pem") && fs.existsSync("./cert/cert.pem")) {
  const privateKey = fs.readFileSync(__dirname + "/cert/key.pem", "utf8");
  const certificate = fs.readFileSync(__dirname + "/cert/cert.pem", "utf8");
  const credentials = { key: privateKey, cert: certificate };

  server = https.createServer(credentials, app);
  server.listen(port, () => console.log("https server Running"));
} else {
  server = app.listen(port, () => {
    console.log(`http server Running`);
  });
}

module.exports = server;

기본으로 https프로토콜로 통신하는데 인증서가 없으면 http 프로토콜로 통신한다.

Gamil 사용시 특징

nodeMailer Gmail사용
여기서 참고하자면

Gmail also always sets authenticated username as the From: email address. So if you authenticate as foo@example.com and set bar@example.com as the from: address, then Gmail reverts this and replaces the sender with the authenticated user.

OAuth2.0 인증 받은 수신자를 생성해주고, 발신받는 to 메일을 개발자 메일로 설정한다. 둘이 일치할경우 본인이 본인에게 보내는 형태가 된다.

그렇기 때문에 아래의 코드로 누가 발송했는지 정보를 얻기 위해
이 코드처럼 보낸이가 누군지 파악할 수 있게 html로 가독성을 좀 더 높혀 작성해줬다.

 html: `
      <div>
       <h2>Contact Us</h2>
       <ul>
         <li>Name: ${name}</li>
         <li>Email: ${email}</li>
         <li>Subject: ${subject}</li>
         <article>${message}</article>
       </ul>
      </div>`,

Client

클라이언트는 React-hookaxios로 통신하기때문에 필요한 패키지를 설치해준다.
npm i axios --save

import { useState } from "react";
import axios from "axios";

axios 를 불러오고 계속 작성해주자.

export default function TestNodeMailer() {

  const [mailerState, setMailerState] = useState({
    name: "",
    email: "",
    subject: "",
    message: "",
  });

필요한 State를 작성하고 초기값을 빈 문자열로 작성한다.

  const emailInputValue = (e) => {
    setMailerState((prevState) => ({
      ...prevState,
      [e.target.name]: e.target.value,
    }));
  };

이 코드는 console.log(emailInputValue)로 찍어보면 일일히 타이핑할 때마다 mailerState 값이 바뀐다.

  const submitEmail = async (e) => {
    e.preventDefault();
    // console.log(mailerState);
    axios
      .post(`https://localhost:5000/contact`, mailerState, {
        headers: { "Content-Type": "application/json" },
      })
      .then(async (res) => {
        const resData = await res;
        console.log(resData);
        if (resData.status === "success") {
          alert("Message Sent");
        } else if (resData.status === "fail") {
          alert("Message failed to send");
        }
        setMailerState({
          name: "",
          email: "",
          subject: "",
          message: "",
        });
      })
      .catch((e) => {
        console.log(e);
      });
  };

핵심 요소다.e.preventDefault(); 로 고유 동작들을 중단시키고

axios로 서버와 통신을 한다.

  setMailerState({
          name: "",
          email: "",
          subject: "",
          message: "",
        });

이 코드를 작성하여, mailerState를 초기화시켜준다.

  return (
    <div>
        <div className="mail_box">
          <p>HAVE SOME QUESTIONS?</p>
          <Form onSubmit={submitEmail}>
            <input
              type="text"
              name="name"
              value={mailerState.name}
              onChange={emailInputValue}
              placeholder="yourName"
              required
            />
            <input
              type="email"
              name="email"
              value={mailerState.email}
              onChange={emailInputValue}
              placeholder="youremail@gmail.com"
              required
            />
            <input
              type="text"
              placeholder="subject"
              name="subject"
              value={mailerState.subject}
              placeholder="subject ex) 제목 작성"
              onChange={emailInputValue}
            />
            <textarea
              name="message"
              value={mailerState.message}
              onChange={emailInputValue}
              placeholder="전달할 메세지를 작성"
            />
            <button>Send Email</button>
          </Form>
        </div>
    </div>
  );
}

마지막으로 Contacg form 을 작성

Postman Test

메세지 전송 성공

ref:nodeMailer,dev.to A,betterprogramming.pub,stackoverflow

profile
다시 처음부터 천천히... 급할필요가 없다.

0개의 댓글