[쉘위헬스] nodemailer 이용하여 메일인증으로 회원가입 기능구현

해달·2021년 12월 4일
1

nodemailer 메일인증 회원가입 기능구현


지난 프로젝트에서는,
nodemailer 인증코드 전송 후 홈페이지에서 확인 방식이였는데
이번 프로젝트는,
메일로 회원가입 인증메일 전송 ->
메일에 걸려있는 링크 클릭 시 다시 홈페이지로 이동 후 회원가입인증이 완료
되는 방식으로 진행하기로 하였다.

프로젝트에서 front 를 담당하였으나,
이전 프로젝트에서 회원가입기능을 구현 한적이 없었고
nodemailer를 사용해보고자 메일인증 기능은 back과 front를 전부 담당하여서 진행하게 되었다.

노드메일러 사용 시 그 외 필요한 내용

(1) 노드메일러 발송 시 전달 될 HTML 템플릿이 필요하였다
찾아보니 템플릿을 만들어주는 사이트가 있어 사이트에서 템플릿을 만든 뒤
코드만 받아와서 적용 시킬 수 있었다.(사이트는 아래 링크에)


Client

//App.js 
<Route path='/verify-email/:token' component={VerifyEmail} />

//VerifyEmail.js
 function VerifyEmail({ match }) {
  const { token } = match.params

  
  const signUpUpdate = async () => {
    await axios.patch(`${process.env.REACT_APP_SERVER_API}/user/email-verification`, { token })
      .then((res) => {
        console.log(res, 'client/verifyEmail')
        if (!res.data.data) {
          setCompleted(false)
        }
      })
      .catch((err) => {
        console.log(err)
      })
  }

  useEffect(() => {
    signUpUpdate()
  }, [])

useEffect로 페이지가 마운트 됐을 때
props 로 받아온 {match} 값 안에 포함되어있는 params로 token 값을 추출한 뒤에
인증확인 컬럼의 값을 0에서 1로 업데이트 시켜주는 aixos 요청을 보낸다


Server

server코드 작성하면서 기록

(1) 템플릿에 주소로 이미지 적용하기

<img src="https://i.imgur.com/0royLEy.jpg" />

HTML 템플릿을 만든 다음에 적용한 뒤 메일을 발송했는데 이미지가 적용이 안되는 에러가 발생하였다
기존 태그는 './img/~' 와 같이 상대경로로 지정해주는 코드로 작성이 되어있었는데
적용이 되지 않아 여러가지 방법으로 시도를 하였지만(..) 되지않았다
절대경로로 이미지를 전달해주고자
이미지업로드사이트에 사진을 업로드 한 뒤 절대경로 로 이미지를 띄워주었다

(2) client 접속 시 경로 연결해주기

<a href="http://localhost:3000/verify-email/${token}"></a>`

client에 아래와 같이 분기를 나누어주었고 링크로 접속하게 되면

<Route path='/verify-email/:token' component={VerifyEmail} />

params로 생성한 Token을 전달 해 주었다

(3) nodemailer 메일 발송 시 스팸메일함으로 수신되는 부분 해결

    let transporter = nodemailer.createTransport({
       ...,
        auth: {
          ...
        },
        from:  `메일 발신자 주소` // 추가
      });

메일 발송이 완료가 되면 스팸메일함으로 메일이 전달 되었는데
메일 발신자 이메일 주소를 from 값으로 추가해주면
스팸메일함으로 전달 되지 않는다


이메일 발송

const jwt = require("jsonwebtoken");
const { User } = require("../../models");
const nodemailer = require('nodemailer');

module.exports = async (req, res) => {
  try {
    const { email } = req.body;
    const userEmail = await User.findOne({ where: { email }, attributes: ["email"] });


    if (userEmail) {
      const email2 = userEmail.dataValues
      const token = jwt.sign(email2, process.env.ACCESS_SECRET, { expiresIn: "1h" })
      //email로 찾은 유저 정보로 token 생성


      let transporter = nodemailer.createTransport({
        //smtp객체 생성하는 명령어
        service: 'gmail',
        host: 'smtp.gmail.com',
        port: 587,
        secure: false, // true for 465, false for other ports
        auth: {
          user: process.env.ACCOUNT_USER,
          pass: process.env.ACCOUNT_PASS,
        },
        from: process.env.ACCOUNT_USER // 스팸메일로 안가도록 설정
        //환경변수로 비밀번호와 메일주소 설정
      });

      transporter.verify(function (error, success) {
        if (error) console.log(error);
        else {
          console.log('Server is ready to take our messages');
        }
      });

      const message = {
        from: process.env.ACCOUNT_USER, // sender address
        to: `${email}`, // list of receivers
        subject: 'Shall We Helath 회원가입 인증메일 입니다🏋🏻‍♀️', // Subject line
        text: 'Shall We Helath 회원가입 인증메일 입니다', // plain text body
        html: // 템플릿 HTML 코드 
             	
          `  ..., 
          <img src="https://i.imgur.com/0royLEy.jpg" />
          <a href="http://localhost:3000/verify-email/${token}"></a>
           ...       
	`

      let send = await transporter.sendMail(message);
		//메일 발송하기
      
      return res.status(200).json({ data: token, message: '메시지가 전송되었습니다' })
    } else {
      return res.status(400).json({
        data: null,
        error: {
          "path": "/users/email-verification",
          "message:": "Insufficient body data",
        }
      })
    }
  }

  catch (err) {
    console.log(err);
  }
};


메일에 있는 링크로 접속 시 회원가입 시키기


const jwt = require("jsonwebtoken");
const { User, sequelize } = require("../../models");
const { QueryTypes } = require("sequelize");

module.exports = async (req, res) => {
  try {
    const { token } = req.body
    const verified = jwt.verify(token, process.env.ACCESS_SECRET, (err, decoded) => {
      if (err) return null;
      return decoded;
    });
    //토큰해독


    
    //1. 토큰을 해독했는데 유효하지 않았을 때
    //2. 유효한데 db에 저장된 이메일이 없을때

    if (!verified) {
      /* 토큰 해독이 안될 떄*/
      return res.status(404).json({
        data: null,
        error: {
          path: "/user/email-verification",
          message: "token fail verfied",
        },
      });
    } else {
      //해독된거로 DB찾기

      const { email } = verified
      const userEmail = await User.findOne({ where: { email }, attributes: ["email"] });


      if (!userEmail) {
        /* DB에 유저 정보가 없을 때*/
        return res.status(404).json({
          data: null,
          error: {
            path: "/user/email-verification",
            message: "token not found userInfo",
          },
        });
      } else {
        const sql = `update users set isEmailVerified = 1 where email = "${email}";`
        const rows = await sequelize.query(sql, { type: QueryTypes.UPDATE });
        //시퀄라이즈에서 mysql 문법 사용 (UPDATE)
        //제대로 되었을 시에 데이터 담김
        
        if (rows) {
          return res.status(200).json({ data: true });
        } else {
          return res.status(404).json({
            data: null,
            error: {
              path: "/user/email-verification",
              message: "page not found",
            },
          })
        }
      }
    }
  } catch (err) {
    console.log(err)
    throw err;
  }
};

도움받은 사이트

0개의 댓글