회원 정보와 관련된 기능을 수행하는 페이지를 구성할 예정
-> "/member/(id번호)" 주소로 회원 정보 조회
-> "/member/delete/(id번호)" 주소로 회원 정보 삭제
-> 회원 수정 및 로그아웃 구현
<-- 회원 조회 -->
1) MemberController에 "/member/{id}" Get 설정
@GetMapping("/member/{id}")
public String findById(@PathVariable Long id, Model model) {
MemberDTO memberDTO = memberService.findById(id);
model.addAttribute("member", memberDTO);
return "detail";
}
-> @PathVariable을 통해 @GetMapping으로 받은 {id} 정보를 사용 (주소 경로 상에 있는 변수를 사용할 때 주로 사용)
-> id에 해당하는 회원 정보를 DB에서 가져와서 화면에 띄워야 하기 때문에 Model 사용
-> list를 get 할 때는 여러명이니까 List 형식으로 만들어주었지만 회원 정보는 한 명을 조회하는 것이기 때문에 MemberDTO로 가져오면 됨
2) MemberService에 findById(id) 함수 구성
public MemberDTO findById(Long id) {
Optional<MemberEntity> optionalMemberEntity = memberRepository.findById(id);
if(optionalMemberEntity.isPresent()) {
return MemberDTO.toMemberDTO(optionalMemberEntity.get());
} else {
return null;
}
}
-> Optional 객체를 .get()으로 한 번 벗겨내야 eneity 객체가 보이고, 이 entity 객체를 dto로 변환해줌
-> memberRepository에 findById(id) 함수는 기존에 존재
3) detail.html 구성하기
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>detail</title>
</head>
<body>
<table>
<tr>
<th>id</th>
<th>email</th>
<th>password</th>
<th>name</th>
</tr>
<tr>
<td th:text="${member.id}"></td>
<td th:text="${member.memberEmail}"></td>
<td th:text="${member.memberPassword}"></td>
<td th:text="${member.memberName}"></td>
</tr>
</table>
</body>
</html>
출력할 객체가 한 개이므로 th:each로 반복할 필요 없음
<-- 회원 수정 -->
4) main.html에서 "/member/update" 주소로 회원 정보 수정 버튼 생성
<a href="/member/update">내 정보 수정</a>
-> 회원 정보는 로그인을 한 상태에서 수행할 수 있어야 하므로 main.html에서 진행
5) MemberController에 "/member/update" GET
@GetMapping("/member/update")
public String updateForm(HttpSession session, Model model) {
String myEmail = (String) session.getAttribute("loginEmail");
MemberDTO memberDTO = memberService.updateForm(myEmail);
model.addAttribute("updateMember", memberDTO);
return "update";
}
-> session을 저장할 때는 setAttribute (login 시), 가져올 때는 getAttribute
-> 회원 정보를 수정하기 위해 로그인할 때 세션에 저장한 이메일 정보를 가져옴
-> 세션에 있는 이메일 정보를 String에 바로 담으려고 하면 session.getAttribute로 반환되는 Object 크기가 더 크므로 type casting 오류가 발생 => (String)으로 강제 형변환
6) MemberService에 updateForm(myEmail) 함수 구성
public MemberDTO updateForm(String myEmail) {
Optional<MemberEntity> optionalMemberEntity = memberRepository.findByMemberEmail(myEmail);
if(optionalMemberEntity.isPresent()) {
return MemberDTO.toMemberDTO(optionalMemberEntity.get());
} else {
return null;
}
}
login할 때 사용했던 MemberRepository의 findByMemberEmail(email) 함수를 사용
7) update.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>update</title>
</head>
<body>
<form action="/member/update" method="post">
<input type="hidden" th:value="${updateMember.id}" name="id"><br>
이메일: <input type="text" th:value="${updateMember.memberEmail}" name="memberEmail" readonly> <br>
비밀번호: <input type="text" th:value="${updateMember.memberPassword}" name="memberPassword"> <br>
이름: <input type="text" th:value="${updateMember.memberName}" name="memberName"> <br>
<input type="submit" value="정보수정">
</form>
</body>
</html>
-> 회원가입을 했던 save.html의 경우 사용자가 입력하는 값을 저장하면 되는 것이었기 때문에 아래와 같이 name="memberEmail"로 지정해주기만 하면 됐었다
이메일 : <input type="text" name="memberEmail"> <br>
-> update.html에서는 사용자가 입력한 내용을 기존 DB에 업데이트 해야하기 때문에 thymeleaf를 사용한다 => th:value 사용
이메일: <input type="text" th:value="${updateMember.memberEmail}" name="memberEmail" readonly> <br>
-> member의 id의 경우 굳이 보여줄 필요가 없기 때문에 type="hidden"으로 설장
-> 이메일은 변경이 불가하고 읽기만 가능하게 하려하므로 readonly 옵션을 넣어준다
8) MemberController에서 "/member/update" POST
@PostMapping("/member/update")
public String update(@ModelAttribute MemberDTO memberDTO) {
memberService.update(memberDTO);
return "redirect:/member/"+memberDTO.getId();
}
-> controller에 담긴 메소드의 처리가 끝난 후 다시 다른 controller에 담긴 메소드의 주소를 요청하기 위해서 redirect 사용
-> "/member/{id}" 형태로 넘겨주기 위해 memberDTO.getId() 사용
9) MemberService에서 update 함수 구성
public void update(MemberDTO memberDTO) {
memberRepository.save(MemberEntity.toUpdateMemberEntity(memberDTO));
}
-> memberRepository에는 update 함수는 없고 회원가입할 때 썼던 save 함수를 다시 사용한다
-> memberRepository의 save 함수는 DB에 넘겨받는 id가 없으면 insert 쿼리를 수행하고 id가 있으면 update 쿼리를 자동으로 수행한다 => id가 있는 부분을 수정하는 것이므로 MemberEntity에 toUpdateMemberEntity 구성이 필요하다
10) MemberEntity에 toUpdateMemberEntity 구성
public static MemberEntity toUpdateMemberEntity(MemberDTO memberDTO) {
MemberEntity memberEntity = new MemberEntity();
memberEntity.setId(memberDTO.getId());
memberEntity.setMemberEmail(memberDTO.getMemberEmail());
memberEntity.setMemberPassword(memberDTO.getMemberPassword());
memberEntity.setMemberName(memberDTO.getMemberName());
return memberEntity;
}
기존의 toMemberEntity와 유사하지만 setId 필드가 생겨났다


