[게시판 프로젝트] Spring Security를 이용한 로그인(2),회원가입

J_Eddy·2021년 12월 7일
0
post-thumbnail

[게시판 프로젝트] Spring Security를 이용한 로그인(1) 게시물과 이어서 진행합니다

📌 회원가입 폼 생성

부트스트랩의 도움을 받아 전체적인 ui를 꾸미기 위해 script와 css추가
thymeleaf도 사용하기 위해 추가
1. 아이디 입력 후 중복체크
2. 패스워드와 패스워드 확인 입력
3. 닉네임 입력 후 중복체크
4. 그 외 성별, 생일 입력

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="utf-8">
    <title>회원가입</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <link th:href="@{/css/signin.css}" rel="stylesheet">
    <link th:href="@{/css/birth.css}" rel="stylesheet">

    <!-- default header name is X-CSRF-TOKEN -->
    <meta id="_csrf" name="_csrf" th:content="${_csrf.token}"/>
    <meta id="_csrf_header" name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>

<body class="text-center">
<form name="registerForm" class="form-signin" th:action="@{/account/register}" method="post" th:object="${register}" onsubmit="return checkAll();">

    <a th:href="@{/}"><img class="mb-4" th:src="@{/image/log_icon.png}" alt="로고" width="72" height="57"></a>
    <h1 class="h3 mb-3 fw-normal">회원가입</h1>
    <hr>

    <label th:for="username" class="form-label">이메일(아이디)</label>
    <div class="mb-3 abc">
        <input type="email" class="form-control mx-1 overlap" id="username" name="username" placeholder="이메일을 입력해 주세요">
        <input class="btn btn-outline-primary btn-sm idCheck mx-1" type="button" id="usernameOverlay" onclick="usernameCheck()" value="중복 체크"/>
        <input class="btn btn-outline-success btn-sm reType" type="button" id="resetUsername" onclick="reUsername()" disabled value="다시입력"/>
    </div>

    <div class="mb-3">
        <label th:for="password">패스워드</label>
        <input onchange="pwSame()" type="password" class="form-control" th:field="*{password}" placeholder="비밀번호를 입력해 주세요">
    </div>

    <div class="mb-3">
        <label for="password_check">패스워드 확인</label>
        <input onkeyup="pwSame()" type="password" class="form-control" id="password_check" name="password_check" placeholder="비밀번호를 입력해 주세요">
        <span id="pw_check_msg"></span>
    </div>

    <label th:for="nickname" class="form-label">이름(닉네임)</label>
    <div class="mb-3 abc">
        <input type="text" class="form-control mx-1 overlap" id="nickname" name="nickname" placeholder="닉네임을 입력해 주세요">
        <input class="btn btn-outline-primary btn-sm idCheck mx-1" type="button" id="nicknameOverlay" onclick="nicknameCheck()" value="중복 체크"/>
        <input class="btn btn-outline-success btn-sm reType" type="button" id="resetNickname" onclick="reNickname()" disabled value="다시입력"/>
    </div>

    <label class="mb-3 form-label" for="birth">생년월일</label>
    <div class="mb-3" id="birth" onchange="checkAge()">
        <div class="birth dropdown">
            <select class="form-control" id="year" name="year">
                <option value="">년(YY)</option>
                <th:block th:each="a: ${#numbers.sequence(2001,1900)}">
                    <option th:value="${a}" th:text="${a}"></option>
                </th:block>
            </select>
        </div>

        <div class="birth mx-1 dropdown">
            <select class="form-control" id="month" name="month">
                <option value="">월(mm)</option>
                <th:block th:each="month: ${#numbers.listFormatInteger(#numbers.sequence(1,12),2)}">
                    <option class="dropdown-item" th:value="${month}" th:text="${month}"></option>
                </th:block>
            </select>
        </div>

        <div class="birth dropdown">
            <select class="form-control" id="day" name="day">
                <option value="">일(dd)</option>
                <th:block th:each="day: ${#numbers.listFormatInteger(#numbers.sequence(1,31),2)}">
                    <option class="dropdown-item" th:value="${day}" th:text="${day}"></option>
                </th:block>
            </select>
        </div>
        <span id="birth_check_msg"></span>
    </div>

    <div class="mb-3">
        <label class="form-label" for="gen">성별</label>
        <div class="dropdown" id="gen">
            <select onkeyup="checkGender()" class="form-control" id="gender" name="gender">
                <option value="">성별</option>
                <option th:each="genderValue : ${T(com.example.CUSHProject.enums.Gender).values()}"
                        th:value="${genderValue.value}"
                        th:text="${genderValue.title}"></option>
            </select>
        </div>
    </div>

    <div class="mb-3">
        <button class="w-100 btn btn-lg btn-primary" type="submit">회원가입</button>
    </div>

    <div class="mb-3">
        <a type="button" style="color:white" class="w-100 btn btn-lg btn-secondary" onclick="window.history.back();">취소</a>
    </div>

