nodemailer에 redis 뿌리기

욱2·2023년 7월 5일

이메일 검증에 필요한 기능을 찾다가 nodemailer이라는 npm 설치 후 사용했다.
1. db에 저장을 해서 비교, 재발행 시 patch를 하거나 시간을 주고 데이터를 삭제해보자

  • 많은 요청에 db를 조회 할 것이고 저장과 삭제 2번을 무슨일이 있어도 걸치게 된다.
    db는 많은 요청처리를 담당하는데 이메일검증을 위해 cost를 줄 필요가 없다고 생각됩니다.
  1. nodejs 서버에 배열 array로 저장.
  • db까지 안가고 서버 memory에서 해결 하는 방식 또한 나쁘지 않았다. 하지만, 많은 요청과
    서버안에서 이루어지는 과정이 조금 불안했다.
  1. redis
  • 가장 효율적인 부분이라고 생각됬다. 이메일 인증은 빠르게 지나가야하는 부분이고 요청도 어느정도 있을거라 생각했습니다.

  • 빠르고 효율적인 검색: Redis의 인메모리 저장소와 빠른 액세스 기능을 통해 검증 번호를 빠르게 검색할 수 있습니다. 데이터베이스를 조회할 필요 없이 Redis에서 이메일 주소에 연결된 검증 번호를 쉽게 가져올 수 있으므로, 성능을 향상시키고 대기 시간을 줄일 수 있습니다.

  • 높은 요청량 처리: Redis는 높은 읽기 및 쓰기 처리량을 처리할 수 있도록 설계되어 있어 많은 요청이 있는 상황에서 효율적으로 처리할 수 있습니다. 동시 작업을 효율적으로 처리하여 이메일 검증에 대한 여러 요청을 성능 저하 없이 처리할 수 있습니다.

const nodemailer = require("nodemailer");
const redis = require("redis");
const bcrypt = require("bcryptjs");
const { UserInfos } = require("../models");
const redisClient = redis.createClient({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT,
  password: process.env.REDIS_PASSWORD,
});

require("dotenv").config();

class RedisRepository {
  constructor() {
    this.transporter = nodemailer.createTransport({
      service: "gmail",

      auth: {
        user: process.env.EMAIL_ADDRESS,
        pass: process.env.EMAIL_PASSWORD,
      },
      tls: {
        rejectUnauthorized: false,
      },
    });

    // Initialize the Redis client

    redisClient.on("error", (error) => {
      console.error("Error connecting to Redis:", error);
    });
  }

  async generateVerificationCode() {
    const plainCode = Math.floor(100 + Math.random() * 900).toString();

    try {
      return plainCode;
    } catch (error) {
      console.error("Error generating verification code:", error);
      throw error;
    }
  }
  sendEmailVerificationCode = async (email) => {
    const verificationCode = await this.generateVerificationCode();
    const mailOptions = {
      from: process.env.EMAIL_ADDRESS,
      to: email,
      subject: "Email Verification",
      text: `Your verification code is: ${verificationCode}`,
    };

    this.transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        console.error("Error sending email:", error);
      } else {
        console.log("Email sent successfully:", info.response);
        console.log(typeof verificationCode);
        redisClient.set(email, verificationCode.toString(), "EX", 600); // Set expiration time of 10 minutes (600 seconds)
      }
    });
    return "success";
  };

  receive_email = async (verified_number, email, user_id) => {
    const storedCode = await new Promise((resolve, reject) => {
      redisClient.get(email, (error, value) => {
        if (error) {
          reject(error);
          return;
        }
        if (!value) {
          resolve(false);
          return;
        }
        console.log(verified_number, value);
        if (verified_number === value) {
          resolve(true);
        } else {
          resolve(false);
        }
      });
    });

    console.log(storedCode);
    if (storedCode === true) {
      await UserInfos.update({ auth: true }, { where: { user_id: user_id } });
      await redisClient.del(email);
    }
    return storedCode;
  };
}

module.exports = RedisRepository;

힘들었던 troubleShoot : 바로 promise를 적용안해줬을때였다.
const storedCode = await new Promise((resolve, reject)
여기서 왜? await 이 있는데 Promise 가 왜 또 필요할까?


이유는 간단했다. 해결 : return 값을 기다렸다 반환하는 개념이 중요한게 아니였다. 흔히 사용했던 fetch등 promise를 자체를 반환하는 것을 너무 그려러니 받아드린듯하다.
하지만! 이번에는 promise 값을 리턴하는게 아니라서! 그냥 callback 함수라서! new Promise를 사용해야하는것이다!

profile
성장하는 날 위한 기록

0개의 댓글