프로젝트

영화 예매 사이트 (MoVieCinema) - 아이디 찾기 및 비밀번호 재설정

sejin2 2024. 4. 13. 22:03

로그인 실패시 사용자에게 잘못된 로그인 정보임을 알려주고 다시 로그인하도록 한다.

MemberController 수정

@PostMapping("/member/login")
public String login(Member member, HttpSession session, Model model) {
  Optional<Member> optionalLoginUser = memberService.selectMemberByUsername(member);

  if (optionalLoginUser.isPresent()) {
      Member loginUser = optionalLoginUser.get();
      if (pEncoder.matches(member.getPassword(), loginUser.getPassword())) {
          session.setAttribute("loginUser", loginUser);
          return "redirect:/";
      }else {
          model.addAttribute("loginError", "로그인 정보가 올바르지 않습니다.");
          return "member/login";
      }
  }
  model.addAttribute("loginError", "로그인 정보가 올바르지 않습니다.");
  return "member/login";
}

login.jsp 버튼 부분 위에 에러 문구 띄워줄 공간을 만들어준다.

<tr>
  <td colspan="2">
      <c:if test="${not empty loginError}">
          <div class="error-text">${loginError}</div>
      </c:if>
  </td>
</tr>

아이디 찾기 기능 구현 : 아이디를 찾을 때는 사용자의 이름과 전화번호를 이용해 일치하는 ID 값을 찾도록 구현하였다.

<section>
  <div class="findId">
      <h3>아이디 찾기</h3>
      <form action="findId" method="post" id="findIdForm">
          <table>
              <tr>
                  <th><label for="name">NAME</label></th>
                  <td>
                      <div>
                          <input name="name" id="name" class="form-control" placeholder="이름" type="text">
                      </div>
                  </td>
              </tr>
               <tr>
                   <th><label for="phoneNumber">PHONE</label></th>
                   <td>
                      <input name="phoneNumber" id="phoneNumber" class="form-control" placeholder="전화번호" type="text" maxlength="11">
                   </td>
               </tr>
               <tr>
                   <td colspan="2">
                      <button type="submit">아이디 찾기</button>
                   </td>
               </tr>
          </table> 
      </form>
           <div id="findIdResult"></div>
  </div>
</section>
@GetMapping("member/findId")
public String findId() {
    return "member/findId";
}

아이디를 찾기 기능도 AJAX로 구현한다. 

$(() => {
    $("#findIdForm").on('submit', function(e) {
            e.preventDefault();

        const name = $("#name").val();
        const phoneNumber = $("#phoneNumber").val();

        if (!name || !phoneNumber) {
            $("#findIdResult").html("모든 필드를 입력해주세요.");
            return;
        }

        $.ajax({
            url: "findId",
            type: "post",
            data : {
                name: name,
                phoneNumber: phoneNumber
            },
            success: function (result) {
                console.log(result);
                if(result) {
                    $("#findIdResult").html("찾으시는 아이디는 `" + result.username + "` 입니다.");
                    $("#findIdResult").append('<div><a href="/member/login">[로그인으로 돌아가기]</a></div>');
                } else {
                    $("#findIdResult").html("아이디를 찾을 수 없습니다.");
                }
            },
            error: function() {
                console.log("아이디 찾기 ajax 통신 실패");
            }
        });
    })
});
</script>

controller

@PostMapping("member/findId")
@ResponseBody
public Member findUserID(@RequestParam("name") String name, @RequestParam("phoneNumber") String phoneNumber) {
        Member findId = memberService.findIdByNameAndPhoneNumber(name, phoneNumber);
    System.out.println("Found Member: " + findId);
    return findId;
}

service

public Member findIdByNameAndPhoneNumber(String name, String phoneNumber) {
      Member findId = memberRepository.findByNameAndPhoneNumber(name, phoneNumber);
      return findId;
  }

repository

@Query("SELECT m FROM Member m WHERE m.name = :name AND m.phoneNumber = :phoneNumber")
Member findByNameAndPhoneNumber(@Param("name") String name, @Param("phoneNumber") String phoneNumber);

