21.03.10 Untact프로젝트(관리자 회원가입시 아이디 중복체크 자동화)

2021. 3. 10. 19:40JAVA/Spring & Vue APP 프로젝트(백엔드)

# NOTE

//실시간으로 아이디 중복체크하는 함수
	$(function(){
		//.inputLoginId에 뭔가 변화가 있을때(change) 중복체크 실시
		$('.inputLoginId').change(function(){
			JoinForm__checkLoginIdDup();
		});

		//.inputLoginId에 키가 입력될 때마다(keyup) 중복체크 실시
		//lodash 적용: _.debounce(JoinForm__checkLoginIdDup, 1000)
		//키 입력 종료 후 1초 후에 중복체크 함수 실시
		$('.inputLoginId').keyup(_.debounce(JoinForm__checkLoginIdDup, 1000));

});

# 주요 소스코드

<join.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>

<%@ include file="../part/head.jspf"%>

<!-- lodash 불러오기 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

<script>

	const JoinForm__checkAndSubmitDone = false;
	
	let JoinForm__validLoginId = '';

	//ajax로 아이디 중복 체크
	function JoinForm__checkLoginIdDup(){
		const form = $('.formLogin').get(0);
		const loginId = form.loginId.value;

		form.loginId.value = form.loginId.value.trim();
		if (form.loginId.value.length == 0) {
			return;
		}

		$.get(
			'getLoginIdDup',
			{
				loginId
			},
			function(data){
				//기본적으로 초록색
				let colorClass = 'text-green-500';

				//실패했으면 빨간색
				if(data.fail){
					colorClass = 'text-red-500';
				}

				$('.loginIdInputMsg').html("<span class='" + colorClass + "'>" + data.msg + "</span>");

				if ( data.fail ) {
					form.loginId.focus();
				}
				else {
					JoinForm__validLoginId = data.body.loginId;
					form.loginPw.focus();
				}

			},
			'json'

		);

	};

	
	function JoinForm__checkAndSubmit(form) {
		if (JoinForm__checkAndSubmitDone) {
			return;
		}
		
		form.loginId.value = form.loginId.value.trim();
		if (form.loginId.value.length == 0) {
			alert('아이디를 입력해주세요.');
			form.loginId.focus();
			return;
		}

		if ( form.loginId.value != JoinForm__validLoginId ) {
			alert('아이디 중복체크를 해주세요.');
			form.loginId.focus();
			return;
		}
		
		form.loginPw.value = form.loginPw.value.trim();
		if (form.loginPw.value.length == 0) {
			alert('비밀번호를 입력해주세요.');
			form.loginPw.focus();
			return;
		}
		
		if (form.loginPwConfirm.value.length == 0) {
			alert('비밀번호를 확인해 주세요.');
			form.loginPwConfirm.focus();
			return;
		}
		
		if (form.loginPw.value != form.loginPwConfirm.value ) {
			alert('비밀번호가 일치하지 않습니다.');
			form.loginPwConfirm.focus();
			return;
		}
		
		form.name.value = form.name.value.trim();
		if (form.name.value.length == 0) {
			alert('이름을 입력해주세요.');
			form.name.focus();
			return;
		}
		
		form.nickname.value = form.nickname.value.trim();
		if (form.nickname.value.length == 0) {
			alert('닉네임을 입력해주세요.');
			form.nickname.focus();
			return;
		}
		
		form.email.value = form.email.value.trim();
		if (form.email.value.length == 0) {
			alert('이메일을 입력해주세요.');
			form.email.focus();
			return;
		}
		
		form.cellphoneNo.value = form.cellphoneNo.value.trim();
		if (form.cellphoneNo.value.length == 0) {
			alert('전화번호를 입력해주세요.');
			form.cellphoneNo.focus();
			return;
		}
		
		form.submit();
		JoinForm__checkAndSubmitDone = true;
	}

	//실시간으로 아이디 중복체크하는 함수
	$(function(){
		//.inputLoginId에 뭔가 변화가 있을때(change) 중복체크 실시
		$('.inputLoginId').change(function(){
			JoinForm__checkLoginIdDup();
		});

		//.inputLoginId에 키가 입력될 때마다(keyup) 중복체크 실시
		//lodash 적용: _.debounce(JoinForm__checkLoginIdDup, 1000)
		//키 입력 종료 후 1초 후에 중복체크 함수 실시
		$('.inputLoginId').keyup(_.debounce(JoinForm__checkLoginIdDup, 1000));

	});
	
