학원에서 팀원들과 함께 쇼핑몰을 주제로 한 프로젝트를 진행하였다.
내가 맡은 부분은 회원가입/마이페이지였는데, 수료 후에 상품결제, 게시판, 상품 상세페이지도 시간되면 만들어보고 싶다.
코드를 넣어놨는데 빠져있는 부분도 있을거고 코드가 깔끔하지 않아서 참고만 하면 좋을 것 같다..!
<전반적 쇼핑몰 구성>
<기획의도>

<내가 구현한 페이지>

/**
* 회원가입 페이지
*
* @param status
*/
@GetMapping("signup")
public String signup() {
return "pages/user/signup";
}
/**
* @param inputUser
* @param ra
*/
@PostMapping("signup")
public String signup(User inputUser, @RequestParam("userAddress") String[] userAddress, RedirectAttributes ra) {
int result = service.signup(inputUser, userAddress);
String path = null;
String message = null;
if (result > 0) {
message = inputUser.getUserNickname() + " 님 환영합니다";
path = "/";
} else {
message = " 회원가입에 실패했습니다";
path = "signup";
}
ra.addFlashAttribute("message", message);
return "redirect:" + path;
}
service
/**
* 회원 회원가입
*
* @param inputUser
* @param userAddress
* @return result
*/
int signup(User inputUser, String[] userAddress);
/**
* 유저 정보 중복 체크
*
* @param input : 체크하고자 하는 유저 정보
* @param method : 체크하고자 하는 정보 (userId, userEmail, userName)
* @return result : COUNT(*)
*/
int check(String input, String method);
serviceImpl
/**
* 회원 회원가입
*
* @param inputUser
*/
public int signup(User inputUser, String[] userAddress) {
// 비밀번호 암호화
if(!inputUser.getUserAddress().equals(",,")) {
String address = String.join("^^^" , userAddress);
inputUser.setUserAddress(address);
}else {
inputUser.setUserAddress(null);
}
// 비밀번호 암호화
String encPw = passwordEncoder.encode(inputUser.getUserPw());
inputUser.setUserPw(encPw);
return mapper.signup(inputUser);
}
/**
* 유저 정보 중복 확인
*/
@Override
public int check(String input, String method) {
Map<String, String> map = new HashMap<>();
map.put("input", input);
map.put("method", method);
return mapper.check(map);
}
Mapper
/**
* 회원 회원가입
*
* @param inputUser
* @return result
*/
int signup(User inputUser);
mapper.xml
<!-- 회원가입 -->
<insert id="signup">
INSERT INTO "USER"
VALUES(
SEQ_USER_NO.NEXTVAL,
#{userId},
#{userPw},
#{userNickname},
#{userName},
#{userAddress},
#{userTel},
#{userEmail},
DEFAULT,
DEFAULT,
DEFAULT,
DEFAULT
)
</insert>
<!-- 중복 체크 -->
<select id="check" resultType="_int">
SELECT COUNT(*)
FROM "USER"
WHERE
<choose>
<!-- 유저 아이디 -->
<when test='method == "userId"'>
USER_ID = #{input}
</when>
<!-- 유저 닉네임 -->
<when test='method == "userNickname"'>
USER_NICKNAME = #{input}
</when>
<!-- 유저 이메일 -->
<when test='method == "userEmail"'>
USER_EMAIL = #{input}
</when>
<!-- 유저 실명 -->
<when test='method == "userName"'>
USER_NAME = #{input}
</when>
<!-- 유저 비밀번호 -->
<when test='method == "userPw"'>
USER_PW = #{input}
</when>
</choose>
</select>
html
<form action="/user/signup" method="POST" name="signupForm" id="signupForm">
<h3>회원 정보</h3>
<!-- 아이디 입력 -->
<label for="userId"> <span class="required">*</span> 아이디 </label>
<div class="signup-input-area">
<input
type="text"
name="userId"
id="userId"
autocomplete="off"
placeholder="아이디를 입력해주세요"
/>
</div>
<span class="signup-message" id="idMessage"
>영어,숫자,_로만 5~10글자</span
>
<br />
<!-- 이름 입력 -->
<label for="userName"> <span class="required"></span>이름 </label>
<div class="signup-input-area">
<input
type="text"
name="userName"
id="userName"
autocomplete="off"
placeholder="이름을 입력해주세요."
/>
</div>
<span class="signup-message" id="nameMessage">한글로만 2~6글자</span>
<br />
<!-- 닉네임 입력 -->
<label for="userNickname"> <span class="required">*</span>닉네임 </label>
<div class="signup-input-area">
<input
type="text"
name="userNickname"
id="userNickname"
autocomplete="off"
placeholder="닉네임을 입력해주세요."
/>
</div>
<span class="signup-message" id="nicknameMessage"
>한글,영어,숫자로만 2~8글자</span
>
<br />
<!-- 비밀번호 입력 -->
<label for="userPw"> <span class="required">*</span>비밀번호 </label>
<div class="signup-input-area">
<input
type="password"
name="userPw"
id="userPw"
placeholder="비밀번호를 입력해주세요."
autocomplete="off"
/>
</div>
<!-- 비밀번호 확인 입력 -->
<label for="userPwConfirm">
<span class="required">*</span>비밀번호 확인
</label>
<div class="signup-input-area">
<input
type="password"
name="userPwConfirm"
id="userPwConfirm"
placeholder="비밀번호를 일치하게 입력해주세요"
autocomplete="off"
/>
</div>
<span class="signup-message" id="pwMessage"
>비밀번호는 최소 6자에서 16자까지, 영문자,숫자,특수문자 1글자를
포함해야합니다.</span
>
<br />
<!-- 전화번호 입력 -->
<label for="userTel">
<span class="required">전화번호</span>
</label>
<div class="signup-input-area">
<input
type="text"
name="userTel"
id="userTel"
placeholder="전화번호를 입력해주세요(-없이 숫자만 입력)"
autocomplete="off"
/>
</div>
<span class="signup-message" id="telMessage"></span>
<br />
<!-- 이메일 입력 -->
<label for="userEmail"> <span class="required">*</span>이메일 </label>
<div class="signup-input-area">
<!-- form 전달용 input -->
<input type="hidden" name="userEmail" id="inputEmail" />
<input type="text" class="domain" id="userEmail" /> @
<input type="text" name="inputDomain" id="inputDomain" />
<select title="이메일 도메인 선택" id="domainList">
<option selected disabled>선택</option>
<option value="naver.com">네이버</option>
<option value="gmail.com">구글</option>
<option value="daum.net">다음</option>
<option value="kh.co.kr">KH</option>
<option value="apple.com">애플</option>
<option value="kakao.com">카카오</option>
<option value="">직접입력</option>
</select>
<button id="sendAuthKeyBtn" type="button">인증번호 요청</button>
</div>
<span class="signup-message" id="emailMessage"
>메일을 받을 수 있는 이메일을 입력해주세요.</span
>
<br />
<!-- 인증번호 입력 -->
<label for="emailCheck">
<span class="required">*</span> 인증번호
</label>
<div class="signup-input-area">
<input
type="text"
name="authKey"
id="authKey"
placeholder="인증번호 입력"
maxlength="6"
autocomplete="off"
/>
<button id="checkAuthKeyBtn" type="button">인증하기</button>
</div>
<span class="signup-message" id="authKeyMessage"></span>
<br />
<!-- 주소 입력 -->
<label for="userAddress"><span class="required">주소</span></label>
<div class="signup-input-area">
<label for="userAddress">주소</label>
<div class="signUp-input-area">
<input
type="text"
name="userAddress"
placeholder="우편번호"
maxlength="6"
id="postcode"
/>
<button type="button" id="searchAddress">검색</button>
</div>
<div class="signUp-input-area">
<input
type="text"
name="userAddress"
placeholder="도로명/지번 주소"
id="address"
/>
</div>
<div class="signUp-input-area">
<input
type="text"
name="userAddress"
placeholder="상세 주소"
id="detailAddress"
/>
</div>
</div>
<button>취소</button>
<button type="submit" id="submitBtn">가입하기</button>
</form>
js
//필수 항목 유효성 검사를 체크하기 위한 객체
const checkObj = {
userId: false,
userNickname: false,
userPw: false,
userPwConfirm: false,
emailVal: false,
authKey: false,
};
// // 이용약관 전체 동의 눌렀을 때 체크
// function selectAll(selectAll) {
// const agreeAll
// = document.getElementsByName('agree');
// agreeAll.forEach((checkbox) => {
// checkbox.checked = selectAll.checked;
// checkObj.agreement = true;
// })
// }
// let agreeAll
// = document.getElementsByName('agree');
// // //전체 동의 누르면 모든 값 체크 되게 하기
// agreeAll.addEventListener('change', (e) => {
// // let agreeChk = document.querySelectorAll('input[name=agree]');
// for(let i = 0; i < agreeAll.length; i++){
// if(!agreeAll.checked){
// checkObj.agreement = false;
// }else {
// checkObj.agreement = true;
// }
// }
// });
//---------------------주소 다음 api ==> 수정 필요---------------------
function execDaumPostCode() {
new daum.Postcode({
oncomplete: function (data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') {
// 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else {
// 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('postcode').value = data.zonecode;
document.getElementById('address').value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById('detailAddress').focus();
},
}).open();
}
// 주소 검색 버튼 클릭 시
document
.querySelector('#searchAddress')
.addEventListener('click', execDaumPostCode);
// Id 유효성 검사
const userId = document.querySelector('#userId');
const idMessage = document.querySelector('#idMessage');
userId.addEventListener('input', (e) => {
if (userId.value.length === 0) {
idMessage.innerText = '아이디를 입력해주세요';
idMessage.classList.add('error');
idMessage.classList.remove('confirm');
checkObj.userId = false;
return;
}
const regExp = /^[a-z0-9]{5,10}$/;
if (!regExp.test(userId.value)) {
idMessage.innerText = '유효하지 않는 아이디입니다.';
idMessage.classList.add('error');
idMessage.classList.remove('confirm');
checkObj.userId = false;
return;
}
idMessage.innerText = '사용 가능한 아이디 입니다';
idMessage.classList.add('confirm');
idMessage.classList.remove('error');
checkObj.userId = true;
const inputId = e.target.value;
fetch('/user/checkId', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: inputId,
})
.then((resp) => resp.text())
.then((result) => {
if (result == 1) {
idMessage.innerText = '이미 사용중인 아이디 입니다';
idMessage.classList.add('error');
idMessage.classList.remove('confirm');
checkObj.userId = false;
return;
}
idMessage.innerText = '사용 가능한 아이디 입니다';
idMessage.classList.add('confirm');
idMessage.classList.remove('error');
checkObj.userId = true;
});
});
// 이름 유효성 검사
const userName = document.querySelector('#userName');
const nameMessage = document.querySelector('#nameMessage');
userName.addEventListener('input', (e) => {
const inputName = e.target.value;
if (inputName.trim().length === 0) {
nameMessage.innerText = '';
inputName.value = null;
return;
}
const regExp = /^[가-힣]{2,6}$/;
if (!regExp.test(inputName)) {
nameMessage.innerText = '유효한 이름 형식이 아닙니다';
inputName.value = null;
return;
}
nameMessage.innerText = '유효한 이름형식입니다';
return inputName.value;
});
// 닉네임 유효성 검사
const userNickname = document.querySelector('#userNickname');
const nicknameMessage = document.querySelector('#nicknameMessage');
userNickname.addEventListener('input', (e) => {
if (userNickname.value.length === 0) {
nicknameMessage.innerText = '닉네임을 입력해주세요';
nicknameMessage.classList.add('error');
nicknameMessage.classList.remove('confirm');
checkObj.userNickname = false;
return;
}
const regExp = /^(?=.*[a-z0-9가-힣])[a-z0-9가-힣]{2,10}$/;
if (!regExp.test(userNickname.value)) {
nicknameMessage.innerText = '유효하지 않는 닉네임입니다.';
nicknameMessage.classList.add('error');
nicknameMessage.classList.remove('confirm');
checkObj.userNickname = false;
return;
}
nicknameMessage.innerText = '사용 가능한 닉네임 입니다';
nicknameMessage.classList.add('confirm');
nicknameMessage.classList.remove('error');
checkObj.userNickname = true;
const inputNickname = e.target.value;
fetch('/user/checkNickname', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: inputNickname,
})
.then((resp) => resp.text())
.then((result) => {
if (result == 1) {
nicknameMessage.innerText = '이미 사용중인 닉네임 입니다';
nicknameMessage.classList.add('error');
nicknameMessage.classList.remove('confirm');
checkObj.userNickname = false;
return;
}
nicknameMessage.innerText = '사용 가능한 닉네임 입니다';
nicknameMessage.classList.add('confirm');
nicknameMessage.classList.remove('error');
checkObj.userNickname = true;
});
});
// 비밀번호 유효성 검사
const userPw = document.querySelector('#userPw');
const userPwConfirm = document.querySelector('#userPwConfirm');
const pwMessage = document.querySelector('#pwMessage');
const checkPw = () => {
if (userPw.value === userPwConfirm.value) {
pwMessage.innerText = '비밀번호가 일치합니다';
pwMessage.classList.add('confirm');
pwMessage.classList.remove('error');
checkObj.userPwConfirm = true;
return;
}
pwMessage.innerText = '비밀번호가 일치하지 않습니다';
pwMessage.classList.add('error');
pwMessage.classList.remove('confirm');
checkObj.userPwConfirm = false;
};
userPw.addEventListener('input', (e) => {
const inputPw = e.target.value;
if (inputPw.trim().length === 0) {
pwMessage.innerText =
'비밀번호는 최소 6자에서 16자까지, 영문자,숫자,특수문자를 포함해야합니다.';
pwMessage.classList.remove('confirm', 'error');
checkObj.userPw = false;
userPw.value = '';
return;
}
const regExp = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*?_]).{6,16}$/;
if (!regExp.test(inputPw)) {
pwMessage.innerText = '비밀번호가 유효하지 않습니다.';
pwMessage.classList.add('error');
pwMessage.classList.remove('confirm');
checkObj.userPw = false;
return;
}
pwMessage.innerText = '유효한 비밀번호 형식입니다';
pwMessage.classList.add('confirm');
pwMessage.classList.remove('error');
checkObj.userPw = true;
if (userPwConfirm.value.length > 0) {
checkPw();
}
});
userPwConfirm.addEventListener('input', () => {
if (checkObj.userPw) {
checkPw();
return;
}
checkObj.userPwConfirm = false;
});
// 이메일 유효성 검사
const userEmail = document.querySelector('#userEmail');
const inputDomain = document.querySelector('#inputDomain');
const emailMessage = document.querySelector('#emailMessage');
const domainList = document.querySelector('#domainList');
// 이메일 입력
userEmail.addEventListener('input', (e) => {
if (
e.target.value.trim().length == 0 ||
inputDomain.value.trim().length == 0
) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
checkObj.emailVal = true;
emailMessage.innerText = '이메일을 입력 성공';
});
// 이메일 도메인 입력
inputDomain.addEventListener('input', (e) => {
if (e.target.value.trim().length == 0 || userEmail.value.trim().length == 0) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
checkObj.emailVal = true;
emailMessage.innerText = '이메일을 입력 성공';
});
// 도메인 리스트
domainList.addEventListener('change', (e) => {
const optionsValue = e.target.options[e.target.selectedIndex].value;
inputDomain.value = optionsValue;
if (!optionsValue == '') {
inputDomain.readOnly = true;
} else {
inputDomain.readOnly = false;
}
if (e.target.value.trim().length == 0 || userEmail.value.trim().length == 0) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
checkObj.emailVal = true;
emailMessage.innerText = '이메일 입력 성공';
const emailVal = userEmail.value + '@' + inputDomain.value;
console.log(emailVal);
});
// 이메일 인증번호
const sendAuthKeyBtn = document.querySelector('#sendAuthKeyBtn');
const authKey = document.querySelector('#authKey');
const checkAuthKeyBtn = document.querySelector('#checkAuthKeyBtn');
const authKeyMessage = document.querySelector('#authKeyMessage');
const emailVal = userEmail.value + '@' + inputDomain.value;
let authTimer;
const initMin = 4;
const initSec = 59;
const initTime = '05:00';
let min = initMin;
let sec = initSec;
sendAuthKeyBtn.addEventListener('click', () => {
checkObj.authKey = false;
authKeyMessage.innerText = '';
const emailVal = userEmail.value + '@' + inputDomain.value;
if (!checkObj.emailVal) {
alert('유효한 이메일 작성 후 클릭해 주세요');
return;
}
min = initMin;
sec = initSec;
clearInterval(authTimer);
console.log(emailVal);
fetch('/email/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: emailVal,
})
.then((resp) => resp.text())
.then((result) => {
if (result == 1) {
console.log('인증 번호 발송 성공');
emailMessage.innerText =
'인증번호 발송에 성공했습니다 인증번호를 입력해주세요';
} else {
console.log('인증 번호 발송 실패');
emailMessage.innerText = '인증번호 발송에 실패했습니다';
}
});
authKeyMessage.innerText = initTime;
authKeyMessage.classList.remove('confirm', 'error');
alert('인증번호를 발송하였습니다. 입력하신 이메일을 확인해주세요');
authTimer = setInterval(() => {
authKeyMessage.innerText = `${addZero(min)}:${addZero(sec)}`;
if (min == 0 && sec == 0) {
checkObj.authKey = false;
clearInterval(authTimer);
authKeyMessage.classList.add('error');
authKeyMessage.classList.remove('confirm');
return;
}
if (sec == 0) {
sec = 60;
min--;
}
sec--;
}, 1000);
});
function addZero(number) {
if (number < 10) return '0' + number;
else return number;
}
// form 전달용 input
const inputEmail = document.querySelector('#inputEmail');
checkAuthKeyBtn.addEventListener('click', () => {
if (min == 0 && sec == 0) {
alert('인증번호 입력 제한시간을 초과하였습니다!');
return;
}
if (authKey.value.length != 6) {
alert('인증번호를 정확히 입력해 주세요');
return;
}
const obj = {
email: userEmail.value + '@' + inputDomain.value,
authKey: authKey.value,
};
fetch('/email/checkAuthKey', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(obj),
})
.then((resp) => resp.text())
.then((result) => {
if (result == 0) {
alert('인증번호가 일치하지 않습니다!');
checkObj.authKey = false;
return;
}
clearInterval(authTimer);
authKeyMessage.innerText = '인증 되었습니다.';
alert('인증이 완료되었습니다'); // 5.8 자습때 이 내용 추가했음!
authKeyMessage.classList.remove('error');
authKeyMessage.classList.add('confirm');
inputEmail.value = userEmail.value + '@' + inputDomain.value;
checkObj.authKey = true;
});
});
//전화번호 유효성 검사
const userTel = document.querySelector('#userTel');
const telMessage = document.querySelector('#telMessage');
userTel.addEventListener('input', (e) => {
const regExp = /^01[0-9]{1}[0-9]{3,4}[0-9]{4}$/;
const inputTel = e.target.value;
if (!regExp.test(inputTel)) {
telMessage.innerText = '유효한 전화번호 형식으로 수정해주세요';
telMessage.classList.add('error');
telMessage.classList.remove('confirm');
//e.preventDefault();
return;
}
if (inputTel.trim().length < 9) {
telMessage.innerText = '유효한 전화번호 형식이 아닙니다';
telMessage.classList.add('error');
telMessage.classList.remove('confirm');
inputTel.value = null; //e.preventDefault();
return;
}
telMessage.innerText = '유효한 전화번호 형식입니다.';
telMessage.classList.add('confirm');
telMessage.classList.remove('error');
});
// 회원가입 버튼 클릭시 전체 유효성 검사 여부 확인
const signupForm = document.querySelector('#signupForm');
const submitBtn = document.querySelector('#submitBtn');
submitBtn.addEventListener('click', (e) => {
for (let key in checkObj) {
if (!checkObj[key]) {
let str;
switch (key) {
case 'userId':
str = '필수항목을 작성해주세요';
break;
case 'userNickname':
str = '필수항목을 작성해주세요';
break;
case 'userPw':
str = '필수항목을 작성해주세요';
break;
case 'userPwConfirm':
str = '비밀번호가 일치하지 않습니다';
break;
case 'userEmail':
str = '필수항목을 작성해주세요';
break;
case 'authKey':
str = '인증번호를 입력하지 않았습니다';
break;
}
alert(str);
document.getElementById(key).focus(); //초점이동
e.preventDefault(); // form 태그 기본 이벤트(제출) 막기
return;
}
}
signupForm.submit();
});

