myPage.js
const secession = document.querySelector("#secession");
if(secession != null) {
secession.addEventListener("submit", e => {
const memberPw = document.querySelector("#memberPw");
const agree = document.querySelector("#agree");
// - 비밀번호 입력 되었는지 확인
if(memberPw.value.trim().length == 0) {
alert("비밀번호를 입력해주세요");
e.preventDefault(); // 제출 막기
return;
}
// 약관 동의 체크 확인
// checkbox 또는 radio checked 속성
// - checked -> 체크 시 true, 미체크시 false 반환
if(!agree.checked) { // 체크 안 됐을 때
alert("약관에 동의해주세요");
e.preventDefault();
return;
}
// 정말 탈퇴? 물어보기
if(!confirm("정말 탈퇴 하시겠습니까?")) {
// 취소 눌렀을 때
alert("취소 되었습니다.");
e.preventDefault();
return;
}
});
}
myPage-secession.html
상대경로로 myPage/secession 으로 값 넘김
<form action="secession" method="POST" name="myPageFrm" id="secession">
세션 완료 용도의 객체
-> @SessionAttributes 로 등록된 세션을 완료시킬 거
서비스 호출
MyPageController
@PostMapping("secession")
public String secession(
@RequestParam("memberPw") String memberPw,
@SessionAttribute("loginMember") Member loginMember,
SessionStatus status,
RedirectAttributes ra
) {
// session scope 에 있는 loginMember 를 지워줘야함
// SessionStatus 이용 등록된 세션 완료
// 서비스 호출
int memberNo = loginMember.getMemberNo();
int result = service.secession(memberPw, memberNo);
MyPageServiceImpl
@Override
public int secession(String memberPw, int memberNo) {
String originPw = mapper.selectPw(memberNo);
if(!bcrypt.matches(memberPw, originPw)) {
return 0;
}
return mapper.secession(memberNo);
}
myPage-mapper.xml
회원 탈퇴 여부 Y 로 변경
<!-- 회원 탈퇴 -->
<update id="secession">
UPDATE "MEMBER" SET
MEMBER_DEL_FL = 'Y'
WHERE MEMBER_NO = #{memberNo}
</update>
MyPageController
String message = null;
String path = null;
if(result > 0) {
// 탈퇴 성공시
message = "탈퇴 되었습니다.";
path = "/";
// loginMember 세션 만료
status.setComplete(); // 세션 완료 시킴
} else {
// 탈퇴 실패시
message = "비밀번호가 일치하지 않습니다.";
path = "secession";
}
ra.addFlashAttribute("message", message);
return "redirect:" + path;
}

main.html
<h3>빠른 로그인</h3>
<button class="quick-login">user01@kh.or.kr</button>
<button class="quick-login">board1@home.com</button>
<button class="quick-login">board2@home.com</button>
<button class="quick-login">delete@home.com</button>
main.js
const quickLoginBtns = document.querySelectorAll(".quick-login");
quickLoginBtns.forEach((item, index) => {
// item : 현재 반복 시 꺼내온 객체
// index : 현재 반복 중인 인덱스
// quickLoginBtns 요소인 button 태그 하나씩 꺼내서 이벤트 리스너 추가
item.addEventListener("click", () => { // 각 버튼에 클릭 이벤트 추가
const email = item.innerText; // 버튼에 작성된 이메일 얻어오기
location.href = "/member/quickLogin?memberEmail=" + email;
})
});
MemberController
@GetMapping("quickLogin")
public String quickLogin(
@RequestParam("memberEmail") String memberEmail,
Model model,
RedirectAttributes ra
) {
Member loginMember = service.quickLogin(memberEmail);
if(loginMember == null) {
ra.addFlashAttribute("message", "해당 이메일이 존재하지 않습니다.");
} else {
model.addAttribute("loginMember", loginMember);
}
return "redirect:/";
}
MemberServiceImpl
@Override
public Member quickLogin(String memberEmail) {
Member loginMember = mapper.login(memberEmail);
// 탈퇴 or 없는 회원
if(loginMember == null) return null;
// 조회된 비밀번호 null 로 변경
loginMember.setMemberPw(null);
return loginMember;
}