</script>
<section class="section-login">
	<div
		class="container mx-auto min-h-screen flex items-center justify-center">
		<div class="w-full">
			<div class="logo-bar flex justify-center mt-3">
				<a href="#" class="logo">
					<span>
						<i class="fas fa-people-arrows"></i>
					</span>
					<span>UNTACT ADMIN</span>
				</a>
			</div>
			<form class="formLogin bg-white shadow-md rounded px-8 pt-6 pb-8 mt-4"
				action="doJoin" method="POST"
				onsubmit="JoinForm__checkAndSubmit(this); return false;">
				<input type="hidden" name="redirectUrl" value="${param.redirectUrl}" />
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>아이디</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="inputLoginId shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="text" placeholder="아이디를 입력해주세요."
							name="loginId" maxlength="20" />
						<div class="loginIdInputMsg"></div>
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>비밀번호</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border border-red rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="password"
							placeholder="비밀번호를 입력해주세요." name="loginPw" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>비밀번호 확인</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border border-red rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="password"
							placeholder="비밀번호를 확인해주세요." name="loginPwConfirm" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>이름</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="text" placeholder="이름을 입력해주세요."
							name="name" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>닉네임</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="text" placeholder="닉네임을 입력해주세요."
							name="nickname" maxlength="20" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>이메일</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="email" placeholder="이메일을 입력해주세요."
							name="email" maxlength="100" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>전화번호</span>
					</div>
					<div class="p-1 md:flex-grow">
						<input
							class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
							autofocus="autofocus" type="tel" placeholder="전화번호를 입력해주세요.(- 없이 입력해주세요.)"
							name="cellphoneNo" maxlength="11" />
					</div>
				</div>
				<div class="flex flex-col mb-4 md:flex-row">
					<div class="p-1 md:w-36 md:flex md:items-center">
						<span>회원가입</span>
					</div>
					<div class="p-1">
						<input
							class="btn-primary bg-blue-500 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded"
							type="submit" value="회원가입" />
						<a onclick="history.back();" class="btn-info bg-green-500 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded inline-block">뒤로가기</a>
					</div>
				</div>
			</form>
		</div>
	</div>
</section>

<%@ include file="../part/foot.jspf"%>

<AdmMemberController.java>

@GetMapping("/adm/member/getLoginIdDup")
	@ResponseBody
	public ResultData getLoginIdDup(String loginId) {
		if (loginId == null) {
			return new ResultData("F-5", "loginId를 입력해주세요.");
		}

		if (Util.allNumberString(loginId)) {
			return new ResultData("F-3", "로그인아이디는 숫자만으로 구성될 수 없습니다.");
		}

		if (Util.startsWithNumberString(loginId)) {
			return new ResultData("F-4", "로그인아이디는 숫자로 시작할 수 없습니다.");
		}

		if (loginId.length() < 5) {
			return new ResultData("F-5", "로그인아이디는 5자 이상으로 입력해주세요.");
		}

		if (loginId.length() > 20) {
			return new ResultData("F-6", "로그인아이디는 20자 이하로 입력해주세요.");
		}

		if (Util.isStandardLoginIdString(loginId) == false) {
			return new ResultData("F-1", "로그인아이디는 영문소문자와 숫자의 조합으로 구성되어야 합니다.");
		}

		Member existingMember = memberService.getMemberByLoginId(loginId);

		if (existingMember != null) {
			return new ResultData("F-2", String.format("%s(은)는 이미 사용중인 로그인아이디 입니다.", loginId));
		}

		return new ResultData("S-1", String.format("%s(은)는 사용가능한 로그인아이디 입니다.", loginId), "loginId", loginId);
	}

<Util.java>

//숫자로만 구성된 문자열인지 여부 판별
	public static boolean allNumberString(String str) {
		if ( str == null ) {
			return false;
		}

		if ( str.length() == 0 ) {
			return true;
		}

		for ( int i = 0; i < str.length(); i++ ) {
			if ( Character.isDigit(str.charAt(i)) == false ) {
				return false;
			}
		}
		
		//숫자로만 구성되어 있으면 true 리턴
		return true;
	}

	//숫자로 시작하는 문자열인지 여부 판별
	public static boolean startsWithNumberString(String str) {
		if ( str == null ) {
			return false;
		}

		if ( str.length() == 0 ) {
			return false;
		}
		
		//숫자로 시작하는 문자열이면 true 리턴
		return Character.isDigit(str.charAt(0));
	}

	//
	public static boolean isStandardLoginIdString(String str) {
		if ( str == null ) {
			return false;
		}

		if ( str.length() == 0 ) {
			return false;
		}

		// 조건
		// 5자 이상, 20자 이하로 구성
		// 숫자로 시작 금지
		// _, 알파벳, 숫자로만 구성
		return Pattern.matches("^[a-zA-Z]{1}[a-zA-Z0-9_]{4,19}$", str);
	}