회원가입 수정, 회원탈퇴 기능 추가

뚜우웅이·2023년 5월 26일
0

SpringBoot웹

목록 보기
18/23

회원가입 요구조건 수정

id는 6글자 이상, password는 알파벳 소문자 + 숫자 + 특수문자를 합쳐서 8글자 이상 작성해야지 회원가입을 할 수 있게 수정해줍니다.

AccountController 수정

@PostMapping("/register")
    public String register(@RequestParam String username, @RequestParam String password1, @RequestParam String password2, Model model){
        if (userService.checkUserName(username)) {
            model.addAttribute("usernameError", "이미 사용 중인 아이디입니다.");
            return "account/register";
        }
        if(!password1.equals(password2)){
            model.addAttribute("passwordError", "비밀번호가 일치하지 않습니다.");
            return "account/register";
        }
        if(username.length() < 6){
            model.addAttribute("usernameError", "id는 6~20자리 이상이어야 됩니다.");
            return "account/register";
        }
        if(password1.length() < 8){
            model.addAttribute("passwordError", "비밀번호는 8자리 이상이어야 됩니다.");
            return "account/register";
        }
        String pattern = "^(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*+=]).+$";
        Pattern regex = Pattern.compile(pattern);
        Matcher matcher = regex.matcher(password1);
        if(!matcher.matches()){
            model.addAttribute("passwordError", "비밀번호는 영어, 숫자, 특수문자(!, @, #, $, %, ^, &, *, +,=)가 포함되어야 합니다.");
            return "account/register";
        }
        User user = new User();
        user.setUsername(username);
        user.setPassword(password1);
        userService.save(user);
        return "redirect:/";
    }
String pattern = "^(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*+=]).+$";

이 정규식은 알파벳 소문자 한 개 이상, 숫자 한 개 이상, !@#$%^&*+= 여기에 있는 특수문자 중 한 개 이상이 있는지 검사하는 정규식입니다.

Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(password1);

위의 코드에서 Pattern.compile(pattern)은 주어진 정규식 패턴을 컴파일하여 Pattern 객체를 생성합니다. Pattern객체는 컴파일된 정규식을 나타내며, 이를 이용하여 특정 문자열에 대한 매칭 작업을 수행할 수 있습니다.

regex.matcher(password)는 주어진 비밀번호를 매칭하기 위한 Matcher 객체를 생성합니다.

matcher.matches()비밀번호정규식 패턴과 완전히 일치하는지 여부를 확인합니다. 일치하는 경우 true를 반환하고, 일치하지 않는 경우 false를 반환합니다.

회원 탈퇴

외래 키(Foreign Key) 제약 조건이 설정된 테이블 간에 관계가 있을 때, 부모 테이블의 레코드를 삭제하거나 업데이트하려고 시도했으나, 자식 테이블에 해당 외래 키를 참조하는 레코드가 존재하는 경우 오류가 발생할 수 있어 회원 탈퇴 과정에서는 DB에 있는 Role 테이블에 정보가 삭제 된 후에 User 테이블에 사용자가 삭제 되어야합니다.

로그인 과정에서 세션에 User 정보를 저장해야 됨

먼저 회원탈퇴 버튼을 로그아웃 버튼 옆에 만들어줍니다.
회원탈퇴 버튼을 누르면 회원탈퇴 페이지로 이동하게 합니다.

<button class="btn btn-secondary my-2 my-sm-0 mr-2" type="submit">로그아웃</button>
                <a class="btn btn-secondary my-2 my-sm-0 mr-2"  th:href="@{/account/delete}"
                   sec:authorize="isAuthenticated()">회원탈퇴</a>

먼저 회원탈퇴 페이지로 이동시켜주는 Controller를 만들어줍니다.

AccountController

@GetMapping("/delete")
    public String deleteForm() {
        return "account/delete";
    }

회원탈퇴 페이지를 만들어줍니다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
          integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <link href="starter-template.css" th:href="@{/css/starter-template.css}" rel="stylesheet">
    <title>회원탈퇴</title>
</head>
<body>
<div class="container">
    <a th:href="@{/}"><img class="mb-4" src="https://getbootstrap.com/docs/5.2/assets/brand/bootstrap-logo.svg" alt="" width="72" height="57"></a>
    <h1>회원탈퇴</h1>
    <form th:action="@{/account/delete}" method="post">
        <div class="form-group">
            <label for="password">비밀번호</label>
            <input type="password" class="form-control col-6" id="password" name="password" placeholder="비밀번호를 입력하세요">
            <p th:if="${passwordError}" th:text="${passwordError}" style="color:red"></p>
        </div>
        <button type="submit" class="btn btn-danger" th:onclick="'showConfirmation(event)'">회원탈퇴</button>

    </form>
</div>
<footer th:fragment="footer">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
            integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
            crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct"
            crossorigin="anonymous"></script>
</footer>
<script th:inline="javascript">
    function showConfirmation(event) {
        if (!confirm("정말로 회원탈퇴 하시겠습니까?")) {
            event.preventDefault();
        }
    }
</script>
</body>

</html>

