node.js ) Nodemailer로 Gmail 보내기

seul_velog·2023년 9월 22일
0
post-thumbnail

SMTP (Simple Mail Transfer Protocol)

SMTP는 단순 메일 전송 프로토콜로, 컴퓨터 간에 전자 메일(이메일)을 전송하기 위해 인터넷에서 사용되는 프로토콜이다.

  • SMTP는 한 서버가 다른 서버나 클라이언트와 통신하여 메시지를 보내는 텍스트 기반 프로토콜이다.

🤔 SMTP는 언제 사용될까?

이메일 전송
SMTP의 주요 목적은 이메일을 전송하는 것이다. 대부분의 이메일 클라이언트(Outlook, Thunderbird 등)는 사용자가 이메일을 보낼 때 SMTP를 사용하여 메시지를 전송한다.

웹 애플리케이션에서 알림 보내기
웹사이트나 온라인 서비스에서 사용자에게 알림, 비밀번호 재설정 링크, 회원가입 확인 메일 등의 메시지를 전송할 때 SMTP를 사용한다.

자동화된 시스템 알림
서버나 다른 IT 인프라에서 문제 발생 시 관리자나 담당자에게 알림 이메일을 전송하기 위해 SMTP를 사용한다.

마케팅 및 프로모션 이메일
기업이나 조직은 고객 또는 구독자에게 뉴스레터나 프로모션 정보를 보내기 위해 SMTP를 사용한다.

이메일 릴레이
일부 조직은 여러 이메일 서버 간의 메시지 전송을 중계하기 위해 SMTP를 사용한다.

프로그램에서 이메일 전송
개발자는 API나 라이브러리를 통해 프로그램에서 자동으로 이메일을 전송할 때 SMTP를 사용한다.





Google SMTP

Google SMTP는 Google의 Simple Mail Transfer Protocol 서버를 지칭한다. Gmail 사용자는 Google의 SMTP 서버를 사용하여 이메일을 전송할 수 있다.

  • 2단계 인증이 활성화된 Gmail 계정에서 Google SMTP를 사용하려면 앱 비밀번호를 생성하고 사용해야 한다.
  • Gmail은 메일 보내기에 대한 요청 수에 제한을 두고 있다. 따라서 대규모 이메일 전송에는 권장되지 않는다. 제한을 초과하면 일시적으로 해당 기능이 정지될 수 있다.
  • 그러므로 이메일 보내기 서비스를 대규모로 이용하거나 안정적으로 이용하려면 전문 이메일 전송 서비스(예: SendGrid, Mailgun 등)를 사용하는 것을 고려해볼 수도 있다!🤔

Google SMTP 서버 설정 정보

SMTP 서버 주소: smtp.gmail.com
포트 번호 (TLS): 587
포트 번호 (SSL): 465
사용자 이름: Gmail 주소
비밀번호: Gmail 비밀번호 (또는 앱 비밀번호)


✍️ 모듈과 라이브러리

모듈 (Module)

  • 특정 기능이나 관련된 기능들을 포함하는 코드 조각이나 파일이다. 코드의 재 사용성을 높이고 관리를 쉽게하며 이름 공간의 충돌을 방지하는 데 도움을 준다.
  • 코드를 더욱 체계적으로 구성하고 관리하기 위해 사용된다. 프로그램이 복잡해질 수록 모듈을 사용해서 기능별로 코드를 분리하는 것이 중요하다.
  • 예를들면 파일 하나가 하나의 모듈로 간주될 수 있다. math.js 파일은 math모듈로써 다른 js 스크립트에서 import math로 불러와 사용한다.

라이브러리 (Library)

  • 라이브러리는 특정 작업을 수행하기 위한 함수, 클래스, 변수 등의 모음이다. 라이브러리는 여러 모듈로 구성될 수 있으며, 그 자체로는 실행되지 않는 코드의 모음이다.
  • 기본적인 휠을 다시 발명하지 않고, 특정 기능을 쉽게 구현하거나 활용하기 위해 사용된다. 라이브러리를 통해 시간을 절약하고 검증된 코드를 사용하여 오류를 줄일 수 있다.
  • javascript의 수학과 과학계산을 위한 라이브러리 mathjs가 있다.

