개발일지

[NestJS] 메일 전송 하기 - SES, Nodemailer , EJS 본문

NestJS, Node.js/#02 NestJS

[NestJS] 메일 전송 하기 - SES, Nodemailer , EJS

lyjin 2022. 11. 23.

Amazon SES (Amazon Simple Email Service)

Amazon SES란 아마존에서 제공하는 이메일 전송 플랫폼을 말합니다.


사전 준비

 

패키지 설치

  • aws-sdk와 nodemailer(ses transport)를 사용합니다.
// 1. nodemailer
$ yarn add nodemailer

// 2. @aws-sdk/credential-provider-node (aws-sdk v3 사용할 경우)
$ yarn add @aws-sdk/credential-provider-node

// 3. @aws-sdk/client-ses
$ yarn add @aws-sdk/client-ses
 

구현

1. nodemailer SES transporter 생성

// email.service.ts
import * as aws from '@aws-sdk/client-ses';
import * as nodemailer from 'nodemailer';
import { AwsConfig } from '../../config';

@Injectable()
export class EmailService {
    private readonly transporter;

    constructor(private readonly logService: LogService) {
        const ses = new aws.SES({
            apiVersion: AwsConfig.AWS_SES_API_VERSION,
            region: AwsConfig.AWS_SES_REGION,
            credentials: {
                accessKeyId: AwsConfig.AWS_ACCESS_KEY_ID,
                secretAccessKey: AwsConfig.AWS_SECRET_KEY,
            },
        });

        this.transporter = nodemailer.createTransport({
            SES: { ses, aws },
        });
    }
    ...
}
// config

export class AwsConfig {
    static readonly AWS_ACCESS_KEY_ID = 'access-key';
    static readonly AWS_SECRET_KEY = 'secret-key';

    // AWS SES
    static readonly EMAIL_SENDER_EMAIL_ADDRESS = 'send@email.com';
    static readonly AWS_SES_API_VERSION = '2010-12-01';
    static readonly AWS_SES_REGION = 'ap-northeast-2';
}

2. 이메일 발송

transporter.sendMail(data[, callback])으로 메일을 발송할 수 있습니다. callback을 설정하지 않은 경우 Promise 객체를 반환합니다. 자세한 내용은 여기를 참고하세요.

 

// class EmailService

  private async requestMailTransport({ to, subject, html }: IEmailMessage) {
      return this.transporter
          .sendMail({
              from: AwsConfig.EMAIL_SENDER_EMAIL_ADDRESS,  // 발신 이메일
              to,         // 수신 이메일
              subject,    // 제목
              html,       // 본문
          })
          .then((res) => {
              // logic...
          })
          .catch((err) => {
              throw new HttpException(
                  exceptionMessagesAuth.FAILED_TO_SEND_EMAIL,
                  400
              );
          });
  }

 


템플릿 렌더링

🔗 EJS 를 사용했습니다.

 

$ yarn add ejs --save

 


구현

// class EmailService

private async renderToString(ejsFileOrStrHtml: string, data: object) {
    const path = join(EmailConfig.TEMPLATE_DIR_PATH, ejsFileOrStrHtml); // ejs 파일 경로
    let html;

    // 1. .ejs file일 경우
    await ejs.renderFile(path, data, async (err, res) => {
        if (err) {
                            // 2. string 타입일 경우  ex) `<h2><%= email %></h2>`
              html = await ejs
                  .render(ejsFileOrStrHtml, data, { async: true })
                  .catch((err) => {
                       throw new HttpException(
                          exceptionMessagesAuth.FAILED_TO_SEND_EMAIL,
                          400
                      );
                  });
          } else {
              html = res;
          }
      });
      return html;
  }
  • ejsFileOrStrHtml 에는 ejs 파일 또는 string 으로 변환한 html이 들어갑니다.
  • { async: true }: true일 경우 비동기 함수를 사용합니다.
 

사용 예시

// email.ejs

<html>
    <head> </head>
    <body>
        <div><%= email %></div>
    </body>
</html>
@Injectable()
export class SendEmailService {
    constructor(private readonly emailService: EmailService) {}

    async sendEmailTest() {
                const ejs = 'email.ejs';
        // 또는 `<html> <head></head> <body> <div><%= email %></div> </body> </html>`;

        const html = await this.renderToString(ejs, { email: 'to@email.com', });
        return await this.requestMailTransport({
            to: toEmail,
            subject: '메일 테스트',
            html,
        });
    }
}