먼저 홈페이지로 이동할 수 있는 a태그를 넣어주고
회원탈퇴를 할 때는 비밀번호를 한 번 더 입력 받은 후 일치하는 경우에만 삭제할 수 있게해줍니다. JS를 이용해서 회원탈퇴를 누르게 되면 확인, 취소 버튼이 뜨고 확인을 누른 경우에만 회원탈퇴를 할 수 있게해줍니다.
또 비밀번호가 틀린 경우에는 비밀번호가 틀렸다는 것을 알려줍니다.

사용자가 해당 폼을 제출하면, 지정된 경로(/account/delete)로 post 메서드로 요청을 전송하게 됩니다.

UserService

 @Transactional //어노테이션을 사용하여 트랜잭션 범위 내에서 지연 로딩을 수행하는 방법
    public void deleteUser(User user) {
        deleteUserRoleByUserId(user.getId());
        userRepository.delete(user);
    }
    @PersistenceContext
    private EntityManager entityManager;

    public void deleteUserRoleByUserId(Long userId) {
        Query query = entityManager.createNativeQuery("DELETE FROM user_role WHERE user_id = :userId");
        query.setParameter("userId", userId);
        query.executeUpdate();
    }

    // 사용자의 암호화된 비밀번호 가져오기
    public String getUserEncodedPassword(String username) {
        User user = userRepository.findByUsername(username);
        if (user != null) {
            return user.getPassword(); // 사용자의 암호화된 비밀번호 반환
        }
        return null;
    }

@Transactional 어노테이션을 사용하여 LazyInitializationException과 같은 지연 로딩(Lazy Loading) 문제를 방지할 수 있습니다.

회원탈퇴를 하기 위해서는 먼저 user에 해당되는 role을 삭제해야 됩니다.
현재 DB에서 user에 해당하는 role은 user_role 테이블에 관리를 하는데 이 테이블에 해당하는 entity가 없기 때문에 Repository를 이용하지 못합니다.
그렇기 때문에 entityManager를 사용하여 정보를 삭제해줍니다.

@PersistenceContext는
JPA(Java Persistence API)에서 제공하는 어노테이션으로, 영속성 컨텍스트(Persistence Context)를 주입받기 위해 사용됩니다.

영속성 컨텍스트는 엔티티 객체를 관리하고 영속성 관리, 캐싱, 변경 감지 등의 기능을 제공하는 일종의 작업 영역입니다.

  1. Query query = entityManager.createNativeQuery("DELETE FROM user_role WHERE user_id = :userId");

entityManager.createNativeQuery() 메서드를 사용하여 원시 SQL 쿼리를 생성합니다. 이 경우 "DELETE FROM user_role WHERE user_id = :userId"라는 SQL 쿼리가 생성됩니다. :userId는 바인딩할 파라미터를 나타냅니다.

  1. query.setParameter("userId", userId);

query.setParameter() 메서드를 사용하여 쿼리에 파라미터 값을 바인딩합니다. 여기서는 userId 값을 :userId 파라미터에 바인딩하고 있습니다.

  1. query.executeUpdate();
    query.executeUpdate() 메서드를 사용하여 쿼리를 실행하고, 데이터베이스에서 영향을 받는 행의 수를 반환합니다. 이 경우 DELETE 문이 실행되어 user_role 테이블에서 주어진 userId와 일치하는 행이 삭제됩니다.
    따라서, deleteUserRoleByUserId 메서드는 user_role 테이블에서 특정 사용자의 역할 정보를 삭제하는 역할을 수행합니다. 해당 메서드는 entityManager을 사용하여 데이터베이스 조작을 수행하고, 네이티브 SQL 쿼리를 사용하여 삭제 작업을 수행합니다

AccountController

//회원탈퇴
    @PostMapping("/delete")
    public String delete(@RequestParam String password, HttpSession session, Model model) {
        // 현재 로그인한 사용자 정보 가져오기
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();

        // 사용자의 암호화된 비밀번호 가져오기
        String encodedPassword = userService.getUserEncodedPassword(username);

        // 입력한 비밀번호와 암호화된 비밀번호 비교
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        boolean isMatch = passwordEncoder.matches(password, encodedPassword);
        User user = userService.findByUsername(username);
        if (isMatch) {
            userService.deleteUser(user);
            session.invalidate();
            return "redirect:/";
        } else {
            model.addAttribute("passwordError", "비밀번호가 일치하지 않습니다.");
            return "/account/delete";
        }
    }

SecurityContextHolder.getContext().getAuthentication() 메서드를 사용하여 현재 실행 중인 스레드보안 컨텍스트에서 Authentication 객체를 가져옵니다. Authentication 객체는 현재 인증된 사용자의 정보를 포함하는 객체입니다.

Authentication 객체에서 getName() 메서드를 호출하여 현재 인증된 사용자의 이름(사용자명)을 가져옵니다.

BCryptPasswordEncoder를 사용하여 PasswordEncoder 객체를 생성합니다. PasswordEncoder는 비밀번호를 암호화하고 비밀번호 일치 여부를 확인하는 인터페이스입니다.

passwordEncoder.matches(password, encodedPassword) 메서드를 사용하여 주어진 비밀번호(password)와 암호화된 비밀번호(encodedPassword)가 일치하는지 확인합니다.

Controller에서는 현재 로그인한 사용자의 비밀번호를 가져와서 회원탈퇴 페이지에서 입력하는 비밀번호와 일치하는지 검사를 한 뒤에 일치하면 회원정보를 삭제시켜주는 역할을 합니다.

profile
공부하는 초보 개발자

0개의 댓글