INTP의 멋대로 개발 세상

[📚상품 구매 사이트 5단계] 구매자 - JavaScript를 이용해서 회원가입 시 아이디 중복체크, 유효성 검사하기 본문

KDT 풀스택 국비 과정/파이널 프로젝트(미니)

[📚상품 구매 사이트 5단계] 구매자 - JavaScript를 이용해서 회원가입 시 아이디 중복체크, 유효성 검사하기

인팁구름 2023. 5. 9. 12:45

 

3-4단계에서는 유효성 체크를 세세하게 하지 않아서,

아무렇게나 가입이 가능했다.

이번 포스팅에서는

이전에 상품명 중복확인 한 걸 회원가입에도 적용해 보고

3부분 다 글자 사이 공백은 허용하지 않도록,

아이디는 영어만 쓰도록,

이메일 형식을 지키도록 만들어 보려고 한다!

사실 input 태그에 required(붙이면 공백 불가) 를 붙이거나 type을 지정해 놓으면 어느 정도의 유효성 체크는 된다.

하지만 required는 전체 공백을 막아주는 것이지 글자 사이의 공백을 막는 것은 아니며,

type 속성에 영어만 쓰도록 하는 것은 없기 때문에! 이 부분을 자바스크립트로 처리해 보자!😋

 

(기록용으로 작성했다. 이 포스팅의 코드들은 뒤에서 대부분 다 수정되므로 직접 사용하지는 말자!!)

 


📺 화면 설계📺

 

joinForm.jsp

 

나는 jsp 화면에 모든 스크립트 코드를 다 적었는데, 이렇게 하면 가독성이 떨어지고 코드도 길어진다.

스크립트 코드는 다른 폴더에 따로 빼놓는 걸 추천한다!! (이 경우에는 JSP로 불러오는 코드를 써 주어야 불러온다.)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    <%@ include file="../layout/header.jsp" %>
    
    <div class="container">
        <form action="/join" method="post" name="form">
                <div class="mb-3 mt-3">
                    <input
                        type="text"
                        class="form-control"
                        placeholder="유저이름"
                        id="name"
                        name="userName"
                    />
                    <button id="CheckUsername" type="button">중복확인</button>
                </div>
                <div class="mb-3">
                    <input
                        type="password"
                        class="form-control"
                        placeholder="비밀번호"
                        id="password"
                        name="userPassword"
                    />
                </div>
                <div class="mb-3">
                    <input
                        type="email"
                        class="form-control"
                        placeholder="이메일"
                        id="email"
                        name="userEmail"
                    />
                </div>

                <button
                    type="submit"
                    class="btn btn-primary">
                    회원가입
                </button>
            </form>
    </div>

    <script>

            // 중복체크 여부 = false - 아직 체크 안했으니까
            let sameCheck = false;

            // 상품명 중복체크
            $('#CheckUsername').on('click', function () {

                // 이렇게 데이터를 변수로 만들면 보기가 편하다
                let data = { userName: $('#name').val() }

                if(blankUserName() == true){
                    alert("이름을 입력해 주세요.");
                    return; // 조건문을 빠져나가기 위해
                }

                if (onlyEng() == false) {
                    alert("이름은 영어로 입력해주세요")
                    return;
                }

                $.ajax({
                    url: '/join/checkName/',
                    type: 'post',
                    data: data,
                    contentType: "application/x-www-form-urlencoded; charset=utf-8"

                }).done((res) => {
                    alert("등록 가능한 유저입니다")
                    // 콘솔창 확인용
                    console.log(res);
                    console.log("sameCheck : " + sameCheck);
                    // 등록 가능하니까 체크 여부를 true로 변경
                    sameCheck = true;

                }).fail((err) => {
                    alert("이미 등록된 유저입니다")
                    // 콘솔창 확인용
                    console.log(err);
                    console.log("sameCheck : " + sameCheck);
                    // 등록 불가이기 때문에 중복체크를 안한 것으로 설정 (아래에 이벤트 처리를 위해)
                    sameCheck = false;
                });
            });

            // 상품명을 입력하는 input 태그에 값이 변경될 때마다 sameCheck 를 false로 설정하는 이벤트
            // => false가 됐으니 상품명을 다른 걸로 바뀌면 꼭 중복체크를 다시 해야되게 만든다.
            $('#name').on('input', function (e) {
                sameCheck = false;
                console.log(sameCheck);
            });
        
            // 동일 유저 등록하지 못하게 처리하는 이벤트 (최종 회원가입 버튼)
            // form이 submit 될 때 실행되는 이벤트
            $('form').on('submit', function(e) {
                
                if(blankUserName() == true){
                    alert("아이디를 입력해주세요");
                    return;
                }
                
                if(blankUserPassword() == true){
                    alert("비밀번호를 입력해주세요");
                    return;
                }
                
                if (emailCheck() == false) {
                    alert("이메일 형식을 지켜주세요")
                    return;
                }

                if (onlyEng() == false) {
                    alert("이름은 영어로 입력해주세요")
                    return;
                }
                
                // == 주의
                if (sameCheck == false) {
                    alert("이름 중복체크 해주세요.");
                    // e.preventDefault(); = 브라우저가 이벤트를 처리하는 동작을 중단시키는 메서드
                    // submit 이벤트를 중단시키기 위해 사용됨
                    e.preventDefault();
                    console.log(sameCheck);
                }else if (sameCheck == true) {
                    alert("회원가입 완료.");
                    console.log(sameCheck);
                }
            }
            
            );

            function blankUserName() {	// 아이디 공백 || 띄어쓰기 막아줌
            let username = $("#name").val();
            let blank = /\s/g;
            if(!username || blank.test(username)){
            return true;
                }
            }

            function blankUserPassword() {	// 비밀번호 공백 || 띄어쓰기 막아줌
            let userpw = $("#password").val();
            let blank = /\s/g;
            if(!userpw || blank.test(userpw)){
                return true;
                }
            }

            function emailCheck() {	// email 형식
            let email = $("#email").val();
            let emailRule = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/;
            if (emailRule.test(email)) {
                return true;
            } else {
                return false;
                }
            }

            function onlyEng() {	// 영어만 입력 가능
                let username = $("#name").val();
                let capiRule = /^[a-zA-Z]+$/;
                /* /^[a-zA-Z]+$  <- 영어만! 포함하게 하는 정규식
                * /[a-zA-Z]/ 와 다르다.
                * /[a-zA-Z]/ 정규식은 "Hello, World!"와 같은 문자열에서도 매치되지만, "/^[a-zA-Z]+$/" 정규식은 "HelloWorld"와 같은 문자열에서만 매치됨.
                */
                if (capiRule.test(username)) {
                    return true;
                } else {
                    return false;
                }
            }
        </script>

    <%@ include file="../layout/footer.jsp" %>

 

