Side Project/STUHEL (학습을 돕는 예약시스템)

초보개발자 Toy-project 개발일지 ▲NO.11 ▲ MyPage 만들기, 회원정보 수정, Update, 모달 만드는법, Sweet-alert, Spring-boot, Oracle, Java, JavaScript, 부트스트랩, 토이프로젝트

isjiji 2022. 8. 18. 23:41

목차 

1. 기획

2. 화면 load 되자마자 회원정보(사용자) 띄우기

        1) 화면 load 되자마자 회원정보(사용자) 띄우기 - Controller
        2) 화면 load 되자마자 회원정보(사용자) 띄우기 - Service
        3) 화면 load 되자마자 회원정보(사용자) 띄우기 - Mapper / xml

3. 정보수정 권한확인하기 (모달)

4. 회원정보 수정가능하게 만들기

5. 수정사항 DB 저장하기

        1) if문 검사
        2)  sweet-alert & DB 저장
        3) 수정사항 DB 저장하기 - Controller
        4) 수정사항 DB 저장하기 - ServiceImpl
        5) 수정사항 DB 저장하기 - Mapper / xml

 

 

 

1. 기획

 - 화면이 load 되면, 텍스트박스에 사용자의 기본정보가 뜨게하되 수정은 못하게 막는다.

 - 사용자 정보를 수정하려면 사용자의 ID와 PW 를 확인해 일치할 경우에만 수정창이 열리게한다.

 - 수정창이 열리면, 텍스트박스에 회원의 정보를 띄우고 수정가능하게 만든다. 

 - PW 입력창은 비워둔다.

 - 고유한 회원의 ID를 기준으로 정보를 수정하게한다. 단, ID도 수정가능하게한다.

 - 변경사항 저장 버튼을 누르면 알림창을 띄워서 정말 변경할 것인지 확인한다.

 - 저장사항을 DB에 UPDATE 한다.

 

 

 

 

2. 화면 load 되자마자 회원정보(사용자) 띄우기

 

my page 화면에 들어가면 특정 액션을 취하지 않아도 화면에 바로 회원정보가 띄워져야한다.

그래서 document화면이 만들어지기 이전에 실행되는 즉시실행함수 (function(){}); 의 마지막부분에 회원정보 조회하는 함수를 호출했다. retrieveMemberInfo();

(본 즉시실행 함수에서는 Session에 저장된 정보가 있는지 확인하고 없으면 퇴장시킨다.)

(function(){
    let xhr = new XMLHttpRequest();
    xhr.open('Get', "/session/sessionCheck"
             ,true);
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.send();
    xhr.onreadystatechange = () => {
        if (xhr.readyState == 4 && xhr.status == 200) {
            // 데이터 확인
            sessionStatus = xhr.responseText;
            if(sessionStatus==null||sessionStatus==''){
                location.href="/index.html";
                return;
            }
            retrieveMemberInfo();
        }
    }
})();

 

함수 retrieveMemberInfo() 가 실행되면 서버로 가서 사용자의 회원정보를 가져올것이다.

select로 조회한 정보를 가져올 것이기 때문에 REST API의 GET방식을 사용했다. 

<!--초기회원정보화면-->
function retrieveMemberInfo(){
    let xhr = new XMLHttpRequest();
        xhr.open('GET', "/stuhel/myPage/retrieveMemberInfo?"
                ,true);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send();
        xhr.onreadystatechange = () => {
            if (xhr.readyState == 4 && xhr.status == 200) {
                // 데이터 확인
                let txt = xhr.responseText;
                txt = JSON.parse(txt);
                dbName=txt.name;
                dbIdentity=txt.id;
                dbBirth=txt.birth;
                name.value=dbName;
                identity.value=dbIdentity;
                birth.value=dbBirth;
            }
        }
        setDate();
}

 

 

1) 화면 load 되자마자 회원정보(사용자) 띄우기 - Controller

 