/**
* 마이페이지 페이지
*
* @return
*/
@GetMapping("myPage")
public String myPage() {
return "pages/user/myPage";
}
html
<div class="pageMain">
<div class="pageTitle">
안녕하세요
<div class="userNickname">
<th:block th:text="${session.loginUser.userNickname}"></th:block>
</div>
님
</div>
<div class="myPage-wrap">
<div class="myPage-content userDetail">
<div class="simpleBox">
<img
class="userIcon userIconMyPage"
th:src="@{/img/userIcon/} + ${session.loginUser.userIcon} + |.png|"
alt="userIcon"
/>
<div>
아이디:
<th:block th:text="${session.loginUser.userId}"></th:block>
</div>
<div>
닉네임:
<th:block th:text="${session.loginUser.userName}"></th:block>
</div>
</div>
</div>
<div class="myPage-content userInfo">
<div class="simpleBox myPageBox1">
<button
type="button"
class="btn btn-shoesing btnInfo"
data-bs-toggle="modal"
data-bs-target="#staticBackdrop"
id="updateProfile"
>
내정보 수정
</button>
<a href="/wishlist/info" class="btn btn-shoesing btnInfo"
>위시리스트</a
>
<!-- <a href="">내가 작성한 게시글</a> -->
<a href="/order/info" class="btn btn-shoesing btnInfo">주문 조회</a>
</div>
</div>
</div>
</div>
<div class="myPage-wrap">
<div class="myPage-content">
<div class="simpleBox myPageBox2">
<button
type="button"
class="btn btn-shoesing btnInfo"
data-bs-toggle="modal"
data-bs-target="#staticBackdrop"
id="updateProfile"
>
내정보 수정
</button>
<a href="/wishlist/info" class="btn btn-shoesing btnInfo"
>위시리스트</a
>
<!-- <a href="">내가 작성한 게시글</a> -->
<a href="/order/info" class="btn btn-shoesing btnInfo">주문 조회</a>
</div>
</div>
</div>
<form
action="/user/checkCurrentPw"
method="post"
name="checkPwForm"
id="checkPwForm"
>
<!-- 비밀번호 입력 모달창 -->
<div
class="modal fade"
id="staticBackdrop"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">
비밀번호 입력
</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<!-- 현재 비밀번호 입력-->
<div class="update-input-area">
현재 비밀번호
<input
type="password"
placeholder="비밀번호를 입력해주세요"
id="inputPw"
name="inputPw"
/>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
취소
</button>
<button class="btn btn-primary">확인</button>
</div>
</div>
</div>
</div>
</form>
js
//비밀번호가 현재 입력한 값과 같은지 조회
// 모달창으로 나온 현재 비밀번호 클릭한 경우
const checkPwForm = document.querySelector("#checkPwForm");
const inputPw = document.querySelector("#inputPw");
checkPwForm.addEventListener("submit",e=>{
e.preventDefault();
if(inputPw.value.trim().length == 0){
alert('현재 비밀번호를 입력해주시기 바랍니다');
e.preventDefault();
return;
}
fetch("/user/checkCurrentPw", {
method : 'POST',
headers : { 'Content-Type': 'application/json; charset=utf-8' },
body : JSON.stringify({
'inputPw' : inputPw.value
})
})
.then(resp => resp.json())
.then(result => {
if(result == 0){
alert('비밀번호가 일치하지 않습니다.');
e.preventDefault();
return;
} else{
window.location.href="/user/updateProfile";
}
})
})

