본문 바로가기

Heute lerne ich/Java

[스프링부트 회원 프로젝트] 회원목록 출력/상세 조회/수정/삭제

https://youtu.be/e6EkbZIVR-4?si=kw4lrvBFGTJ5lLOd

이 글은, 코딩레시피 님의 <스프링 부트 쉽게 해보기> 영상을 참조하였으며, 해당 영상을 공부하며 겪었던 오류들과 관련 지식들을 정리했습니다.

 

07. 회원목록 출력

08. 회원정보 상세조회

09. 회원정보 수정

10. 회원 삭제

 


01. 회원목록 조회

(1) 회원 목록 출력 페이지를 생성한다. (list.html)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>list</title>
</head>
<body>
<h2>list.html</h2>
<table>
    <tr>
        <th>id</th>
        <th>memberEmail</th>
        <th>memberPassword</th>
        <th>memberName</th>
        <th>상세조회</th>
        <th>삭제</th>
    </tr>
    <tr th:each="member: ${memberList}">
        <td th:text="${member.id}"></td>
        <td th:text="${member.memberEmail}"></td>
        <td th:text="${member.memberPassword}"></td>
        <td th:text="${member.memberName}"></td>
        <td>
            <a th:href="@{|/member/${member.id}|}">조회</a>
        </td>
        <td>
            <a th:href="@{|/member/delete/${member.id}|}">삭제</a>
        </td>
    </tr>
</table>
</body>
</html>

 

th:each는 thymeleaf에서 제공하는 for문이다.

따라서 memberList를 member라는 이름으로 하나씩 가져오며,

member.memberEmail, member.memberPassword등으로 하나씩 데이터를 꺼내 화면에 보여준다.

 

 

(2) list로 회원 목록을 가져온다.

- memberController

    @GetMapping("/member/")
    public String findAll(Model model) {
        List<MemberDTO> memberDTOList = memberService.findAll();
        // 어떠한 html로 가져갈 데이터가 있다면 model 사용
        model.addAttribute("memberList", memberDTOList);
        return "list";
    }

 

데이터를 뷰로 전달하기 위해 model을 사용한다.

memberSerivce.findAll로 찾은 멤버 DTO 리스트 데이터들을 model에 memberList라는 이름으로 추가한다. (addAttribute)

 

 

- memberService

public List<MemberDTO> findAll() {
        List<MemberEntity> memberEntityList = memberRepository.findAll();
        List<MemberDTO> memberDTOList = new ArrayList<>();
        // 여러개의 entity를 여러개의 dto로 옮겨 담는 과정. 하나 하나씩 꺼내 옮겨야 함. -> for 문
        for (MemberEntity memberEntity: memberEntityList){ // for each 문법
            memberDTOList.add(MemberDTO.toMemberDTO(memberEntity));
        }
        return memberDTOList;
    }

 

repository와 관련된 객체는 무조건 entity로 주고받는다.

이를 controller에 전달하기 위해서는 entity에서 dto로 변환이 필요하다.

for each문을 사용하여 여러 개의 entity를 여러 개의 dto로 변환한다.

 

+) Mac에서 option + enter로 local variable 생성이 가능하다.

 

 

(3) 결과

모든 회원 목록이 출력된다.

현재는 조회/삭제가 구현되어 있지 않아 해당 문구를 누르면 에러 페이지로 이동한다.


02. 회원정보 상세조회

(1) 회원정보 상세 조회 페이지를 생성한다. (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>

 

list.html에서 memberList로 반복문을 돌렸던 것과 달리 detail.html은 상세 조회이므로, member 하나의 정보만을 출력한다.

 

 

(2) 회원정보를 id로 찾는다.

- memberController

@GetMapping("/member/{id}")
    public String findById(@PathVariable Long id, Model model){
        MemberDTO memberDTO = memberService.findById(id);
        model.addAttribute("member",memberDTO);
        return "detail";
    }

 

/member?id= 이 방식의 쿼리스트링이 아닌, /member/{id}를 사용하여 rest api를 만들었다.

따라서 @PathVariable을 사용하여 경로상에 있는 id와 데이터를 뷰로 전달하기 위해 model을 매개변수로 만들었다.

 

memberDTO에 service에서 findbyId한 값(회원정보)를 저장하고 이를 model에 추가한다.

 

 

- memberService

public MemberDTO findById(Long id) {
        Optional<MemberEntity> optionalMemberEntity = memberRepository.findById(id);
        if(optionalMemberEntity.isPresent()) {
            return MemberDTO.toMemberDTO(optionalMemberEntity.get());
        }else{
            return null;
        }
    }

 

 

repository에서 Id로 찾은 회원정보 즉, optionMemberEntity가 존재하면 그 값을 DTO로 변경한 것을 return한다.

이때 intellij에서 빨간줄과 함께 replace가 떠서 한번 바꿔보았다.

 

public MemberDTO findById(Long id) {
        Optional<MemberEntity> optionalMemberEntity = memberRepository.findById(id);
        return optionalMemberEntity.map(MemberDTO::toMemberDTO).orElse(null);
    }

 

위의 코드와 같은 역할을 하지만 한 줄로 간단하게 줄인 것을 알 수 있다.

역시 갓텔리제이 ..

 

 

(3) 결과

조회를 클릭하면

해당 회원의 상세 정보가 조회된다.

사실 list.html에서 이미 상세 정보가 조회되었으므로 여기에서는 필요 없는 기능이지만, 쇼핑몰 제품 상세 조회와 같이 다른 경우에 쓰일 수 있다.

 

 


03. 회원정보 수정

(1) <내 정보 수정하기> 링크를 main에 추가한다.

<a href="/member/update">내 정보 수정하기</a>

 

 

(2) update 페이지를 작성한다.

- 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>

 

input type이 hidden이면 사용자가 볼 수 없고,

readonly를 추가하면 사용자가 볼 수는 있지만 수정은 불가능하다.

 

name필드는 dto필드와 반드시 일치해야 한다

 

 

(3) /member/update 경로로 get 요청이 왔을 때 (수정 전)

- memberController

@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";
    }

 