비밀번호 재설정 기능 구현 : 요즘에는 비밀번호를 잊어버렸을 경우 찾기가 아니라 재설정하도록 한다. 재설정하는 경우 계정 확인을 다시 한 번 하도록 한다. 계정 확인은 사용자의 아이디와 이름, 전화번호를 통해 확인하고 계정이 확인된 경우 비밀번호를 재설정할 수 있도록하였다. 

<section>
    <div class="resetPw">
    <h3>비밀번호 재설정</h3>
    <form action="checkForResetPw" method="post" id="checkForResetPwForm">
        <table>
            <tr>
                <th><label for="username">ID</label></th>
                <td>
                    <div>
                        <input name="username" id="username" class="form-control" placeholder="아이디" type="text">
                    </div>
                </td>
            </tr>
            <tr>
                <th><label for="name">NAME</label></th>
                <td>
                    <div>
                        <input name="name" id="name" class="form-control" placeholder="이름" type="text">
                    </div>
                </td>
            </tr>
             <tr>
                 <th><label for="phoneNumber">PHONE</label></th>
                 <td>
                    <input name="phoneNumber" id="phoneNumber" class="form-control" placeholder="전화번호" type="text" maxlength="11">
                 </td>
             </tr>
             <tr>
                <td colspan="2">
                    <button type="submit">계정 확인</button>
                </td>
             </tr>
        </table>
    </form>
        <div id="findIdResult"></div>
    <form action="resetPw" method="post" id="resetPwForm" style="display:none;">
        <table>
             <tr>
                <th><label for="password">PW</label></th>
                <td>
                    <div>
                        <input type="password" name="password" id="password" placeholder="영문, 숫자, 영문자 포함 8~12글자"  maxlength="12">
                         <div id="checkPwResult1" style="font-size: 0.8em; display: none;"></div>
                    </div>
                </td>
             </tr>
              <tr>
                 <th><label for="password">PW 확인</label></th>
                 <td>
                     <div>
                         <input type="password" id="passwordCheck" placeholder="비밀번호를 한번 더 입력해주세요."  maxlength="12">
                          <div id="checkPwResult2" style="font-size: 0.8em; display: none;"></div>
                     </div>
                 </td>
              </tr>
              <tr>
                  <td colspan="2">
                      <!-- hidden 필드 추가 -->
                      <input type="hidden" name="memberId" id="hiddenMemberId">
                      <input type="hidden" name="username" id="hiddenUsername">
                      <input type="hidden" name="name" id="hiddenName">
                      <input type="hidden" name="email" id="hiddenEmail">
                      <input type="hidden" name="phoneNumber" id="hiddenPhoneNumber">
                      <input type="hidden" name="homeAddress" id="hiddenHomeAddress">
                   </td>
              </tr>
              <tr>
                <td colspan="2">
                     <button type="submit">비밀번호 재설정</button>
                </td>
              </tr>
        </table>
    </form>
    </div>
</section>

비밀번호 재설정 폼은 재설정 하기 전에 계정 확인을 먼저 해야하므로 처음 페이지에 들어오면 계정확인하는 폼을 먼저
보이게 하고 계정 확인이 완료되면 비밀번호를 재설정하는 폼이 뜬다.

@GetMapping("member/resetPw")
public String resetPw() {
    return "member/resetPw";
}

계정 확인이 안되면, 비밀번호 재설정 창이 뜨지 않는다.

계정 확인이 되면 재설정 창이 뜬다.

<script>
$(() => {
    $("#checkForResetPwForm").on('submit', function(e) {
        e.preventDefault();

        const username = $("#username").val();
        const name = $("#name").val();
        const phoneNumber = $("#phoneNumber").val();

        // 입력값 검증
        if (!username || !name || !phoneNumber) {
            $("#findIdResult").html("모든 필드를 입력해주세요.");
            $("#resetPwForm").hide();
            return;
        }

        $.ajax({
            url: "checkForResetPw",
            type: "post",
            data : {
                username: username,
                name: name,
                phoneNumber: phoneNumber
            },
            success: function (result) {
                if(result && result.username === username) {
                    $("#findIdResult").html("계정확인이 되었습니다.");

                    $("#hiddenMemberId").val(result.memberId);
                    $("#hiddenUsername").val(result.username);
                    $("#hiddenName").val(result.name);
                    $("#hiddenEmail").val(result.email);
                    $("#hiddenPhoneNumber").val(result.phoneNumber);
                    $("#hiddenHomeAddress").val(result.homeAddress);

                    $("#resetPwForm").show();
                } else {
                    $("#findIdResult").html("계정을 찾을 수 없습니다.");
                    $("#resetPwForm").hide();
                }
            },
            error: function() {
                console.log("계정 확인 ajax 통신 실패");
                $("#resetPwForm").hide();
            }
        });
    });
});

