Node.js에서 메일 발송하기

이메일은 사용자에게 낮은 비용으로 메시지를 전달할 수 있는 수단입니다.

이번 포스트에서는 Node.js에서 간단한 방법으로 메일을 발송하는 방법을 알아봅니다.



서버 구성

이번에 공유하는 예시는 이용하는 서버 환경에 따라 작동이 되지 않을 수 있습니다.

이 점 유의해주시기 바랍니다.


- OS: Ubuntu 16.04 (aws)

- Node.js: v8.11.1



사전 준비: Postfix 설치

1. 아래 명령어를 이용하여 Postfix를 설치를 시작합니다.

sudo apt-get install -y postfix

2. 설정 화면이 실행됩니다. enter 혹은 return키를 눌러 다음 화면으로 이동합니다.


3. Internet Site를 선택합니다.


4. 자신의 도메인을 입력합니다. 도메인이 없는 경우 기본으로 입력된 값을 그대로 이용하셔도 됩니다.


5. 잠시 기다리면 Postfix의 설치가 완료됩니다.



사전 준비: 의존성

1. 아래 명령어를 입력하여 필요한 패키지를 설치합니다.

- ejs: Embedded JavaScript templates입니다. JSP와 같이 미리 생성한 탬플릿에 값을 입력할 수 있도록 도와줍니다. 자세한 내용은 추후 별도로 다루겠습니다.

- email-templates: 미리 설정한 탬플릿을 이용하여 메일을 보낼 수 있도록 도와줍니다. 본 예제에서는 앞서 언급된 ejs 방식으로 생성된 탬플릿을 이용합니다.

- nodemailer: Node.js에서 SMTP 서버에 접속하여 메일을 보낼 수 있도록 도와주는 패키지입니다. 실질적으로 메일 서버에 접속하는 패키지이며, 당연히 email-templates 없이도 메일을 보낼 수 있습니다.

npm i --save body-parser ejs email-templates express nodemailer



사전 준비: 이메일 탬플릿

- email-templates는 실행된 경로의 'emails' 디렉토리를 찾습니다. 아래와 같이 파일 구조를 생성합니다. (package-lock.json은 존재하지 않을 수 있습니다.)


- /emails/general/html.ejs 파일에 아래 코드를 입력합니다.

<html>

<head></head>

<body>
    <h1>
        <%= name %> <<%= subject %>>
    </h1>
    <p>
        <%= contents %>
    </p>
</body>

</html>

/emails/general/html.ejs:7, 10: app.js에서 locals 객체에 담긴 변수로 치환됩니다.



실습

1. Sample Code: 아래 코드를 app.js로 저장합니다.

const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const Email = require('email-templates');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    limit: '150mb',
    extended: false,
}));

const getContents = (result) => {
    let contents = '';
    contents += '<html>';
    contents += '<head>';
    contents += '<style> input, textarea { display: block; width: 640px; } </style>';
    contents += '</head>';
    contents += '<body>';
    contents += (result)?'<span>전송되었습니다.</span>':'';
    contents += '   <form action="/" method="POST">';
    contents += '       <input type="text" name="subject" placeholder="메일 제목" required="required" />';
    contents += '       <input type="text" name="name" placeholder="수신자 이름" required="required" />';
    contents += '       <input type="email" name="to" placeholder="수신 메일 주소" required="required" />';
    contents += '       <textarea name="contents" style="height: 100px;" required="required"></textarea>';
    contents += '       <input type="submit" value="전송" />';
    contents += '   </form>';
    contents += '</body>';
    contents += '</html>';

    return contents;
};

app.get('/', (req, res, next) => {
    res.send(getContents());
});

app.post('/', (req, res, next) => {
    const mailOption = {
        host: "localhost",
        port: 25,
        secure: false,
        tls: {
            rejectUnauthorized: false
        }
    };
    const transporter = nodemailer.createTransport(mailOption);

    const email = new Email({
        message: {
            from: 'noreply@jetalab.net', // @ 이후의 도메인을 앞서 지정했던 도메인으로 설정해야 합니다.
            attachments: [],
        },
        transport: transporter,
        views: {options: {extension: 'ejs'}},
        preview: false, // 별도로 false를 지정하지 않으면 실제 전송을 하지 않고 메일 생성 결과를 웹브라우저로 띄웁니다.
        send: true,     // 별도로 지정하지 않으면 보내지 않고 보냈다는 결과만 반환합니다.
    });

    const mailContents = {
        template: 'general',
        message: {
            subject: req.body.subject, // 메일 제목
            to: req.body.to,           // 받는 사람의 메일 주소
        },
        locals: {
            name: req.body.name,
            subject: req.body.subject,
            contents: req.body.contents,
        },
    };

    email.send(mailContents).then((info) => {
        res.send(getContents(true));
    }).catch((err) => {
        res.status(500).send(err);
    });
});

http.createServer(app).listen(4020, () => {
    console.log('HTTP server listening on port 4020');
});

app.js:15~34: 화면에 그릴 페이지를 반환합니다. 중복 코드를 제거하기 위한 역할이 큽니다. 22행에서는 실행 결과에 따라 성공 메시지를 함께 보내줍니다.

app.js:41~48: 메일 서버와 관련한 설정을 지정합니다. Postfix가 현재 서버에 설치되어 있기 때문에 주소는 localhost로 지정합니다. SMTP의 기본 포트는 25번입니다. 임의로 포트를 변경하지 않았다면 그대로 둡니다. secure와 tls 역시 보안 설정을 별도로 하지 않았다면 그대로 둬도 됩니다.

app.js:49: 지정한 서버 설정으로 transporter를 생성합니다.

app.js:51~60: 이메일과 관련한 설정을 지정합니다.

app.js:62~73: 메일 본문을 설정합니다. 생성할 때 'emails' 디렉토리 내부에 있는 template에 지정된 이름의 디렉토리를 찾습니다. 이후 html.ejs 파일을 찾아서 불러들입니다. 이는 메일을 보낼 때마다 필요한 탬플릿을 동적으로 불러올 수 있음을 의미합니다.

app.js:75~79: 실제로 메일을 보내는 코드입니다. 비동기 방식이 Promise 패턴으로 구현되어 있습니다. 메일을 보낸 경우 then()에 넘겨진 함수가, 실패한 경우 catch()에 넘겨진 함수가 실행됩니다.


2. 터미널에서 아래 명령어를 입력하면 서버가 실행됩니다.

node app.js


3. 실행된 서버에 접속하면 메일 작성 화면이 표시됩니다.


4. 메일 내용을 작성한 후 전송을 누릅니다.


5. 정상적으로 전송된 경우 상단에 전송완료 메시지가 표시됩니다.


6. 잠시 후 받는 사람 메일함에 도착한 메일을 확인합니다.



GitHub

본 강의에 사용한 코드는 GitHub에 공개되어 있습니다.

https://github.com/jETA-Kor/examples



참고 문서

- Postfix: 우분투 SMTP 메일서버 만들기: http://archmond.net/?p=7835

- npm - ejs: https://www.npmjs.com/package/ejs

- npm - email-templates: https://www.npmjs.com/package/email-templates

- npm - nodemailer: https://www.npmjs.com/package/nodemailer