국비교육(22-23)

36일차(2)/jsp(24) : 회원가입시 유효성 검증(validation) 기능 구현

서리/Seori 2022. 11. 28. 18:35

 

*회원가입시 입력값에 대한 유효성 검증

- 중복 아이디 확인 기능 구현

- 비밀번호 확인시, 동일한 비밀번호를 넣었는지 확인하는 기능 구현

- 이메일을 양식에 맞추어 작성했는지 확인하는 기능 구현

 

 

*bootstrap 홈페이지- docs - form - validation : 링크

 

- 사용자가 폼 입력시 입력한 값이 사용 가능한지 불가능한지 여부에 따라

 보이는 방식을 다르게 해주는 CSS가 있다. (유효성 검증)

 

 

/users/ <signup_form.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/signup_form.jsp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
<style>
	h1{
		 margin: 20px 0px 20px; 
		 border-bottom:3px solid #0d6efd; 
		 padding:10px 0px 10px;
	}
</style>
</head>
<body>
	<jsp:include page="/include/navbar.jsp">
	<jsp:param value="signupform" name="thisPage"/>
	</jsp:include>
	<div class="container">
		<h1>회원 가입 폼입니다.</h1>
		<form action="signup.jsp" method="post" id="signupForm">
			<div>
			 	<label for="id" class="control-label">아이디</label>
			 	<input type="text" class="form-control" name="id" id="id" />
			 	<div class="valid-feedback">사용할 수 있습니다.</div>
			 	<div class="invalid-feedback">사용할 수 없는 아이디입니다.</div>
			</div>
			<div>
				<label for="pwd" class="control-label">비밀번호</label>
				<input type="password" class="form-control" name="pwd" id="pwd" />
				<div class="invalid-feedback">비밀번호를 확인하세요.</div>
			</div>
			<div>
				<label for="pwd2" class="control-label">비밀번호 확인</label>
				<input type="password" class="form-control" name="pwd2" id="pwd2" />
			</div>
			<div>
				<label for="email" class="control-label">이메일</label>
				<input type="text" class="form-control" name="email" id="email" />
				<div class="invalid-feedback">이메일 형식에 맞게 입력하세요.</div>
			</div>	
			<button class="btn btn-outline-primary" style="margin-top: 10px;" type="submit">회원가입</button>			
		</form>
	</div>
	<script>
		//유효성 여부를 저장할 변수를 만들고 초기값 대입
		let isIdValid=false;	
		let isPwdValid=false;
		let isEmailValid=false;
	
	    document.querySelector("#email").addEventListener("input", function(){
	        
	        this.classList.remove("is-valid");
	        this.classList.remove("is-invalid");
	        //입력한 이메일
	        const inputEmail=this.value;
	        //이메일을 검증할 정규 표현식  
	        const reg=/@/;
	        //만일 입력한 이메일이 정규표현식 검증을 통과하지 못했다면
	        if(!reg.test(inputEmail)){
	           this.classList.add("is-invalid");
	           isEmailVaild=false;
	        }else{//통과했다면
	           this.classList.add("is-valid");
	           isEmailVaild=true;
	        }
		});	
	
		function checkPwd(){
			//먼저 두개의 클래스를 제거하고
			document.querySelector("#pwd").classList.remove("is-valid");
			document.querySelector("#pwd").classList.remove("is-invalid");
			//입력한 두개의 비밀번호를 읽어와서
			const pwd=document.querySelector("#pwd").value;
			const pwd2=document.querySelector("#pwd2").value;
			
			//만일 비밀번호 입력란과 확인란이 다르다면
			if(pwd != pwd2){
				document.querySelector("#pwd").classList.add("is-invalid");
				isPwdVaild=false;
			}else{
				document.querySelector("#pwd").classList.add("is-valid");
				isPwdVaild=true;
			}			
		}	
	
		document.querySelector("#pwd").addEventListener("input", function(){
			checkPwd();
		});
		document.querySelector("#pwd2").addEventListener("input", function(){
			checkPwd();			
		});				
			
		//id를 입력할때마다 호출되는 함수 등록
		document.querySelector("#id").addEventListener("input", function(){
			//input 요소의 참조값을 self에 미리 담아놓기
			const self=this;
			//일단 2개의 클래스를 모두 제거한 다음
			self.classList.remove("is-valid");
			self.classList.remove("is-invalid");
			
			//1.입력한 아이디를 읽어와서
			const inputId=self.value;
			
			//2.서버에 페이지 전환 없이 전송을 하고 응답을 받는다.
			fetch("checkid.jsp?inputId="+inputId)
			.then(function(response){
				return response.json();
			})
			.then(function(data){
				//3. 사용가능한지 여부에 따라 id 입력란에 is-valid or is-invalid 클래스를 적절히 추가, 제거
				console.log(data);
				if(data.isExist){
					self.classList.add("is-invalid")
					isIdVaild=false;
				}else{
					self.classList.add("is-valid")
					isIdVaild=true;
				}
			});			
		});
		
		//폼에 submit이벤트가 일어났을 때 실행할 함수 등록
		document.querySelector("#signupForm").addEventListener("submit", function(event){
									
			//아래의 코드는 아이디, 비밀번호, 이메일 유효성 검증결과를 고려해서 조건부로 실행되도록 해야한다.
			//폼 전체의 유효성 여부 
			const isFormValid= isIdValid && isPwdValid && isEmailValid;
			//만일 폼이 유효하지 않으면
			if(!isFormValid){
				event.preventDefault(); //폼 전송 막기
			}
		});
		
	</script>	
	<jsp:include page="/include/footer.jsp"></jsp:include>
