프로젝트(1)-신발쇼핑몰

이민경·2024년 6월 30일

프로젝트

목록 보기
1/2
post-thumbnail

학원에서 팀원들과 함께 쇼핑몰을 주제로 한 프로젝트를 진행하였다.

내가 맡은 부분은 회원가입/마이페이지였는데, 수료 후에 상품결제, 게시판, 상품 상세페이지도 시간되면 만들어보고 싶다.

코드를 넣어놨는데 빠져있는 부분도 있을거고 코드가 깔끔하지 않아서 참고만 하면 좋을 것 같다..!

<전반적 쇼핑몰 구성>

  • 회원가입(일반로그인)
  • 로그인, 로그아웃
  • 마이페이지(회원정보수정,회원탈퇴)
  • 상품 목록, 조회
  • 상품 구매(결제는 x)
  • 장바구니
  • 관심상품
  • 관리자 페이지

<기획의도>

<내가 구현한 페이지>

  • 회원가입

    controller

/**
	 * 회원가입 페이지
	 * 
	 * @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();
});
  • 마이페이지

    controller
/**
	 * 마이페이지 페이지
	 * 
	 * @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";
        }
    })
})  
  • 마이페이지 내 정보 수정

    controller
// 프로필 아이콘 변경하기
	@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();
})
  • 비밀번호 변경

    controller
/**
	 * 입력한 비밀번호가 현재 비밀번호와 같은지 체크 (마이페이지에서 내 정보 수정 들어갈때)
	 * 
	 * @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';
      }
    });
});
  • 회원 탈퇴

    controller
/**
	 * 회원 탈퇴
	 * 
	 * @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;
});

profile
풀스택 개발자

0개의 댓글