[React] Nodemailer Gmail Contact form 제작

nemo·2022년 2월 22일
3

React

목록 보기
10/28

Nodemailer

Nodemailer는 Node.js 애플리케이션에서 이메일을 쉽게 전송할 수 있게 해주는 모듈이다.



Nodemailer Contact form

폴더 구조

my-app
├── client
│   ├── public
│   └── src
│       ├── components
│       │   └── Contact.js
│       ├── App.scss
│       ├── App.tsx
│       ├── index.scss
│       ├── index.tsx
│       └── setupProxy.js
└── server
    ├── index.js
    └── mailer.js



Gamil 앱 비밀번호 생성

우선 지메일을 사용하기 위해서는 앱 비밀번호를 발급받아야 한다. 아래와 같은 에러가 뜬다면 앱 비밀번호가 정상적으로 적용되지 않아서 그런 것이다.

🚫 Error: Invalid login: 534-5.7.9 Please log in with your web browser and then try again.

1. 구글 계정 로그인 후, 아래 경로로 들어간다.

https://myaccount.google.com/security

2. 앱 비밀번호를 클릭한다.

3. 앱과 기기를 선택한 후 생성을 클릭한다.

4. 생성된 16자리 앱 비밀번호를 복사해둔다.




Server

1. Nodemaier 설치

npm install nodemailer

Express, dotenv가 설치되어 있지 않다면 설치해준다.

  • Express: Node.js를 사용해 서버를 쉽게 구성할 수 있게 해주는 프레임워크
    npm install express --save
  • dotenv: 서버에서 환경 변수를 사용할 수 있게 해준다.
    npm install dotenv --save

2. 환경 변수 생성

루트 경로에 .env 파일을 생성한 후 아래와 같이 환경 변수를 작성한다. 환경 변수를 사용하는 이유는, 민감한 개인 정보를 여기에 담아두고 배포 시 공개되지 않도록 하기 위함이다.

REACT_APP_GMAIL_ADDRESS=본인지메일주소
REACT_APP_GMAIL_PASSWORD=16자리앱비밀번호

REACT_APP_은 리액트 환경 변수 예약어이기 때문에 변경하면 안됨.


3. nodemailer 세팅

우선 mailer.js라는 파일을 생성해 모듈로 만들어 내보낸다.

(server/mailer.js)

const nodeMailer = require('nodemailer')

module.exports = async (name, email, subject, message) => {
  const transporter = await nodeMailer.createTransport({
    service: 'gmail',
    host: 'smtp.gmail.com', // gmail server
    port: 587,
    secure: false,
    auth: {
      user: process.env.REACT_APP_GMAIL_ADDRESS,
      pass: process.env.REACT_APP_GMAIL_PASSWORD,
    }
  });

  const mailOption = {
    from: name,
    to: process.env.REACT_APP_GMAIL_ADDRESS,
    subject: subject,
    html: 
      `You got a message from <br /> 
      Email : ${email} <br />
      Name: ${name} <br />
      Message: ${message}`,
  };

  try {
    await transporter.sendMail(mailOption);
    return "success"
  } catch (error) {
    return error
  }
}

(server/index.js)

const express = require('express')
const app = express()
const port = process.env.PORT || 5000
// dotenv 불러오기
require("dotenv").config();
// 모듈 불러오기
const mailer = require('./mailer.js');

// 메일 전송 라우트
app.post("/mail", (req, res) => {
  const { yourname, youremail, yoursubject, yourmessage } = req.body.data;

  mailer(yourname, youremail, yoursubject, yourmessage)
    .then((response) => {
      if (response === "success") {
        res.status(200).json({
          status: 'Success',
          code: 200
          message: 'Message Sent Successfully!',
        })
    } else {
      res.json({
        status: 'Fail',
        code: response.code
      })
    }
  })
});

...

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})



Client

클라이언트에서 서버에 요청하는 방법은 여러 가지가 있다. HTML에서 form 태그에 기본적으로 제공하는 action, method를 활용하는 방법이 있고, Axios나 Jquery Ajax 등 다양하다.

form action, method 방식

action으로 post 요청 시 각 input의 name이 Key로 설정되어 서버로 전송된다.

(client/src/Contact.js)

