NodeJS (이메일 발송 기능 구현하기)

Jeonghun·2023년 7월 6일
4

NodeJS

목록 보기
2/2


NodeJS NodeMailer를 이용한 이메일 전송 기능 구현

쇼핑몰 프로젝트에서 담당했던 Admin 관련 기능 중 사용자가 주문을 하거나 회원을 탈퇴하게 될 경우 해당 사용자의 이메일로 관련 메일을 전송하는 기능을 구현했다. 이는 NodeJS 환경에서 이용할 수 있는 'NodeMailer'라는 라이브러리를 통해 쉽게 구현할 수 있었다.

🤔 NodeMailer는 뭘까?
NodeMailer는 NodeJS 환경에서 이메일을 쉽게 전송할 수 있도록 해주는 모듈이다. 여러 가지 옵션을 제공하며, SMTP나 OAuth2와 같은 인증 방식도 지원한다.

- NodeMailer를 이용하는 방법

아래 예제를 통해서 SMTP를 이용해 이메일을 보내는 간단한 방법을 살펴보자.

📌 NPM 모듈 설치하기

먼저 NodeMailer를 설치해준다.

npm install nodemailer

📌 이메일 전송 코드 작성

설치한 NodeMailer를 import한 후, 'nodemailer.createTransport()' 함수를 통해 메일 서버와 연결한다. 다음은 Gmail의 SMTP 서버를 이용하는 방법이다.

const nodemailer = require('nodemailer'); // 모듈 import

const transporter = nodemailer.createTransport({
  service: 'gmail', // gmail을 사용함
  auth: {
    user: 'youremail@gmail.com', // 나의 (작성자) 이메일 주소
    pass: 'yourpassword' // 이메일의 비밀번호
  }
});

const mailOptions = {
  from: 'youremail@gmail.com', // 작성자
  to: 'myfriend@yahoo.com', // 수신자
  subject: 'Sending Email using Node.js', // 메일 제목
  text: 'That was easy!' // 메일 내용
};

transporter.sendMail(mailOptions, function(error, info){
  if (error) {
    console.log(error);
  } else {
    console.log('Email sent: ' + info.response);
  }
});

위 예제 코드에서 nodemailer.createTransport()는 메일 서버와의 연결을 설정하고, auth 객체는 이메일 계정의 인증 정보를 설정하는 역할을 한다. mailOptions 객체는 보낼 메일의 내용을 설정하며, transporter.sendMail() 함수는 실제로 메일을 보내는 함수이다.

NodeMailer를 이용한 이메일을 전송하는 방법은 이게 전부다. 이메일 기능을 구현하는데, 생각보다 너무 간단해서 놀랐다. NodeMailer를 이용하면 위와 같은 방법 외에도 HTML로 메일 전송하기, 첨부 파일 전송하기, 여러 명의 수신자에게 동시 전송하기 등 다양한 기능을 구현할 수 있다.

- SMTP와 OAuth2는 뭘까?

위에서 Gmail SMTP 방식을 이용한 방식이라는 말을 언급했는데, 그렇다면 과연 SMTP는 무엇일까? NodeMailer를 이용하면 다양한 방식을 이용하여 이메일 전송 기능을 구현할 수 있는데, 그 중 SMTP와 OAuth2에 대해 알아보자.

📌 SMTP

SMTP는 인터넷에서 이메일을 보내기 위해 사용하는 프로토콜이다. SMTP는 이메일을 보내는 서버에서 이메일을 받는 서버로 메시지를 전달하는 역할을 한다. SMTP는 보내는 메일 서버에서 메시지를 받고, 메시지의 수신자가 자신의 도메인에 속해 있으면 메시지를 로컬 사용자에게 전달하게 된다. 수신자가 자신의 도메인에 속하지 않는 경우, SMTP는 메시지를 더 가까운 SMTP 서버(또는 연결된 네트워크에 직접 연결된 SMTP 서버)로 전달한다.

📌 OAuth2

OAuth 2.0은 인터넷 사용자가 애플리케이션에 계정 데이터를 제공하지 않고, 특정 데이터에 대한 접근을 공유할 수 있게 하는 인증 프로토콜이다. 이는 "허가"를 의미하는 "Authorization"에서 이름이 유래했다고 한다.

예를 들어, 사용자가 Google 계정 정보를 직접 제공하지 않고도 Google에서 제공하는 서비스(Gmail, Google Calendar 등)에 대한 데이터를 볼 수 있는 애플리케이션이 존재하는데, 이 애플리케이션은 OAuth를 통해 Google에 인증을 요청하고, Google은 애플리케이션에 토큰을 제공한다. 이 토큰은 애플리케이션이 사용자를 대신하여 특정 작업을 수행하는 데 필요한 권한을 제공하고, 이렇게 하면 애플리케이션은 사용자의 Google 계정 자격 증명을 직접 알지 못하고도 사용자가 허용한 작업을 수행할 수 있게 된다.