UserController.java

 

뷰 단에서 유효성 체크했었지만, 서버 측에서도 한 번 더 체크해 주자!

@PostMapping("/join")
    public String join(JoinDto joinDto) {

        // 유효성 체크
        if (joinDto.getUserName().isEmpty()) {
            // System.out.println("JoinCustomException - userName 실행됨");
            throw new JoinCustomException(HttpStatus.BAD_REQUEST);
        }
        if (joinDto.getUserPassword().isEmpty()) {
            // System.out.println("JoinCustomException - userPassword 실행됨");
            throw new JoinCustomException(HttpStatus.BAD_REQUEST);
        }
        if (joinDto.getUserEmail().isEmpty()) {
            // System.out.println("JoinCustomException - userEmail 실행됨");
            throw new JoinCustomException(HttpStatus.BAD_REQUEST);
        }

        // 기존 동일 유저 확인 (username,email만)
        if (userRepository.findByUserName(joinDto.getUserName()) != null) {
            throw new CustomException("이미 가입된 유저입니다.", HttpStatus.BAD_REQUEST);
        }
        if (userRepository.findByUserEmail(joinDto.getUserEmail()) != null) {
            throw new CustomException("이미 가입된 이메일입니다.", HttpStatus.BAD_REQUEST);
        }

        userRepository.insert(joinDto);

        return "redirect:/loginForm";
    }

 

 

⏬주석에도 적어 놓았지만, 회원가입 시에는 이미 뷰 단에서 유효성 체크를 해 주기 때문에 굳이 서버 측에서 alert를 띄우는 CustomException이 필요없어서, alert가 없는 CustomException을 새로 만들었다!!
(이렇게 하면 될거라 나름 생각해서 만든 건데, 맞는 방법 아니고!! API Exception을 만들면 됨!!)

 

CustomExceptionHandler.java

package shop.mtcoding.productapp_v5.handler;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import shop.mtcoding.productapp_v5.handler.exception.CustomException;
import shop.mtcoding.productapp_v5.handler.exception.JoinCustomException;

@RestControllerAdvice
public class CustomExceptionHandler {

    // 무조건 데이터를 응답할 거기 때문에 RestController
    // 자바 스크립트를 응답
    // 어노테이션 ()안에 타입을 적어줘야 함
    // 런타임익셉션 - 모든 익셉션이 다 여기로 올 거임

    // exception터지면 다 여기로 올 거임! 무조건 뒤로가기

    @ExceptionHandler(CustomException.class)
    public String basicException(Exception e) {
        StringBuilder sb = new StringBuilder();
        sb.append("<script>");
        sb.append("alert('" + e.getMessage() + "');");
        sb.append("history.back();");
        sb.append("</script>");
        return sb.toString();
    }

