프로젝트/고농축 백엔드 코스

[고농축 백엔드 코스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를 붙여주면 어떤 에러가 나고, 

안붙여주면 다른 에러가 나고.. 포기 ㅠㅠ 

반응형