// 프로필 아이콘 변경하기
@ResponseBody
@PostMapping("changeIcon")
public int changeIcon(HttpServletRequest request, @RequestBody Map<String, String> map) {
log.info("map {}", map);
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
loginUser.setUserIcon(map.get("inputIcon"));
session.setAttribute("loginUser", loginUser);
String userId = loginUser.getUserId();
int result = service.changeIcon(userId, map.get("inputIcon"));
return result;
}
@GetMapping("updateProfile")
public String setAddress(@SessionAttribute("loginUser") User loginUser, Model model) {
// 주소만 꺼내옴
String userAddress = loginUser.getUserAddress();
// 주소가 있을 경우에만 동작
if (userAddress != null) {
String[] arr = null;
log.info(userAddress);
if (userAddress.equals("^^^^^^")) {
arr = new String[3];
arr[0] = "";
arr[1] = "";
arr[2] = "";
} else {
arr = userAddress.split("\\^\\^\\^"); // regEx : 정규표현식
}
log.info(Arrays.toString(arr));
model.addAttribute("postcode", arr[0]);
model.addAttribute("address", arr[1]);
model.addAttribute("detailAddress", arr[2]);
}
// /templates/myPage/myPage-info.html로 forward
return "pages/user/updateProfile";
}
/**
* 내정보 수정
*
* @return
*/
@PostMapping("updateProfile")
public String updateProfile(User inputUser, @SessionAttribute("loginUser") User loginUser,
@RequestParam("userAddress") String[] userAddress, RedirectAttributes ra) {
log.info("inputUser {}", inputUser);
log.info("userAddress {}", userAddress[0]);
String userId = loginUser.getUserId();
inputUser.setUserId(userId);
int result = service.updateProfile(inputUser, userAddress);
String message = null;
if (result > 0) {
message = loginUser.getUserNickname() + "님의 정보가 수정되었습니다";
loginUser.setUserName(inputUser.getUserName());
loginUser.setUserNickname(inputUser.getUserNickname());
loginUser.setUserTel(inputUser.getUserTel());
loginUser.setUserAddress(inputUser.getUserAddress());
loginUser.setUserEmail(inputUser.getUserEmail());
} else {
message = loginUser.getUserNickname() + "님의 정보 수정에 실패했습니다";
}
ra.addFlashAttribute("message", message);
return "redirect:updateProfile";
}
service
/**
* 유저 아이콘 변경하기
*
* @param inputIcon
* @param inputIcon2
* @return
*/
int changeIcon(String userId, String inputIcon);
/**
* 내정보 수정
*
* @param inputUser
* @return
*/
serviceImpl
/**
* 회원 아이콘 변경
*/
@Override
public int changeIcon(String userId, String inputIcon) {
Map<String, String> map = new HashMap<>();
map.put("userId", userId);
map.put("inputIcon", inputIcon);
return mapper.changeIcon(map);
}
/**
* 내 정보 수정
*/
@Override
public int updateProfile(User inputUser, String[] userAddress) {
if (inputUser.getUserAddress().equals(",,")) {
inputUser.setUserAddress(null);
} else {
String address = String.join("^^^", userAddress);
inputUser.setUserAddress(address);
}
return mapper.updateProfile(inputUser);
}
Mapper
/**
* 회원 아이콘 변경
*
* @param Map<String, String> map
* @return
*/
int changeIcon(Map<String, String> map);
/** 회원 정보 수정
* @param inputUser
* @return
*/
int updateProfile(User inputUser);
mapper.xml
<!-- 회원 아이콘 변경 -->
<update id="changeIcon">
UPDATE "USER" SET
USER_ICON = #{inputIcon}
WHERE USER_ID = #{userId}
</update>
<!-- 내 정보 수정 -->
<update id="updateProfile">
UPDATE "USER" SET
USER_NAME = #{userName},
USER_NICKNAME= #{userNickname},
USER_TEL= #{userTel},
USER_ADDRESS= #{userAddress},
USER_EMAIL= #{userEmail}
WHERE USER_ID = #{userId}
</update>
html
<div class="myProfile-area">
<h1>내 정보 수정</h1>
</div>
<!-- 프로필 사진 -->
<div class="profile-image-area" style="text-align: center; width: 100%">
<p>나의 프로필 사진</p>
<img
th:src="@{/img/userIcon/} + ${session.loginUser.userIcon} + |.png|"
class="userIcon myIcon"
/>
<br />
<p style="text-align: center; width: 100%">프로필 사진 변경</p>
<div class="updateIconDiv">
<button class="profileIcon" value="shoesing">
<img th:src="@{/img/userIcon/shoesing.png}" class="userIconUpdate" />
</button>
<button class="profileIcon" value="userIcon1">
<img th:src="@{/img/userIcon/userIcon1.png}" class="userIconUpdate" />
</button>
<button class="profileIcon" value="userIcon2">
<img th:src="@{/img/userIcon/userIcon2.png}" class="userIconUpdate" />
</button>
<button class="profileIcon" value="userIcon3">
<img th:src="@{/img/userIcon/userIcon3.png}" class="userIconUpdate" />
</button>
</div>
</div>
<br /> <br />
<form
action="updateProfile"
method="POST"
name="updateProfileForm"
id="updateProfileForm"
>
<!-- 아이디 (수정x 보여지기만) -->
<div class="update-input-area">
<div class="form-floating">
<input
type="text"
class="form-control"
placeholder="수정할 이름을 입력해주세요"
th:value="${session.loginUser.userId}"
readonly
/>
<label for="loginPagePw">아이디</label>
</div>
</div>
<!-- 이름 수정-->
<div class="update-input-area">
<div class="form-floating">
<input
type="text"
class="form-control"
id="updateName"
name="userName"
placeholder="수정할 이름을 입력해주세요"
th:value="${session.loginUser.userName}"
/>
<label for="loginPagePw">수정할 이름을 입력해주세요</label>
</div>
</div>
<span id="updateNameMessage"></span>
<!-- 닉네임 수정-->
<div class="update-input-area">
<div class="form-floating">
<input
type="text"
class="form-control"
th:value="${session.loginUser.userNickname}"
id="updateNickname"
name="userNickname"
placeholder="수정할 닉네임을 입력해주세요"
/>
<label for="loginPagePw">수정할 닉네임을 입력해주세요</label>
</div>
<span id="updateNicknameMessage"></span>
</div>
<!-- 전화번호 수정-->
<div class="update-input-area">
<div class="form-floating">
<input
type="text"
class="form-control"
th:value="${session.loginUser.userTel}"
id="updateTel"
name="userTel"
placeholder="수정할 닉네임을 입력해주세요"
/>
<label for="loginPagePw">수정할 전화번호를 입력해주세요</label>
</div>
<br />
<span id="updateTelMessage"></span>
</div>
<!-- 주소 입력 -->
<label for="userAddress">주소</label>
<div class="signUp-input-area input-group" style="display: flex">
<input
type="text"
name="userAddress"
placeholder="우편번호"
maxlength="6"
id="postcode"
th:value="${postcode}"
class="form-control"
/>
<button
type="button"
id="searchAddress"
class="btn btn-outline-secondary"
style="width: 170px"
>
검색
</button>
</div>
<div class="signUp-input-area">
<input
type="text"
name="userAddress"
placeholder="도로명/지번 주소"
id="address"
th:value="${address}"
class="form-control"
/>
</div>
<div class="signUp-input-area">
<input
type="text"
name="userAddress"
placeholder="상세 주소"
id="detailAddress"
th:value="${detailAddress}"
class="form-control"
/>
</div>
<br />
<!-- 이메일 입력 -->
<div class="update-input-area">
<div>
이메일
<div
class="update-input-area input-group mb-3"
id="emailDiv"
style="pointer-events: none"
>
<label for="updateEmail" class="input-group-text">
<span class="required">*</span>이메일
</label>
<input
type="hidden"
class="email"
id="inputEmail"
name="userEmail"
/>
<input
type="text"
class="email form-control"
id="updateEmail"
th:value="${#strings.substringBefore(session.loginUser.userEmail,'@')}"
/>
<span class="input-group-text">@</span>
<input
type="text"
id="updateDomain"
class="form-control"
th:value="${#strings.substringAfter(session.loginUser.userEmail,'@')}"
/>
<select
title="이메일 도메인 선택"
id="domainList"
class="btn btn-outline-secondary"
>
<option selected disabled>선택</option>
<option value="naver.com">네이버</option>
<option value="gmail.com">구글</option>
<option value="daum.net">다음</option>
<option value="kh.co.kr">KH</option>
<option value="apple.com">애플</option>
<option value="kakao.com">카카오</option>
<option value="">직접입력</option>
</select>
</div>
<!-- 이메일 변경 버튼 -->
<button
type="button"
class="btn btn-shoesing"
data-bs-toggle="modal"
data-bs-target="#staticBackdrop"
id="updateEmailBtn"
>
이메일 변경
</button>
</div>
<!-- 이메일 변경 모달창 -->
<div
class="modal fade"
id="staticBackdrop"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">
이메일 변경
</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>변경할 이메일을 작성후 인증번호 요청 버튼을 눌러주세요</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
확인
</button>
</div>
</div>
</div>
</div>
<span class="signup-message" id="emailMessage"></span>
<br />
<!--인증번호 요청 버튼 -->
<div id="authKeyDiv">
<button
type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#exampleModal1"
id="sendAuthKeyBtn"
style="display: none"
>
인증번호 요청
</button>
</div>
<!-- 인증번호 입력하는 모달창 -->
<div
class="modal fade"
id="exampleModal1"
tabindex="-1"
aria-labelledby="exampleModalLabel1"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel1">
인증번호 요청
</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<span class="required">*</span> 인증번호
<input
type="text"
name="authKey"
id="authKey"
placeholder="인증번호 입력"
maxlength="6"
autocomplete="off"
/>
<br />
<span class="update-message" id="authKeyMessage"></span>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
취소
</button>
<button
type="button"
class="btn btn-shoesing"
id="checkAuthKeyBtn"
>
인증하기
</button>
</div>
</div>
</div>
</div>
</div>
<br />
<!-- 수정완료버튼, 취소버튼 -->
<div
class="profile-submit"
style="width: 100%; display: flex; justify-content: space-between"
>
<button
id="updateProfileBtn"
class="btn btn-shoesing-secondary"
style="width: 80%; height: 80px"
>
수정완료
</button>
<div style="width: 8px"></div>
<a
href="/user/myPage"
id="cancleprofile"
class="btn btn-shoesing-secondary"
style="
width: 20%;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
"
>취소</a
>
</div>
js
//프로필 사진 변경
const userIcon = document.querySelectorAll('.userIcon');
const profileIcon = document.querySelectorAll('.profileIcon');
profileIcon.forEach((i) => {
i.addEventListener('click', (e) => {
const inputIcon = i.value;
fetch('/user/changeIcon', {
method: 'post',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify({ inputIcon: inputIcon }),
})
.then((resp) => resp.text())
.then((result) => {
if (result > 0) {
console.log('성공');
alert('프로필 사진이 변경되었습니다');
userIcon.forEach((u) => {
u.src = '/img/userIcon/' + inputIcon + '.png';
console.log(u.src);
});
} else {
console.log('실패');
}
});
});
});
/// 회원 정보 수정 페이지
//필수 항목 => 닉네임,이메일(이메일 변경했을 때 인증번호 필수)
const checkObj ={
updateNickname : true,
updateEmail : true,
};
// 이름 수정
const updateName = document.querySelector('#updateName');
const updateNameMessage = document.querySelector('#updateNameMessage');
// 이름 정규식
const regExp = /^[가-힣]{2,6}$/;
updateName.addEventListener('input', (e) => {
if (!regExp.test(e.target.value)) {
updateNameMessage.innerText = '유효한 이름 형식이 아닙니다.';
return;
}
updateNameMessage.innerText = '';
return e.target.value;
});
// 닉네임 수정
const updateNickname = document.querySelector("#updateNickname");
const updateNicknameMessage = document.querySelector("#updateNicknameMessage");
// 닉네임정규식
updateNickname.addEventListener("input",(e)=>{
const regExp = /^(?=.*[a-z0-9가-힣])[a-z0-9가-힣]{2,10}$/;
if(!regExp.test(updateNickname.value)){
updateNicknameMessage.innerText="유효하지 않는 닉네임입니다.";
checkObj.updateNickname=false;
return;
}
// 닉네임 중복 검사
updateNicknameMessage.innerText=""
const inputNickname = e.target.value;
fetch("/user/checkNickname",{
method : 'POST',
headers : {'Content-Type' : 'application/json'},
body : inputNickname,
})
.then(resp => resp.text())
.then(result => {
if (result == 1) {
updateNicknameMessage.innerText = '이미 사용중인 닉네임 입니다';
checkObj.updateNickname = false;
return;
}
updateNicknameMessage.innerText = '사용가능한 닉네임 입니다';
checkObj.updateNickname = true;
});
});
// 전화번호 수정
const updateTel = document.querySelector("#updateTel");
const updateTelMessage = document.querySelector("#updateTelMessage");
// 전화번호 정규식
updateTel.addEventListener("input",(e)=>{
const regExp=/^01[0-9]{1}[0-9]{3,4}[0-9]{4}$/;
if(!regExp.test(e.target.value)){
updateTelMessage.innerText="유효한 전화번호 형식으로 수정해주세요";
return;
}
updateTelMessage.innerText="";
});
// 주소 수정
function execDaumPostCode() {
new daum.Postcode({
oncomplete: function (data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') {
// 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else {
// 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('postcode').value = data.zonecode;
document.getElementById('address').value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById('detailAddress').focus();
},
}).open();
}
// 주소 검색 버튼 클릭 시
document
.querySelector('#searchAddress')
.addEventListener('click', execDaumPostCode);
// 주소 유효성
// // const userAddress = document.querySelectorAll(input[name:"userAddress"])
// const addr0 = userAddress[0].value.trim().length == 0;
// const addr1 = userAddress[1].value.trim().length == 0;
// const addr2 = userAddress[2].value.trim().length == 0;
// //모두 true 인 경우만 true 저장
// const result1 = addr0 && addr1 && addr2; //아무것도 입력안한 경우
// //모두 false 인 경우만 true 저장
// const result2 = !(addr0 || addr1 || addr2); //모두 다 입력한 경우
// //모두 입력 또는 모두 미입력이 아니면
// if( !(result1 || result2) ){
// alert("주소를 모두 작성 또는 미작성 해주세요");
// e.preventDefault();
// }
// 이메일 수정
const updateEmailBtn = document.querySelector("#updateEmailBtn");
const emailDiv = document.querySelector("#emailDiv");
const sendAuthKeyBtn = document.querySelector("#sendAuthKeyBtn");
// 이메일 변경 버튼 눌렀을 때 인증번호 버튼 나타나게하기
updateEmailBtn.addEventListener("click",e =>{
console.log(updateEmailBtn);
emailDiv.setAttribute("style","pointer-events:auto");
sendAuthKeyBtn.setAttribute("style","display:block");
updateEmailBtn.setAttribute("style","display : none");
;
})
// 이메일 유효성
let updateEmail = document.querySelector("#updateEmail");
const updateDomain = document.querySelector("#updateDomain");
const domainList = document.querySelector("#domainList");
const emailMessage = document.querySelector("#emailMessage");
// 이메일 아이디 쓰는 부분 검사
updateEmail.addEventListener('input', (e) => {
if (
e.target.value.trim().length == 0 ||
updateDomain.value.trim().length == 0
) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
emailMessage.innerText = '이메일을 입력 성공';
});
// 이메일 도메인 쓰는 부분 바꿀때 나타나는 이벤트
updateDomain.addEventListener('change', (e) => {
let updateEmail = document.querySelector("#updateEmail");
if (e.target.value.trim().length == 0 || updateEmail.value.trim().length == 0) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
checkObj.updateEmail = true;
emailMessage.innerText = '이메일을 입력 성공';
});
// 도메인 option 바꿀때 이벤트
domainList.addEventListener('change', (e) => {
const optionsValue = e.target.options[e.target.selectedIndex].value;
updateDomain.value = optionsValue;
if (!optionsValue == '') {
updateDomain.readOnly = true;
} else {
updateDomain.readOnly = false;
}
let updateEmail = document.querySelector("#updateEmail");
if (e.target.value.trim().length == 0 || updateEmail.value.trim().length == 0) {
emailMessage.innerText = '이메일을 입력해주세요';
return;
}
emailMessage.innerText = '이메일 입력 성공';
inputEmail.value = updateEmail.value + '@' + updateDomain.value;
console.log(updateEmail);
});
// 이메일 인증번호
const authKey = document.querySelector("#authKey");
const checkAuthKeyBtn = document.querySelector("#checkAuthKeyBtn");
const authKeyMessage = document.querySelector("#authKeyMessage");
let authTimer;
const initMin = 4;
const initSec = 59;
const initTime = '05:00';
let min = initMin;
let sec = initSec;
// 인증번호 발생 클릭시 나타나는 이벤트
sendAuthKeyBtn.addEventListener('click', () => {
checkObj.updateEmail = false;
authKeyMessage.innerText = '';
let updateEmail = document.querySelector("#updateEmail");
const inputEmail = updateEmail.value + '@' + updateDomain.value;
if (updateEmail.value.length == 0 || updateDomain.value.length == 0) {
alert('이메일 작성 후 클릭해 주세요');
return;
}
min = initMin;
sec = initSec;
clearInterval(authTimer);
// 인증번호 발송 비동기
fetch('/email/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: inputEmail,
})
.then((resp) => resp.text())
.then((result) => {
if (result == 1) {
console.log('인증 번호 발송 성공');
emailMessage.innerText =
'인증번호 발송에 성공했습니다 인증번호를 입력해주세요';
} else {
console.log('인증 번호 발송 실패');
emailMessage.innerText = '인증번호 발송에 실패했습니다';
}
});
authKeyMessage.innerText = initTime;
authKeyMessage.classList.remove('confirm', 'error');
alert('인증번호를 발송하였습니다. 입력하신 이메일을 확인해주세요');
authTimer = setInterval(() => {
authKeyMessage.innerText = `${addZero(min)}:${addZero(sec)}`;
if (min == 0 && sec == 0) {
checkObj.updateEmail = false;
clearInterval(authTimer);
authKeyMessage.classList.add('error');
authKeyMessage.classList.remove('confirm');
return;
}
if (sec == 0) {
sec = 60;
min--;
}
sec--;
}, 1000);
});
function addZero(number) {
if (number < 10) return '0' + number;
else return number;
}
const inputEmail = document.querySelector('#inputEmail');
checkAuthKeyBtn.addEventListener('click', () => {
if (min == 0 && sec == 0) {
alert('인증번호 입력 제한시간을 초과하였습니다!');
return;
}
const obj = {
email: updateEmail.value + '@' + updateDomain.value,
authKey: authKey.value,
};
fetch('/email/checkAuthKey', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(obj),
})
.then((resp) => resp.text())
.then((result) => {
if (result == 0) {
alert('인증번호가 일치하지 않습니다!');
return;
}
clearInterval(authTimer);
authKeyMessage.innerText = '인증 되었습니다.';
inputEmail.value = updateEmail.value + '@' + updateDomain.value;
checkObj.updateEmail =true;
});
});
const updateProfileBtn = document.querySelector("#updateProfileBtn");
updateProfileBtn.addEventListener("click", (e) => {
inputEmail.value = updateEmail.value + '@' + updateDomain.value;
console.log(updateEmail);
// 닉네임이 입력되지 않았을때
if(!checkObj.updateNickname){
alert('닉네임 에러');
updateNickname.focus();
checkObj.updateNickname = false;
e.preventDefault();
return;
}
//이메일이 입력되지 않았을때
if(!checkObj.updateEmail){
//인증번호 요청 버튼이 클릭되는 경우에
if(sendAuthKeyBtn.clicked){
alert('인증번호에러')
checkObj.updateEmail = false;
e.preventDefault();
return;
}
alert('이메일에러');
checkObj.updateEmail = false;
updateEmail.focus();
return;
}
updateProfileForm.submit();
})

