✍🏻 index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<div>
<input type="email" id="email" placeholder="Enter email">
<button onclick="sendEmail()">이메일 전송</button>
</div>
<!-- 인증번호 입력 필드와 제출 버튼을 숨기고 있다가 이메일 전송 후에 표시 -->
<div id="verification-section" style="display: none;">
<input type="text" id="verification-code" placeholder="Enter verification code">
<button onclick="verifyCode()">Verify Code</button>
<p id="timer"></p>
<p id="verification-message"></p> <!-- 인증 메시지를 표시할 위치 -->
</div>
<script>
let verificationExpiryTime; // 만료 시간 저장 변수
let email; // 이메일 저장 변수
function sendEmail() {
email = document.getElementById('email').value;
fetch('/send-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: email })
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('Email sent:', data.message);
// 이메일 전송 성공 시 인증번호 입력 필드와 타이머 표시
document.getElementById('verification-section').style.display = 'block';
// 인증번호 만료 시간 설정 (현재 시간 + 1분)
verificationExpiryTime = Date.now() + 1 * 60 * 1000;
startTimer();
} else {
console.error('Error:', data.message);
}
})
.catch((error) => {
console.error('Error:', error);
});
}
function verifyCode() {
const code = document.getElementById('verification-code').value;
fetch('/verify-code', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: email, code: code })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 인증 성공 시 메시지 표시 및 타이머와 버튼 숨기기
document.getElementById('verification-message').textContent = 'Authentication successful!';
document.getElementById('verification-section').style.display = 'none'; // 인증 입력 필드와 타이머 숨김
} else {
console.error('Error:', data.message);
}
})
.catch((error) => {
console.error('Error:', error);
});
}
function startTimer() {
const timerElement = document.getElementById('timer');
const updateTimer = () => {
const now = Date.now();
const timeLeft = verificationExpiryTime - now;
if (timeLeft <= 0) {
timerElement.textContent = 'Verification code has expired.';
// 인증번호 만료 후 처리
document.getElementById('verification-section').style.display = 'none'; // 인증 입력 필드와 타이머 숨김
} else {
const minutes = Math.floor(timeLeft / 60000);
const seconds = Math.floor((timeLeft % 60000) / 1000);
timerElement.textContent = `Time left: ${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
setTimeout(updateTimer, 1000); // 1초마다 업데이트
}
};
updateTimer();
}
</script>
</body>
</html>
- <input type="email" id="email" placeholder="Enter email">: 사용자로부터 이메일 주소를 입력받는 필드입니다. id="email"은 자바스크립트에서 이 필드를 참조하기 위한 식별자입니다.
- <button onclick="sendEmail()">이메일 전송</button>: 사용자가 이메일을 전송하려고 클릭하는 버튼입니다. 클릭 시 sendEmail() 함수가 호출됩니다.
- <div id="verification-section" style="display: none;">: 이 섹션은 처음에는 숨겨져 있으며, 사용자가 이메일을 전송한 후에 표시됩니다. display: none; 스타일 속성으로 숨겨져 있습니다.
- <input type="text" id="verification-code" placeholder="Enter verification code">: 사용자가 이메일로 받은 인증 코드를 입력할 수 있는 필드입니다.
- <button onclick="verifyCode()">Verify Code</button>: 사용자가 인증 코드를 제출하는 버튼입니다. 클릭 시 verifyCode() 함수가 호출됩니다.
- <p id="timer"></p>: 인증 코드의 남은 시간을 표시할 위치입니다.
- <p id="verification-message"></p>: 인증 성공 또는 실패 메시지가 표시될 위치입니다.
- let verificationExpiryTime;: 인증 코드의 만료 시간을 저장하는 변수입니다.
- let email;: 사용자가 입력한 이메일 주소를 저장하는 변수입니다.
sendEmail()
- function sendEmail(): 이메일 전송을 처리하는 함수입니다.
- email = document.getElementById('email').value;: 사용자가 입력한 이메일 주소를 가져와 email 변수에 저장합니다.
- fetch('/send-email', { ... }): 서버에 이메일 전송을 요청하는 POST 요청을 보냅니다. body에 이메일 주소가 포함됩니다.
- .then(response => response.json()): 서버의 응답을 JSON 형식으로 파싱합니다.
- .then(data => { ... }): 이메일 전송 성공 여부에 따라 처리합니다. 성공하면 인증번호 입력 필드가 나타나고 타이머가 시작됩니다.
- verificationExpiryTime = Date.now() + 1 * 60 * 1000;: 현재 시간으로부터 1분 후를 만료 시간으로 설정합니다.
- startTimer();: 타이머를 시작합니다.
verifyCode()
- function verifyCode(): 인증 코드를 검증하는 함수입니다.
- const code = document.getElementById('verification-code').value;: 사용자가 입력한 인증 코드를 가져와 code 변수에 저장합니다.
- fetch('/verify-code', { ... }): 서버에 인증 코드 검증을 요청하는 POST 요청을 보냅니다. 이메일과 인증 코드가 body에 포함됩니다.
- .then(response => response.json()): 서버의 응답을 JSON 형식으로 파싱합니다.
- .then(data => { ... }): 인증 성공 여부에 따라 처리합니다. 성공하면 인증 메시지를 표시하고 인증번호 입력 필드를 숨깁니다.
startTimer()
- function startTimer(): 인증 코드의 남은 시간을 카운트다운하는 타이머를 시작하는 함수입니다.
- const timerElement = document.getElementById('timer');: 타이머가 표시될 HTML 요소를 가져옵니다.
- const updateTimer = () => { ... };: 타이머를 업데이트하는 함수입니다.
- const now = Date.now();: 현재 시간을 가져옵니다.
- const timeLeft = verificationExpiryTime - now;: 인증 코드의 남은 시간을 계산합니다.
- if (timeLeft <= 0) { ... } else { ... }: 만료 시간이 지났는지 확인하고, 만료되면 타이머를 숨기고 메시지를 표시합니다. 그렇지 않으면 남은 시간을 표시하고 1초마다 업데이트합니다.
✍🏻index.js
const express = require('express');
const app = express();
const path = require('path');
const port = 3000;
const nodemailer = require('nodemailer');
require('dotenv').config(); // 환경 변수 로드
app.set("view engine", "ejs");
app.set('views', path.join(__dirname, 'views'));
app.use(express.json());
const transporter = nodemailer.createTransport({
service: "gmail",
port: 587,
auth: {
user: process.env.user, // 환경 변수로 설정
pass: process.env.pass // 환경 변수로 설정
}
});
let verificationData = {}; // 이메일과 인증코드를 저장할 객체
app.post('/send-email', (req, res) => {
const { email } = req.body;
// 생성할 인증번호 (6자리 랜덤 숫자)
const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();
verificationData[email] = { code: verificationCode, expiry: Date.now() + 1 * 60 * 1000 }; // 1분 만료
const mailOptions = {
to: email,
subject: 'Your Verification Code',
text: `Your verification code is ${verificationCode}`
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Error sending email:', error);
return res.status(500).json({ success: false, message: 'Failed to send email' });
}
console.log('Email sent:', info.response);
res.json({ success: true, message: 'Email sent successfully' });
});
});
app.post('/verify-code', (req, res) => {
const { email, code } = req.body;
const data = verificationData[email];
if (data) {
if (Date.now() > data.expiry) {
delete verificationData[email]; // 만료된 코드 제거
return res.json({ success: false, message: 'Verification code has expired.' });
}
if (code === data.code) {
delete verificationData[email]; // 인증 성공 후 코드 제거
res.json({ success: true, message: 'Code verified successfully' });
} else {
res.json({ success: false, message: 'Invalid code' });
}
} else {
res.json({ success: false, message: 'No code found for this email' });
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Nodemailer 설정
- nodemailer.createTransport(): 이메일 전송을 위한 SMTP 설정을 구성합니다.
- service: "gmail": 이메일을 전송할 서비스로 Gmail을 사용합니다.
- port: 587: SMTP 서버의 포트 번호입니다. 587은 TLS(Transport Layer Security)를 사용한 이메일 전송에 주로 사용됩니다.
- auth: 이메일 인증 정보를 설정합니다. user와 pass는 환경 변수로 저장된 Gmail 계정의 이메일과 비밀번호입니다.
인증 데이터 저장
- verificationData: 이메일과 인증 코드를 저장하는 객체입니다. 키로는 이메일 주소, 값으로는 인증 코드와 만료 시간을 저장합니다
이메일 전송 엔드포인트
- app.post('/send-email', (req, res): 이메일 전송을 처리하는 POST 요청의 엔드포인트입니다.
- const { email } = req.body;: 클라이언트로부터 받은 이메일 주소를 추출합니다.
- const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();: 6자리 랜덤 숫자를 생성하여 인증 코드로 사용합니다.
- verificationData[email] = { code: verificationCode, expiry: Date.now() + 1 * 60 * 1000 };: 이메일을 키로 사용하여 인증 코드와 만료 시간을 저장합니다. 만료 시간은 현재 시간으로부터 1분 후로 설정됩니다.
- const mailOptions = { ... };: 이메일 전송 옵션을 설정합니다. to는 수신자 이메일 주소, subject는 이메일 제목, text는 이메일 내용입니다.
- transporter.sendMail(mailOptions, (error, info): 설정한 이메일을 전송합니다. 전송 성공 여부에 따라 클라이언트에 응답합니다. 성공 시 JSON 형식으로 성공 메시지를 반환합니다. 실패 시 오류 메시지를 반환합니다.
인증 코드 검증 엔드포인트
- app.post('/verify-code', (req, res): 인증 코드를 검증하는 POST 요청의 엔드포인트입니다.
- const { email, code } = req.body;: 클라이언트로부터 받은 이메일과 인증 코드를 추출합니다.
- const data = verificationData[email];: verificationData 객체에서 이메일에 해당하는 인증 정보를 가져옵니다.
- if (data): 인증 정보가 존재하는지 확인합니다.
- if (Date.now() > data.expiry): 인증 코드가 만료되었는지 확인합니다. 만료되었다면, 해당 이메일의 인증 정보를 삭제하고 만료 메시지를 반환합니다.
- if (code === data.code): 사용자가 입력한 코드가 서버에 저장된 코드와 일치하는지 확인합니다. 일치하면 인증 성공 메시지를 반환하고, 인증 정보를 삭제합니다. 일치하지 않으면 오류 메시지를 반환합니다.
- else: 인증 정보가 없을 경우 오류 메시지를 반환합니다.