먼저 HttpServletRequest 객체를 사용해서 login할 때 session에 세팅해둔 memberId의 존재여부를 확인한다.

memberId가 존재한다면, memberId를 조건으로 사용자정보를 가져올 것이다.

@GetMapping("/retrieveMemberInfo")
String retrieveMemberInfo(HttpServletRequest request) {
    HttpSession session = request.getSession();
    HashMap<String, MemberTO> map=new HashMap<>();
    MemberTO memberTO=new MemberTO();
    String result;

    if(session.getAttribute("memberId")==null) {
       result=null;
    }else {
        memberTO.setId((String)session.getAttribute("memberId"));
        memberTO.setName((String)session.getAttribute("memberName"));
        memberTO=myPageService.retrieveMemberInfo(memberTO);

        result =gson.toJson(memberTO);
    }
    return result;
}

 

 

2) 화면 load 되자마자 회원정보(사용자) 띄우기 - Service

 

Service단에서 DAO로 데이터를 넘겨준다. 현재 로직은 return문에서 DAO를 바로 호출하고, 동시에 받아온 데이터를Controller로 return 하고있다. 아마 별로 좋은 방법은 아닐것 같다. Service단의 Exception을 잡아주는 try catch를 넣어주고, 업무적으로 선언해줘야하는 것이 더 없는지 확인해봐야겠다.

    @Override
    public MemberTO retrieveMemberInfo(MemberTO memberTO) {
        return myPageDAO.selectMemberInfo(memberTO);
    }

 

 

3) 화면 load 되자마자 회원정보(사용자) 띄우기 - Mapper / xml

 

mapper에서 session에 저장되어있던 ID와 동일한 ID를 가진 Member_SEQURITY 테이블의 데이터를 select했다.

<select id="selectMemberInfo" parameterType="com.helper.study.stuhel.member.to.MemberTO" resultMap="myPageMember">
    select ID, PASSWORD ,NAME, BIRTH
    from MEMBER_SECURITY
    where ID = #{id}
</select>

 

이렇게 계층을 통과통과-통과통과 된 데이터들은

javascript를 이용해 화면에 셋팅된다. 

    let txt = xhr.responseText;
    txt = JSON.parse(txt);
    dbName=txt.name;
    dbIdentity=txt.id;
    dbBirth=txt.birth;
    name.value=dbName;
    identity.value=dbIdentity;
    birth.value=dbBirth;

 

그리고 html을 disabled로 설정해주면, 화면이 로드되자 마자, 데이터는 보여지고 수정할 수 없다. 

<!-- Name input-->
    <div class="form-floating mb-3">
        <h6 class="mt-0"> NAME </h6> <input class="form-control" id="name" type="text" onkeyup="enterkeyEvent(identity)" disabled/>
    </div>

<!-- ID input-->
    <div class="form-floating mb-3">
        <h6 class="mt-0"> I D </h6><input class="form-control" id="identity" type="text" onkeyup="enterkeyEvent(birth)" disabled/><!--placeholder="ID" data-sb-validations="required" -->
    </div>
    <div>
        <div class="text-center mb-3">
            <div class="fw-bolder" id="idCheckResultMsg" style="color:tomato; font-size:18px;"></div>
        </div>
    </div>

<!-- birth input-->
    <div class="form-floating mb-3">
        <h6 class="mt-0"> BIRTH </h6><input class="form-control" id="birth" onkeyup="enterkeyEvent(password)" disabled/><!--placeholder="ID" data-sb-validations="required" -->
    </div>

 

3. 정보수정 권한확인하기 (모달)

 

기본상태는 회원정보수정이 되지 않게끔 막혀있는상태다. "정보수정"버튼을 누르고 막혀있는 Text창을 열어서 회원정보를 변경해야한다. 

그전에!!!!! 먼저해야하는 것이있다. 사용자의 정보를 수정하려는 사람이 진짜 사용자인지 한번 더 확인해야하는 것이다.