필자는 해당 기능을 구현하기 위해 간단한 SMTP 방식을 사용했지만, NodeMailer에서 OAuth2 인증을 사용하여 Gmail과 같은 서비스로 메일을 보낼 수 있다. 이렇게 하면 사용자는 이메일과 패스워드를 직접 제공하지 않고도 메일을 보낼 수 있어, 사용자의 계정 보안을 더욱 강화할 수 있다.

- 프로젝트에서의 실제 서비스 코드 구현

위에서 설명한 NodeMailer 활용하여 실제 프로젝트에서 내가 구현한 코드는 다음과 같다.

📌 실제 코드

// sendEmail.js

const nodemailer = require('nodemailer'); // 이메일 전송을 위한 nodemailer 모듈 불러오기

async function sendEmail({ to, subject, text }) {

    // 이메일 전송을 위한 메일 서버 연결
    const transporter = nodemailer.createTransport({
        host: 'smtp.gmail.com', // 사용할 이메일 서비스의 호스트 주소 (gamil)
        port: 587, // 이메일 서비스의 포트 번호 (일반적으로 25, 587, 465, 2525 중 하나 사용)
        auth: { // 이메일 서버 인증을 위한 사용자의 이메일 주소와 비밀번호
            user: 'mygmail@gmail.com', // 이메일 주소
            pass: 'myPassword', // 이메일 비밀번호 (그대로 노출되기 때문에 구글의 app 패스워드를 사용할 것을 추천한다.)
        },
    });

    // 메일 옵션 설정
    const mailOptions = {
        from: 'mygmail@gmail.com',
        to,
        subject,
        text,
    };

    // 이메일 전송
    await transporter.sendMail(mailOptions);
}

module.exports = sendEmail;
// adminService.js

const sendEmail = require('../utils/sendEmail'); // sendEmail 유틸리티 import

class AdminService {
    // 이전 코드 생략 . . .

    // 사용자의 회원 정보 삭제 및 이메일 전송
    async deleteUser(userId) {
        const user = await userModel.findByUserId(userId);

        if (!user) {
            throw new Error('해당 사용자를 찾을 수 없습니다.');
        }

        // userId로 해당 회원을 검색해 정보 삭제
        await userModel.deleteByUserId(userId);

        // 회원 탈퇴 처리가 완료된 후 이메일로 탈퇴 사실을 알림
        const emailContent =
            `안녕하세요, ${user.name}님.
  
            회원 정보가 성공적으로 삭제되었습니다.
            그동안 저희 'HOLO'를 이용해주셔서 대단히 감사합니다.`;

        await sendEmail({
            to: user.email,
            subject: '[HOLO] 회원 탈퇴 처리 완료',
            text: emailContent,
        });
    };

    // 관리자가 모든 주문 내역 조회 (회원 및 비회원)
    async getAllOrders() {
        const allOrders = await orderModel.find(); // 모든 주문 찾기
        return allOrders;
    };

    // 관리자가 사용자의 주문 삭제 및 이메일 전송
    async deleteOrder(orderId) {
        const order = await orderModel.findOne({ _id: orderId });

        if (!order) {
            throw new Error('해당 주문을 찾을 수 없습니다.');
        }

        const user = await userModel.findByEmail(order.email);

        // 주문 정보 삭제
        await orderModel.deleteByOrderId(orderId);

        // 주문 삭제 처리가 완료된 후 이메일로 삭제 사실을 알림
        const emailContent =
            `안녕하세요, ${user.name}님.
    
        고객님의 요청으로, 주문 번호 ${orderId}의 주문이 성공적으로 삭제되었습니다.
        
        저희 'HOLO'의 서비스를 이용해주셔서 감사합니다.`;

        await sendEmail({
            to: user.email,
            subject: '[HOLO] 주문 삭제 처리 완료',
            text: emailContent,
        });
    };

  // 이하 코드 생략 . . .

const adminService = new AdminService;

module.exports = adminService;

📌 구현한 방식

NodeMailer에서 지원하는 Gmail SMTP 방식을 이용하여 간단하게 이메일 전송 기능을 구현했다. 이는 내가 담당했던 파트인 adminService 에서 사용자의 주문 처리, 탈퇴 처리에서 요긴하게 사용할 수 있었다.


포스팅을 마치며

나는 2주라는 짧은 프로젝트 기간이 정해져 있어 빠르게 기능을 구현하려고 하다 보니, NodeMailer 모듈에 대해 깊게 공부하지 못한 채 기능 구현을 완성했다. 간단한 방식인 SMTP 방식을 이용했지만, 이는 나의 gmail 계정과 패스워드를 그대로 코드상에 노출하게 된다는 단점이 존재했다. 이를 OAuth2를 이용한 방식으로 리팩토링 함으로서 보안에 조금 더 신경쓰도록 할 예정이다. 또한, 첨부파일과 같은 NodeMailer의 다양한 기능을 조금 더 활용해보고 싶다는 생각이 든다.

profile
안녕하세요, 프론트엔드 개발자 임정훈입니다.

0개의 댓글