/**
* 입력한 비밀번호가 현재 비밀번호와 같은지 체크 (마이페이지에서 내 정보 수정 들어갈때)
*
* @param userId
* @param inputPw
* @return result
*/
@ResponseBody
@PostMapping(value = "checkCurrentPw", produces = "application/json; charset=UTF-8")
public int checkCurrentPw(HttpServletRequest request, @RequestBody Map<String, Object> reqMap) {
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
String userId = loginUser.getUserId();
String inputPw = (String) reqMap.get("inputPw");
int result = service.checkCurrentPw(userId, inputPw);
return result;
}
/**
* 비밀번호 변경
*
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@PostMapping(value = "changePw", produces = "application/json; charset=UTF-8")
public int changePw(HttpServletRequest request, @RequestBody Map<String, Object> paramMap) {
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
String userId = loginUser.getUserId();
String currentPw = (String) paramMap.get("currentPw");
String newPw = (String) paramMap.get("newPw");
// 현재 비밀번호가 일치하는지 확인
int result = service.checkCurrentPw(userId, currentPw);
int result2 = 0;
// 현재 비밀번호가 입력한 비밀번호와 일치하는 경우
if (result != 0) {
result2 = service.changePw(loginUser, newPw);
}
return result2;
}
service
/**
* 입력한 비밀번호가 현재 비밀번호와 같은지 체크
*
* @param userId
* @param inputPw
* @return
*/
int checkCurrentPw(String userId, String inputPw);
/**
* 비밀번호 변경
*
* @param loginUser
* @param inputPw
* @return
*/
int changePw(User loginUser, String inputPw);
serviceImpl
/**
* 비밀번호 변경
*/
@Override
public int changePw(User loginUser, String inputPw) {
// 현재 비밀번호가 같은지 보기
String currentPw = mapper.checkCurrentPw(loginUser.getUserId());
if (passwordEncoder.matches(inputPw, currentPw)) {
return 2;
}
// 변경한 비밀번호 암호화 처리해주기
loginUser.setUserPw(passwordEncoder.encode(inputPw));
User user = new User();
user.setUserId(loginUser.getUserId());
user.setUserPw(loginUser.getUserPw());
return mapper.changePw(user);
}
// 비밀번호 맞는지 확인
@Override
public int checkCurrentPw(String userId, String inputPw) {
String currentPw = mapper.checkCurrentPw(userId);
if (!passwordEncoder.matches(inputPw, currentPw)) {
return 0;
}
return 1;
}
Mapper
/** 비밀번호가 입력한 값과 같은지 체크
* @param userId
* @return
*/
String checkCurrentPw(String userId);
/**
* 비밀번호 변경
*
* @param userId
* @param inputPw
* @return
*/
int changePw(User user);
mapper.xml
<!-- 회원 비밀번호가 입력한 비밀번호와 같은지 조회 -->
<select id="checkCurrentPw">
SELECT USER_PW
FROM "USER"
WHERE USER_ID =#{userId}
AND USER_DEL_FL ='N'
</select>
<!-- 회원 비밀번호 변경 -->
<update id="changePw">
UPDATE "USER" SET
USER_PW = #{userPw}
WHERE USER_ID = #{userId}
</update>
html
<!-- 비밀번호 변경 버튼 -->
<div style="display: flex; margin-top: 10px">
<div>
<button
type="button"
class="btn btn-shoesing"
data-bs-toggle="modal"
data-bs-target="#exampleModal2"
>
비밀번호 변경
</button>
<!-- 비밀번호 변경 모달창 -->
<form
action="/user/changePw"
method="POST"
id="updatePwForm"
name="updatePwForm"
>
<div
class="modal fade"
id="exampleModal2"
tabindex="-1"
aria-labelledby="exampleModalLabel2"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel2">
비밀번호 변경
</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<label>
변경할 비밀번호 입력
<input
type="password"
placeholder="변경할 비밀번호를 입력해주세요"
id="newPw"
name="newPw"
/>
<br />
변경될 비밀번호 확인
<input
type="password"
placeholder="변경 비밀번호를 확인해주세요"
id="newPwConfirm"
name="newPwConfirm"
/>
</label>
<br />
<span class="update-message" id="updatePwMessage"></span>
<br />
</div>
<div class="modal-footer">
<label>
현재 비밀번호 입력
<input
type="password"
placeholder="비밀번호를 입력해주세요"
id="currentPw"
name="currentPw"
/>
</label>
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
취소
</button>
<button class="btn btn-primary" id="changePwBtn">변경</button>
</div>
</div>
</div>
</div>
</form>
</div>
js
//비밀번호 변경
const newPw = document.querySelector('#newPw');
const newPwConfirm = document.querySelector('#newPwConfirm');
const updatePwMessage = document.querySelector('#updatePwMessage');
const currentPw = document.querySelector('#currentPw');
const checkUpdatePw = () => {
if (newPw.value == newPwConfirm.value) {
updatePwMessage.innerText = '';
updatePwMessage.innerText = '비밀번호가 일치합니다';
return;
}
updatePwMessage.innerText = '비밀번호가 일치하지 않습니다';
};
newPw.addEventListener('input', (e) => {
const inputNewPw = e.target.value;
if (inputNewPw.trim().length == 0) {
updatePwMessage.innerText =
'비밀번호는 최소 6자에서 16자까지, 영문자,숫자,특수문자를 포함해야합니다.';
newPw.value = '';
return;
}
if (newPwConfirm.value.trim().length == 0) {
updatePwMessage.innerText = '변경할 비밀번호를 한번 더 입력해주세요';
return;
}
const regExp = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*?_]).{6,16}$/;
if (!regExp.test(inputNewPw)) {
updatePwMessage.innerText = '비밀번호가 유효하지 않습니다.';
return;
}
updatePwMessage.innerText = '유효한 비밀번호 형식입니다';
if (newPwConfirm.value.length > 0) {
checkUpdatePw();
}
});
newPwConfirm.addEventListener('input', () => {
if (newPw.value.length !== 0) {
checkUpdatePw();
return;
}
});
// 비밀번호 변경 ajax
const updatePwForm = document.querySelector('#updatePwForm');
updatePwForm.addEventListener('submit', (e) => {
e.preventDefault();
if (currentPw.value.trim() == 0) {
alert('현재 비밀번호를 입력해 주세요.');
e.preventDefault();
return;
}
fetch('/user/changePw', {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify({
currentPw: currentPw.value,
newPw: newPw.value,
}),
})
.then((resp) => resp.json())
.then((result) => {
if (result == 0) {
alert('현재 비밀번호가 일치하지 않습니다.');
e.preventDefault();
return;
}
if (result == 2) {
alert('현재 비밀번호와 변경된 비밀번호가 일치합니다.');
e.preventDefault();
return;
}
if (result == 1) {
alert('비밀번호가 변경되었습니다.');
window.location.href = '/user/myPage';
}
});
});