그러기 위해서 모달을 띄워서 ID와 PW를 한번더 확인해야한다. 

여기서 확인할 때는, 로그인된 사용자 ID, DB에 존재하는 ID-PW 가 전부 동일해야한다.

 

 

기본적으로 모달은 display : none 설정이 되어있다. 존재하지만 보여지지 않는상태이다.

그렇기에 "정보수정"버튼을 누르면 userCheckModal의 display가 flex로 변경되고 모달을 띄워준다.

<div class="d-grid">
    <input type="button" class="btn btn-primary btn-xl" id="changeButton" onclick="userInfoChange()" value="정보수정"/>
</div>

    function userInfoChange(){
        userCheckModal.style.display="flex";
        checkId.focus();
    }
HTML
<!--회원정보수정 사용자확인 모달-->
<div class="modal-wrapper" id="userCheckModal"  style="display: none;">
    <div class="modal-new">
        <div class="modal-title">사용자 확인</div>
        <hr class="divider" />
        <div>
            I  D  <input class="form-control" type="text" onkeyup="enterkeyEvent(checkPw)" id="checkId"/>
            P W  <input class="form-control" type="password" onkeyup="enterkeyEvent(idPwConfirm)" id="checkPw"/>
        </div>
        </br>
        <div class="fw-bolder" id="checkMsg" style="color:#1a1e21;"></div>
        </br>
        <div class="close-wrapper">
            <button class="btn btn-light btn-xl" id="idPwConfirm" onclick="idPwConfirmFunc()">확인</button>&nbsp;&nbsp;
            <button class="btn btn-light btn-xl" value="userCheckModalClose" onclick="modalClose(this)" >닫기</button>
        </div>
    </div>
</div>
CSS
<style>
.modal-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-new {
  background: white;
  padding: 24px 16px;
  border-radius: 4px;
  width: 320px;
  align: center;
}
.modal-title {
  font-size: 30px;
  font-weight: bold;
  text-align: center;
}
.modal-p {
  font-size: 20px;
}
.close-wrapper {
  text-align: right;
}
</style>

 

아래의 움짤을 확인해보자~ 정보수정을 클릭하면 사용자 확인 모달이 뜨고 ID text박스가 focus 된다.

이제 모달창에서 ID와 PW를 확인하고 수정창이 열리게하는 로직을 살펴보잣

 

 

4. 회원정보 수정가능하게 만들기

 

모달창에서 ID와 PW를 입력하고 확인버튼을 누르면 아래의 자바스크립트 로직 실행된다.

1. 먼저 ID와 PW를 잘 입력했는지 확인하고

2. ID가 로그인한 ID와 동일한지 임시 확인한다 (뒷단으로 가기전, session으로 받아놓고 저장해둔 ID로 먼저 비교한다.)

3. ID와 PW가 잘 입력되었으면 두 정보를 담아서 서버로 보내준다. 

 

1. 만약 ID와 PW가 잘못되었을 경우에는 서버에서 errorCode 와 errorMsg 를 보낸다. errorCode를 확인해 -1 이거나 -2이면 모달 화면에 errorMsg를 띄운다. (바로 위 움짤을 확인해보세요!)

2. ID와 PW가 정상적으로 입력된 경우, 모달창은 닫히고 disabled 된 text태그들이 사용가능하게 변경된다.

그리고 보이지 않던 "ID중복체크"버튼, PW text태그가 보여지며 "정보수정"버튼은 사라지고 그 위치에 "완료"버튼이 생긴다.

