반응형
참고
- nodemailer로 구글, 네이버, 다음 카카오 메일 보내기
nodemailer의 html 설정으로 어느 정도 형식이 있는 메일을 보낼 수 있다.
리액트에서 Toast UI Editor를 이용하면 getHTML() 으로 html 태그를 쉽게 얻을 수 있다.
먼저 링크를 참고하여 express로 nodemailer 라우터를 구축하자.
./routes/post_man.js
메일이 전송되지 않은 경우 try catch를 이용해 false를 send하도록 하였다.
const express = require("express");
const router = express.Router();
const nodemailer = require("nodemailer");
const bodyParser = require("body-parser");
router.use(bodyParser.json({ limit: 100000000 })); // 100MB
const sendEmail = async (to, cc, bcc, subject, html) => {
const transporter = nodemailer.createTransport({
service: "gmail",
//secure: true, // use TLS
auth: { user: "GMAIL ID", pass: "GMAIL PASSWORD" },
});
const mailOptions = {
// from: google 생략 가능
to, // a@hanmail.net, b@hanmail.net : , 로 구분
cc, // 참조
bcc, // 비밀 참조
subject, // 제목
// text : ""
html,
// attachments:
};
try {
await transporter.sendMail(mailOptions);
console.log("이메일 전송 완료");
return true;
} catch (e) {
console.error("이메일 전송 오류:", e);
throw e;
}
};
const sendWrapper = async (to, cc, bcc, subject, html, res) => {
try {
let ret = await sendEmail(to, cc, bcc, subject, html);
console.log(ret);
res.json({ result: true });
} catch (error) {
console.error("에러 발생:", error);
res.json({ result: false });
}
};
router.post("/", (req, res) => {
// let from = req.body.from; // auth로 고정
let to = req.body.to;
let cc = req.body.cc;
let bcc = req.body.bcc;
let subject = req.body.subject;
let html = req.body.html;
console.log({ to, cc, bcc, subject, html });
sendWrapper(to, cc, bcc, subject, html, res);
});
module.exports = router;
server.js
const express = require("express");
const app = express();
const cors = require("cors");
app.use(cors());
const post_man = require("./routes/post_man");
app.use("/post_man", post_man);
app.listen(3002, () => {
console.log("Node.js Server is running on port 3002...");
});
리액트에서 axios를 이용해 응답을 받은 로그는 다음과 같다.
리액트 구현
Material UI와 Toast UI Editor 링크를 참고하여 아래와 같이 템플릿을 만들자.
참고로 보내는 사람은 형식만 갖출 뿐, Node JS에서 설정한 auth로 보내게 된다.
import React, { useRef, useState } from "react";
import axios from "axios";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
// Toast UI Editor
import "@toast-ui/editor/dist/toastui-editor.css";
import "@toast-ui/editor/dist/toastui-editor-viewer.css"; // Viewer css
import { Editor } from "@toast-ui/react-editor";
//import Viewer from "@toast-ui/editor/dist/toastui-editor-viewer";
// Dark Theme 적용
// import '@toast-ui/editor/dist/toastui-editor.css';
// import '@toast-ui/editor/dist/theme/toastui-editor-dark.css';
// Color Syntax Plugin
import "tui-color-picker/dist/tui-color-picker.css";
import "@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css";
import colorSyntax from "@toast-ui/editor-plugin-color-syntax";
// Table Merged Cell Plugin
import "@toast-ui/editor-plugin-table-merged-cell/dist/toastui-editor-plugin-table-merged-cell.css";
import tableMergedCell from "@toast-ui/editor-plugin-table-merged-cell";
const colorSyntaxOptions = {
preset: [
"#333333", "#666666", "#FFFFFF", "#EE2323", "#F89009", "#009A87", "#006DD7", "#8A3DB6",
"#781B33", "#5733B1", "#953B34", "#FFC1C8", "#FFC9AF", "#9FEEC3", "#99CEFA", "#C1BEF9",
],
};
const ToastMailer = () => {
const editorRef = useRef(null);
const [info, setInfo] = useState({
to: "",
cc: "",
bcc: "",
subject: "",
});
const sendEmail = async () => {
let htmlContents = editorRef.current.getInstance().getHTML();
const response = await axios.post(
"http://192.168.55.120:3002/post_man",
{
to: info.to,
cc: info.cc,
bcc: info.bcc,
subject: info.subject,
html: htmlContents,
},
{
header: { "content-type": "application/json" },
}
);
console.log(response);
};
return (
<div>
<Box sx={{ m: 2 }}>
<h1>Toast Mail</h1>
<Button
variant="outlined"
color="secondary"
sx={{ m: 1 }}
onClick={sendEmail}
>
보내기
</Button>
<Box
component="form"
sx={{
m: 2,
display: "flex",
flexDirection: "column",
width: "98%",
}}
noValidate
autoComplete="off"
>
<TextField
sx={{ marginBottom: 3 }}
color="primary"
label="보내는사람"
/>
<TextField
sx={{ marginBottom: 3 }}
color="primary"
label="받는사람"
value={info.to}
onChange={(e) => setInfo({ ...info, to: e.target.value })}
/>
<TextField
sx={{ marginBottom: 3 }}
color="primary"
label="참조"
value={info.cc}
onChange={(e) => setInfo({ ...info, cc: e.target.value })}
/>
<TextField
sx={{ marginBottom: 3 }}
color="warning"
label="비밀참조"
value={info.bcc}
onChange={(e) => setInfo({ ...info, bcc: e.target.value })}
/>
<TextField
sx={{ marginBottom: 3 }}
color="success"
label="제목"
value={info.subject}
onChange={(e) => setInfo({ ...info, subject: e.target.value })}
/>
</Box>
<Editor
ref={editorRef}
// initialValue={initContents}
height="1000px"
placeholder="Please Enter Text."
previewStyle="tab" // or vertical
initialEditType="wysiwyg" // or markdown
hideModeSwitch={true} // 하단 숨기기
toolbarItems={[
// 툴바 옵션 설정
["heading", "bold", "italic", "strike"],
["hr", "quote"],
["ul", "ol", "task", "indent", "outdent"],
["table", /* "image", */ "link"],
["code", "codeblock"],
]}
//theme="dark"
//useCommandShortcut={false} // 키보드 입력 컨트롤 방지 ex ctrl z 등
usageStatistics={false} // 통계 수집 거부
plugins={[[colorSyntax, colorSyntaxOptions], tableMergedCell]}
/>
</Box>
</div>
);
};
export default ToastMailer;
결과는 다음과 같다.
이제 에디터에서 여러 편집 기능을 사용해서 메일을 보내보자.
전송받은 메일은 다음과 같다.
아쉽게도 block quote, check box, 테이블 등은 class가 존재하지 않기 때문에 반영되지 않는다.
반응형
'개발 > React' 카테고리의 다른 글
리액트 - 이미지 캡처해서 웹에 붙여넣기 (Capture and Paste Image) (0) | 2024.01.15 |
---|---|
리액트 - useLocation으로 페이지 이동시 스크롤 상단으로 초기화하기 (Scroll to Top on Page Transition) (0) | 2023.12.16 |
리액트 - IP 변환하기 (IP Converter) (0) | 2023.12.09 |
React Material - Mui Pagination으로 댓글 페이지로 나누기 (0) | 2023.11.19 |
리액트 - 쿠키로 GitHub OAuth 로그인 인증 관리하기 with react-cookie (0) | 2023.11.15 |
댓글