/**
* 회원 탈퇴
*
* @param request
* @param ra
* @return
*/
@PostMapping("delete")
public String delete(HttpServletRequest request, RedirectAttributes ra, SessionStatus status) {
// 현재 세션
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null) { // 세션은 존재하지만 로그인한 회원은 존재하지 않을 경우
ra.addFlashAttribute("message", "로그인한 유저가 존재하지 않습니다");
return "redirect:/";
}
int result = service.delete(loginUser.getUserId());
// 성공 시
if (result > 0) {
// 세션 만료 (로그아웃)
status.setComplete();
ra.addFlashAttribute("message", "성공적으로 탈퇴가 완료되었습니다");
return "redirect:/";
}
// 이외의 방법으로 실패 시
ra.addFlashAttribute("message", "실패");
return "redirect:/";
}
/**
* 회원 탈퇴 시 입력한 값과 현재 비밀번호 일치하는지 체크
*
* @param userId
* @param inputPw
* @return result
*/
@ResponseBody
@PostMapping("checkPw")
public int checkPw(HttpServletRequest request, @RequestBody String inputPw) {
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
String userId = loginUser.getUserId();
int result = service.checkCurrentPw(userId, inputPw);
return result;
}
service
/**
* 회원 탈퇴
*/
int delete(String userId);
/**
* 입력한 비밀번호가 현재 비밀번호와 같은지 체크
*
* @param userId
* @param inputPw
* @return
*/
int checkCurrentPw(String userId, String inputPw);
serviceImpl
/**
* 회원 탈퇴
*/
@Override
public int delete(String userId) {
return mapper.delete(userId);
}
Mapper
/** 비밀번호가 입력한 값과 같은지 체크
* @param userId
* @return
*/
String checkCurrentPw(String userId);
/**
* 회원 탈퇴
*
* @param userId
* @return
*/
int delete(String userId);
mapper.xml
<!-- 회원 비밀번호가 입력한 비밀번호와 같은지 조회 -->
<select id="checkCurrentPw">
SELECT USER_PW
FROM "USER"
WHERE USER_ID =#{userId}
AND USER_DEL_FL ='N'
</select>
<!-- 회원 탈퇴 -->
<update id="delete">
UPDATE "USER" SET
USER_DEL_FL = 'Y'
WHERE USER_ID = #{userId}
</update>
html
<!-- 회원탈퇴 -->
<div>
<button
type="button"
class="btn btn-shoesing"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
style="margin-left: 8px"
>
회원탈퇴
</button>
<div
class="modal fade"
id="exampleModal"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">회원탈퇴</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<h1>회원탈퇴 이용규약</h1>
<br />
<br />
제1조 (회원탈퇴에 따른 제한)
<br /><br />
“회원”이 탈퇴하는 경우 3개월간 재가입이 제한됩니다. 다만
“고객사”가 “회사”에 “회원”의 재가입을 요청하는 경우에는 3개월
이내에도 재가입이 가능합니다. “회원”이 탈퇴하는 경우, “회원 “이
보유한 쿠폰, 마일리지는 모두 삭제됩니다. “회원”의 미결제 금액 및
환불 금액이 존재하는 경우, 탈퇴 후 반환 처리됩니다. 제2조
(개인정보 및 위치정보 보유) “회원”이 이용계약을 해지하는 경우,
“회사”는 관련법령 및 개인정보처리방침에 따라 “회원”의 개인정보
등을 보유하는 경우를 제외하고 해지 즉시 “회원”의 모든 정보를
삭제합니다.
<br /><br />
다만 “회원”이 탈퇴하거나 자격을 상실할 경우에도 불구하고,
“회사”는 [회사 내부 방침], [관계법령]에 따라 아래의 기간동안
개인정보 및 위치정보를 보유할 수 있습니다.
<br /><br /><br />
[회사 내부 방침에 의한 정보 보유]
<br /><br />
개인화 프로파일은 서비스 이용 종료 후 5년 또는 동의 철회 시까지
(단, 관계법령의 별도의 규정이 명시되어 있는 경우 그 기간을 따름)
농기계 작동/상태 정보, 사용 이력 정보, 진단 정보, 소모품 관리
정보 및 개인 위치 정보는 수집 · 이용 목적 달성 시 또는 동의 철회
시까지 요금 결제 및 정산이 완료되지 않은 경우 요금 결제 및 정산
완료 시까지 고객의 불만/민원 처리, 소송 등 분쟁이 진행 중이거나
예상되는 경우 처리 완료 시까지 수집된 개인 정보의 마케팅 및
광고를 위한 이용은 회원 탈퇴 또는 동의 철회 시까지
<br /><br /><br />
[관계법령에 의한 정보 보유]
<br /><br />
전자상거래 등에서의 「소비자 보호에 관한 법률」 에 따른 계약
내용 및 이행 등 거래에 관한 기록
<br />
계약 또는 청약 철회 등에 관한 기록 : 5년
<br />
대금 결제 및 재화 등의 공급에 관한 기록 : 5년
<br />
소비자의 불만 또는 분쟁 처리에 관한 기록 : 3년
<br />
「상법」 에 따른 회사의 상업 장부와 영업에 관한 중요 서류 및
전표 등에 관련된 정보 상업 장부 및 영업에 관한 중요 서류 : 10년
<br />
전표 또는 이와 유사한 서류: 5년
<br /><br />
「국세기본법」, 「법인세법」 에 따른 모든 거래에 관한 장부 및
증빙 서류와 관련된 정보 : 그 거래 사실이 속하는 과세 기간에 대한
해당 국세의 법정 신고 기한이 지난 날부터 5년 「부가가치세법」 에
따른 장부와 교부한 세금 계산서 또는 영수증 : 5년 「신용정보의
이용 및 보호에 관한 법률」 에 따른 신용 정보의 수집·처리 및 이용
등에 관한 기록 : 3년 개인정보를 제공받은 제3자는 제공 목적을
달성하거나 이용자의 철회 요청이 있더라도, 내부보고, 감사 및
검사, 비용 정산 (청구) 등 계약이행, 분쟁 대비를 위해 필요한
정보는 서비스 이용 종료 후 6개월까지, 미이행·분쟁이 계속될 경우
이행 완료·분쟁 해결 시까지 개인정보를 보유·이용할 수 있으며,
아래의 제공받는 자의 보유 및 이용기간과 상법 등 관련 법령에
특별한 규정에 따른 기간 동안 개인정보 및 위치정보를 보관할 수
있습니다.
</div>
<div class="modal-footer">
<form action="delete" method="POST" id="signoutForm">
동의<input type="checkbox" id="agreeSignout" />
<br />
<label>
비밀번호 확인
<input
type="password"
placeholder="현재 비밀번호를 입력해주세요"
id="currentPwConfirm"
/>
<br />
<span id="currentPwConfirmMessage"></span>
</label>
<br />
<br />
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
취소
</button>
<button class="btn btn-primary" id="signupBtn">탈퇴</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
js
// 회원 탈퇴 (성공!)
const checkSignout = {
agreeSignout: false,
currentPwConfirm: false,
};
const currentPwConfirm = document.querySelector('#currentPwConfirm');
const agreeSignout = document.querySelector('#agreeSignout');
const currentPwConfirmMessage = document.querySelector(
'#currentPwConfirmMessage'
);
const signoutBtn = document.querySelector('#signoutBtn');
const signoutForm = document.querySelector('#signoutForm');
// 비밀번호 입력 시 이벤트
currentPwConfirm.addEventListener('input', () => {
if (currentPwConfirm.value.trim().length == 0) {
currentPwConfirmMessage.innerText = '비밀번호를 입력해주세요';
checkSignout.currentPwConfirm = false;
return;
}
const inputPw = currentPwConfirm.value;
fetch('/user/checkPw', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: inputPw,
})
.then((resp) => resp.text())
.then((result) => {
if (result == 0) {
currentPwConfirmMessage.innerText = '비밀번호 불일치';
checkSignout.currentPwConfirm = false;
return;
}
currentPwConfirmMessage.innerText = '비밀번호 일치';
checkSignout.currentPwConfirm = true;
});
});
// 동의체크박스
agreeSignout.addEventListener('change', (e) => {
console.log(e.target.checked);
if (e.target.checked) checkSignout.agreeSignout = true;
else checkSignout.agreeSignout = false;
});
// 최종 서브밋 될 때 이벤트
signoutForm.addEventListener('submit', (e) => {
if (!checkSignout.agreeSignout) {
e.preventDefault();
alert('탈퇴 약관에 동의해주세요');
return;
}
if (!checkSignout.currentPwConfirm) {
e.preventDefault();
alert('비밀번호가 일치하지 않습니다');
return;
}
alert('탈퇴 되었습니다!');
return true;
});
끝