<!--회원정보수정시 회원인증-->
function idPwConfirmFunc(){
    if(checkPw.value.trim()==null||checkPw.value.trim()=="" ||
       checkId.value.trim()==null||checkId.value.trim()==""
    ){
       checkMsg.innerHTML = "※ ID 혹은 비밀번호를 입력해주세요";
       return;
    }else if(dbIdentity!=checkId.value.trim()){
       checkMsg.innerHTML = "※ 잘못된 ID를 입력하셨습니다.";
       return;
    }else{
       let loginData = {"password":checkPw.value, "id":checkId.value}
       loginData = JSON.stringify(loginData);

       let xhr = new XMLHttpRequest();
       xhr.open('GET', "/member/login?"
           + "loginData=" + encodeURI(loginData)
           ,true);
       xhr.setRequestHeader('Accept', 'application/json');
       xhr.send();
       xhr.onreadystatechange = () => {
           if (xhr.readyState == 4 && xhr.status == 200) {
               // 데이터 확인
               let txt = xhr.responseText;
               txt = JSON.parse(txt);
               if (txt.errorCode == -1 || txt.errorCode == -2 ) {
                  checkMsg.innerHTML = "※ " + txt.errorMsg;
                   return;
               } else {
                  userCheckModal.style.display = "none";
                  name.disabled=false;
                  identity.disabled=false;
                  birth.disabled=false;
                  password.style.display="block";
                  idCheckDiv.style.display="block";
                  pwDiv.style.display="block";
                  pwConfirmDiv.style.display="block";
                  changeButton.style.display="none";
                  submitButton.style.display="block";
                  changeCancelButton.display="block";
                  checkPw.value="";
                  checkId.value="";
                  checkMsg.innerHTML="";
                  name.value = dbName;
                  identity.value  = dbIdentity;
                  birth.value  = dbBirth;
               }
           }
       }
    }
}

 

 

5. 수정사항 DB 저장하기

마무으리 단계,,!

 

1) if문 검사

    - ID, 이름, 생년월일 text박스가 비워져있는지 아닌지 확인

    - 이름이 변경되었는지 확인(화면 load될 때 db에서 받아온 기존 이름과 비교)

    - ID가 변경되었는지 확인

            *변경되었다면 ID중복체크했는지 확인 (그런데 여기서 ID중복체크 여부를 버튼의 value로 판가름내다보니 아이디를 여러번 수정한 경우, 중복검사하지 않은 ID가 저장될 수도 있음. 수정이 필요함)

    - 생년월일이 숫자인지, 8글자인지 확인

    - 비밀번호 이중체크 여부 확인

