프로젝트/고농축 백엔드 코스
[고농축 백엔드 코스Quiz6]프론트엔드와 API 연동하기 - 문자 & 이메일 보내기
개발자국S2
2023. 8. 1. 18:13
quiz6/frontend/login
더보기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>로그인/회원가입</title>
<link rel="stylesheet" href="./index.css" />
<script src="./index.js"></script>
<script src="./signup.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<!-- 전체 컨테이너 -->
<div class="Container">
<!-- 여기서부터 회원가입 모달창 -->
<div id="ModalContainer" onclick="CloseModal()"></div>
<div id="SignupModalWrapper">
<div class="SignupTitle">회원가입</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">이름</span>
<input
id="SignupName"
class="SignupInput"
type="text"
placeholder="이름을 입력해주세요"
/>
</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">주민등록번호</span>
<div style="display: flex; align-items: center">
<input
id="SignupPersonal"
style="width: 40%; text-align: center"
class="SignupInput"
type="text"
maxlength="6"
/>
<span
style="
text-align: center;
color: #fff;
padding: 0 5px 0 5px;
font-size: 20px;
"
>-
</span>
<input
style="width: 40%; text-align: center"
class="SignupInput"
type="password"
maxlength="7"
/>
</div>
</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">핸드폰 번호</span>
<div
style="
display: flex;
justify-content: space-between;
padding-bottom: 20px;
"
>
<div style="display: flex">
<input
id="PhoneNumber01"
class="SignupInput"
type="text"
maxlength="3"
value="010"
readonly
/><span class="PhoneSlash">-</span
><input
id="PhoneNumber02"
class="SignupInput"
type="text"
maxlength="4"
/><span class="PhoneSlash">-</span
><input
id="PhoneNumber03"
class="SignupInput"
type="text"
maxlength="4"
/>
</div>
<span class="NumberVailidationBtn" onclick="getValidationNumber()"
>인증 번호 전송</span
>
</div>
<div
id="ValidationInputWrapper"
style="display: none; align-items: center"
>
<input
id="TokenInput"
class="ValidationInput"
type="text"
placeholder="전송 받은 인증번호를 입력해주세요"
/>
<span id="LimitTime">1:00</span>
<div class="NumberVailidationBtn" onclick="submitToken()">
인증 완료
</div>
</div>
</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">좋아하는 사이트</span>
<input
id="SignupPrefer"
class="SignupInput"
type="text"
placeholder="좋아하는 사이트를 입력해주세요"
/>
</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">이메일</span>
<input
id="SignupEmail"
class="SignupInput"
type="text"
placeholder="이메일을 입력해주세요"
/>
</div>
<div class="SignupInputWrapper">
<span class="SignupInputName">비밀번호</span>
<input
id="SignupPwd"
class="SignupInput"
type="password"
placeholder="비밀번호를 입력해주세요"
/>
</div>
<div class="ButtonWrapper">
<div class="Signup" onclick="submitSignup()">회원 가입</div>
</div>
</div>
<!-- 여기까지가 회원가입 모달창 -->
<!-- 본 컨텐츠 -->
<div class="Wrapper">
<div class="LogoWrapper">
<img class="LogoImg" src="../img/starbucks.png" />
<div class="LogoTitle">CodeBucks</div>
<a href="../menu/index.html" class="Menu_Button"> 메뉴 바로가기</a>
</div>
<div id="SocialLoginGoogle">
<img class="SocialLoginButtonImg" src="../img/google.png" />
<div class="SocialLoginButtonTitle">구글로 로그인</div>
</div>
<div id="SocialLoginNaver">
<img class="SocialLoginButtonImg" src="../img/naver.png" />
<div class="SocialLoginButtonTitle">네이버로 로그인</div>
</div>
<div id="SocialLoginKakao">
<img class="SocialLoginButtonImg" src="../img/kakao.png" />
<div class="SocialLoginButtonTitle">카카오톡으로 로그인</div>
</div>
<div id="SocialLoginFacebook">
<img class="SocialLoginButtonImg" src="../img/facebook.png" />
<div class="SocialLoginButtonTitle">페이스북으로 로그인</div>
</div>
<div class="SignupWrapper">
아직 회원이 아니신가요?
<span class="SignupButton" onclick="OpenModal()">가입 하기</span>
</div>
</div>
</div>
</body>
</html>
quiz/backend/index.js
더보기
import express from "express";
import { sendTokenToSMS, checkPhone, getToken } from "./phone.js";
import swaggerUi from "swagger-ui-express";
import swaggerJsdoc from "swagger-jsdoc";
import { options } from "./swagger/config.js";
import cors from "cors";
import "dotenv/config";
import nodemailer from "nodemailer";
import { format } from "date-fns";
const app = express();
app.use(express.json()); //.use는 get&post 모두에서 작동한다
app.use(cors());
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerJsdoc(options)));
app.post("/tokens/phone", function (req, res) {
// 들어온 값 확인하기
// postman으로 요청 보낼 때 {"<key>":"값"}을 해줘야함
// key = req.body.<key> 로 설정하고.
const phoneNo = req.body.number;
// 1. 휴대폰 번호 자리수 맞는지 확인하기
const isValid = checkPhone(phoneNo);
if (isValid === false) return;
// 2. 핸드폰 토큰 6자리 만들기
const token = getToken();
// 3. 핸드폰 번호에 토큰 전송하기
sendTokenToSMS(phoneNo, token);
res.send(phoneNo);
});
app.post("/email", function (req, res) {
const userInfo = req.body.userInfo;
// 1. 회원이 입력한 정보 받아오기
const sendEmailFormat = joinMailTemplate(userInfo);
// 2a. 회원 이메일 받아오기
const userEmail = userInfo.email;
// // 2b. 회원 이메일 유효한지 확인하기
const isValid = checkEmail(userEmail);
if (isValid === false) {
return;
}
// 2. 회원이 받아온 정보를 이메일로 보내기
sendWelcomeEmail(userEmail, sendEmailFormat);
console.log("메일 전송완료");
});
const checkEmail = (email) => {
// 1. 이메일이 정상인지 확인 (a. 존재여부, b. "@"존재여부)
// console.log("email : ", email);
if (!email || !email.includes("@")) {
console.log("이메일을 제대로 입력해주세요 ! ");
return false;
} else {
return true;
}
};
function joinMailTemplate(data) {
const today = new Date();
const formattedDate = format(today, "yyyy-MM-dd");
const myTemplate = `
<html>
<body>
<div style="display : flex; flex-direction:column; align-items:center">
<div style="width : 500px;">
<h1>${data.name}님 가입을 환영합니다!!!</h1>
<hr />
<div>이름 : ${data.name}</div>
<div>전화번호 : ${data.phoneNo}</div>
<div>좋아하는 사이트 : ${data.prefer}</div>
<div style="color:red";>가입일 : ${formattedDate}</div>
</div>
</div>
</body>
</html>
`;
return myTemplate;
}
async function sendWelcomeEmail(emailAddr, welcomeTemplate) {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.MY_EMAIL,
pass: process.env.GOOGLE_PW, // Google에서 제공한 2차비밀번호 => email / windows computer
},
});
const res = await transporter.sendMail({
from: process.env.MY_EMAIL,
to: emailAddr,
subject: " === 메일 전송 테스트 === ",
html: welcomeTemplate,
});
}
app.listen(4000);
quiz6/frontend/login/index.js
더보기
const OpenModal = () => {
let ModalBackground = document.getElementById("ModalContainer");
let Modal = document.getElementById("SignupModalWrapper");
ModalBackground.style.display = "flex";
Modal.style.display = "flex";
};
const CloseModal = () => {
let ModalBackground = document.getElementById("ModalContainer");
let Modal = document.getElementById("SignupModalWrapper");
Modal.style.display = "none";
ModalBackground.style.display = "none";
};
quiz6/frontend/login/signup.js
더보기
// 휴대폰 인증 토큰 전송하기
const getValidationNumber = async () => {
document.querySelector("#ValidationInputWrapper").style.display = "flex";
phoneNo = validatePhoneNo();
// 파라미터로 getNumber()에 넘기기
await getNumber(phoneNo);
console.log("인증 번호 전송");
};
// 휴대폰 번호 받기
const validatePhoneNo = () => {
// html에 있는 핸드폰 번호를 하나의 번호로 받기
const phoneNo =
document.querySelector("#PhoneNumber01").value +
document.querySelector("#PhoneNumber02").value +
document.querySelector("#PhoneNumber03").value;
return phoneNo;
};
// 회원 가입 API 요청
const submitSignup = async () => {
const userInfoArray = {
name: document.querySelector("#SignupName").value,
personal: document.querySelector("#SignupPersonal").value,
phoneNo: validatePhoneNo(),
prefer: document.querySelector("#SignupPrefer").value,
password: document.querySelector("#SignupPwd").value,
email: document.querySelector("#SignupEmail").value,
};
await getUserInfo(userInfoArray);
};
const getNumber = (phoneNo) => {
return axios.post("http://localhost:4000/tokens/phone", { number: phoneNo });
};
const getUserInfo = (userInfoArray) => {
return axios.post("http://localhost:4000/email", {
userInfo: userInfoArray,
});
};
backend/index.js의 파일의 checkEmail(), joinMailTemplate(), sendWelcomEmail()을 너무 분리시키고 싶은데 분리시킬 때마다 export 에러가 발생한다. 너무 시간을 많이 잡아먹어서 일단 스킵한다.
에러 :
Uncaught SyntaxError: Unexpected token 'export'
Uncaught ReferenceError: "함수명" is not defined at HTMLSpanElement.onclick
해결법을 찾아서 export를 붙여주면 어떤 에러가 나고,
안붙여주면 다른 에러가 나고.. 포기 ㅠㅠ
반응형