본문 바로가기
Spring

Spring Security 회원탈퇴 기능

by allwing12 2022. 12. 16.

이번에 프로젝트를 하면서 자꾸 로그인 정보도 안되고 해서 혼자서 구글링 열심히 하면서 여러가지 많이 찾아봤습니다.

근데 알고보니 이상하게도 다른 컨트롤러에서는 되는데 제가 계속 시도해봤던 컨트롤러에서만 안되더라구요...?

다른 컨트롤러에 코드를 써놓고 그걸 프론트로 불러오면 불러오는데 제가 시도한 컨트롤러에서는 계속

값을 못받아오고 null값이 뜨거나 타임리프에서 계속 읽지 못한다고 에러를 발생을 했었어요

그 부분을 기록하기 위해서 이번 글을 작성해보려고 합니다!

 

@RequiredArgsConstructor
@Controller
@RequestMapping("")
public class MypageController {
    private final UserServices userServices;
    private final UserRepository userRepository;

    @GetMapping("/mypage")
    public String mypage (Principal principal, Model model) {
        model.addAttribute("member",userServices.getUser(principal.getName()));
        System.out.println("구분" + userServices.getUser(principal.getName()));
        return "Mypage";
    }
    @GetMapping("/mypage/delete")
    public String delete () {
        return "DeleteUser";
    }
    @PostMapping("/mypage/delete")
    public String delete (Principal principal) {
        SiteUser siteUser = userServices.getUser(principal.getName());
        if (siteUser != null) {
            userRepository.delete(siteUser);
            SecurityContextHolder.clearContext();
        }
        return "deleteu_form";
    }

 

위에는 컨트롤러 입니다.

저는 로그인을 한 상태에서 mypage 라는 실질적인 프로필 부분에서 회원탈퇴를 할 수 있도록 해놨어요

그리고 그 프로필에는 아이디와 닉네임을 불러와야하는데 저는 아이디를 username 으로 해놨고

닉네임은 nickname 으로 엔티티를 짜놨고 그리고 그걸 타임리프를 활용해서 불러왔습니다.

 

<div th:replace="secondnavbar :: secondnavbarfragment"></div>
<body>
<div style="width:100%; height:100%; background-color: #fafafa; ">


