Nodemailer는 Node.js 애플리케이션에서 이메일을 쉽게 전송할 수 있게 해주는 모듈이다.
my-app
├── client
│ ├── public
│ └── src
│ ├── components
│ │ └── Contact.js
│ ├── App.scss
│ ├── App.tsx
│ ├── index.scss
│ ├── index.tsx
│ └── setupProxy.js
└── server
├── index.js
└── mailer.js
우선 지메일을 사용하기 위해서는 앱 비밀번호를 발급받아야 한다. 아래와 같은 에러가 뜬다면 앱 비밀번호가 정상적으로 적용되지 않아서 그런 것이다.
🚫 Error: Invalid login: 534-5.7.9 Please log in with your web browser and then try again.
https://myaccount.google.com/security
npm install nodemailer
Express, dotenv가 설치되어 있지 않다면 설치해준다.
- Express: Node.js를 사용해 서버를 쉽게 구성할 수 있게 해주는 프레임워크
npm install express --save
- dotenv: 서버에서 환경 변수를 사용할 수 있게 해준다.
npm install dotenv --save
루트 경로에 .env 파일을 생성한 후 아래와 같이 환경 변수를 작성한다. 환경 변수를 사용하는 이유는, 민감한 개인 정보를 여기에 담아두고 배포 시 공개되지 않도록 하기 위함이다.
REACT_APP_GMAIL_ADDRESS=본인지메일주소
REACT_APP_GMAIL_PASSWORD=16자리앱비밀번호
REACT_APP_
은 리액트 환경 변수 예약어이기 때문에 변경하면 안됨.
우선 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}`)
})
클라이언트에서 서버에 요청하는 방법은 여러 가지가 있다. HTML에서 form 태그에 기본적으로 제공하는 action, method를 활용하는 방법이 있고, Axios나 Jquery Ajax 등 다양하다.
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>
서버 요청 후 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;
/mail
로 시작되는 요청 시 5000번 포트를 사용하도록 미들웨어를 세팅한다.
http://localhost:5000/mail
로 작성하게 되면 배포 후 배포된 서버를 사용할 수 없기 때문에
npm install --save-dev http-proxy-middleware
아래 경로에 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 작성 후 전송해보자. 본인 메일주소로 메일이 도착하면 성공이다.
위와 똑같이 작성하면 아래의 에러가 발생합니다.
TypeError: mailer(...).then is not a function
이건 어떻게 조치해야 하나요?