Commit ed9e5a14 authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

Merge branch 'gyumin'

parents 308d4f7f 3c84b7c1
......@@ -2,8 +2,8 @@ import axios from "axios";
import { baseUrl } from "../utils/baseUrl.js";
import config from "../utils/clientConfig.js";
const login = async (id, password) => {
const payload = { id, password };
const login = async (login) => {
const payload = login;
const { data } = await axios.post(`${baseUrl}/api/auth/login`, payload);
return data
};
......
import { useState } from "react";
import styles from "./login.module.scss";
import { useState } from "react";
import authApi from "../../apis/auth.api.js";
import { Redirect } from "react-router";
import catchErrors from "../../utils/catchErrors";
const Login = () => {
//useState를 이용해서 각 state 생성 및 초기값 저장
......@@ -9,9 +12,11 @@ const Login = () => {
id:'',
password:''
});
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [guestText, setGusetText] = useState({
const [guest, setGuset] = useState({
guestName:'',
gusetBirthday:'',
gusetMbnum:'',
......@@ -28,15 +33,54 @@ const Login = () => {
};
const handleGuestOnChange = (e) =>{
setGusetText({ ...guestText,
setGuset({ ...guest,
[e.target.name]:e.target.value
})
}
const handleOnSummitUser = async(e) => {
e.preventDefault();
try{
console.log("하하")
setError("");
setLoading(true);
const userData = login;
await authApi.login(userData);
alert('로그인이 완료되었습니다.')
setSuccess(true);
}catch(error){
catchErrors(error, setError);
}finally{
setLoading(false);
}
}
const handleOnSummitGuest = async(e) => {
e.preventDefault();
try{
setError("");
setLoading(true);
const gusetData = guest;
await authApi.login(gusetData);
alert('로그인이 완료되었습니다.')
setSuccess(true);
}catch(error){
catchErrors(error, setError);
}finally{
setLoading(false);
}
}
if (success) {
return <Redirect to="/" />;
}
return (
<div className={`d-flex flex-column col-md-5 col-10`}>
{/* nav-tabs */}
{/* {console.log(login)} */}
{console.log(success)}
<ul className="nav nav-fill nav-tabs w-100" id="loginTab" role="tablist">
<li className="nav-item fs-6" role="presentation">
<button className={`nav-link active px-2 ${styles.fontSize}`} style={{ color: state ? "black" : "#FEDC00", backgroundColor: state ? "#FEDC00" : "black"}}
......@@ -53,27 +97,27 @@ const Login = () => {
<div className="tab-content w-100" id="myTabContent">
{/* 로그인 */}
<div className="tab-pane fade show active" id="login" role="tabpanel" aria-labelledby="login-tab">
<div className="d-flex flex-column ">
<input className={styles.input} type="text" name="id" placeholder="ID" onChange={handleLoginOnChange}/>
<input className={styles.input} type="text" name="password" placeholder="Password" onChange={handleLoginOnChange} minlength="8" required />
<input className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} type="submit" value="Login" />
<form className="d-flex flex-column" onSubmit={handleOnSummitUser}>
<input className={styles.input} type="text" name="id" placeholder="ID" onChange={handleLoginOnChange} maxLength="10" required/>
<input className={styles.input} type="password" name="password" placeholder="Password" onChange={handleLoginOnChange} maxLength="8" required />
<input className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} type="submit" value="Login" disabled={loading} />
<span><a href="./signup" className={styles.intoSignupPage}>회원이 아니십니까?</a></span>
</div>
</form>
</div>
{/* 비회원예매 학인 */}
<div className="tab-pane fade" id="guest" role="tabpanel" aria-labelledby="guest-tab">
<div className="d-flex flex-column">
<input className={styles.input} type="text" name="guestName" id="guestName" placeholder="이름" onChange={handleGuestOnChange} minlength="8" required />
<input className={styles.input} type="number" name="gusetBirthday" id="gusetBirthday" placeholder="생년월일" onChange={handleGuestOnChange} minlength="8" required />
<input className={styles.input} type="number" name="gusetMbnum" id="gusetMbnum" placeholder="휴대폰 번호" onChange={handleGuestOnChange} minlength="8" required />
<input className={styles.input} type="text" name="guestPassword" id="password" placeholder="비밀번호" onChange={handleGuestOnChange} minlength="8" required />
<form className="d-flex flex-column" onSubmit={handleOnSummitGuest}>
<input className={styles.input} type="text" name="guestName" id="guestName" placeholder="이름" onChange={handleGuestOnChange} maxLength="5" required />
<input className={styles.input} type="number" name="gusetBirthday" id="gusetBirthday" placeholder="생년월일" onChange={handleGuestOnChange} maxLength="6" required />
<input className={styles.input} type="number" name="gusetMbnum" id="gusetMbnum" placeholder="휴대폰 번호" onChange={handleGuestOnChange} maxLength="11" required />
<input className={styles.input} type="password" name="guestPassword" id="password" placeholder="비밀번호" onChange={handleGuestOnChange} maxLength="8" required />
<p className={`text-white ${styles.fontSizeTwo}`}>
비회원 정보 입력 예매 내역 확인/취소 티켓 발권이 어려울 있으니 다시 한번 확인해 주시기 바랍니다.
</p>
<input className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} type="submit" value="비회원 예매 확인" />
</div>
<input className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} type="submit" value="비회원 예매 확인" disabled={loading}/>
</form>
</div>
</div>
</div>
......
......@@ -14,6 +14,14 @@
border-radius: 3px;
}
input[type=password]{
font-family: 'Courier New', Courier, monospace;
}
input::placeholder{
font-family: 'HangeulNuriB'
}
.fontSize{
font-size: 16px;
}
......
import styles from "./signup.module.scss";
import { useState, useEffect } from 'react';
import { useState } from 'react';
import authApi from "../../apis/auth.api.js";
import { Redirect } from "react-router";
import catchErrors from "../../utils/catchErrors.js";
const Signup = () => {
const [user, setUser] = useState({
userId: '',
userName: '',
userBirthday: '',
userMbnum: '',
userPassword: '',
userRePassword: ''
userId: "",
userNickName: "",
userBirthday: "",
userMbnum: "",
userPassword: "",
userRePassword: ""
})
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
//각 타입별 error 유무 state
const [error,setError] = useState("");
const [errorMsg, setErrorMsg] = useState({
errorId: null,
errorName: false,
errorNickName: false,
errorBirthday: false,
errorMbnum: false,
errorPassword: false,
......@@ -36,36 +36,56 @@ const Signup = () => {
...user,
[e.target.name]: e.target.value
})
if(e.target.name === "userBirthday" || e.target.name === "userMbum"){
setUser({
...user,
[e.target.name]: String(e.target.value)
})
}
}
//id(중복확인 체크, 형식 에러)
const handleOnClickId = async (e) => {
e.preventDefault();
const existId = await authApi.compareId(user.userId)
try{
setError("");
if (user.userId.length < 5) {
setErrorMsg(errorMsg => ({
...errorMsg,
[e.target.name]: true
}));
if (existId === true) {
if (overlapId === true) {
setOverlapId(() => (false));
};
} else {
const userId = user.userId;
await authApi.compareId(userId);
if(!await authApi.compareId(userId)){
alert("이 아이디는 사용가능합니다.")
setErrorMsg(errorMsg => ({
...errorMsg,
[e.target.name]: false
}));
setOverlapId(() => (true));
alert("이 아이디는 사용가능합니다.")
setOverlapId(()=>(true));
}else{
alert("이미 사용중인 아이디입니다.")
setOverlapId(()=>(false));
}
}
}catch(error){
catchErrors(error,setError)
}finally {
setLoading(false);
}
}
const handleOnSummit = async (e) => {
e.preventDefault();
try {
setError("");
setError(()=>(""));
//처리가 될때까지 버튼(가입하기)이 안눌리게 지정
setLoading(true);
setLoading(()=>(true));
//유효성 검사
validation();
const userData = user;
......@@ -81,46 +101,49 @@ const Signup = () => {
}
}
//유효성 검사 함수
const vaildationData = (text, compareValue, error) =>{
//비교하여 error메세지 반환
const vaildationData = (text, compareValue, error) => {
if (text !== compareValue) {
setErrorMsg(errorMsg => ({ ...errorMsg, [error]: true }));
} else{
setErrorMsg(errorMsg => ({ ...errorMsg, [error]: false }));
}
}
//유효성 검사
const validation = () => {
setPreId(()=> (user.userId));
//아이디 유효성 검사
if ((user.userId.length < 5)) {
setErrorMsg(errorMsg => ({ ...errorMsg, errorId: true }));
} else if((user.userId.length >= 5) && (overlapId === true)){
//아이디 비번 유효성 검사
const vaildationIdPw = (text, minValue, error) => {
if((text < minValue)){
setErrorMsg(errorMsg => ({...errorMsg, [error]: true}));
} else if(text >= minValue){
setErrorMsg(errorMsg => ({ ...errorMsg, [error]: false }));
if(overlapId === true){
if(preId !== user.userId){
console.log(preId);
setOverlapId(()=> (false));
setOverlapId(false);
}
}
else if(user.userId >= 5){
setErrorMsg(errorMsg => ({ ...errorMsg, errorId: false }));
}
}
//유효성 검사
const validation = () => {
setPreId(user.userId);
//아이디 유효성 검사
vaildationIdPw(user.userId.length,5,"errorId");
//별명 유효성 검사
vaildationData((user.userName.length === 0), false, "errorName");
vaildationData((user.userNickName.length === 0), false, "errorNickName");
// 생일 유효성 검사
vaildationData(user.userBirthday.length, 6, "errorBirthday");
// 휴대폰 유효성 검사
vaildationData(user.userMbnum.length, 11, "errorMbnum");
// 비밀번호 유효성 검사
vaildationData(user.userPassword.length, 8, "errorPassword");
vaildationIdPw(user.userPassword.length,8,"errorPassword");
// 비밀번호 확인 유효성 검사
vaildationData(user.userRePassword, user.userPassword, "errorRePassword");
// 최종 유효성 검사
if (overlapId && (Object.values(errorMsg).some((element) => (element)) === false)) {
console.log(";sadasda")
}else if(!overlapId && (Object.values(errorMsg).some((element) => (element)) === false)){
setErrorMsg(errorMsg => ({...errorMsg, errorId: false}));
throw new Error("먼저 아이디 중복확인을 해주세요");
}else{
console.log("에러발생")
throw new Error("유효하지 않은 데이터입니다.");
}
}
......@@ -133,7 +156,6 @@ const Signup = () => {
return (
// 데이터 입력과 유효성 검사 후 보이는 경고창
<form className={`d-flex col-md-6 col-12 justify-content-center`} onSubmit={handleOnSummit}>
{console.log("user==", user, errorMsg, overlapId)}
<div className="d-flex flex-column">
<span className={styles.title}>회원가입</span>
<div className="d-flex flex-column">
......@@ -141,20 +163,19 @@ const Signup = () => {
<label className={styles.signupLabel}>아이디</label>
<div className="d-flex col-md-auto">
<input className={styles.input} type="text" name="userId" id="userId" placeholder="5~10자리 사이" onChange={handleUserOnChange} maxLength="10" required />
<button type="button" name="errorId" className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickId}>중복확인</button>
<button type="button" disabled={loading} name="errorId" className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickId}>중복확인</button>
</div>
</div>
{(overlapId === false) && errorMsg.errorId && <p className={styles.passwordConfirmError}>5~10자리 사이로 입력해주세요.</p>}
{overlapId && (errorMsg.errorId === false) && <p className={styles.passwordConfirmError}>아이디 중복이 확인되었습니다.</p>}
{(errorMsg.errorId === false) && (overlapId === false) && <p className={styles.passwordConfirmError}>아이디 중복확인을 해주세요.</p>}
</div>
<div className="d-flex flex-column">
<div className={styles.inputContent}>
<label className={styles.signupLabel}>별명</label>
<input className={`${styles.input} ${styles.inputSize}`} type="text" name="userName" id="userName" placeholder="10자리 이내" onChange={handleUserOnChange} maxLength="10" required />
<input className={`${styles.input} ${styles.inputSize}`} type="text" name="userNickName" id="userNickName" placeholder="10자리 이내" onChange={handleUserOnChange} maxLength="10" required />
</div>
{errorMsg.errorName && <p className={styles.passwordConfirmError}>10 이내로 입력해주세요.</p>}
{errorMsg.errorNickName && <p className={styles.passwordConfirmError}>10 이내로 입력해주세요.</p>}
</div>
<div className="d-flex flex-column">
......@@ -168,23 +189,22 @@ const Signup = () => {
<div className="d-flex flex-column">
<div className={styles.inputContent}>
<label className={styles.signupLabel}>휴대폰 번호</label>
<input className={`${styles.input} ${styles.inputSize}`} type="number" name="userMbnum" id="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="0" max="99999999999" required />
<input className={`${styles.input} ${styles.inputSize}`} type="number" name="userMbnum" id="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required />
</div>
{errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>}
</div>
<div className="d-flex flex-column">
<div className={`${styles.inputContent}`}>
<label className={styles.signupLabel}>비밀번호</label>
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" id="password" placeholder="8자리 입력" onChange={handleUserOnChange} maxLength="8" required />
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" id="password" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required />
</div>
{errorMsg.errorPassword && <p className={styles.passwordConfirmError}>8자리를 입력해주세요.</p>}
{errorMsg.errorPassword && <p className={styles.passwordConfirmError}>8~11자리 사이로 입력해주세요.</p>}
</div>
<div className="d-flex flex-column">
<div className={styles.inputContent}>
<label className={styles.signupLabel}>비밀번호 확인</label>
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userRePassword" id="userRePassword" placeholder="8자리 입력" onChange={handleUserOnChange} maxLength="8" required />
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userRePassword" id="userRePassword" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required />
</div>
{errorMsg.errorRePassword && <p className={styles.passwordConfirmError}>비밀번호가 일치하지 않습니다.</p>}
</div>
......
const config = {
env: process.env.NODE_ENV === 'production' ? 'production' : 'development',
port: process.env.PORT || 3001,
// jwtSecret: 'dfkasf23i$efksdfks!',
jwtSecret: 'dfkasf23i$efksdfks!',
jwtExpires: '7d',
cookieName: 'butterStudio',
cookieMaxAge: 60 * 60 * 24 * 7 * 1000,
......
import axios from 'axios'
import sequelize from 'sequelize'
import { User } from '../db/index.js'
import jwt from "jsonwebtoken";
import config from "../config/app.config.js";
import { User, Role } from '../db/index.js'
const login = async(req, res) => {
try {
const { id, password } = req.body;
//사용자 존재 확인
const user = await User.scope("withPassword").findOne({ where: { userId: id } });
if (!user) {
return res.status(422).send(`사용자가 존재하지 않습니다`);
}
// 2) 비밀번호 확인은 데이터베이스 프로토타입 메소드에서 처리(사용자가 입력한 비밀번호와 서버에 있는 비번 비교)
const passwordMatch = await user.comparePassword(password);
if (passwordMatch) {
// 3) 비밀번호가 맞으면 토큰 생성
// const userRole = await user.getRole();
const signData = {
userId: user.id,
// role: userRole.name,
};
const token = jwt.sign(signData, config.jwtSecret, {
expiresIn: config.jwtExpires,
});
console.log("token = ", token);
// 4) 토큰을 쿠키에 저장
res.cookie(config.cookieName, token, {
maxAge: config.cookieMaxAge,
path: "/",
httpOnly: config.env === "production",
secure: config.env === "production",
});
// 5) 사용자 반환
res.json({
userId: user.id,
isLoggedIn: true,
// role: userRole.name,
// isMember: user.isMember,
});
} else {
// 6) 비밀번호 불일치
res.status(401).send("비밀번호가 일치하지 않습니다");
}
} catch (error) {
console.error(error);
return res.status(500).send("로그인 에러");
}
}
const compareId = async (req, res) => {
const id = req.params.userId;
const userid = await User.findAll({where:{userId: id}});
if(userid){
const userid = await User.findOne({where:{userId: id}});
if(userid !== null){
return res.json(true);
}else{
return res.json(false);
}
}
const signup = async (req, res) => {
const { userId, userNickName, userBirthday, userPassword } = req.body;
// 휴대폰 중복 확인
const userMbnum = String(req.body.userMbnum);
try {
const mbnum = await User.findOne({ where: { phoneNumber: userMbnum } });
if (mbnum) {
return res.status(422).send(`이미 있는 휴대폰번호입니다.`);
}
const role = await Role.findOne({ where: {name: "user"} })
const newUser = await User.create({
userId: userId,
nickname: userNickName,
birth: userBirthday,
phoneNumber: userMbnum,
password: userPassword,
roleId: role.id
});
res.json(newUser);
} catch (error) {
console.error(error.message);
res.status(500).send("회원가입 에러. 나중에 다시 시도 해주세요");
}
};
export default {
compareId
login,
compareId,
signup
}
......@@ -29,6 +29,9 @@ const Cinema = CinemaModel(sequelize)
User.belongsTo(Role);
Role.hasOne(User);
User.belongsTo(Role);
Role.hasOne(User);
export {
sequelize,
User,
......
import dotenv from "dotenv";
import { sequelize } from "./db/index.js";
import { sequelize, Role } from "./db/index.js";
import app from "./app.js";
import appConfig from "./config/app.config.js";
import { ROLE_NAME } from './models/role.model.js';
dotenv.config({
path: `${process.env.NODE_ENV === "production" ? ".env" : ".env.development"
......@@ -11,11 +12,11 @@ dotenv.config({
sequelize
.sync({ force: false })
.then(async () => {
// await Promise.all(
// Object.keys(ROLE_NAME).map((name) => {
// return Role.create({ name });
// })
// );
await Promise.all(
Object.keys(ROLE_NAME).map((name) => {
return Role.create({ name });
})
);
// const adminRole = await Role.findOne({ where: { name: "admin" } });
......
......@@ -16,33 +16,53 @@ const UserModel = (sequelize) => {
userId: {
type: DataTypes.STRING,
},
password: {
type: DataTypes.STRING,
},
nickname: {
type: DataTypes.STRING,
},
birth: {
type: DataTypes.INTEGER,
type: DataTypes.STRING,
},
phoneNumber: {
type: DataTypes.INTEGER
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING,
},
},
{
timestamps: true,
freezeTableName: true,
tableName: "users"
// defaultScope: {
// attributes: { exclude: ["password"] },
// },
// scopes: {
// withPassword: {
// attributes: { include: ["password"] },
// },
// },
tableName: "users",
defaultScope: {
attributes: { exclude: ["password"] },
},
scopes: {
withPassword: {
attributes: { include: ["password"] },
},
},
}
);
User.beforeSave(async (user) => {
if (!user.changed("password")) {
return;
}
if (user.password) {
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
}
});
User.prototype.comparePassword = async function (plainPassword) {
const passwordMatch = await bcrypt.compare(
plainPassword,
this.password
);
return passwordMatch;
};
return User
};
......
......@@ -4,9 +4,13 @@ import userCtrl from "../controllers/user.controller.js";
const router = express.Router();
router
.route("/")
.post()
.get()
.route("/login")
.post(userCtrl.login)
router
.route("/signup")
.post(userCtrl.signup)
router
.route("/:userId")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment