개인프로젝트/기능프로그램_오늘뭐입지

20240519_여러 기능 구현(아이디찾기/비밀번호변경/이메일변경/회원탈퇴 등)

일일일코_장민기 2024. 5. 19. 15:08
728x90

1. 아이디 찾기

 

더보기

컨트롤러

@PostMapping("/FindUsernameByEmail")
public ModelAndView FindUsernameByEmail(String email) {
    String username = memberService.FindUsernameByEmail(email);
    ModelAndView view = new ModelAndView();
        view.addObject("username", username);
        view.setViewName("Member/ViewUsername");
    return view;
}

 서비스

//유저 아이디 찾기
public String FindUsernameByEmail(String email) {
    MemberVO memberVO = memberRepository.findByEmail(email);
    if (memberVO != null) {
        return memberVO.getUsername();
    }
    return null;
}

 

 jsp

<%@ page import="org.core.vo.MemberVO" %>
<%@ page import="org.member.MemberDTO" %>
<%@ page import="org.member.clothController.ClothDTO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>

<h1>View Username Page</h1>
<p>
    <c:set var="username" value="${username}"></c:set>
    <c:choose>
        <c:when test="${not empty username}">

            고객님의 아이디는 [ <c:out value="${username}"/> ] 입니다.

        </c:when>
        <c:otherwise>

            유저 아이디를 찾을 수 없습니다. 입력하신 이메일을 확인해주세요.

        </c:otherwise>
    </c:choose>
</p>
<p>
    <a href="<c:url value='/LoginPage'/>">로그인</a>
</p>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

2. 임시 비밀번호 생성

 

// 임시 비밀번호 생성기
public String makeTemporaryPassword(int passwordLength){

    //비밀번호를 형성할 배열
    char[] passwordChars  = new char[]{
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    };

    //글자 길이 지정
    StringBuilder newPassword = new StringBuilder(passwordLength);

    //입력한 숫자만큼의 길이의 비밀번호 생성
    Random random = new Random();
    for (int i = 0; i < passwordLength; i++) {
        int randomIndex = random.nextInt(passwordChars.length);
        newPassword.append(passwordChars[randomIndex]);
    }
    return newPassword.toString();
}

 

char 배열과 Random 클래스를 사용한 간단한 임시 비밀번호 생성기이다.

 

 

 

3. 임시 비밀번호로 변경

더보기

컨트롤러

@PostMapping("/UpdatePasswordByEmail")
public String FindUsernameByEmail(String username, String email) {
    String foundedUsername = memberService.UpdatePasswordByEmail(username, email);
    return "redirect:/prgForUpdatePasswordByEmail?username=" + foundedUsername;
}   //prg Pattern
@GetMapping("/prgForUpdatePasswordByEmail")
public ModelAndView PrgForUpdatePasswordByEmail(String username) {
    ModelAndView view = new ModelAndView();
    view.addObject("username", username);
    view.setViewName("Member/ChangeToTemporaryPassword");
    return view;
}

 서비스

//유저 비밀번호 변경하기
public String UpdatePasswordByEmail(String username, String email) {
    MemberVO memberVO = memberRepository.findByUsernameAndEmail(username, email);
    if (memberVO != null) {
        String newPassword = makeTemporaryPassword(15);
        log.info("New password: {}", newPassword);
        memberVO.setPassword(passwordEncoder.encode(newPassword));
        memberRepository.save(memberVO);

        /**
         * 유저 이메일로 비밀번호 전송하기
         */

        return memberVO.getUsername();
    }
    return null;
}

jsp

<%@ page import="org.core.vo.MemberVO" %>
<%@ page import="org.member.MemberDTO" %>
<%@ page import="org.member.clothController.ClothDTO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>

<h1>Change To Temporary Page</h1>
<p>
    <c:choose>
        <c:when test="${username ne 'null'}">

            고객님의 비밀번호가 임시 비밀번호로 변경되었습니다.<br>
            회원가입 시 입력하신 이메일로 임시 비밀번호가 전송되었으니 확인해주세요.

        </c:when>
        <c:otherwise>

            유저 정보를 찾을 수 없습니다. 입력하신 아이디와 이메일을 확인해주세요.

        </c:otherwise>
    </c:choose>
</p>
<p>
    <a href="<c:url value='/LoginPage'/>">로그인</a>
</p>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

 

컨트롤러 부분

@PostMapping("/UpdatePasswordByEmail")
public String FindUsernameByEmail(String username, String email) {
    String foundedUsername = memberService.UpdatePasswordByEmail(username, email);
    return "redirect:/prgForUpdatePasswordByEmail?username=" + foundedUsername;
}   //prg Pattern
@GetMapping("/prgForUpdatePasswordByEmail")
public ModelAndView PrgForUpdatePasswordByEmail(String username) {
    ModelAndView view = new ModelAndView();
    view.addObject("username", username);
    view.setViewName("Member/ChangeToTemporaryPassword");
    return view;
}

 

컨트롤러부분은 prg 패턴을 사용하여, 결과 창에서 새로고침을 눌러도 같은 작업이 반복되지 않도록 막았다.

 

4. 회원 이메일 정보 변경

더보기

컨트롤러

//회원 이메일 변경
@PostMapping("/UpdateEmailByUsername")
public String UpdateEmailByUsername(Principal  principal, String newEmail) {
    Boolean changeSuccess = principalService.UpdateEmailByUsername(principal.getName(), newEmail);
    return "redirect:/logout";
}

 서비스

public Boolean UpdateEmailByUsername(String username, String newEmail) {
    MemberVO memberVO = principalRepository.findByUsername(username);
    if (memberVO != null) {
        memberVO.setEmail(newEmail);
        principalRepository.save(memberVO);
        return true;
    }
    return false;
}

js

$(function(){

    $("#newEmail").on("focusout", function () {
        checkEmail()
    })

    $("#ChangeEmailForm").on("submit", function(event){
        event.preventDefault();
        const duplicatedEmailField = $("#duplicatedEmailField")
        if(duplicatedEmailField.html().trim() !== ""){
            event.preventDefault();
        } else {
            $("#ChangeEmailForm")[0].submit();
        }
    })

    $("#newEmail").on("input", function(){
        $("#duplicatedEmailField").html("");
    })
})
function checkEmail(){
    const duplicatedEmailField = $("#duplicatedEmailField")
    const email = $("#newEmail").val()
    $.ajax({
        type: "post",
        url: "/AjaxExistByEmail",
        data: {email: email},
        dataType: "text",
        success: function (response) {
            if(response === "true"){
                duplicatedEmailField.html("중복된 이메일입니다.").css("color", "red")
            } else {
                duplicatedEmailField.html("");
            }
        },
        error: function (error) {
            console.error("이메일 중복 검사 에러")
        }
    })
}

 jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <script type="text/javascript" src="/resources/js/ChangeEmailPage.js"></script>
</head>
<body>

<h1>Change Email Page</h1>

<form id="ChangeEmailForm" method="post" action="<c:url value='/UpdateEmailByUsername'/>">
    New Email: <input type="email" id="newEmail" name="newEmail"><br>
    <input type="submit" value="이메일 변경">
</form>
<span id="duplicatedEmailField"></span>

<a href="<c:url value="/MemberPage"/>">돌아가기</a>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

 

 

5. 비밀번호 변경

더보기

 컨트롤러

// 비밀번호 변경
@PostMapping("/UpdatePasswordByUsername")
public String UpdatePasswordByUsername(Principal  principal, String newPassword) {
    Boolean changeSuccess = principalService.UpdatePasswordByUsername(principal.getName(), newPassword);
    return "redirect:/logout";
}

 서비스

public Boolean UpdatePasswordByUsername(String username, String newPassword) {
    MemberVO memberVO = principalRepository.findByUsername(username);
    if (memberVO != null) {
        memberVO.setPassword(passwordEncoder.encode(newPassword));
        principalRepository.save(memberVO);
        return true;
    }
    return false;
}

 

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

</head>
<body>

<h1>Change Password Page</h1>

<form method="post" action="<c:url value='/UpdatePasswordByUsername'/>">
    New Password: <input type="text" name="newPassword"><br>
    <input type="submit" value="비밀번호 변경">
</form>

<a href="<c:url value="/MemberPage"/>">돌아가기</a>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

 

6. 회원 탈퇴

더보기

컨트롤러

@PostMapping("/withdrawByUsername")
public String withdrawByUsername(Principal principal) {
    principalService.deleteByUsername(principal.getName());
    return "redirect:/logout";
}

 서비스

public void deleteByUsername(String username) {
    principalRepository.deleteByUsername(username);
}

 

 리포지토리

@Transactional
void deleteByUsername(String username);

 

jpa에서 crud 중 c, u, d를 직접 만들 때는 @Transactional을 붙여주어야 한다.

이유 - 데이터베이스 연산의 일관성과 무결성을 보장하기 위함

1. 원자성

- 처리 | not 처리 상태만 존재

2. 일관성

- 데이터를 삭제하면 관련된 모든 데이터 삭제

3. 격리성

- 동시에 실행되는 다른 트랜잭션으로부터 독립적

4. 내구성

- 완료되면 결과가 DB에 영구적으로 저장

## 반드시 리포지토리에 붙이는 것이 아니라, 여러 작업이 순차적으로 발생하는 서비스 메서드에도 붙임으로서 트랜잭션을 사용할 수도 있다.

 

 

7. 회원가입 시 중복 확인 ajax 개선

이전 코드

//유저 아이디 중복 검사
    var username = $("#username").val();
    if(username){
        var existUsername = $("#existUsername")
        $.ajax({
                type: "post",
                url: "<c:url value='/AjaxExistByUsername'/>",
                data: {username: username},
                dataType: "text",
                success: function(response){
                    if(response){
                        existUsername.text('중복된 아이디입니다.')
                    }
                    else {
                        existUsername.text('')
                    }
                },
                error: function (error){
                    console.error("아이디 중복 체크 에러")
                }
            }
        )
    }//유저 아이디 중복 검사 끝

    //유저 이메일 중복 검사
    var email = $("#email").val();
    if(email){
        var existEmail = $("#existEmail")
        $.ajax({
            type: "post",
            url: "<c:url value='/AjaxExistByEmail'/>",
            data: {email: email},
            dataType: "text",
            success: function(response){
                if(response){
                    existEmail.text("중복된 이메일입니다.")
                }
                else{
                    existEmail.text("");
                }
            },
            error: function (error) {
                console.error("이메일 중복 검사 에러")
            }
        })
    }//이메일 중복 검사 끝

})

 

개선된 코드

//유저 아이디 중복 검사
$("#username").on("focusout", function (){
    checkUsername()
})//유저 아이디 중복 검사 끝

//유저 이메일 중복 검사
$("#email").on("focusout", function () {
    checkEmail()
})//이메일 중복 검사 끝

//아이디 칸 입력 시 경고창 삭제
$("#username").on("input", function(){
    $("#existUsername").html('')
})

//이메일 칸 입력 시 경고창 삭제
$("#email").on("input", function(){
    $("#existEmail").html('')
})

//제출 시 재확인 및 경고창 확인
$("#registerForm").on("submit", function(event){
    checkUsername()
    checkEmail()
    if($("#existUsername").html().trim() !== "" || $("#existEmail").html().trim() !== ""){
        event.preventDefault();
    } else {
        $("#registerForm")[0].submit();
    }
})

function checkEmail(){
    var email = $("#email").val();
    var existEmail = $("#existEmail")
    $.ajax({
        type: "post",
        url: "/AjaxExistByEmail",
        data: {email: email},
        dataType: "text",
        success: function (response) {
            if(response === "true"){
                existEmail.html("중복된 이메일입니다.").css("color", "red")
            } else {
                existEmail.html("");
            }
        },
        error: function (error) {
            console.error("이메일 중복 검사 에러")
        }
    })
}

function checkUsername(){
    var username = $("#username").val();
    var existUsername = $("#existUsername")
    console.log(username)
    $.ajax({
            type: "post",
            url: "/AjaxExistByUsername",
            data: {username: username},
            dataType: "text",
            success: function(response){
                if(response === "true"){
                    existUsername.html('중복된 아이디입니다.').css("color", "red")
                }
                else {
                    existUsername.html('')
                }
            },
            error: function (error){
                console.error("아이디 중복 체크 에러")
            }
        }
    )
}

 

기존 코드는 ajax만 있었지, 실제로 기능을 하진 못하고 있었다.

왜 이렇게 만들었지?

또한 변수 var로 하고 블록 밖에 선언을 해서 안에서 블록 단위인 var를 ajax 내에서 못 읽는 문제가 발생하고 있었다.

 

1. ajax 내로 변수 이동

2. ajax 경고 출력

3. 입력 시 경고 문구 삭제

4. 경고 문구 출력 시 subtmit 제한

 

이라는 기본적인 기능을 추가했다.