</form>
<!--<script src="https://code.jquery.com/jquery-3.6.0.js"></script>-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/register.js"></script>
</body>
</html>

📌 아이디, 닉네임 중복 확인 로직

중복 확인 JS

중복확인 버튼과 다시입력 버튼을 만들어 다시입력 버튼은 비활성화
사용자가 중복확인버튼을 누를시 ajax를 통해 중복확인
중복이면 중복입니다 라는 alert생성
중복이 아닐 시 confirm으로 사용하시겠습니까를 물어본 후 사용한다면 중복확인버튼 비활성화, 다시입력버튼 활성화, input창 readonly로 수정
다시입력버튼 클릭시 input값 초기화

input을 readOnly로 수정한 이유는 사용자가 사용가능한 아이디를 인가 받은 상태에서 다른 아이디로의 변경을 막기위해

function usernameCheck() {
    const username = $("#username").val();
    if (username == "") {
        alert("아이디를 입력해주세요!. 필수항목입니다.");
        $("#username").focus();
        return false;
    }
    $.ajax({
        type: "get",
        url: "/api/overlap/usernameRegister",
        data: {"username": username},
        dataType: "JSON",

        success: function (result) {
            if (result.result == "0") {
                if (confirm("이 아이디는 사용 가능합니다. \n사용하시겠습니까?")) {
                    usernameOverlapCheck = 1;
                    $("#username").attr("readonly", true);
                    $("#usernameOverlay").attr("disabled", true);
                    $("#usernameOverlay").css("display", "none");
                    $("#resetUsername").attr("disabled", false);
                    $("#resetUsername").css("display", "inline-block");
                }
                return false;
            } else if (result.result == "1") {
                alert("이미 사용중인 아이디입니다.");
                $("#username").focus();
            } else {
                alert("success이지만 result 값이 undefined 잘못됨");
            }
        },
        error: function (request, status,error) {
            alert("ajax 실행 실패");
            alert("code:" + request.status + "\n" + "error :" + error);
        }
    });

}

중복 확인 비지니스 로직

memberEntity에 대해 Repository를 만들어 JPA사용

@Repository
public interface MemberRepository extends JpaRepository<MemberEntity, Long> {
    ...

    //중복가입
    boolean existsByUsername(String username);
    boolean existsByNickname(String nickname);
}

MemberService
해당 값을 map에 담아 리턴후 해당 값의 결과에 따라 중복여부 체크

//email 중복 검사
    public HashMap<String, Object> usernameOverlap(String username) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", memberRepository.existsByUsername(username));
        return map;
    }

    //닉네임 중복 검사
    public HashMap<String, Object> nicknameOverlap(String nickname) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", memberRepository.existsByNickname(nickname));
        return map;
    }

📌 결과 화면


중복일시

사용가능

📌 회원가입 수행

모든 validate를 통과하고 회원가입 버튼 누를시 해당 form이 post형식으로 전달

MemberService

BCryptPasswordEncoder를 통해 패스워드를 암호화 하고 기본적으로 Role를 ROLE_MEMBER로 세팅 후 JPA를 이용하여 entity에 저장

@Transactional
    public Long singUp(MemberDto memberDto) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        memberDto.setPassword(passwordEncoder.encode(memberDto.getPassword()));
        memberDto.setRole(Role.ROLE_MEMBER);

        return memberRepository.save(memberDto.toEntity()).getId();
    }

패스워드는 아래와 같이 인코딩 된 형태로 DB에 저장

다음 포스트에 이어서 작성!

profile
논리적으로 사고하고 해결하는 것을 좋아하는 개발자입니다.

0개의 댓글