</body>
</html>

 

- bootstrap의 내용을 참고하여 class="is-valid" 추가

 

- 위와 같이 해당 input 요소가 초록색으로 표시된다.

- class="is-invalid" 를 작성하면 빨간색으로 표시됨.

 

<div class="invalid-feedback">사용할 수 없는 아이디입니다.</div>

- 아이디 창 아래에 위와 같이 작성하면 input 요소 아래에 메시지가 나타난다.

 

- valid일 경우, invalid 일 경우 각각의 피드백을 작성하여 넣어준 후,

 화면전환 없이 서버에 보내서 valid인지 invalid인지를 검증하여 응답하면 된다.

 

- classList 함수를 사용해 어떤 클래스를 추가하거나 제거할 수 있다.

- 이 내용을 javascript 영역 안에서 작성하면 된다.

 


 

- 페이지에 javascript를 추가하고, 

  fetch 사용, JSON으로 응답하기

 

- 여기서 this는 저 요소 자체를 가리킨다.(#id인 요소)

 

- 입력한 id를 어떤 jsp 페이지에 get방식 패러미터로 전송해본다.

- 3번 작업은 마지막 then안에서 작성해주면 된다.

 

- input요소에 입력이(이벤트가) 일어날 때마다 서버에 보내서 응답을 받는 구조이다.

 

 

<checkid.jsp> 생성

<%@page import="test.users.dao.UsersDao"%>
<%@page import="test.users.dto.UsersDto"%>
<%@ page language="java" contentType="application/json; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//inputId라는 파라미터명으로 전달되는 문자열 읽어오기
	String inputId=request.getParameter("inputId");
	//dto는 null일수도 있고 아닐 수도 있다.
	UsersDto dto=UsersDao.getInstance().getData(inputId);
%>
<%-- 만일 null이면 존재하지 않는 아이디(회원가입이 가능한 아이디) --%>
<% if(dto==null){%>
	{"isExist":false}	
<%}else{%> <%-- 만일 null이 아니면 이미 존재하는 아이디(회원가입이 불가능한 아이디) --%>
	{"isExist":true}	
<%}%>

 

String inputId=request.getParameter("inputId");

- signup_form.jsp의 이벤트리스너가 일어나는 javascript문의 변수명과 동일하게 작성하는 것에 주의하기!

  (fetch에서 지정한 값)

 

- UserDto 안에 id를 사용해서 어떤 결과를 내보내는 getData 함수가 있다.

 

- id를 전달하면 바인딩해서 select하는데, 존재하지 않는 아이디면 if문을 건너뛴다.

- if문이 실행될 경우에만 new 가 일어나므로, if문을 건너뛰면 new 되지않으므로 null인 상태로 리턴한다.

 

- ajax요청은 보통 json으로 응답한다.

- checkid.jsp의 상단 내용을 application/json 으로 수정

 

- null일 때와 아닐 때 각각 다른 값을 리턴하여 응답한다.

 

- 여기는 json문서이므로 반드시 <%-- --%> 주석을 써야한다! 아니면 오류 발생..

 

 

- 우리가 만든 파일에서는 문자열을 출력했을 뿐인데(true/false),

 서버에서는 object를 리턴해주는 것을 볼 수 있다.

 

- 서버에서 응답한 내용을 가지고 javascript에서 data.isExist 형태로 활용할 수 있다.(object로 바뀌어서 들어오므로)

 

 

- const inputId 위에서의 this와 then 안에서의 this는 다르다.

 

- this를 아래에서도 사용할 수 있도록 전역변수에 담아두기!

- 상수화시켜 두고 반복해서 쓰는 것이다.

- then안에서의 this는 document.querySelector("#id")를 가리키지 못하기때문에!

 

 

- 존재하는 id, 존재하지 않는 id를 넣었을때 즉각적으로 응답이 나타난다.

 


 

- 비밀번호 확인 기능 구현

- 두개의 폼 안에 비밀번호를 똑같이 입력하지 않으면 invalid feedback이 뜨도록 한다.

- 각각 아래, 위의 input 요소와 비교해서 둘이 같으면 ok

- 두개의 폼 모두에 eventListener를 등록해야 한다. 어느쪽에 입력하더라도 비교 작업을 하는 것!

 

- javascript 추가

<script>	
		//pwd를
		document.querySelector("#pwd").addEventListener("input", function(){
			//먼저 두개의 클래스를 제거하고
			document.querySelector("#pwd").classList.remove("is-valid");
			document.querySelector("#pwd").classList.remove("is-invalid");
			//입력한 두개의 비밀번호를 읽어와서
			const pwd=document.querySelector("#pwd").value;
			const pwd2=document.querySelector("#pwd2").value;
			
			//만일 비밀번호 입력란과 확인란이 다르다면
			if(pwd != pwd2){
				document.querySelector("#pwd").classList.add("is-invalid");
			}else{
				document.querySelector("#pwd").classList.add("is-valid");
			}			
		});
		document.querySelector("#pwd2").addEventListener("input", function(){
			//먼저 두개의 클래스를 제거하고
			document.querySelector("#pwd").classList.remove("is-valid");
			document.querySelector("#pwd").classList.remove("is-invalid");
			//입력한 두개의 비밀번호를 읽어와서
			const pwd=document.querySelector("#pwd").value;
			const pwd2=document.querySelector("#pwd2").value;
			
			//만일 비밀번호 입력란과 확인란이 다르다면
			if(pwd != pwd2){
				document.querySelector("#pwd").classList.add("is-invalid");
			}else{
				document.querySelector("#pwd").classList.add("is-valid");
			}			
		})	
  </script>

 

- 두개의 폼 안에서 해야할 작업이 같다! 양쪽의 값을 읽어와서 비교하는것

 

- isvalid, isinvalid 클래스를 remove로 한번 제거했다가 입력값에 따라 분기하여 다시 생성한다.

 

- 같은 값을 입력하지 않으면 invalid feedback 발생!

 

 

- 그런데 두 버튼에 적용할 코드가 같다면, 한 함수에 넣어놓고 그 함수를 여러번 활용하는 것이 더 편하다.

- checkPwd 함수를 만들어두고, 아래에서는 호출만 한다.

 

 


 

- 이메일 검증 : @라는 문자열을 입력했는지 검증

 

- 이메일 검증에는 정규 표현식을 사용한다. 

 

- test라는 함수를 사용. 

  만족하는지/만족하지 못하는지를 검증하여 boolean 타입으로 리턴된다.

 

- 구글에서 'javascript 이메일 정규 표현식' 을 검색해보면 다양하게 나온다.

 

- 정규 표현식은 / / 으로 감싸서 만들수있다.

 

- @ 가 들어가지 않으면 invalid로 판단한다.

 


 

- 이 폼이 전부 제출되려면 모든 버튼이 초록색으로 표시되어야 한다.

(회원가입 버튼의 동작 조건 설정)

 

id="signupForm"

- 폼에 아이디 부여하기

 

document.querySelector("#signupForm").addEventListener("submit", function(event){
	event.preventDefault();
})

- 위와 같은 식을 만들어놓고, 조건부로 preventDefault() 가 호출되도록 작성하면 된다.

- 하나라도 invalid이면 preventDefault()가 호출되도록 한다.

 

 

- 이 페이지 안에서 모든 함수들의 if, else절들이 실행되었는지 확인해야 한다.

 

- 어느 하나라도 입력을 잘못했으면 가입 버튼이 동작하지 않도록!

→ 모두 다 초록색으로 처리되어야 가입버튼을 클릭(submit으로 제출)할 수 있다.

 

- 어느 함수에서나 접근할 수 있는 글로벌 영역에 변수를 만들어둔다.

 

- 글로벌 영역에 초기값을 false로 만들어두고, 각각의 if/else절 안에서 해당 변수의 값이 바뀌도록 한다.

 

document.querySelector("#signupForm").addEventListener("submit", function(event){
    const isFormValid= isIdValid && isPwdValid && isEmailValid;  
    if(!isFormValid){
        event.preventDefault();
    }
});

- && 조건을 사용해서 어느 하나라도 유효하지 않으면 폼 전송을 막는다.

 

 

- 서버에서 하는 일은 json을 응답하는 일뿐이고, 나머지는 전부 웹브라우저를 프로그래밍하고 있다.

- javascript를 사용하면 웹브라우저의 동작을 프로그래밍할 수 있다.