<div className="contact-form">
  <form action="/mail" method="post">
    <label htmlFor="name">
      <div>Name</div>
      <input type="text" id="name" name="yourname" placeholder="성함을 입력해주세요." required />
    </label>
    <label htmlFor="email">
      <div>Email</div>
      <input type="email" id="email" placeholder="본인의 메일 주소를 정확히 입력해주세요." name="youremail" required />
    </label>
    <label htmlFor="subject">
      <div>Subject</div>
      <input type="text" id="subject" placeholder="제목을 입력해주세요." name="yoursubject" required />
    </label>
    <label htmlFor="message">
      <div>Message</div>
      <textarea id="message" name="yourmessage" placeholder="message" required />
    </label>
    <div>
      <input type="submit" value="Send"/>
    </div>
  </form>
</div>

axios 방식

서버 요청 후 response를 받고 그 뒤에 클라이언트 단에서 어떤 작업을 해야한다면, 위 방법 대신 axios를 사용해보자.

우선 axios를 설치한다.

npm install axios --save


(client/src/Contact.js)

import axios from 'axios';
import React, { useState } from 'react'

const ContactPage = () => {
  const [Name, setName] = useState('');
  const [Email, setEmail] = useState('');
  const [Subject, setSubject] = useState('');
  const [Message, setMessage] = useState('');

  const nameHandler = (e) => {
    e.preventDefault();
    setName(e.target.value);
  }
  const emailHandler = (e) => {
    e.preventDefault();
    setEmail(e.target.value);
  }
  const subjectHandler = (e) => {
    e.preventDefault();
    setSubject(e.target.value);
  }
  const messageHandler = (e) => {
    e.preventDefault();
    setMessage(e.target.value);
  }

  const submitHandler = async (e) => {
    e.preventDefault();

    axios.post('/mail', {
      data: {
        yourname: Name,
        youremail: Email,
        yoursubject: Subject,
        yourmessage: Message
      }
    }).then((response) => {
      // 전송 뒤에 실행할 코드 작성
      console.log(response.data);
    })
  }

  return (
    <div className="form-cont">
      <form onSubmit={ submitHandler }>
        <label htmlFor="name">
          <div>Name</div>
          <input 
            type="text" 
            id="name" 
            name="yourname" 
            placeholder="성함을 입력해주세요." 
            required 
            onChange={ nameHandler }/>
        </label>
        <label htmlFor="email">
          <div>Email</div>
          <input 
            type="email" 
            id="email" 
            placeholder="본인의 메일 주소를 정확히 입력해주세요." 
            name="youremail" 
            required 
            onChange={ emailHandler }/>
        </label>
        <label htmlFor="subject">
          <div>Subject</div>
          <input 
            type="text" 
            id="subject" 
            placeholder="제목을 입력해주세요." 
            name="yoursubject" 
            required 
            onChange={ subjectHandler }/>
        </label>
        <label htmlFor="message">
          <div>Message</div>
          <textarea 
            id="message"
            name="yourmessage" 
            required 
            onChange={ messageHandler }/>
        </label>
        <div className="btn-box">
          <input type="submit" value="Send">
        </div>
      </form>
    </div>
  )
}
      
export default Contact;



Middleware

/mail로 시작되는 요청 시 5000번 포트를 사용하도록 미들웨어를 세팅한다.

http://localhost:5000/mail로 작성하게 되면 배포 후 배포된 서버를 사용할 수 없기 때문에 /mail로 작성한다. 로컬에서는 미들웨어 세팅을 통해 5000 포트 서버와 연결하는 것이다.

1. http-proxy-middleware 설치

npm install --save-dev http-proxy-middleware


2. setupProxy.js 생성

아래 경로에 setupProxy.js를 생성 후 코드를 작성한다.

(client/src/setupProxy.js)

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/mail',
    createProxyMiddleware({
      target: 'http://localhost:5000',
      changeOrigin: true,
    })
  );
};



테스트

Contact form 작성 후 전송해보자. 본인 메일주소로 메일이 도착하면 성공이다.

1개의 댓글

comment-user-thumbnail
2022년 9월 30일

위와 똑같이 작성하면 아래의 에러가 발생합니다.

TypeError: mailer(...).then is not a function

이건 어떻게 조치해야 하나요?

답글 달기