엘리스 AI트랙 4기/프로젝트

[Nodejs] nodemailer를 이용한 gmail 전송하기

남쪽마을밤송이 2022. 6. 23. 21:57

 [01 nodemailer란? ] 

  • nodeMailer는 Node.js 환경에서 email을 보내주도록 도와주는 모듈이다.
    • npm에 있는 메일 전송 패키지 중 가장 인기 있는 것 같다.
    • 나는 기본 기능만 사용했지만 역사가 깊은(?)만큼 공식문서도 잘 쓰여 있으니 확인하자.
  • 사용자에게 메일을 전송할 수 있고 메일의 내용을 HTML 문서로 커스텀하던가 나는 사용하지 않았지만 파일 첨부도 가능하다고 한다.
    • 나는 메일 발송 플랫폼으로 gmail을 사용했지만 찾아보면 naver 메일이나 daum 메일 등으로도 가능하다.
  • nodemailer는 gmail을 사용할 경우 하루에 500통을 발송 가능한데, 프로젝트 용으로는 큰 문제 없지만 실제 서비스에 쓰기는 턱없이 부족하기 때문에 더 많은 메일 전송을 위해서는 AWS SES, Mailgun 같은 메일 서비스를 이용하는 것이 좋다.

  • 참고로 이 기능은 2차도 아닌 1차 프로젝트 때 구현한 기능인데 3차인 지금 작성하는 이유는 블로그 소재가 떨어져서...!? 하핳 3차 프로젝트 때 웹푸시라는 대장정을 하느라 정신이 없는데 처음부터 포스팅 하고 싶어 관련 기능들은 프로젝트 이후에 포스팅하려고 한다.

 

 [02 nodemailer 사용 준비 ] 

 (1) gmail 설정 

  • 나는 프로젝트를 위해 계정을 새로 만들었다. google은 딱히 계정 생성 개수 제한을 두지 않는 것 같아서 필요시 만들면 좋을 것 같다.
  • 계정을 생성한 뒤 먼저 액세스를 허용해준다.
    (https://accounts.google.com/b/0/DisplayUnlockCaptcha)

  • 그리고 계정 설정으로 들어가 아래 절차를 따라 앱 비밀번호를 발급한다.

  • 생성된 앱 비밀번호를 복사해둔다.

 (2) 패키지 설치 

  • 아래 명령어로 nodemailer를 설치해준다.
npm install nodemailer

 

 [03 코드 적용] 

  • 해당 프로젝트에서는 회원가입과 비밀번호 변경시에 이메일 인증을 사용했다. 그 중 회원가입에서 구현한 코드로 설명해보겠다.

 (1) userRouter.js 

  • 먼저 userRouter에서  emailUtil 에 따로 작성해 둔 sendEmail 메소드를 실행한다. 그리고 나는 회원가입과 비밀번호 변경시 보내는 메세지가 다르기 때문에 message 변수를 사용해 입력값으로 주었다. 
    • 회원가입 단계이기 때문에 이메일 중복 검사 단계도 추가해주었다.
  • 여기서 나는 편한 인증 방식을 사용하기 위해  authCode 를 바로 front에 response 값으로 전송했는데, 보안상 취약하므로 SSL을 적용하던가 backend와 front에서 단방향 암호화를 진행하던가 하는 방법이 추가로 필요하다.
// 이메일 입력 -> 인증코드 발송하고 front로 인증코드 send
userAuthRouter.post("/emailAuth", async (req, res, next) => {
    try {
        if (is.emptyObject(req.body)) {
            throw new Error("headers의 Content-Type을 application/json으로 설정해주세요")
        }

        const { email } = req.body
        // 이메일 중복검사
        await userAuthService.isExistUser({ email })
        const message = `<p>회원가입을 위한 인증번호입니다.</p>`

        const authCode = await emailUtil.sendEmail(email, message)

        res.status(200).send(`${authCode}`)
    } catch (error) {
        next(error)
    }
})

 (2) emailUtil.js 

  • 이메일을 전송할 때 필요한 설정과 함수가 들어있는 파일이다.
  •  nodemailer.createTransport 로 사용하고자 하는 메일 플랫폼과 포트 번호, 인증 정보를 설정해준다.
    • gmail 계정 정보는 당연히 .env 파일로 관리해야 한다.
    • pass 값으로 위에서 생성한 앱 비밀번호를 사용하면 된다.
  • 다음은  Math.floor( ) 와  Math.random( ) 를 사용해 난수 인증번호를 생성해준다.
    • 비밀 번호 변경을 위해서는 임시 비밀번호를 발급해줄수도 있지만 회원가입과 함께 사용하기 위해 그냥 난수 인증번호로 인증해서 사용자가 새로운 비밀번호를 설정하도록 했다.
  • 그리고 위에서 만든 nodemailer 객체의  sendMail  메소드를 사용해 메일 전송 정보를 입력하면 끝이다.
  • 예제와 같이 html 문법을 사용할 수 있기 때문에 다양한 메세지 전송이 가능하다.
const nodemailer = require("nodemailer")
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'

const transporter = nodemailer.createTransport({
    service: 'gmail', //사용하고자 하는 서비스
    port: 587,
    host: 'smtp.gmail.com',
    secure: true, // true for 587, false for other ports
    requireTLS: true,
    auth: {
        user: process.env.GMAIL_ID, // .env 파일 GMAIL_ID 변수 = gmail 주소
        pass: process.env.GMAIL_PASSWORD // .env 파일 GMAIL_PASSWORD 변수 = gmail 패스워드 혹은 2단계 앱인증 사용시 앱 비밀번호
    }
})

const emailUtil = {
    sendEmail: async(email, message) => {
        let number = Math.floor(Math.random() * 1000000) + 100000 // 난수 인증번호 생성
        if (number > 1000000) {
            number = number - 100000;
        }

        await transporter.sendMail({   
            from: process.env.GMAIL_ID, // 보내는 주소 입력
            to: email, // 위에서 선언해준 받는사람 이메일
            subject: `안녕하세요, 포트폴리오 서비스입니다.`, // 메일 제목
            html: // 내용
                message + 
                `<p>아래의 인증번호를 입력하여 인증을 완료해주세요.</p>` +
                `<b style="font-size:25px; color:blue;">${number}</b>`
        })
        
        return number
    }
}

module.exports = { emailUtil }
  • 주의할 점은 자꾸 다음과 같은 오류가 뜬다면  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'  를 추가해줘야 한다는 것이다. NodeJS request로 https를 호출했을 때 발생하는 오류라고 한다.
    • 다만 오류의 내용이 공신력 있는 기관에서 발급받은 인증서가 아니기 때문에 문제를 삼은 것이라 해당 설정으로 SSL Validate를 하지 않으면 보안적으로 위험해질 수 있다.
Error: self signed certificate in certificate chain

    at TLSSocket.onConnectSecure (_tls_wrap.js:1051:34)

    at TLSSocket.emit (events.js:189:13)

    at TLSSocket._finishInit (_tls_wrap.js:633:8)

 

 [04 메일 수신 확인] 

  • (message를 비밀번호 변경으로 했을 때)  /emailauth  경로로 post 요청을 하면 다음과 같이 메일이 잘 오는 것을 확인할 수 있다.

 

+) 2022-06-27 추가

결국 3차 프로젝트에도 사용한 nodemailer이다. 저번에 써보기도 했고 블로그로 정리도 해둬서 아주 쉽게 적용 가능했다.

이번에는 css를 좀 신경써서 작성해봤는데 inline-style 관련 설정( flex-direction: colume )이 제대로 적용되지 않아 확인해보니 HTML 버전이 낮다나 그래서 결국  padding-left 로 배치를 조정했다.