모듈과 라이브러리

  • 모듈은 일반적으로 코드의 조각이나 파일을 의미하며, 프로그램 내에서 독립적인 기능 단위로 작동한다.
  • 라이브러리는 여러 모듈 또는 함수들의 모음으로, 특정 기능 또는 작업을 수행하기 위해 제공된다.





Nodemailer

Node.js에서 이메일을 보내기 위한 모듈이다.
nodemailer 라이브러리를 사용하면 Node.js 애플리케이션에서 다양한 전송 방식을 통해 이메일을 쉽게 보낼 수 있다.

  • SMTP를 사용하는 것이 가장 흔한 방식이지만, nodemailer는 다른 전송 방식도 지원한다.

nodemailer 주요 기능 및 특징

SMTP 연결
대부분의 이메일 서비스 제공자와 호환되는 SMTP를 통한 이메일 전송이 가능하다.

다양한 전송 방식 지원
SMTP 외에도 다양한 플러그인을 통해 다른 전송 방식을 사용할 수 있다.

HTML 및 일반 텍스트 메시지 지원
이메일의 내용으로 HTML 또는 일반 텍스트를 보낼 수 있다.

첨부 파일 지원
이메일에 첨부 파일을 추가하는 것이 쉽다.

유니코드 지원
메시지 제목, 내용 및 이름에 유니코드 문자를 사용할 수 있다.

스트리밍
대용량 첨부 파일을 스트리밍으로 처리할 수 있다.

템플릿 지원
템플릿 라이브러리와 통합하여 동적으로 이메일을 생성할 수 있다.

고급 기능
DKIM 서명, MIME 메시지 구성, 커스텀 플러그인 등의 고급 기능이 있다.



Nodemailer 설치

npm install nodemailer
yarn add nodemailer



Gmail 설정

Gmail의 보안 설정에서 "보안 수준이 낮은 앱의 액세스"를 허용하거나 2단계 인증을 사용하고 있다면 앱 비밀번호를 생성해야 한다.



nodemailer로 Gmail 전송 예시

const nodemailer = require('nodemailer');

let transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: 'youremail@gmail.com', // Gmail 주소
    pass: 'yourpassword' // Gmail 비밀번호 or 앱 비밀번호
  }
});

let mailOptions = {
  from: 'testmail@gmail.com',
  to: 'recipient@example.com',
  subject: '이메일 제목',
  text: 'Node.js의 Nodemailer + Google SMTP를 사용해서 보낸 메일입니다!'
};

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





Nodemailer로 Gmail 보내기✨

Next.js 프로젝트 내의 api 라우트로 작성하기

Next.js는 서버사이드렌더링(SSR) 및 API 라우트를 지원한다.


Next.js의 API 라우트 지원?🧐
Next.js 프로젝트 내에서 서버사이드 함수나 엔드포인트를 쉽게 생성하고 관리할 수 있게 해주는 기능이다. 이를 통해 별도의 서버나 백엔드 시스템 없이 API로직을 직접 구현할 수 있다.

따라서 이러한 API라우트 내에서 Node.js코드를 실행하는 것이 가능하다.


✍️ 작성 내용)
1. 서비스를 사용하는 이용자가 물건을 구매하거나 판매하기위한 form(거래타입, 이름, 설명, 가격, 이미지)을 작성한다.
2. 그리고 submit 버튼을 누르면 관리자와 사용자에게 form 내용에 기반한 내용을 이메일로 보내준다!

