본문 바로가기
프로젝트/로그인, 회원가입

3. 이메일, 정규 표현식

by 갱생angel 2024. 5. 23.

※회원가입에 이메일 항목 추가

※정규표현식으로 이메일 형식 검사

※에러 사항을 alert 대신 input 하단에 표시되도록 수정

 

-백엔드-

 

model - <userModel.js> : email 추가

const mongoose = require("mongoose");

const Schema = mongoose.Schema;

const UserSchema = new Schema({
  username: {
    type: String,
    require: true,
    unique: true,
  },
  password: {
    type: String,
    require: true,
  },
  email: { //email 추가
    type: String,
    require: true,
  },
  imageScore: {
    type: Number,
    default: 0,
  },
  combineScore: {
    type: Number,
    default: 0,
  },
});

module.exports = mongoose.model("User", UserSchema);

 

controller - <userController.js>

정규 표현식: 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어

  -/^ : 시작, $/ : 끝

  -[A-Za-z0-9] : 영문 대소문자 혹은 숫자로 시작

  -([-_.][A-Za-z0-9]) : 두 번째 글자부터는 영문 대소문자 혹은 숫자이며 - _ .이 들어갈 수 있음

  -* : 문자 또는 숫자가 반드시 있어야 함

  -{2,3} : 2-3자리 지정

  -i : 대소문자를 구분하지 않음

  -test() : 문자열에 일치하는 부분이 있는지 확인하고, true 혹은 false로 return하는 메서드

(...)

//Post Login User, /login
const loginUser = asynchHandler(async (req, res) => {
  const { username, password } = req.body;

  const user = await User.findOne({ username });
  if (!user) {
    return res.status(401).json({ nameMessage: "일치하는 아이디가 없습니다." });
  }

  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) {
    return res
      .status(401)
      .json({ pwdMessage: "비밀번호가 일치하지 않습니다." });
  }

  const token = jwt.sign({ id: user._id }, jwtSecret);
  res.cookie("token", token, { httpOnly: true });
  res.status(200).json({ message: "로그인 성공", token });
});

//Post Register User, /register
const registerUser = asynchHandler(async (req, res) => {
  const { username, password, chackPassword, email } = req.body;
  //이메일 정규 표현식
  const emailRegEx =
    /^[A-Za-z0-9]([-_.]?[A-Za-z0-9])*@[A-Za-z0-9]([-_.]?[A-Za-z0-9])*\.[A-Za-z]{2,3}$/i; 

  const existingUser = await User.findOne({ username });
  if (existingUser) {
    return res.status(400).json({ nameMessage: "이미 사용중인 아이디입니다." });
  }
  if (password !== chackPassword) {
    return res
      .status(401)
      .json({ pwdMessage: "비밀번호가 일치하지 않습니다." });
  }
  if (!emailRegEx.test(email)) { //이메일 형식 확인
    return res
      .status(401)
      .json({
        emailMessage: "이메일 형식이 올바르지 않습니다. ex) admin@aaa.com",
      });
  }

  if (password === chackPassword) {
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = await User.create({
      username,
      password: hashedPassword,
      email,
    });
    res.status(201).json({ message: "회원가입 성공" });
  }
});

(...)

-프론트엔드-

 

page - <Register.js> : 회원가입 페이지

some() : 배열의 1개 요소라도 특정 조건을 충족하는지 확인하는 함수

import React, { useEffect, useState } from "react";
import axios from "axios";
import "../../css/user.css";
import { useNavigate } from "react-router-dom";