HttpSession과 Model을 매개변수로 받는다.

HttpSesion session은, HTTP 세션을 나타내는 객체로, 로그인된 사용자의 이메일 정보를 가져온다.

세션에서 loginEmail이라는 이름으로 저장된 값을 가져와서 myEmail 변수에 저장한다. 이는 로그인된 사용자의 이메일 정보를 의미한다.

memberSerivce에서 updatedForm 메서드를 호출하여 사용자의 정보를 가져온다.

이때 myEmail을 전달하여 해당 이메일에 해당하는 사용자 정보를 가져온다.

 

 

- memberService

public MemberDTO updateForm(String myEmail) {
        Optional<MemberEntity> optionalMemberEntity = memberRepository.findByMemberEmail(myEmail);
        if (optionalMemberEntity.isPresent()){
            return MemberDTO.toMemberDTO(optionalMemberEntity.get());
        } else{
            return null;
        }
    }

 

회원 이메일을 받아와서 해당 이메일을 가진 회원의 정보를 조회한다.

 

 

(4) /member/update 경로로 post 요청이 왔을 때 (수정 후)

- memberController

@PostMapping("/member/update")
    public  String update(@ModelAttribute MemberDTO memberDTO){
        memberService.update(memberDTO);
        return "redirect:/member/" + memberDTO.getId();
    }

 

member/member.id로 redirect 한다.

 

 

- memberService

public void update(MemberDTO memberDTO) {
        memberRepository.save(MemberEntity.toUpdateMemberEntity(memberDTO));
    }

 

Repository의 save 메서드는 pk가 없으면 insert query를 수행하지만, pk가 있으면 update query를 수행한다.

즉 수정된 entity를 save하면 이미 pk(id)가 존재하므로 기존 id를 가진 entity를 업데이트하게 될 것이다.

 

 

- memberEntity

    public static MemberEntity toMemberEntity(MemberDTO memberDTO){
        MemberEntity memberEntity = new MemberEntity();
        memberEntity.setMemberEmail(memberDTO.getMemberEmail());
        memberEntity.setMemberPassword(memberDTO.getMemberPassword());
        memberEntity.setMemberName(memberDTO.getMemberName());
        return memberEntity;
    }

    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;
    }

 

toUpdateMemberEntity는 toMemberEntity와 거의 동일하나, memberEntity.setId(memberDTO.getId()) 부분이 추가되었다.

dto가 기존에 있는 경우, dto의 id를 그대로 유지하며 id와 나머지들을 entity로 변환한다.

 

 

(5) 결과

이렇게 이름을 변경하고, 정보 수정 버튼을 누르면

name이 update된다.

 

마찬가지로 전체 회원 목록 조회를 했을 때도, 성공적으로 이름이 변경된 것을 볼 수 있다.

 


04. 회원정보 삭제

(1) 회원 삭제

- memberController

@GetMapping("member/delete/{id}")
    public String deleteById(@PathVariable Long id){
        memberService.deleteById(id);
        return "redirect:/member/";
    }

 

회원 id를 받아와서 해당 id를 가진 회원을 삭제한다.

회원 삭제 후, /member/경로로 리다이렉트 한다. 이는 삭제 작업을 완료한 후에 회원 목록 페이지로 이동하도록 하는 역할을 한다.

 

 

- memberService

public void deleteById(Long id) {
        memberRepository.deleteById(id);
    }

 

주어진 id에 해당하는 회원을 데이터베이스에서 삭제한다.

 

 

(2) 결과

 

두번째 회원의 삭제 링크를 클릭하면,

삭제가 완료되고 member 페이지로 리다이렉트되는 것을 확인할 수 있다.