    /*
     * 기존 CustomException은 핸들러에서 alert 창을 띄우기 때문에, alert창을 안 띄우는 새로운 Exception을
     * 생성했다.
     * history.back이 있어야 회원가입 시에 예외가 생겨도 기존에 썼던 값이 지워지지 않기 때문에 만듦!
     */
    @ExceptionHandler(JoinCustomException.class)
    public String basicException2(Exception e) {
        StringBuilder sb = new StringBuilder();
        sb.append("<script>");
        sb.append("history.back();");
        sb.append("</script>");
        return sb.toString();
    }

}

 

JoinCustomException.java

package shop.mtcoding.productapp_v5.handler.exception;

import org.springframework.http.HttpStatus;

public class JoinCustomException extends RuntimeException {
    // 상태코드
    private HttpStatus status;

    // 생성자
    /*
     * 기존 CustomException은 핸들러에서 alert 창을 띄우기 때문에, alert창을 안 띄우는 새로운 Exception을
     * 생성했다.
     */
    public JoinCustomException(HttpStatus status) {
        this.status = status;
    }
}

 

 


🎤 코드 리뷰 🎤

 

 

공백처리와 띄어쓰기 막아주는 것을 동시에 처리해 주는 자바스크립트 함수이다.

이런 식의 처리를 하려면 정규표현식을 써야 하는데.....

아직 아가 개발자는 정규표현식을 설명할 수가 없네..😥😥

그냥 원하는 정규표현식 찾아서 쓰자..!💦💦

 /\s/g;

공백 문자(스페이스, 탭, 개행 등)를 찾기 위한 정규 표현식이다

 

이 정규표현식을 blank라는 객체에 담아서

input 태그의 id 속성으로 지정한  name과 password값을 검사한다!! ➡ 이 값들을 또 새로운 username과 userpw 객체에 담는다.

name이나 password가 없거나 (!)

blank의 .test() 속성을 사용해서 검사했을 때 공백 등이 존재한다면!!

true를 리턴한다!! ( 함수 이름이 blankUserName이니까 true면 blank가 있다는 뜻으로 )

 

.test()는 자바스크립트에서 정규 표현식 객체의 메소드 중 하나다!

주어진 문자열 ()안에서 정규 표현식과 일치하는 문자열이 있는지 여부를 검사하고, 일치하는 경우 true, 그렇지 않은 경우 false를 반환한다.

 

 

이메일 형식을 체크하는 함수이다. 정규 표현식으로 나타내면 이러한데, 굳이 이렇게 만들 필요는 없고, type 속성에 email을 넣으면 된다!

 

username 칸에 영어만 입력하도록 하는 정규식이다. 이것도 위와 마찬가지로, input 태그의 id 속성인 name의 값을 받아와서 .test()로 검사한다. 

onlyEng이면 true, 다른 문자열이 섞여있다면 false를 반환한다.

 

/^[a-zA-Z]+$/

다른 특수기호 없이 정말 영어만! 포함하게 하는 정규식 표현이다.

 

 

 

 

방금 만든 함수들을 아이디 중복체크 "안"에 넣었다.

이 곳 저 곳 다 넣어봤는데 중복체크 이전에 넣어야 프로그램상(??)으로 맞는 결과가 나온다 생각해서이다..🤔

그렇게 하지 않으면, 한글로 적었는데 중복체크 결과는 당연히 완료되고,

나중에 submit 버튼을 누를 때가 되어서야 이름을 영어로 입력해만 달라고 뜨는 꼴이니까

 

 

⏬ 중복체크는 이전 단계에 상품명 중복체크 코드를 그대로 가져왔다. ⏬

 

[📚상품 구매 사이트 2단계] 상품명 중복체크, 이벤트 처리 Ajax 수정

GitHub - JungminK1m/Springboot-Product-Study-V1-V2 Contribute to JungminK1m/Springboot-Product-Study-V1-V2 development by creating an account on GitHub. github.com 1단계는 저번 게시글의 CRUD 구현이었다. 나는 자바스크립트 수업할 때

whiteclouds-dev.tistory.com

 

 

 

 

이제 컨트롤러(서버 측)에도 유효성 검사를 추가해 주자!!

 

 

(ResponseEnum은 지금 신경쓰지 말자!! 원래는 안에 일일이 메세지를 적었었는데, 편의를 위해 만들었다!)

원래는 CustomException 대신 위에서 만든 JoinCustomException을 사용했었다.

(근데 회원가입을 Ajax 통신으로 처리했어서 새로운 CustomException을 만드는 게 아니라, API Exception을 쓰는 게 맞다.)

 

Ajax 처리했으면 이렇게 APIException으로 처리해서 JSON 형태로 해야 한다!

Comments