Commit 4517a35f authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

Merge branch 'gyumin'

parents 992699ae 1563c9a4
/node_modules /node_modules
.env.development .env.development
.env .env
\ No newline at end of file ./upload
\ No newline at end of file
import axios from "axios"; import axios from "axios";
import { baseUrl } from "../utils/baseUrl.js"; import { baseUrl } from "../utils/baseUrl.js";
import config from "../utils/clientConfig.js";
const getUser = async () => { const getUser = async () => {
const url = `${baseUrl}/api/auth/user` const url = `${baseUrl}/api/auth/user`
const { data } = await axios.get(url) const { data } = await axios.get(url)
return data return data
} }
const login = async (login) => { const login = async (login) => {
...@@ -32,14 +30,25 @@ const compareId = async (userId) => { ...@@ -32,14 +30,25 @@ const compareId = async (userId) => {
return data return data
} }
const confirmMbnum = async (id, token) => { const confirmMbnum = async (phone) => {
const url = `${baseUrl}/api/auth/${id}/${token}` const url = `${baseUrl}/api/auth/phone/${phone}`
const { data } = await axios.post(url)
return data
}
const confirmNum = async (num) => {
const url = `${baseUrl}/api/auth/num/${num}`
const { data } = await axios.get(url) const { data } = await axios.get(url)
return data return data
} }
const getNickName = async (id) => { const profile = async (formData) => {
const url = `${baseUrl}/api/auth/nickname/${id}` const url = `${baseUrl}/api/auth/profile`
const { data } = await axios.post(url, formData)
return data
}
const getMember = async (id) => {
const url = `${baseUrl}/api/auth/member`
const { data } = await axios.get(url) const { data } = await axios.get(url)
return data return data
} }
...@@ -62,7 +71,9 @@ const authApi = { ...@@ -62,7 +71,9 @@ const authApi = {
signup, signup,
compareId, compareId,
confirmMbnum, confirmMbnum,
getNickName, confirmNum,
profile,
getMember,
comparePw, comparePw,
modifyUser, modifyUser,
}; };
......
import styles from "./my-info.module.scss"; import styles from "./my-info.module.scss";
import { useAuth } from "../../context/auth_context";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import authApi from "../../apis/auth.api"; import authApi from "../../apis/auth.api";
import catchErrors from "../../utils/catchErrors.js"; import catchErrors from "../../utils/catchErrors.js";
const MyInfo = () => { const MyInfo = () => {
const { user } = useAuth();
const [userNickName, setUserNickName] = useState("사용자"); const [userNickName, setUserNickName] = useState("사용자");
const [img, setImg] = useState("");
const [profile, setProfile] = useState("");
// 사용자 이름 가져오는 함수 // 사용자 이름 가져오는 함수
const getNickName = async (id) => { const getMember = async () => {
const nickname = await authApi.getNickName(id); const member = await authApi.getMember();
setUserNickName(nickname); setUserNickName(member.nickname);
setProfile(member.img);
} }
const [page, setPage] = useState(true);
//현재 비밀번호 state //현재 비밀번호 state
const [presentPw, setPresentPw] = useState(""); const [presentPw, setPresentPw] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [style, setStyle] = useState("visually-hidden-focusable");
//변경할 데이터 입력받는 state //변경할 데이터 입력받는 state
const [userRe, setUserRe] = useState({ const [userRe, setUserRe] = useState({
...@@ -39,7 +40,7 @@ const MyInfo = () => { ...@@ -39,7 +40,7 @@ const MyInfo = () => {
}) })
useEffect(() => { useEffect(() => {
getNickName(user.id); getMember();
}, []) }, [])
const handlePwOnChange = (e) => { const handlePwOnChange = (e) => {
...@@ -65,6 +66,24 @@ const MyInfo = () => { ...@@ -65,6 +66,24 @@ const MyInfo = () => {
handleOnSummitVerify(e); handleOnSummitVerify(e);
} }
} }
const handleOnChange = (e) => {
setImg(e.target.files[0]);
}
const handleOnSummitForm = async (e) => {
e.preventDefault();
try {
setError("")
const formData = new FormData();
formData.append("image", img);
const image = await authApi.profile(formData);
console.log(image.img);
setProfile(image.img);
} catch (error) {
catchErrors(error, setError);
}
}
//기존 비밀번호 확인 //기존 비밀번호 확인
const handleOnSummitVerify = async (e) => { const handleOnSummitVerify = async (e) => {
e.preventDefault(); e.preventDefault();
...@@ -73,10 +92,10 @@ const MyInfo = () => { ...@@ -73,10 +92,10 @@ const MyInfo = () => {
setLoading(() => (true)); setLoading(() => (true));
const pw = presentPw; const pw = presentPw;
const confirmPw = await authApi.comparePw(pw); const confirmPw = await authApi.comparePw(pw);
console.log("confirmPw : "+ confirmPw);
if (confirmPw) { if (confirmPw) {
setStyle(""); setPage(false);
} else { } else {
setStyle("visually-hidden-focusable");
alert("비밀번호가 일치하지 않습니다."); alert("비밀번호가 일치하지 않습니다.");
} }
} catch (error) { } catch (error) {
...@@ -103,46 +122,45 @@ const MyInfo = () => { ...@@ -103,46 +122,45 @@ const MyInfo = () => {
} }
} }
//유효성 검사 //유효성 검사
const validation = async () => { const validation = () => {
try {//별명 유효성 검사 //별명 유효성 검사
vaildationData((userRe.userNickName.length === 0), false, "errorNickName"); vaildationData((userRe.userNickName.length === 0), false, "errorNickName");
// 휴대폰 유효성 검사 // 휴대폰 유효성 검사
vaildationData(userRe.userMbnum.length, 11, "errorMbnum"); vaildationData(userRe.userMbnum.length, 11, "errorMbnum");
// 비밀번호 유효성 검사 // 비밀번호 유효성 검사
vaildationIdPw(userRe.userPassword.length, 8, "errorPassword"); vaildationIdPw(userRe.userPassword.length, 8, "errorPassword");
// 비밀번호 확인 유효성 검사 // 비밀번호 확인 유효성 검사
vaildationData(userRe.userPassword, userRe.userRePassword, "errorRePassword"); vaildationData(userRe.userPassword, userRe.userRePassword, "errorRePassword");
// 최종 유효성 검사 // 최종 유효성 검사
if ((Object.values(errorMsg).some((element) => (element)) === false)) { if ((Object.values(errorMsg).some((element) => (element)) === false)) {
return true return true
} else { } else {
throw new Error("유효하지 않은 데이터입니다."); return false
}
} catch (error) {
catchErrors(error, setError);
} }
} }
const handleOnSummit = async (e) => { const handleOnSummit = async (e) => {
e.preventDefault(); e.preventDefault();
try { try {
setError(() => ("")); setError(() => (""));
//처리가 될때까지 버튼(가입하기)이 안눌리게 지정 //처리가 될때까지 버튼(가입하기)이 안눌리게 지정
setLoading(() => (true)); setLoading(() => (true));
console.log("userRe : " + userRe.userEmail); console.log("userRe : " + userRe.userEmail);
//유효성 검사 //유효성 검사
const vaild = validation(); let valid = validation();
if (vaild) { console.log("valid :" + valid);
if (valid) {
const userData = userRe; const userData = userRe;
console.log(userData); console.log(userData);
//서버로 전송 //서버로 전송
const process = await authApi.modifyUser(userData); const process = await authApi.modifyUser(userData);
console.log("process : " + process); console.log("process : " + process);
alert("회원정보 수정 완료"); alert("회원정보 수정 완료");
} valid = false;
} else { throw new Error("유효하지 않은 데이터입니다.") }
} catch (error) { } catch (error) {
//에러전송 //에러전송
catchErrors(error, setError); catchErrors(error, setError);
...@@ -151,111 +169,125 @@ const MyInfo = () => { ...@@ -151,111 +169,125 @@ const MyInfo = () => {
} }
} }
const handleOnClick = (e) => {
e.preventDefault();
setPage(true);
}
return ( return (
<> <>
{/* 마이페이지 창 */} {/* 마이페이지 창 */}
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<span className={styles.title}>마이페이지</span> <span className={styles.title}>마이페이지</span>
<div className="d-flex flex justify-content-center"> <div className="d-flex justify-content-around">
<img src="https://search.pstatic.net/sunny/?src=https%3A%2F%2Fi.pinimg.com%2Foriginals%2F1e%2F1c%2F2e%2F1e1c2e4e07f5c440c3adad38bcd6f854.jpg&type=a340" <div className={`${styles.box} me-5`}>
className="img-fluid px-5 rounded-circle" /> <p className={`${styles.hoverTxt}`}>프로필 변경</p>
{/* <button type="submit"></button> */} <img src={`/upload/${profile}`} className={`figure-img img-fluid rounded-circle ${styles.profile}`} role="button" data-bs-toggle="modal" data-bs-target="#staticBackdrop" />
<div className="d-flex flex-column py-4 justify-content-around"> </div>
<span className={`${styles.userName}`}>{`${userNickName}`} 반갑습니다!</span> <div className="d-flex flex-column py-4 justify-content-around">
<button className={`rounded my-3 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} data-bs-toggle="modal" href="#verifyPassword" >회원정보 수정</button> <span className={`${styles.userName}`}>{`${userNickName}`} 반갑습니다!</span>
<button className={`rounded my-3 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} data-bs-toggle="modal" href="#verifyPassword" >회원정보 수정</button>
</div>
</div> </div>
</div> </div>
</div>
{/* 기존 비밀번호 확인 모달창 */} <div className="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div className="modal fade" id="verifyPassword" data-bs-backdrop="static" data-bs-keyboard="false" aria-hidden="true" aria-labelledby="verifyPasswordLabel" tabindex="-1"> <div className="modal-dialog modal-dialog-centered">
<div className="modal-dialog modal-dialog-centered"> <form className="modal-content" onSubmit={handleOnSummitForm}>
<form className="modal-content" onSubmit={handleOnSummitVerify}> <div className="modal-header">
<div className="modal-header"> <h5 className="modal-title" id="staticBackdropLabel">프로필변경</h5>
<h5 className="modal-title" id="verifyPasswordLabel">기존 비밀번호 확인</h5> <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div clasNames="modal-body">
<div className="d-flex flex-column ">
<div className="d-flex justify-content-around align-items-center my-4">
<label className={styles.signupLabel}>현재 비밀번호</label>
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" placeholder="8~11자리 사이" onChange={handlePwOnChange} onKeyPress={enterKey} maxLength="11" required />
</div>
{(style !== "visually-hidden-focusable") && <p className={styles.passwordConfirmError}>다음을 클릭해주세요.</p>}
</div> </div>
</div> <div className="modal-body">
<div className="modal-footer"> <input type="file" onChange={handleOnChange} />
<button className={`${style} rounded my-3 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} data-bs-target="#modify" data-bs-toggle="modal" data-bs-dismiss="modal" disable={style === "visually-hidden-focusable"}>다음</button> </div>
<button type="submit" className={`rounded my-3 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} disabled={loading}>확인</button> <div className="modal-footer">
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
</div> <button type="submit" className={`rounded fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`}>변경</button>
</form> </div>
</form>
</div>
</div> </div>
</div>
{/* 회원정보 수정 모달창 */} {/* 기존 비밀번호 확인 모달창 */}
<div className="modal fade" id="modify" data-bs-backdrop="static" data-bs-keyboard="false" aria-hidden="true" aria-labelledby="modifyLabel" tabindex="-1"> <div className="modal fade" id="verifyPassword" data-bs-backdrop="static" data-bs-keyboard="false" aria-hidden="true" aria-labelledby="verifyPasswordLabel" tabindex="-1">
<div className="modal-dialog modal-dialog-centered"> <div className="modal-dialog modal-dialog-centered modal-dialog-centered">
<form className={`modal-content d-flex col-md-6 col-12 justify-content-center d-flex flex-column`} onSubmit={handleOnSummit}> {page ?
<div className="modal-header"> <><form className="modal-content" onSubmit={handleOnSummitVerify}>
<h5 className="modal-title" id="modifyLabel">회원정보 수정</h5> <div className="modal-header">
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <h5 className="modal-title" id="verifyPasswordLabel">기존 비밀번호 확인</h5>
</div> <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick={handleOnClick}></button>
<div className={`modal-body`}>
<div className="d-flex flex-column">
<div className={styles.inputContent}>
<label className={styles.signupLabel}>이메일</label>
<input className={`${styles.input} ${styles.inputSize}`} type="email" name="userEmail" placeholder="이메일을 입력해주세요" onChange={handleUserOnChange} maxlength="20" required />
</div> </div>
{errorMsg.errorEmail && <p className={styles.passwordConfirmError}>이메일을 입력해주세요</p>} <div clasNames="modal-body">
</div> <div className="d-flex flex-column">
<div className="d-flex flex-column"> <div className="d-flex justify-content-around align-items-center my-4">
<div className={styles.inputContent}> <label className={styles.signupLabel}>현재 비밀번호</label>
<label className={styles.signupLabel}>별명</label> <input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" placeholder="8~11자리 사이" onChange={handlePwOnChange} onKeyPress={enterKey} maxLength="11" required />
<input className={`${styles.input} ${styles.inputSize}`} type="text" name="userNickName" placeholder="10자리 이내" onChange={handleUserOnChange} maxLength="10" required /> </div>
</div>
</div> </div>
{errorMsg.errorNickName && <p className={styles.passwordConfirmError}>10 이내로 입력해주세요.</p>} <div className="modal-footer">
</div> <button type="submit" className={`rounded my-3 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} disabled={loading}>확인</button>
</div>
</form></>
: <><form className={`modal-content d-flex col-md-6 col-12 justify-content-center d-flex flex-column`} onSubmit={handleOnSummit}>
<div className="modal-header">
<h5 className="modal-title" id="modifyLabel">회원정보 수정</h5>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick={handleOnClick}></button>
</div>
<div className={`modal-body`}>
<div className="d-flex flex-column">
<div className={styles.inputContent}>
<label className={styles.signupLabel}>이메일</label>
<input className={`${styles.input} ${styles.inputSize}`} type="email" name="userEmail" placeholder="이메일을 입력해주세요" onChange={handleUserOnChange} maxlength="20" required />
</div>
{errorMsg.errorEmail && <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="userNickName" placeholder="10자리 이내" onChange={handleUserOnChange} maxLength="10" required />
</div>
{errorMsg.errorNickName && <p className={styles.passwordConfirmError}>10 이내로 입력해주세요.</p>}
</div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<div className={styles.inputContent}> <div className={styles.inputContent}>
<label className={styles.signupLabel}>휴대폰 번호</label> <label className={styles.signupLabel}>휴대폰 번호</label>
<div className="d-flex col-md-auto"> <div className="d-flex col-md-auto">
<input className={`${styles.input} ${styles.inputSize}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required /> <input className={`${styles.input} ${styles.inputSize}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required />
</div>
</div>
{errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>}
</div> </div>
</div>
{errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>}
</div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<div className={styles.inputContent}> <div className={styles.inputContent}>
<label className={styles.signupLabel}> 비밀번호</label> <label className={styles.signupLabel}> 비밀번호</label>
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required /> <input className={`${styles.input} ${styles.inputSize}`} type="password" name="userPassword" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required />
</div> </div>
{errorMsg.errorPassword && <p className={styles.passwordConfirmError}>8~11자리 사이로 입력해주세요.</p>} {errorMsg.errorPassword && <p className={styles.passwordConfirmError}>8~11자리 사이로 입력해주세요.</p>}
</div> </div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<div className={styles.inputContent}> <div className={styles.inputContent}>
<label className={styles.signupLabel}> 비밀번호 확인</label> <label className={styles.signupLabel}> 비밀번호 확인</label>
<input className={`${styles.input} ${styles.inputSize}`} type="password" name="userRePassword" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required /> <input className={`${styles.input} ${styles.inputSize}`} type="password" name="userRePassword" placeholder="8~11자리 사이" onChange={handleUserOnChange} maxLength="11" required />
</div> </div>
{errorMsg.errorRePassword && <p className={styles.passwordConfirmError}>비밀번호가 일치하지 않습니다.</p>} {errorMsg.errorRePassword && <p className={styles.passwordConfirmError}>비밀번호가 일치하지 않습니다.</p>}
</div> </div>
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-secondary rounded my-3 py-2 fs-5" data-bs-dismiss="modal" disabled={loading}>닫기</button> <button type="button" className="btn btn-secondary rounded my-3 py-2 fs-5" data-bs-dismiss="modal" onClick={handleOnClick} disabled={loading}>닫기</button>
<button type="submit" className={`rounded my-3 py-2 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} disabled={loading}>수정하기</button> <button type="submit" className={`rounded my-3 py-2 fs-5 ${styles.butterYellowAndBtn} ${styles.btnHover}`} disabled={loading}>수정하기</button>
</div> </div>
</form> </form></>}
</div>
</div> </div>
</div> </>
</>
) )
} }
......
...@@ -9,7 +9,17 @@ img{ ...@@ -9,7 +9,17 @@ img{
width: 40%; width: 40%;
} }
.confirm{
color: black;
padding-right: 8px;
text-align: center;
display: flex;
align-items: center;
}
.input2 {
width: 9.01rem;
}
.userName{ .userName{
color: white; color: white;
font-size: 1.3rem; font-size: 1.3rem;
...@@ -28,13 +38,45 @@ width: 40%; ...@@ -28,13 +38,45 @@ width: 40%;
padding-right: 8px; padding-right: 8px;
text-align: left; text-align: left;
} }
.input_file_button{
padding: 6px 25px;
background-color:#FF6600;
border-radius: 4px;
color: white;
cursor: pointer;
}
.inputContent{ .inputContent{
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
margin: 1rem 0; margin: 1rem 0;
} }
.box{
width:12rem;
margin: 0px auto;
position: relative;
}
.box:hover{
display: block;
}
.hoverTxt{
display: none;
position:absolute;
top:5rem;
left:2.5rem;
color : #FEDC00;
font-size: 1.5rem;
}
.box:hover .hoverTxt{
display: block;
}
.profile{
width: 12rem;
height: 12rem;
}
.profile:hover{
opacity: 0.5;
}
.input { .input {
margin: 0.5rem 0 0 0; margin: 0.5rem 0 0 0;
padding: 0.5rem 0 0.5rem 0; padding: 0.5rem 0 0.5rem 0;
......
import styles from "./reservation-details.module.scss";
const ReservationDetails = () => {
return(
<div className={`${styles.width}`}>
<header className={`${styles.header}`}>나의 예매 내역</header>
<main>
</main>
</div>
)
}
export default ReservationDetails
\ No newline at end of file
export { default } from "./ReservationDetails"
\ No newline at end of file
.width{
width : 100%;
display: flex;
margin-top: 5rem;
justify-content: center;
}
.header{
display: flex;
justify-content: center;
background-color: #FEDC00;
width: 80%;
text-align: center;
font-size: 2.5rem;
}
\ No newline at end of file
...@@ -14,10 +14,12 @@ const Signup = () => { ...@@ -14,10 +14,12 @@ const Signup = () => {
userPassword: "", userPassword: "",
userRePassword: "" userRePassword: ""
}) })
const [number, setNumber] = useState(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false); const [success, setSuccess] = useState(false);
//각 타입별 error 유무 state //각 타입별 error 유무 state
const [mbError,setMbError] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [errorMsg, setErrorMsg] = useState({ const [errorMsg, setErrorMsg] = useState({
errorId: null, errorId: null,
...@@ -30,6 +32,7 @@ const Signup = () => { ...@@ -30,6 +32,7 @@ const Signup = () => {
}) })
// id중복확인 여부 state와 가입하기 누르면 id 임시 저장 // id중복확인 여부 state와 가입하기 누르면 id 임시 저장
const [overlapId, setOverlapId] = useState(false); const [overlapId, setOverlapId] = useState(false);
const [confirmMb, setConfirmMb] = useState(false);
const [preId, setPreId] = useState(""); const [preId, setPreId] = useState("");
//입력할때마다 state에 저장 //입력할때마다 state에 저장
...@@ -82,12 +85,47 @@ const Signup = () => { ...@@ -82,12 +85,47 @@ const Signup = () => {
} }
const handleOnClickMbnum = async (e) => { const handleOnClickMbnum = async (e) => {
e.preventDefault();
try { try {
const id = "AC01ecdbffb36dc0766cfea487a54a4c6e"; setError("");
const token = "1d86d5d43760b5dce5582badf7b0a775"; setLoading(true)
await authApi.confirmMbnum(id,token); const phone = user.userMbnum;
console.log("phone : ", phone)
const message = await authApi.confirmMbnum(phone);
console.log("message : ", message);
if(message.isSuccess){
console.log("mberror: "+mbError);
setMbError("보냄");
}
} catch (error) { } catch (error) {
console.log('twilio error'+ error) console.log('error'+ error)
}finally {
setLoading(false);
}
}
const handleOnChangeMb = (e) => {
setNumber(String(e.target.value));
}
const handleOnClickMbConfirm = async (e) => {
e.preventDefault();
try {
setError("");
setLoading(true)
const confirmNum = number;
console.log(confirmNum)
const message = await authApi.confirmNum(confirmNum);
console.log(message);
setMbError(message);
if(message === "성공"){
setConfirmMb(true);
console.log("인증완료");
}
} catch (error) {
catchErrors(error, setError);
}finally {
setLoading(false);
} }
} }
...@@ -148,17 +186,26 @@ const Signup = () => { ...@@ -148,17 +186,26 @@ const Signup = () => {
vaildationIdPw(user.userPassword.length, 8, "errorPassword"); vaildationIdPw(user.userPassword.length, 8, "errorPassword");
// 비밀번호 확인 유효성 검사 // 비밀번호 확인 유효성 검사
vaildationData(user.userRePassword, user.userPassword, "errorRePassword"); vaildationData(user.userRePassword, user.userPassword, "errorRePassword");
let validation = false;
validation = (Object.values(errorMsg).some((element) => (element)) === false);
// 최종 유효성 검사 // 최종 유효성 검사
if (overlapId && (Object.values(errorMsg).some((element) => (element)) === false)) { if (overlapId) {
} else if (!overlapId && (Object.values(errorMsg).some((element) => (element)) === false)) { if(confirmMb){
if(!validation){
throw new Error("유효하지 않은 데이터입니다.");
}else{
console.log("가입성공");
return true
}
}else{
throw new Error("휴대폰 인증도 해주세요");
}
}else{
setErrorMsg(errorMsg => ({ ...errorMsg, errorId: false })); setErrorMsg(errorMsg => ({ ...errorMsg, errorId: false }));
throw new Error("먼저 아이디 중복확인을 해주세요"); throw new Error("먼저 아이디 중복확인을 해주세요");
} else {
throw new Error("유효하지 않은 데이터입니다.");
} }
} }
if (success) { if (success) {
return <Redirect to="/login" />; return <Redirect to="/login" />;
} }
...@@ -208,12 +255,29 @@ const Signup = () => { ...@@ -208,12 +255,29 @@ const Signup = () => {
<div className={styles.inputContent}> <div className={styles.inputContent}>
<label className={styles.signupLabel}>휴대폰 번호</label> <label className={styles.signupLabel}>휴대폰 번호</label>
<div className="d-flex col-md-auto"> <div className="d-flex col-md-auto">
<input className={`${styles.input}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required /> <input className={`${styles.input} ${styles.input2}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required />
<button type="button" disabled={loading} name="errorId" className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbnum}>인증번호받기</button> <button type="button" disabled={loading} className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} data-bs-toggle="collapse" data-bs-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample" onClick={handleOnClickMbnum}>인증번호받기</button>
</div> </div>
</div> </div>
{errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>} {errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>}
<div class="collapse" id="collapseExample">
{/* <div className="d-flex col-md-auto justify-content-end"> */}
<div className="d-flex justify-content-between mt-3">
<label className={`${styles.confirm}`}>인증하기</label>
<div>
<input className={`${styles.input} ${styles.input2}`} type="number" placeholder="인증번호를 입력" onChange={handleOnChangeMb} require />
<button type="button" className={`rounded-2 py-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbConfirm}>확인</button>
<button type="button" className={`rounded-2 py-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbnum}>재전송</button>
</div>
</div>
{(mbError === "재전송") && <p className={styles.passwordConfirmError}>유효시간이 만료되었습니다. 재전송해주세요.</p>}
{(mbError === "보냄") && <p className={styles.passwordConfirmError}>5분이내에 입력해주세요.</p>}
{(mbError === "성공") && <p className={styles.passwordConfirmError}>인증되었습니다.</p>}
{(mbError === "실패") && <p className={styles.passwordConfirmError}>인증번호를 다시 입력해주세요.</p>}
</div>
</div> </div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<div className={`${styles.inputContent}`}> <div className={`${styles.inputContent}`}>
<label className={styles.signupLabel}>비밀번호</label> <label className={styles.signupLabel}>비밀번호</label>
......
...@@ -5,7 +5,13 @@ ...@@ -5,7 +5,13 @@
font-size: 1.7rem; font-size: 1.7rem;
margin-top: 2rem; margin-top: 2rem;
} }
.confirm{
color: white;
padding-right: 8px;
text-align: center;
display: flex;
align-items: center;
}
.contents{ .contents{
display: flex; display: flex;
width: 100%; width: 100%;
...@@ -31,7 +37,9 @@ ...@@ -31,7 +37,9 @@
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
} }
.input2 {
width: 9.01rem;
}
input[type=password]{ input[type=password]{
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
} }
......
import MyInfo from "../components/MyInfo/MyInfo" import MyInfo from "../components/MyInfo/MyInfo"
import ReservationDetails from "../components/ReservationDetails";
const MyPage = () => { const MyPage = () => {
return ( return (
<div className="d-flex justify-content-center py-4"> <div className="d-flex flex-column justify-content-center align-items-center py-4">
<MyInfo/> <MyInfo/>
<ReservationDetails/>
</div> </div>
) )
} }
......
...@@ -421,6 +421,11 @@ ...@@ -421,6 +421,11 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"crypto-js": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
},
"crypto-random-string": { "crypto-random-string": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
...@@ -624,6 +629,11 @@ ...@@ -624,6 +629,11 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
}, },
"fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ="
},
"fsevents": { "fsevents": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
......
...@@ -7,7 +7,7 @@ const app = express() ...@@ -7,7 +7,7 @@ const app = express()
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: true })) app.use(express.urlencoded({ extended: true }))
app.use(cookieParser()) app.use(cookieParser())
app.use('/upload', express.static('upload'))
app.use('/api', mainRouter) app.use('/api', mainRouter)
......
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import config from "../config/app.config.js"; import config from "../config/app.config.js";
import { User, Role, Guest } from '../db/index.js'; import { User, Role, Guest } from '../db/index.js';
import fs from "fs";
import CryptoJS from "crypto-js";
import axios from "axios";
const getUser = async (req, res) => { const getUser = async (req, res) => {
try { try {
...@@ -90,57 +94,152 @@ const compareId = async (req, res) => { ...@@ -90,57 +94,152 @@ const compareId = async (req, res) => {
} }
} }
const confirmMbnum = async (req, res) => { // 휴대폰 인증
// const id = req.params.id;
// const token = req.params.token; const NCP_serviceID = 'ncp:sms:kr:270376424445:butterstudio';
const NCP_accessKey = 'GQmVCT2ZFxnEaJOWbrQs';
// const client = Twilio(id, token); const NCP_secretKey = 'XLQQ8sd9WxW40hNi0xNBTOG0T8ksRsr8c8sUIEvy';
// // console.log(client);
// client.messages const date = Date.now().toString();
// .create({ const uri = NCP_serviceID;
// to: '+8201086074580', const secretKey = NCP_secretKey;
// from: '+14159428621', const accessKey = NCP_accessKey;
// body: '[config.cookieName] 인증번호[1234]를 입력해주세요', const method = 'POST';
// }) const space = " ";
// .then(message => console.log(message.sid)) const newLine = "\n";
// .catch(e => console.log(error)); const url = `https://sens.apigw.ntruss.com/sms/v2/services/${uri}/messages`;
// console.log("id = ", id, "token = ", token); const url2 = `/sms/v2/services/${uri}/messages`;
res.json(true);
//시크릿 키를 암호화하는 작업
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
hmac.update(method);
hmac.update(space);
hmac.update(url2);
hmac.update(newLine);
hmac.update(date);
hmac.update(newLine);
hmac.update(accessKey);
const hash = hmac.finalize();
const signature = hash.toString(CryptoJS.enc.Base64);
let inherentNum = "";
// 유효시간 5분 설정
const time = () => {
inherentNum = false;
return inherentNum
} }
// 인증번호 발송
const confirmMbnum = async (req, res) => {
try {
const phoneNumber = req.params.phone;
console.log(phoneNumber);
//인증번호 생성
const verifyCode = Math.floor(Math.random() * (999999 - 100000)) + 100000;
console.log("verifyCode : ",verifyCode);
let result = await axios({
method: method,
json: true,
url: url,
headers: {
'Content-Type': "application/json",
'x-ncp-apigw-timestamp': date,
'x-ncp-iam-access-key': accessKey,
'x-ncp-apigw-signature-v2': signature,
},
data: {
type: 'SMS',
contentType: 'COMM',
countryCode: '82',
from: '01086074580',
content: `[본인 확인] 인증번호 [${verifyCode}]를 입력해주세요.`,
messages: [
{
to: `${phoneNumber}`,
},
],
},
});
const resultMs = result.data.messages;
console.log('resultMs', resultMs);
console.log('response', res.data, res['data']);
inherentNum = String(verifyCode);
// 5분 유효시간 설정
setTimeout(time, 300000);
res.json({ isSuccess: true, code: 202, message: "본인인증 문자 발송 성공", result: res.data });
} catch (error) {
console.log("error: ", error);
if (error.res == undefined) {
res.json({ isSuccess: true, code: 200, message: "본인인증 문자 발송 성공", result: res.data });
}
else res.json({ isSuccess: true, code: 204, message: "본인인증 문자 발송에 문제가 있습니다.", result: error.res });
}
};
// 인증번호 확인
const confirmNum = async (req, res) => {
try {
const verifyCode = inherentNum;
const confirmNum = req.params.num;
if(!verifyCode){
res.send("재전송")
}else{
if (confirmNum !== verifyCode) {
res.send("실패");
}else {
res.send("성공");
}
}
} catch (error) {
console.error("error : ", error.message);
res.status(500).send("잘못된 접근입니다.");
}
};
const signup = async (req, res) => { const signup = async (req, res) => {
const { userId, userEmail, userNickName, userBirthday, userPassword } = req.body; const { userId, userEmail, userNickName, userBirthday, userMbnum, userPassword } = req.body;
// 휴대폰 중복 확인 // 휴대폰 중복 확인
const userMbnum = String(req.body.userMbnum);
try { try {
const mbnum = await User.findOne({ where: { phoneNumber: userMbnum } }); const mbnum = await User.findOne({ where: { phoneNumber: userMbnum } });
if (mbnum) { const email = await User.findOne({ where: { email: userEmail } });
if (mbnum && email) {
return res.status(422).send(`이미 있는 이메일, 휴대폰번호입니다.`);
} else if (!mbnum && email) {
return res.status(422).send(`이미 있는 이메일입니다.`);
} else if (mbnum && !email) {
return res.status(422).send(`이미 있는 휴대폰번호입니다.`); return res.status(422).send(`이미 있는 휴대폰번호입니다.`);
} else {
const role = await Role.findOne({ where: { name: "member" } })
const newUser = await User.create({
userId: userId,
email: userEmail,
nickname: userNickName,
birth: userBirthday,
phoneNumber: userMbnum,
password: userPassword,
img:"",
roleId: role.id
});
res.json(newUser);
} }
const role = await Role.findOne({ where: { name: "member" } })
const newUser = await User.create({
userId: userId,
email: userEmail,
nickname: userNickName,
birth: userBirthday,
phoneNumber: userMbnum,
password: userPassword,
roleId: role.id
});
res.json(newUser);
} catch (error) { } catch (error) {
console.error(error.message); console.error(error.message);
res.status(500).send("회원가입 에러. 나중에 다시 시도 해주세요"); res.status(500).send("회원가입 에러. 나중에 다시 시도 해주세요");
} }
}; };
const getNickName = async (req, res) => { const getMember = async (req, res) => {
try { try {
const token = req.cookies.butterStudio; const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret); const decoded = jwt.verify(token, config.jwtSecret);
if (decoded.role === "member") { if (decoded.role === "member") {
const user = await User.findOne({ where: { id: decoded.id }, attributes: ["nickname"] }); const user = await User.findOne({ where: { id: decoded.id } });
res.json(user.nickname); res.json({ nickname: user.nickname, img: user.img });
} else { } else {
res.status(401).send("잘못된 접근입니다."); res.status(401).send("잘못된 접근입니다.");
} }
...@@ -150,6 +249,34 @@ const getNickName = async (req, res) => { ...@@ -150,6 +249,34 @@ const getNickName = async (req, res) => {
} }
} }
const uploadProfile = async (req, res) => {
try {
const image = req.file.filename;
console.log(req.file);
const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret);
if (decoded) {
const img = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
console.log("여기여기");
fs.unlink("upload" + `\\${img.img}`, function (data) { console.log(data); });
const user = await User.update({
img: image
}, { where: { id: decoded.id } });
if (user) {
const success = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
res.json(success)
} else {
throw new Error("프로필 등록 실패")
}
}
} catch (error) {
console.error(error.message);
res.status(500).send("프로필 에러");
}
}
const comparePw = async (req, res) => { const comparePw = async (req, res) => {
try { try {
//쿠키 안 토큰에서 id추출 //쿠키 안 토큰에서 id추출
...@@ -159,6 +286,7 @@ const comparePw = async (req, res) => { ...@@ -159,6 +286,7 @@ const comparePw = async (req, res) => {
const user = await User.scope("withPassword").findOne({ where: { id: decoded.id } }); const user = await User.scope("withPassword").findOne({ where: { id: decoded.id } });
//입력한 비번과 해당 행 비번을 비교 //입력한 비번과 해당 행 비번을 비교
const passwordMatch = await user.comparePassword(req.params.pw); const passwordMatch = await user.comparePassword(req.params.pw);
console.log("passwordMatch : ", passwordMatch);
//클라이언트로 동일여부를 전송 //클라이언트로 동일여부를 전송
if (passwordMatch) { if (passwordMatch) {
return res.json(true) return res.json(true)
...@@ -171,26 +299,54 @@ const comparePw = async (req, res) => { ...@@ -171,26 +299,54 @@ const comparePw = async (req, res) => {
} }
} }
const overlap = async (decoded, dataType, data) => {
try {
let overlap = await User.findOne({ where: { id: decoded.id } });
console.log("기존 데이터 : ", overlap, "변경할 데이터 : ", data);
if (overlap[dataType] === data) {
console.log("여기여기")
return true
} else {
let overlap2 = await User.findOne({ attributes: [dataType] });
console.log(overlap2)
if (overlap2[dataType] === data) {
return false
} else {
return true
}
}
} catch (error) {
console.error(error.message);
}
}
const modifyUser = async (req, res) => { const modifyUser = async (req, res) => {
try { try {
const token = req.cookies.butterStudio; const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret); const decoded = jwt.verify(token, config.jwtSecret);
const { userEmail, userNickName, userMbnum, userPassword } = req.body; const { userEmail, userNickName, userMbnum, userPassword } = req.body;
const emailOverlap = await User.findOne({ where: { email: userEmail } }); console.log(userEmail);
console.log(userMbnum);
if (emailOverlap) { const overlapEmail = await overlap(decoded, "email", userEmail);
return res.status(422).send(`이미 있는 이메일입니다.`); const overlapMbnum = await overlap(decoded, "phoneNumber", userMbnum);
console.log("overlapEmail", overlapEmail, " overlapMbnum : ", overlapMbnum);
if (overlapEmail && overlapMbnum) {
const user = await User.update({
email: userEmail,
nickname: userNickName,
phoneNumber: userMbnum,
password: userPassword,
}, { where: { id: decoded.id } });
console.log("user22 :", user);
res.json(user);
} else if (!overlapEmail && overlapMbnum) {
res.status(500).send("이미 있는 이메일입니다.");
} else if (overlapEmail && !overlapMbnum) {
res.status(500).send("이미 있는 핸드폰번호입니다.");
} else { } else {
if (decoded) { res.status(500).send("이미 있는 이메일, 핸드폰번호입니다.");
let user = await User.findOne({ where: { id: decoded.id } });
await user.update({
email: userEmail,
nickname: userNickName,
phoneNumber: userMbnum,
password: userPassword,
});
res.json(user);
}
} }
} catch (error) { } catch (error) {
console.error(error.message); console.error(error.message);
...@@ -236,10 +392,12 @@ export default { ...@@ -236,10 +392,12 @@ export default {
logout, logout,
compareId, compareId,
confirmMbnum, confirmMbnum,
confirmNum,
signup, signup,
getNickName,
comparePw, comparePw,
modifyUser, modifyUser,
getUserInfo, saveGuestInfo,
saveGuestInfo getMember,
uploadProfile,
getUserInfo
} }
...@@ -28,10 +28,11 @@ sequelize ...@@ -28,10 +28,11 @@ sequelize
birth: "990926", birth: "990926",
phoneNumber: "01086074580", phoneNumber: "01086074580",
password: "admin!", password: "admin!",
img: "970aaa79673a39331d45d4b55ca05d25",
roleId: adminRole?.id, roleId: adminRole?.id,
}); });
} else {}
} else { }
app.listen(appConfig.port, () => { app.listen(appConfig.port, () => {
console.log(`Server is running on port ${appConfig.port}`); console.log(`Server is running on port ${appConfig.port}`);
}); });
......
...@@ -14,23 +14,26 @@ const UserModel = (sequelize) => { ...@@ -14,23 +14,26 @@ const UserModel = (sequelize) => {
autoIncrement: true, autoIncrement: true,
}, },
userId: { userId: {
type: DataTypes.STRING, type: DataTypes.STRING
}, },
email: { email: {
type: DataTypes.STRING, type: DataTypes.STRING
}, },
nickname: { nickname: {
type: DataTypes.STRING, type: DataTypes.STRING
}, },
birth: { birth: {
type: DataTypes.STRING, type: DataTypes.STRING
}, },
phoneNumber: { phoneNumber: {
type: DataTypes.STRING type: DataTypes.STRING
}, },
password: { password: {
type: DataTypes.STRING, type: DataTypes.STRING
}, },
img: {
type: DataTypes.STRING
}
}, },
{ {
timestamps: true, timestamps: true,
......
...@@ -6,7 +6,6 @@ import timetableRouter from "./timetable.route.js"; ...@@ -6,7 +6,6 @@ import timetableRouter from "./timetable.route.js";
import cinemaRouter from "./cinema.route.js"; import cinemaRouter from "./cinema.route.js";
import kakaopayRouter from "./kakaopay.route.js"; import kakaopayRouter from "./kakaopay.route.js";
import emailRouter from './email.route.js' import emailRouter from './email.route.js'
import theaterRouter from './theater.route.js'
import reservationRouter from './reservation.route.js' import reservationRouter from './reservation.route.js'
const router = express.Router(); const router = express.Router();
......
import express from "express"; import express from "express";
import userCtrl from "../controllers/user.controller.js"; import userCtrl from "../controllers/user.controller.js";
import multer from "multer"
const router = express.Router(); const router = express.Router();
const upload = multer({
dest: "upload/"
})
router router
.route("/user") .route("/user")
...@@ -19,14 +23,16 @@ router ...@@ -19,14 +23,16 @@ router
.route("/signup") .route("/signup")
.post(userCtrl.signup) .post(userCtrl.signup)
router
.post("/profile", upload.single("image"), userCtrl.uploadProfile)
router router
.route("/modify") .route("/modify")
.post(userCtrl.modifyUser) .post(userCtrl.modifyUser)
router router
.route("/nickname/:id") .route("/member")
.get(userCtrl.getNickName) .get(userCtrl.getMember)
router router
.route("/pw/:pw") .route("/pw/:pw")
...@@ -37,8 +43,12 @@ router ...@@ -37,8 +43,12 @@ router
.get(userCtrl.compareId) .get(userCtrl.compareId)
router router
.route("/:id/:token") .route("/phone/:phone")
.get(userCtrl.confirmMbnum) .post(userCtrl.confirmMbnum)
router
.route("/num/:num")
.get(userCtrl.confirmNum)
router.route('/getuserinfo') router.route('/getuserinfo')
.post(userCtrl.getUserInfo) .post(userCtrl.getUserInfo)
......
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