회원탈퇴 구현
학습일 : 2022. 12. 28
delete.html
탈퇴하기 페이지에 들어왔을 경우
유저에게 본인의 이메일과 패스워드를 입력하게 한 뒤
그것이
실제로 DB에 있는 유저의 이메일, 패스워드와 일치하는지를 체크한 후
일치했을 경우 탈퇴가 진행되도록 구현할 것이다.
따라서 html상에 form 태그를 열어주고
input 태그 안에 name 속성으로 email, password를 부여해주었다.
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>잔이비어 :: 회원탈퇴</title>
<!-- common.css + common.js 연결 -->
<th:block th:replace="~{fragments/head :: common}"></th:block>
<link rel="stylesheet" th:href="@{/member/resources/stylesheets/delete.css}">
<script defer th:src="@{/member/resources/scripts/delete.js}"></script>
</head>
<body id="smoothScroll">
<!-- header -->
<th:block th:replace="~{fragments/body :: header}"></th:block>
<!--무언가를 불러오고 있습니다 loading-->
<th:block th:replace="~{fragments/body :: cover}"></th:block>
<!-- main -->
<main>
<form id="form">
<table class="table">
<caption class="title">탈퇴하기</caption>
<tbody>
<!--이메일 입력창-->
<tr>
<td>
<label class="email">
<input autofocus class="email-input" maxlength="50" name="email" id="email"
placeholder="이메일 주소를 입력해 주세요." type="email">
</label>
</td>
</tr>
<!-- 비밀번호 입력창-->
<tr>
<td>
<label class="password">
<input autofocus class="password-input" maxlength="50" name="password" id="password"
placeholder="비밀번호를 입력해 주세요." type="password">
</label>
</td>
</tr>
<!--탈퇴하기 버튼-->
<tr>
<td>
<label class="delete-container">
<button id="deleteUserButton">회원탈퇴</button>
</label>
</td>
</tr>
<tr>
<td>그동안 잔이비어를 이용해주셔서 감사합니다.</td>
</tr>
</tbody>
</table>
</form>
</main>
</body>
</html>
MemberMapper.xml
xml 상에서는
email 값을 기준으로 select 해올 값들은
이메일 인증했을때의 쿼리를 그대로 사용하면 될 것 같아서
그대로 사용하였고,
delete 쿼리는 아래와 같이 추가하였다.
유저의 email을 기준으로 그에 해당하는 행 전체를 삭제할 것이다.
<!--이메일, 회원탈퇴-->
<select id="selectUserByEmail"
resultType="com.emptybeer.etb.entities.member.UserEntity">
SELECT `email` AS `email`,
`password` AS `password`,
`nickname` AS `nickname`,
`name` AS `name`,
`contact` AS `contact`,
`address_postal` AS `addressPostal`,
`address_primary` AS `addressPrimary`,
`address_secondary` AS `addressSecondary`,
`registered_on` AS `registeredOn`
FROM `etb_member`.`users`
WHERE BINARY `email` = #{email}
LIMIT 1
</select>
<!-- 회원탈퇴 -->
<delete id="deleteUser">
DELETE
FROM `etb_member`.`users`
WHERE `email` = #{email}
LIMIT 1
</delete>
MemberService
일단 서비스로 왔을 경우
memberMapper와 연결,
SUCCESS를 리턴해주도록 한다.
// 회원 탈퇴
public Enum<? extends IResult> deleteUser(UserEntity user) {
int existingUser = this.memberMapper.deleteUser(user);
return CommonResult.SUCCESS;
}
MemberController
컨트롤러에서는 일단 GetMapping으로 member/delete 페이지를 열어주도록 하고
DELETE 맵핑을 한다.
SessionAttribute로 유저엔티티의 유저,
RequestParam으로 유저가 입력한 email과 password 값을 받아오고,
또 로그아웃 처리를 바로 하기 위해 session도 가져오도록 한다.
1. DB에 저장된 유저의 이메일과 입력받은 이메일이 같고,
DB에 저장된 유저의 해싱된 패스워드와 입력받은 해싱된 패스워드가 모두 같을 경우
result는 CommonResult.SUCCESS를 반환하게 된다 (SUCCESS는 서비스단에서 반환하고 있음.)
2. 위 조건에 해당하지 않을 경우 (else)
CommonResult.FAILURE를 반환하도록 한다.
3. 만약 CommonResult.SUCCESS 일 경우
responseObject에 user의 email 값과 password 값을 put 해준다.
4. 마지막으로 탈퇴했을 경우
session.setAttribute("user", null);
을 통해 user 세션을 null로 처리하여
자동으로 로그아웃이 될 수 있도록 한다.
// 회원탈퇴
@GetMapping(value = "delete",
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getDelete() {
ModelAndView modelAndView = new ModelAndView("member/delete");
return modelAndView;
}
@RequestMapping(value = "user",
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String deleteUser(@SessionAttribute(value = "user", required = false) UserEntity user,
@RequestParam(value = "email") String email, // 입력한 이메일
@RequestParam(value = "password") String password,
HttpSession session) { // 입력받은 패스워드
Enum<?> result;
System.out.println("check1 " + user.getEmail());
System.out.println("check1 " + user.getPassword());
System.out.println("check1 " + email);
System.out.println("check1 " + password);
if (user.getEmail().equals(email) && CryptoUtils.hashSha512(user.getPassword()).equals(CryptoUtils.hashSha512(password))) {
result = this.memberService.deleteUser(user);
} else {
result = CommonResult.FAILURE;
}
// 입력받은 이메일과 유저의 이메일이 같고, 입력받은 해싱된 패스워드와 해싱된 유저의 패스워드가 같을 경우
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
if (result == CommonResult.SUCCESS) {
responseObject.put("email", user.getEmail());
responseObject.put("password", user.getPassword());
}
session.setAttribute("user", null);
return responseObject.toString();
}
delete.js
자바스크립트에서는
id 값이 deleteUserButton인 버튼을 클릭시
정확한 의사 확인을 위해
alert로 탈퇴할건지 한번 물어봐 준 다음 확인을 눌렀을 경우
formData로 유저가 입력한 email과 password 값을 실어주고
DELETE를 위한 xhr을 오픈한다.
만약 result가 success가 넘어올 경우
alert로 탈퇴가 완료 되었고, 다시 만나자고 알림을 띄워준 후
index 페이지로 바로 넘어가도록 해준다.
그 외, default, else 문을 아래와 같이 상황에 적절하게 작성해준다.
const form = window.document.getElementById('form');
const deleteUserButton = window.document.getElementById('deleteUserButton');
deleteUserButton.addEventListener('click', e => {
e.preventDefault();
if (!confirm('정말로 탈퇴하실건가요?')) {
return;
}
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('email', form['email'].value);
formData.append('password',form['password'].value);
xhr.open('DELETE', './user');
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
const responseObject = JSON.parse(xhr.responseText);
switch (responseObject['result']) {
case 'success':
alert('회원탈퇴가 완료 되었습니다.');
alert('우리 다시 만나요');
window.location.href = '/';
break;
default:
alert('알 수 없는 이유로 탈퇴하지 못하였습니다. 다시 시도해주세요.');
}
} else {
alert('서버와 통신하지 못하였습니다. 잠시후 다시 시도해주세요');
}
}
};
xhr.send(formData);
});
이와 같은 과정을 겪으면
DB에서 유저의 정보가
깔끔하게 사라지게 된다.
이상 탈퇴하기 구현 완료!!