비밀번호를 재설정을 하면 재설정한 비밀번호에 대해서 회원가입시에 했던 것과 마찬가지로 같은 조건으로 유효성 검사를 해준다.
비밀번호 유효성 검사 

 $(() => {
    const $pwInput = $("#password");
    var getPwCheck = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@#$!%])[A-Za-z\d@#$!%]{8,12}$/;
    const $checkPwResult1 = $("#checkPwResult1");
    const $enrollFormSubmit = $("#resetPwForm :submit");

    $pwInput.on('input', function() {
        const pw = $pwInput.val();

        if(!pw) {
            $checkPwResult1.show().css("color", "red").text('비밀번호를 입력해주세요.');
            $enrollFormSubmit.attr("disabled", true);
            return;
        } else if (!getPwCheck.test(pw)) {
             $checkPwResult1.show().css("color", "red").text('비밀번호는 영문과 숫자, 특수기호를 포함해 8~12자 입니다.');
             $enrollFormSubmit.attr("disabled", true);
             return;
        } else {
            $checkPwResult1.show().css("color", "green").text('유효한 비밀번호입니다.');
            $enrollFormSubmit.attr("disabled", false);
        }
    })
});

비밀번호 입력, 비밀번호 확인 두 정보가 일치하는지를 확인한다. 

$(function() {
    const $password = $("#password");
    const $passwordCheck = $("#passwordCheck");
    const $checkPwResult2 = $("#checkPwResult2");
    const $resetPwFormSubmit = $("#resetPwForm :submit");

    function validatePassword() {
        const pw = $password.val();
        const pwCheck = $passwordCheck.val();

        if (pw !== pwCheck) {
            $checkPwResult2.show().css("color", "red").text('비밀번호가 불일치합니다. 다시 입력해주세요.');
            $resetPwFormSubmit.attr("disabled", true);
        } else {
            $checkPwResult2.show().css("color", "green").text('비밀번호가 일치합니다.');
            $resetPwFormSubmit.attr("disabled", false);
        }
    }

    $password.on('input', validatePassword);
    $passwordCheck.on('input', validatePassword);
});

</script>

controller

@PostMapping("member/checkForResetPw")
@ResponseBody
public Member checkForResetPw(@RequestParam("username") String username,
                              @RequestParam("name") String name,
                              @RequestParam("phoneNumber") String phoneNumber) {
    Member findMemberForPw = memberService.findIdByUserNameAndNameAndPhoneNumber(username,name, phoneNumber);
    System.out.println("Found Member: " + findMemberForPw);
    return findMemberForPw;
}

service

public Member findIdByUserNameAndNameAndPhoneNumber(String username, String name, String phoneNumber) {
  Member findMemberForRestPw = memberRepository.findByUserNameAndNameAndPhoneNumber(username, name, phoneNumber);
  return findMemberForRestPw;
}

repository

@Query("SELECT m FROM Member m WHERE m.username = :username AND m.name = :name AND m.phoneNumber = :phoneNumber")
Member findByUserNameAndNameAndPhoneNumber(String username, String name, String phoneNumber);

처음에 ID와 PW를 찾아올 때 string으로 username(ID) 만 찾도록 했다.그런데 이렇게 하면 비밀번호를 재설정하고 다시 저장을 하면 아래와 같이 비밀번호만 들어가고 다른 부분은 다 null로 새로 저장이 되는 문제가 있었다.

따라서 객체를 받아와 다른 필드도 사용할 수 있도록 처리해줬다.

이렇게 비밀번호를 제외한 필드들은 hidden으로 추가해준다.

ajax 요청이 성공하면 계정 확인된 객체의 값을 불러와 히든 처리해준 곳에 값을 넣어준다.
그러면 정상적으로 비밀번호만 업데이트되고 나머지 값들은 그대로 저장이 된다.