Nodemailer로 메일 보내기 (with 첨부파일)

MIMI·2022년 12월 7일
3

https://github.com/seomimi/nodemailer

이 글은 node와 express를 이용한 노드 서버 생성에 대한 선수 지식이 있어야 원활한 이해가 가능합니다.

Nodemailer 란?

Nodemailer는 이메일을 보낼 수 있는 Node.js 애플리케이션용 모듈로, EmailEngine에 등록된 이메일 계정을 사용하여 이메일을 수신하고 이메일을 보낼 수 있습니다.
더 자세한 기능 및 특징은 https://nodemailer.com/about/를 참고해 주세요.

주요 개념

  • SMTP (Simple Mail Transfer Protocol)
    - 네트워크를 통해 이메일을 전송하는 기술 표준
    - Nodemailer에서 메시지 전달을 위한 주요 전송 수단
    - 메일 전송 프로토콜
    - 컴퓨터와 서버는 SMTP를 이용하여 기반 하드웨어나 소프트웨어와 관계없이 데이터를 교환

사용하게 된 이유

고객이 회사에 어떠한 문의사항을 보낼 때, outlook이나 외부 메일 시스템으로 연결 없이, 회사 홈페이지에서 바로 보낼 수 있는 환경을 구축하기 위해 사용했습니다.

전체 작업 과정

1. 간단한 UI작업

제가 실제 업무에서 만든 형식은 아니지만, 블로그 글 업로드를 위해 간단히 만들었습니다.
관련 코드는 깃에서 확인해 주세요.

2. 노드서버 구축 및 nodemailer 설치

Node.js + express 사용하여 간단하게 로컬에 백서버를 만들었습니다.
이 또한 관련 코드는 깃에서 확인해 주세요.
(서버를 만들지 않고 AWS lambda를 이용하여 서버리스로 메일 보내기도 가능합니다... 다음 포스팅으로 해볼까 싶기도...ㅎㅎ)

> npm i nodemailer

3. transporter 생성

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

메일을 보내기 위한 송신객체를 createTransport를 사용하여 생성합니다.

  • options : 송신을 위한 데이터를 정의하는 개체
    - port : 연결할 포트 (기본값은 보안이 false 인 경우 587, true 인 경우 465 )
    - host : 연결한 호스트 이름 또는 IP 주소 (기본값은 localhost)
    - auth : 인증 데이터를 정의
    • type : 인증 유형 (기본값은 login, 다른 옵션은 oauth2)
    • user : username, 보내는 메일의 주소
    • pass : 보내는 메일의 비밀번호
    • 그 외, authMethod, secure, requireTLS 등

저는 gmail을 통해 메일을 보낼 것이므로 구글 계정 설정에서 앱 비밀번호를 발급받아야 합니다.

구글 앱 비밀번호 발급




저는 앱선택 : 기타 -> 이름 : nodemailer 로 설정하여 생성했습니다.
16자리의 앱 비밀번호가 생성되는데 한 번 발급받으면 다시 알 수 없으므로 어딘가에 잘 기록해두세요.

nodemailer.createTransport({
  host: "smtp.gmail.com",
  port: 587,
  secure: false,//port 587의 경우, secure가 false로 유지
  //secure가 false라고 해서 암호화된 연결을 사용하지 않는다는 의미가 아닙니다.
  auth: {
    user: "자신의 구글계정@gmail.com",
    pass: "16자리 앱 비밀번호",
  },
});

전에는 node서버에서 구글 메일에 접속하기 위해 구글 2단계 인증을 해제해서 써야했지만, 이제는 앱 비밀번호를 발급받아 사용하므로 보안문제가 해결되었습니다.

4. 메일 보내기

transporter.sendMail(data[, callback])

// back/routes/mailSender.js

let mailOptions = {
            from: "xxx@gmail.com", //송신할 이메일
            to: "xxx@gmail.com", //수신할 이메일
            subject: "메일 제목",
            html: "메일 내용",
            attachments: "첨부파일",
        };
transporter.sendMail(mailOptions)

callback 인수가 설정되지 않은 경우 메서드는 Promise 객체를 반환합니다.
attachments에 첨부파일을 넣어 보낼 수 있습니다. (배열로 여러 개의 첨부파일을 보내기 가능)
attachments의 형식 예시는 https://nodemailer.com/message/attachments/에서 볼 수 있습니다.

저는 첨부파일의 형식을 data uri로 변경하여 보냅니다.
(데이터가 base-64로 인코딩 되면서 크기가 1/3정도 증가하므로 주의)

file을 data uri로 변경

// front/components/MailForm.tsx

const onSelectFile = useCallback((e) => {
    if (fileCheck(e.target.files[0].type)) {
        let reader = new FileReader();
        reader.readAsDataURL(e.target.files[0]);
        reader.onloadend = () => {
            const base64 = reader.result;
            if (base64) {
                let base64Sub = base64.toString();
                setFileInfo(base64Sub);
            }
        };
    } else {
        alert('pdf 파일만 업로드 가능합니다.');
    }
}, []);

최종 코드 요약 정리

https://github.com/seomimi/nodemailer

1. front에서 메일 보내기

// front/components/MailForm.tsx

const onSubmit = useCallback(
    (e) => {
        e.preventDefault();
        if (email && title && message && fileInfo) {
            const payload = { email: email, title: title, message: message, file: fileInfo };
            axios
                .post('http://localhost:3005/back/mail', payload)
                .then((res) => console.log(res))
                .catch((err) => console.error(err));
        }
    },
    [email, title, message, fileInfo]
);

2. back에서 메일 받기

위 이미지처럼 내용을 작성해서 보내면, back에서 req.body로 받습니다.

3. back에서 메일 보내기

front에서 받은 메일을 최종 수신할 메일로 보냅니다.

// back/routes/mailSender.js
// back/routes/mail.js

const express = require('express');
const router = express.Router();

router.post('/', async (req, res, next) => {
   try {
       let transporter = nodemailer.createTransport({
           port: 587,
           host: 'smtp.gmail.com',
           auth: {
               user: 'xxx@gmail.com', //송신할 이메일
               pass: '16자리 앱 비밀번호',
           },
       });
       const { email, title, message, file } = req.body;
       let mailOptions = {
           from: 'xxx@gmail.com', //송신할 이메일
           to: 'xxx@gmail.com', //수신할 이메일
           subject: 'nodemailer test',
           html: `
            <div>
                <h2>Message Details</h2>
                <div class="email" style="font-size: 1.1em;">Email : ${email}</div>
                <div class="phone" style="font-size: 1.1em;">Title : ${title}</div>
                <div class="message" style="font-size: 1.1em;">message : </div>
                <pre class="message" style="font-size: 1.2em;">${message}</pre>
            </div>
            `,
           attachments: [{ path: file }],
       };
       await transporter
           .sendMail(mailOptions)
           .then(() => res.status(200).send('저장 및 발송 성공'))
           .catch(() => res.status(500).send('에러'));
   } catch (err) {
       console.error(err);
       next(err);
   }
});

module.exports = router;

송신이 완료되면 보낸 메일에서 확인 할 수 있습니다.

에러 사항

nodemailer로 인해 생긴 에러 사항은 아니고, 백서버에 파일 한계 용량을 늘리지 않아서 axios 에러가 떴었다.

express 서버에 아무 설정을 추가하지 않으면 기본값은 100kb 입니다.

// back/app.js
app.use(express.json({ limit: '25mb' }));
app.use(express.urlencoded({ limit: '25mb', extended: true }));
profile
Web Developer

0개의 댓글