정보 수정 후 보이는 회원 정보
(아이디는 readonly이기 때문에 수정이 불가)
<-- 회원 삭제 -->
11) MemberController에 "/member/delete/{id}" GET
@GetMapping("/member/delete/{id}")
public String deleteById(@PathVariable Long id) {
memberService.deleteById(id);
return "redirect:/member/";
}
삭제 후 controller 내의 "/member/" 주소 redirect
12) MemberService에 deleteById(id) 함수 구성
public void deleteById(Long id) {
memberRepository.deleteById(id);
}
memberRepository에 deleteById 함수 존재
회원 목록에서 '삭제' 버튼을 누르면 DB에 있던 회원 정보가 삭제된다
<-- 로그아웃 -->
13) main.html에 로그아웃 버튼 생성
<a href="/member/logout">로그아웃</a>
14) MemberController에서 "/member/logout" GET
@GetMapping("/member/logout")
public String logout(HttpSession session) {
session.invalidate();
return "index";
}
Httpsession을 해제하기 위해 .invalidate() 함수 사용
로그아웃 이후 index.html로 돌아가기 (처음 화면)
회원가입 / 로그인 / 로그아웃 / 회원 정보 조회.수정.삭제 기능을 수행할 수 있는 기본적인 코드를 따라 작성하며 java 기반의 spring으로 웹 페이지를 어떻게 구현하면 될지에 대한 감을 익혀봤다
회원 정보와 관련된 기능은 보안을 신경써서 보다 멋지게 만들어야 하겠지만 일단은 spring의 첫 시작으로써 흐름에 대한 이해가 우선이라고 생각했고 며칠간 열심히 해보았다
이후로는 게시판 만들기를 따라하며 응용할 수 있는 기능들을 좀 더 살펴볼 예정이다