버튼 클릭 시 로그인 됨
main.html
<h3>
회원 목록 조회(비동기)
<button id="selectMemberList">조회</button>
</h3>
<table border="1">
<thead>
<th>회원번호</th>
<th>이메일</th>
<th>닉네임</th>
<th>탈퇴 여부</th>
</thead>
<tbody id="memberList"></tbody>
</table>
// 조회 버튼
const selectMemberList = document.querySelector("#selectMemberList");
// tbody
const memberList = document.querySelector("#memberList");
// 조회 버튼 클릭 시
selectMemberList.addEventListener("click", () => {
// 1) 비동기로 회원 목록 조회
// (포함될 회원 정보 : 회원번호, 이메일, 닉네임, 탈퇴여부)
fetch("/member/selectMemberList")
@ResponseBody
@GetMapping("selectMemberList")
public List<Member> selectMemberList() {
return service.selectMemberList();
}
MemberServiceImpl
@Override
public List<Member> selectMemberList() {
return mapper.selectMemberList();
}
member-mapper.xml
<select id="selectMemberList" resultType="Member">
SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_DEL_FL
FROM "MEMBER"
ORDER BY MEMBER_NO
</select>
넘어온 값이 memberList 라서 .json() 으로 바꿈
main.js
// td 요소를 만들고 text 추가 후 반환
const createTd = (text) => {
const td = document.createElement("td");
td.innerText = text;
return td;
}
// 조회 버튼 클릭 시
selectMemberList.addEventListener("click", () => {
// 1) 비동기로 회원 목록 조회
// (포함될 회원 정보 : 회원번호, 이메일, 닉네임, 탈퇴여부)
fetch("/member/selectMemberList")
.then(resp => resp.json())
.then(list => {
// list 바로 이용 -> JS 객체 배열
// 이전 내용 삭제
memberList.innerHTML = "";
//tbody에 들어갈 요소를 만들고 값 세팅 후 추가
list.forEach((member,index) => {
// member : 현재 반복 접근 중인 요소
// index : 현재 접근 중인 인덱스
// tr 만들어서 그 안에 td 만들고, append 후
// tr 을 tbody에 append
const keyList = ['memberNo', 'memberEmail', 'memberNickname', 'memberDelFl'];
const tr = document.createElement("tr");
keyList.forEach(key => tr.append(createTd(member[key])));
// tbody 자식으로 tr 추가
memberList.append(tr);
});
})
});

main.html
<h3>특정 회원 비밀번호 초기화(Ajax)</h3>
<div>
회원번호:
<input type="text" id="resetMemberNo">
<button id="resetPw">비밀번호 초기화</button>
</div>
const resetMemberNo = document.querySelector("#resetMemberNo");
const resetPw = document.querySelector("#resetPw");
resetPw.addEventListener("click", () => {
// 입력 받은 회원 번호 얻어오기
const inputNo = resetMemberNo.value;
if(inputNo.trim().length === 0) {
alert("회원 번호를 입력해주세요.");
return;
}
fetch("/member/resetPw", {
method : "PUT", // PUT : 수정 요청 방식
headers : {"Content-Type" : "application/json"},
body : inputNo
})
@ResponseBody
@PutMapping("resetPw")
public int resetPw(@RequestBody int inputNo) {
return service.resetPw(inputNo);
}
@Override
public int resetPw(int inputNo) {
// pass01! -> 암호화
String encPw = bcrypt.encode("pass01!");
Map<String, Object> map = new HashMap<>();
map.put("inputNo", inputNo);
map.put("encPw", encPw);
return mapper.resetPw(map);
}
mapper.xml
<update id="resetPw">
UPDATE "MEMBER" SET
MEMBER_PW = #{encPw}
WHERE MEMBER_NO = #{inputNo}
</update>
.then(resp => resp.text())
.then(result => {
if(result > 0) {
alert("초기화 성공");
} else {
alert("해당 회원이 존재하지 않습니다.");
}
});
main.html
<h3>특정 회원(회원번호) 탈퇴 복구 (Ajax)</h3>
<div>
회원번호 :
<input type="text" id="restorationMemberNo">
<button id="restorationBtn">복구하기</button>
</div>
const restorationBtn = document.querySelector("#restorationBtn");
const restorationMemberNo = document.querySelector("#restorationMemberNo");
restorationBtn.addEventListener("click", () => {
if(restorationMemberNo.value.trim().length == 0) {
alert("회원번호를 입력해주세요");
return;
} else {
const memberNo = restorationMemberNo.value;
fetch("/member/restorationMemberNo", {
method : "PUT",
headers : {"Content-Type" : "application/json"},
body : memberNo
})
@ResponseBody
@PutMapping("restorationMemberNo")
public int restorationMemberNo(@RequestBody int memberNo) {
return service.restorationMemberNo(memberNo);
}
@Override
public int restorationMemberNo(int memberNo) {
return mapper.restorationMemberNo(memberNo);
}
.then(resp => resp.text())
.then(result => {
// update 결과값 반환
if(result > 0) {
restorationMemberNo.value = "";
alert("탈퇴 복구 성공");
} else {
restorationMemberNo.value = "";
alert("존재하지 않는 회원 번호입니다.");
}
})
}