    <div style="width:100%; height:100%; border:1px solid white; margin:0; padding:0;">
        <div style="border:1px solid #e9e9e9; width: 300px; height:500px; margin-left: 150px; margin-bottom: 50px; margin-top:150px; background-color:white; border-radius:5px;">
                        <div th:inline="text" style="margin-left:28px; margin-top:25px; font-size:20px; font-weight:bold; font-family: 'Do Hyeon', sans-serif; ">안녕하세요 [[${#httpServletRequest.remoteUser}]] 님</div>
            <div style="height:60px; margin:auto auto; width:240px; margin-top:20px; margin-bottom: 20px; border:1px solid #e9e9e9; border-radius:5px; background-color:#fafafa;">
                <a th:href="@{/}" style="margin-left:10px; margin-top:15px; position:absolute; font-family: 'Do Hyeon', sans-serif; font-size:20px;">계정관리</a>
            </div>
            <div style="border: 1px solid #e9e9e9; width:240px; height:60px; margin:auto auto; border-radius:5px; background-color:#fafafa;">
                <a th:href="@{/}" style="margin-left:10px; margin-top:15px; position:absolute; font-family: 'Do Hyeon', sans-serif; font-size:20px;">내 글 관리</a>
            </div>
        </div>
        <div style="border:1px solid #e9e9e9; width: 800px; height: 800px; margin-left: 550px; margin-right:auto; margin-bottom:150px; margin-top:-550px; background-color:white; border-radius:5px;">
            <div style="font-size:24px; font-weight:bold; display:inline-block; margin-left:40px; margin-top:70px; border-radius:5px;">계정관리</div>
            <hr style="border: 1px solid gray; width:720px; margin-left:auto; margin-right:auto;">
            <div style="width:730px; height:630px; border:1px solid #e9e9e9; margin-left:auto; margin-right:auto; border-radius:5px;">
                <div style="margin-left:300px; margin-right:auto; display:inline-block;">
                    <img src="/bootstrap/img/프로필사진.jpg" alt="" style="height:100px; width:100px; border-radius:50px; margin-top:50px;">
                </div>
                <div th:inline="text" style="border:1px solid #e9e9e9;  width:450px; height:50px; margin-top:40px; margin-left:auto; margin-right:auto; border-radius:50px;">
                    <div style="font-size:20px; font-weight:bold; padding-left:20px; padding-top:8px;">[[${#httpServletRequest.remoteUser}]]</div>
                </div>
                <hr style="border: 1px solid gray; width:680px; margin-left:auto; margin-right:auto;">
                <div style="font-size:20px; font-weight:bold; margin-left:350px; margin-top:10px;">닉네임</div>
                <div style="border:1px solid #e9e9e9; width:450px; height:50px; margin-left:auto; margin-right:auto; margin-top:20px; border-radius:50px;">
                    <div th:text="${member.nickname}" style="font-size:18px; font-weight:bold; margin-left:20px; margin-top:10px;"></div>
                </div>
                <div>
                </div>
                <div style="border:1px solid white; width:700px; height:200px; margin-left:auto; margin-right:auto; margin-top:30px; background-color:#e9e9e9; border-radius:5px;">
                    <a href="@{/}" style="border:1px solid gray; width:300px; height:75px; display:inline-block; margin-left:30px; margin-top:10px; background-color:white; border-radius:5px;">
                        <div style="border:1px solid red; text-align:center;">이메일 변경</div>
                    </a>
                    <a href="@{/}" style="border:1px solid gray; width:300px; height:75px; display:inline-block; margin-left:30px; background-color:white;">
                        <div style="border:1px solid red;">비밀번호 변경</div>
                    </a>
                    <a th:href="@{/mypage/delete}" style="border:1px solid gray; width:300px; height:75px; display:inline-block; margin-left:30px; margin-top:20px; ">
                        <button type="submit" style="border:1px solid red;">회원탈퇴</button> // 회원탈퇴 버튼 

                    </a>
                </div>
            </div>
        </div>
    </div>
</div>
<div th:replace="Footer :: footerFragment"></div>
</body>
</head>
</html>

 

프론트 부분인데요 이 부분에서 현재 로그인한 로그인 정보의 아이디와 닉네임을 불러올 때 방법을 두가지를 사용했습니다.

1. [[${#httpServletRequest.remoteUser}]] 

2. th:text ="${member.nickname}

 

이렇게 두가지 방법을 활용했는데 1번의 경우에는 현재 로그인한 아이디만 불러올 수 있습니다.

2번의 경우에는 member라고 제가 이름을 붙여놓은건데 실질적으로 로그인한 정보는 세션에 저장이 되어있습니다.

저는 그 부분을 불러오기 위해서 model 과 principal을 활용했는데

위의 백엔드 코드에 보면

 

@GetMapping("/mypage")
public String mypage (Principal principal, Model model) {
    model.addAttribute("member",userServices.getUser(principal.getName()));
    return "Mypage";
}

 

이 부분이 있는데 principal 과 model 을 활용했습니다.

위와 같이 하고 System.out.println 으로 먼저 확인을 했을 때 저장된 주소가 전부 보이더라구요

그래서 model.addAttribute() 에 이름을 member라고 해놓은 후 그 member를 활용해서

현재 로그인 된 user 의 정보를 불러 왔습니다.

이 걸 알아 보면서 spring security 에 user와 userDetailsService 등 다양하게 있다는 것을 알았고

그렇기 때문에 이름을 user 가 아닌 member로 지정을 해놨습니다.

그리고 회원 탈퇴의 경우에는 Repository를 활용했는데 userRepository 에는 기본적으로 CRUD 기능이 있어서

생각보다 쉽게 회원탈퇴 기능을 구현할 수 있었습니다.

 

@PostMapping("/mypage/delete")
public String delete (Principal principal) {
    SiteUser siteUser = userServices.getUser(principal.getName());
    if (siteUser != null) {
        userRepository.delete(siteUser);
        SecurityContextHolder.clearContext();
    }
    return "deleteu_form";
}

 

우선 로그인 된 정보를 담고 있는 userServices.getUser(principal.getName()) 을

siteUser에 담고 if문은 사실상 없어도 될 정도 인데 혹시 나중을 위해서 if 문을 작성해놨습니다.

그냥 간단하게 null 이 아닐 경우에 userRepository.delete(siteUser); 를 통해

siteUser 인 로그인 된 유저 정보를 삭제하는 식으로 했으며 이렇게 한 후 데이터베이스에서 삭제 되는 모습도 확인했습니다.

그리고 마지막에 SecurityContextHolder.clearContext() 가 있어야 회원탈퇴 후 로그아웃도 자동적으로 처리를 해줍니다.

없으면 계속 로그인이 되어있는 상태라고 생각하시면 되서 회원탈퇴 기능을 만드셨다면

꼭 SecurityContextHolder.clearContext() 를 써주셔야 합니다.

 

이렇게 저는 회원탈퇴 기능을 만들었습니다.

 

<form th:action="@{/mypage/delete}" method="post">

    <input type="text" placeholder="아이디를 입력해주세요">
    <input type="hidden" name="_method" value="delete"/>
    <button id="delete-btn">삭제</button>
    <hr class="my-4" />

</form>

 

위에는 회원탈퇴 버튼을 눌렀을 때 뜨는 프론트 코드인데 일단 정말 간단하게 기능만 될 정도로만 만들어놨습니다.

혹시 궁금하신 부분은 댓글로 남겨주세요!

댓글