function Register() {
  const navigate = useNavigate();

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [password2, setPassword2] = useState("");
  const [email, setEmail] = useState(""); //이메일 state

  const [usernameError, setUsernameError] = useState(""); //아이디 에러
  const [passwordError, setPasswordError] = useState(""); //비밀번호 에러
  const [emailError, setEmailError] = useState(""); //이메일 에러

  const changeUsername = (e) => setUsername(e.target.value);
  const changePassword = (e) => setPassword(e.target.value);
  const changePassword2 = (e) => setPassword2(e.target.value);
  const changeEmail = (e) => setEmail(e.target.value); //이메일 onChange

  const registerSubmit = async (e) => {
    e.preventDefault();

    const inputs = [username, password, password2, email]; //input 모음
    const registerData = {
      username: username,
      password: password,
      chackPassword: password2,
      email: email,
    };

    if (inputs.some((input) => input === "")) { //input 모두 빈칸일 시
      alert("빈칸을 입력해주세요.");
    } else {
      try {
        await axios
          .post("http://localhost:5000/register", registerData)
          .then((res) => {
            alert(res.data.message);
            navigate("/");
          });
      } catch (err) {
        setUsernameError(err.response.data.nameMessage); //아이디 에러 메시지
        setPasswordError(err.response.data.pwdMessage); //비밀번호 에러 메시지
        setEmailError(err.response.data.emailMessage); //이메일 에러 메시지
      }
    }
  };

  useEffect(() => {
    if (localStorage.getItem("token") !== null) navigate("/main");
  }, [navigate]);

  return (
    <div className="userContainer">
      <h1>회원가입</h1>
      <form onSubmit={registerSubmit}>
        <div>
          <label>아이디</label>
          <input type="text" value={username} onChange={changeUsername} />
          <h4>{usernameError}</h4>
        </div>
        <div>
          <label>비밀번호</label>
          <input type="password" value={password} onChange={changePassword} />
        </div>
        <div>
          <label>비밀번호 확인</label>
          <input type="password" value={password2} onChange={changePassword2} />
          <h4>{passwordError}</h4>
        </div>
        <div>
          <label>이메일</label>
          <input
            type="text"
            value={email}
            onChange={changeEmail}
            placeholder="ex) admin@aaa.com"
          />
          <h4>{emailError}</h4>
        </div>
        <button type="submit">회원가입</button>
      </form>
      <p onClick={() => navigate("/")}>-#로그인-</p>
    </div>
  );
}

export default Register;

 

page - <Login.js> : 로그인 페이지

import React, { useEffect, useState } from "react";
import axios from "axios";
import "../../css/user.css";
import { useNavigate } from "react-router-dom";

function Login() {
  const navigate = useNavigate();

  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  const [usernameError, setUsernameError] = useState(""); //아이디 에러
  const [passwordError, setPasswordError] = useState(""); //비밀번호 에러

  const changeUsername = (e) => setUsername(e.target.value);
  const changePassword = (e) => setPassword(e.target.value);

  const loginSubmit = async (e) => {
    e.preventDefault();

    const loginData = { username: username, password: password };

    if (username === "" || password === "") {
      alert("빈칸을 입력해주세요.");
    } else {
      try {
        await axios
          .post("http://localhost:5000/login", loginData)
          .then((res) => {
            alert(res.data.message);
            const { token } = res.data;
            localStorage.setItem("token", token);
            navigate("/main");
          });
      } catch (err) {
        setUsernameError(err.response.data.nameMessage); //아이디 에러 메시지
        setPasswordError(err.response.data.pwdMessage); //비밀번호 에러 메시지
      }
    }
  };

  useEffect(() => {
    if (localStorage.getItem("token") !== null) navigate("/main");
  }, [navigate]);

  return (
    <div className="userContainer">
      <h1>로그인</h1>
      <p></p>
      <form onSubmit={loginSubmit}>
        <div>
          <label>아이디</label>
          <input type="text" value={username} onChange={changeUsername} />
          <h4>{usernameError}</h4>
        </div>
        <div>
          <label>비밀번호</label>
          <input type="password" value={password} onChange={changePassword} />
          <h4>{passwordError}</h4>
        </div>
        <button type="submit">로그인</button>
      </form>
      <p onClick={() => navigate("/register")}>-#계정 생성-</p>
    </div>
  );
}

export default Login;

 

css - <user.css>

(...)

.userContainer h4 {
  color: red;
  height: 15px;
}

'프로젝트 > 로그인, 회원가입' 카테고리의 다른 글

6. 비밀번호 변경  (0) 2024.05.27
5. nodemailer, 인증코드  (0) 2024.05.27
4. 아이디 찾기  (0) 2024.05.24
2. JWT 토큰 검증  (0) 2024.05.20
1. user 파일, score 파일 병합  (0) 2024.05.20