[Node.js_4기] TIL : Nodemailer (24/02/13)

2024. 2. 13. 21:52공부/내배캠 TIL

목차

 

1. 문제

2. 시도

3. 결과

4. 배운점

 

1. 문제 

 

회원가입 라우터는 email, password, password_check, name, interest를 포함하는 body를 post하고,

email이 db에 이미 존재하는지, password와 password_check가 같은지, password의 길이가 6 이상인지 확인한 뒤

이메일을 bcrypt를 사용해 암호화한 뒤  email, (암호화된)password, name, interest를 저장하는 것으로 구현하였다.

 

2. 시도 

 

1. 설치

npm i nodemailer

 

2. google 계정 준비

[NodeJS] nodemailer로 이메일 보내기 (tistory.com)

해당 링크의 [2단계 인증 해결 방법] 부분을 참고하였다.

2단계 인증이 완료된 google 계정에서 [앱 비밀번호]를 발급받아 .env에 추가해주었다.

# Gamil_2
GMAIL_USER = "이메일"
GAMIL_APP_PW = "앱 비밀번호"

 

3. Nodemailer를 사용하여 이메일 서버의 환경을 설정해준다.

import nodemailer from 'nodemailer';
import dotenv from 'dotenv';

dotenv.config();

const { GMAIL_USER, GAMIL_APP_PW } = process.env;

const transporter = nodemailer.createTransport({
	service: 'gmail',
	auth: {
		user: GMAIL_USER,
		pass: GAMIL_APP_PW,
	},
});

export default function emailSender(toEmail, Token) {
	const mailOptions = {
		from: GMAIL_USER,
		to: toEmail,
		subject: '우리동네 동아리(우동) 회원가입 인증',
		html: `<p>아래의 주소를 클릭하여 이메일을 인증해 주세요 : </p>
        <p> <a href="http://localhost:3000/api/auth?email=${toEmail}&token=${Token}">인증하기</a></p>`,
	};
	transporter.sendMail(mailOptions, (error, info) => {
		if (error) {
			console.error(error);
		} else {
			console.log('Email Sent : ', info);
		}
	});
}

먼저 nodemailer를 사용하여 email을 보내줄 transporter를 생성해준다.

mailOptions에서는 발송될 객체를 작성한다.

 -> href를 생성하는 것도 함수화 했다면 더 좋았을지도 모르겠다.

 

4. 이메일 발송 api 작성

router.post('/email', async (req, res, next) => {
	const { email } = req.body;
	const user = await prisma.users.findUnique({ where: { email } });

	if (!user.isVerified) {
		const verifyToken = createVerifyToken(user.email);
		res.cookie('verification', `Bearer ${verifyToken}`);
		await emailSender(email, verifyToken);
		try {
			res.json({
				ok: true,
				msg: '이메일이 성공적으로 전송되었습니다.',
				token: verifyToken,
			});
		} catch (error) {
			console.error('이메일 전송에 실패했습니다:', error);
			res.status(500).json({ ok: false, msg: '이메일 전송에 실패했습니다.' });
		}
	} else {
		res.status(400).json({ ok: false, msg: '이미 인증 완료된 이메일입니다.' });
	}
});

유저의 인증 상태를 검증하고(isVerified) 조건을 만족한다면 verifyToken을 생성하고, 해당 토큰과 email을 입력받아

emailSender로 전송을 시도한다.

 

5. 이메일 인증 api 작성

import express from 'express';
import { prisma } from '../utils/index.js';
import jwt from 'jsonwebtoken';

const router = express.Router();

router.get('/auth', async (req, res) => {
	const email = req.query.email;
	const token = req.query.token;
	try {
		const decodedToken = jwt.verify(token, process.env.CUSTOM_SECRET_KEY);
		if (decodedToken.email === email) {
			const updatedVerify = await prisma.users.update({
				where: { email: email },
				data: {
					isVerified: true,
				},
			});
			return res.status(200).json({
				message: 'Email 인증 처리 완료.',
				user: updatedVerify,
			});
		} else {
			res.status(400).send('부적절한 email 또는 token.');
		}
	} catch (error) {
		console.error('토큰 인증 오류:', error);
		res.status(500).send('토큰 인증 오류.');
	}
});

export default router;

전송된 email과 verifyToken에서 email과 Token을 복호화 했을때 나오는 email을 비교하여 둘이 같다면 isVerified(default는 false)를 true로 바꿔주는 방식을 사용했다.

 

3. 결과 

isVeryfied(false->true)
isVerified(true->true)

 

4. 배운점 

 

nodemailer를 사용하여 이메일을 보내고, email로 받은 링크를 통해 email 인증을 구현하는 방법을 알 수 있었습니다.

이메일과 토큰을 그대로 url에 포함시키는 상황이라, 걱정되기는 하지만, bcrty등의 암호화를 사용하면 보안성을 향상시킬 수 있을 것이라 생각합니다.