submitButton.addEventListener('click',()=>{
idCheckResultMsg.innerHTML=""; idCheckResultMsg.innerHTML="";birthCheckResultMsg.innerHTML="";pwCheckResultMsg.innerHTML="";
    if (identity.value.trim()==""||identity.value==null
        ||name.value.trim()==""||name.value==null
        ||birth.value.trim()==""||birth.value==null
        ){
       idCheckResultMsg.innerHTML ="※ ID, 이름, 생년월일은 비워둘 수 없습니다.";
    }
    if(name.value.trim()!=dbName){
        changeName=name.value.trim();
    }if(identity.value.trim()!=dbIdentity){
        if(idDoubleCheck.value=="사용가능"){
            changeIdentity=identity.value.trim();

        }else{
            idCheckResultMsg.innerHTML="※ 변경 아이디 중복체크가 필요합니다.";
            return;
        }
    }if(birth.value.trim()!=dbBirth){
        if(isNaN(birth.value)||birth.value.length != 8){
            birthCheckResultMsg.innerHTML="※ 8자리 숫자만 입력가능합니다. ex) 19450815";
            return;
        }else {
            changeBirth=birth.value.trim();
        }
    }else changeBirth=dbBirth;
    if(password.value!=null || password.value.trim()!="" && confirmPassword.value!=null || confirmPassword.value.trim()!=""){
        if(password.value==confirmPassword.value){
            changePassword=password.value.trim();

        }else{
            pwCheckResultMsg.innerHTML="입력한 비밀번호를 확인해주세요.";
            return;
        }
    }

 

 2)  sweet-alert & DB 저장

if문 검사가 성공적으로 통과되면 sweet-alert 띄워서 정말 저장할 것인지 확인한다.

저장-확인되면, 변경된 Data들만 담아서 서버로 보낸다. 부분데이터만 변경되야하기때문에..(사실 전체가수정되지만) REST API PUT방식을 사용했다.

 

서버 저장중 오류가 생기면 오류문을 alert()로 알리고, 제대로 이행되면 회원정보 text박스를 disabled, display:none 해서  첫 load 화면과 동일하게 만들어준다.

        *encodURI() - URI 데이터를 전달할 때, 포함된 문자가 오류를 발생시키지 않고 전달되게 하기 위해 인코딩해줌.

                             - escapeing이라고하기도함

                             - 비밀번호에 특수문자들이 들어가기 때문에 escaping 하지 않으면 안된다.(다른 파트에도 다해줘야겠..)

    swal({
              title : '회원정보 변경'
            , text : 'ヽ(✿゚▽゚)ノ \n'
                    +'변경된 정보를 저장하시겠습니까?'
            , buttons : ["취소","확인"]
            , confirmButtonColor : 'tomato'
    }).then(function(result){
            console.log(result);
            if(result==null) return;
            else {
                let changeMemberInfo = {"birth":changeBirth,"password":changePassword,"id":changeIdentity,"name":changeName} //데이터 추가하기
                changeMemberInfo = JSON.stringify(changeMemberInfo);

                let xhr = new XMLHttpRequest();
                xhr.open('PUT',  "/stuhel/myPage/changeMemberInfo?"
                                    + "changeMemberInfo=" + encodeURI(changeMemberInfo)
                        ,true);
                xhr.setRequestHeader('Accept', 'application/json');
                xhr.send();
                xhr.onreadystatechange = () => {
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        // 데이터 확인
                        let txt = xhr.responseText;
                        txt = JSON.parse(txt);
                        console.log(txt.errorCode);
                        console.log(txt.errorMsg);
                        if (txt.errorCode == -1 || txt.errorCode == -2 ) {
                            alert(errMsg);
                            return;
                        } else {
                            alert("변경이 완료되었습니다.");
                        }
                        name.disabled=true;
                        identity.disabled=true;
                        birth.disabled=true;
                        password.style.display="none";
                        idCheckDiv.style.display="none";
                        pwDiv.style.display="none";
                        pwConfirmDiv.style.display="none";
                        changeButton.style.display="block";
                        submitButton.style.display="none";
                        changeCancelButton.display="none";
                        name.value = dbName;
                        identity.value  = dbIdentity
                        birth.value  = dbBirth;
                        idDoubleCheck.value="ID중복확인";
                        password.value="";
                        confirmPassword.value="";
                        idCheckResultMsg.innerHTML="";
                        pwCheckResultMsg.innerHTML="";
                        birthCheckResultMsg.innerHTML="";
                        confirmBeforeCheckMsg.innerHTML="";
                        retrieveMemberInfo();
                    }
                }
            }
    });
});

 

 

3) 수정사항 DB 저장하기 - Controller

 

여기서 고민이 많았다.

 

사용자 정보를 수정할 때, 회원의 고유한 정보인 ID가 수정되면 Data를 Update할 때 조건문에 기준을 삼을 수 있을만한 것이 없었기 때문이다. 다행히도 session 에 login 정보를 담아주면 된다는 생각이 들어서 session을 이용해 사용자 정보수정이 편리하게 가능해졌다. 

사용자가 고유식별데이터인 ID 를 변경한다고해도 session에 등록되어있는 기존의 ID를 조건으로  정보를 변경하는 것이기 때문에 ID든 PW든 전부 변경가능하게된다.

단 변경한 후에 session에 저장되어있는 memberId를 새로운 ID로 변경해주어야한다. 그래야 사용자가 다른 기능들을(게시판, 예약 등) 사용할 수 있다.

 

또다른 문제는.. 아래로직이 Controller에서 null인 data들에 sessionData를 넣어줘도 되는걸까..? 하는것이다.