// src/pages/api/contact.ts
import { thousandthCommaSeparator } from 'lib/utils';
import type { NextApiRequest, NextApiResponse } from 'next';
import nodemailer from 'nodemailer';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const userEmail = req.body.userEmail;
  const { type, title, description, price, images } = req.body.askForm;

  const makeImgTags = (images: string[]): string => {
    return images
      .map(
        (imageUrl: string) => `<img src="${encodeURI(imageUrl)}" alt="user_image" width="150" />`
      )
      .join('');
  };

  const MAIL_SUBJECT = '요청이 완료되었습니다.';
  const MAIL_TEXT = `
  <div>
    <p>회원님의 요청이 정상적으로 접수되었습니다.
    <br />
    <br />
    - 요청 타입: ${type === 'BUY' ? '구매' : '판매'}<br />
    - 상품명: ${title}<br />
    - 추가 정보: ${description}<br />
    - 희망 가격 : ${thousandthCommaSeparator(price)} 원<br />
    <br />
    남은 궁금한 사항이 있으시다면 언제든지 연락해 주시기 바랍니다.
    <br />
    감사합니다.
    </p>
  </div>
  <div>
    <table>
      <tr>
        <td style="padding-right: 20px;"><img src="https://... .png" alt="seul_logo" width="120" /></td>
        <td>
          <p>
          Seul's velog<br />
          +82 (0)1.123.4567<br />
          seul@... .com<br />
          www.seul.com<br />
          @seul<br />
          </p>
        </td>
      </tr>
    </table>
  </div>
  `;

  const ADMIN_MAIL_TEXT = `
  <div>
    고객 이메일 : ${userEmail}
    <br />
    <p> 회원님의 요청이 정상적으로 접수되었습니다.
    <br />
    <br />
    - 요청 타입: ${type === 'BUY' ? '구매' : '판매'}<br />
    - 상품명: ${title}<br />
    - 추가 정보: ${description}<br />
    - 희망 가격 : ${thousandthCommaSeparator(price)} 원<br />
    </p>
    <br />
    - 유저 첨부 사진: <br />
    ${makeImgTags(images)}
  </div>
  `;

  if (req.method !== 'POST') {
    return res.status(405).end();
  }

  const transporter = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      user: process.env.EMAIL_USERNAME,
      pass: process.env.EMAIL_PASSWORD
    }
  });

  // 유저 전송메일
  const mailOptionsUser = {
    from: process.env.EMAIL_USERNAME,
    to: userEmail,
    subject: MAIL_SUBJECT,
    html: MAIL_TEXT
  };

  // 관리자 전송메일
  const mailOptionsAdmin = {
    from: process.env.EMAIL_USERNAME,
    to: 'seul@... .com',
    subject: MAIL_SUBJECT,
    html: ADMIN_MAIL_TEXT
  };

  const sendMail = (options: any) => {
    return new Promise((resolve, reject) => {
      transporter.sendMail(options, (error, info) => {
        if (error) {
          reject(error);
        } else {
          resolve(info);
        }
      });
    });
  };

  try {
    await sendMail(mailOptionsUser);
    await sendMail(mailOptionsAdmin);
    res.status(200).end();
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
}

해당 프로젝트가 실행되는 동안 /api/contact 경로로 POST 요청이 오면 이 코드가 실행되어 사용자와 관리자에게 이메일이 전송된다.

(1) 라이브러리 및 유형 가져오기: 필요한 라이브러리와 타입들을 가져온다.

(2) handler 함수: API 라우트의 핸들러 함수를 정의한다. 이 함수는 비동기로 동작하며, 사용자의 요청을 처리하여 응답을 반환한다.

(3) 이메일 템플릿 생성: 사용자와 관리자에게 보낼 이메일의 내용을 HTML 형식의 문자열로 정의한다.

(4) 메서드 확인: 요청 메서드가 POST가 아닌 경우, 405 상태 코드로 응답하여 메서드가 허용되지 않음을 알린다.

(5) Nodemailer 설정: Gmail을 사용하여 이메일을 전송하기 위한 Nodemailer의 transporter를 생성한다.

(6) 이메일 옵션 정의: 사용자와 관리자에게 보낼 이메일의 옵션(보내는 사람, 받는 사람, 제목, 내용 등)을 정의한다.

(7) sendMail 함수: 이메일을 전송하는 함수를 정의한다. 이 함수는 Promise를 반환하여 비동기로 동작한다.

(8) 이메일 전송: 앞서 정의한 sendMail 함수를 사용하여 사용자와 관리자에게 이메일을 전송한다. 오류가 발생하면 500 상태 코드로 응답한다.


✍️ 나는 이때 사용자로부터 이미지 파일을 입력받을 수도 있는 상황이므로 makeImgTags 함수를 정의, 주어진 이미지 URL의 배열을 입력으로 받아, 각 이미지 URL에 해당하는 <img> 태그의 문자열을 반환하도록 한다.

또 사용자 전용, 관리자 전용 이메일 템플릿을 각각 필요에 따라 다르게 작성했다.

위의 handler 함수 는 Next.js API 라우트에서 중요한 부분으로, 요청을 처리하고 응답을 반환하는 역할을 한다. Next.js는 이 함수를 통해 들어오는 HTTP 요청을 처리하게 된다고 한다. 😤

profile
기억보단 기록을 ✨

0개의 댓글