업무로직은 아닌것같은데,, 그래도 업무와 영 상관없는 것은 아니라서 sessionData를 TO에 넣어주는 일을 ServiceImpl에서 해줘야할지 Controller에서 해줘야할지 잘모르겠다.

아무래도 필요한 Data를 Controllrt에서 TO에 넣어주는게 좋을 것 같아서 일단은 Controller에 넣어주었다. 이것은 검색해도 정보를 찾지 못해서,, 참고할 만한것이,,,,없다. 흐앙앙

 

이제야 보이는 문제... 굳이 null인 데이터들에 session에 저장해둔 회원 데이터를 넣은 이유는 sql 때문이었다.

sql update문이 실행될 때, null이 들어가 버리면 겉잡을 수 없는 문제가 발생하기 때문이다!!!! 그런데 이제 보니 동적쿼리를 사용해도 될것같다. null 이면 쿼리를 넣지 않고, null이 아닌 경우에만 쿼리가 작동되게끔!! 할 수 있을듯하다. 

흠흠~! 이것도 수정해야겠다.

@PutMapping("/changeMemberInfo")
HashMap<String, Integer> changeMemberInfo(HttpServletRequest request, @RequestParam("changeMemberInfo") String changeMemberInfo){
    HttpSession session = request.getSession();
    String sessionMemberId=(String)session.getAttribute("memberId");
    String sessionName=(String)session.getAttribute("memberName");
    MemberTO memberTO = gson.fromJson(changeMemberInfo, MemberTO.class);

    HashMap<String, Integer> map = new HashMap<>();
    memberTO.setSessionId(sessionMemberId);
    if(memberTO.getId().isEmpty()|| memberTO.getId()==null || memberTO.getId().trim()=="") {
        memberTO.setId(sessionMemberId);
    }
    if(memberTO.getName().isEmpty()|| memberTO.getName()==null || memberTO.getName().trim()==""){
        memberTO.setName(sessionName);
    }
    if(memberTO.getPassword().isEmpty()||memberTO.getPassword()==null){
        memberTO.setPassword((String)session.getAttribute("password"));
    }
    if(memberTO.getBirth()==0){
        memberTO.setBirth((int)session.getAttribute("birthday"));
    }

    map=myPageService.changeMemberInfo(memberTO);

	session.setAttribute("memberName",memberTO.getName());
    session.setAttribute("memberId",memberTO.getId());
    session.setAttribute("memberBirth",memberTO.getBirth());
    
    return map;
}

 

 

4) 수정사항 DB 저장하기 - ServiceImpl

 

여전히 단조로운 ServiceImpl을 볼때마다 이게.. 맞나.. 싶다

암튼 Controller에서 받은 데이터를 DAO에 잘 전달해주었다. Update이기 때문에 DAO는 void 메서드이고 

만약 오류가 발생하면 try-catch가 잡아서 에러를 날려준다. 

public HashMap<String,Integer> changeMemberInfo(MemberTO memberTO){
    HashMap<String, Integer> map = new HashMap<>();
    map.put("errorCode", 1);
    try{
    	myPageDAO.updateMemberInfo(memberTO);
    }catch(Exception x){
    	map.put("errorCode", -1);
    }
    return map;
}

 

 

5) 수정사항 DB 저장하기 - Mapper / xml

 

Controller에서 말한 새롭게 발견한 문제가 바로 여기 !! 동적쿼리를 말하는 것이다. 바꿔주자~~

암튼 이렇게해서 회원 정보를 전부 수정해주었다.긋.끗.

<update id="updateMemberInfo" parameterType="com.helper.study.stuhel.member.to.MemberTO">
    UPDATE MEMBER_SECURITY
    SET
    ID = #{id}
    ,NAME = #{name}
    ,PASSWORD = #{password}
    ,BIRTH = #{birth}
    WHERE ID = #{sessionId}
</update>