Programmer's Progress

도서 정보 제공 웹 서비스 - 로그아웃, 아이디/비밀번호 찾기 구현하기 본문

Web Service/도서 정보 제공 웹 서비스

도서 정보 제공 웹 서비스 - 로그아웃, 아이디/비밀번호 찾기 구현하기

Blanc et Noir 2021. 11. 24. 20:25

기본적인 회원가입과 로그인을 구현했으니, 로그아웃과 아이디/비밀번호 찾기를 구현할 차례다.

기본적인 원리는 아래와 같이 설계되었다.

 

 

 

 

 

 

 

데이터베이스 릴레이션 스키마 다이어그램의 일부

 

우선 스키마 다이어그램에서 빨간색 애트리뷰트는 기본키, 파란색 애트리뷰트는 외래 키를 의미한다.

그렇다면 노란색은 어떤 애트리뷰트를 의미하는 걸까?

바로 UNIQUE 한 값, 즉, 후보 키로써의 역할을 수행함을 의미한다.

 

 

 

 

 

상식적으로 회원가입을 진행할 때 같은 이메일, 전화번호로 회원가입이 가능한 경우는 거의 경험하지 못했을 것이다.

그도 그럴 것이, 하나의 이메일, 전화번호는 모두 한 명의 사람에게 속해있기 때문이다.

 

 

 

 

 

 

각 개인은 회원가입 시 입력한 전화번호와 이메일 주소를 아이디를 찾을 수 있게 된다.

그렇다면 비밀번호는 어떤 식으로 찾을 수 있게 할 것인가?

 

 

 

 

 

 

회원가입 시에 비밀번호 찾기 질문과 그에 대한 답을 설정했던 것을 기억하는가?

 

비밀번호 찾기 질문과 그에 대한 답을 기록해야 한다.

 

 

 

 

 

 

즉, 사용자는 자신이 비밀번호를 찾고자 하는 아이디를 선택하고

그 아이디에 할당된 비밀번호 찾기 질문을 얻은 후 그 질문에 대한 답을 수행함으로써

비밀번호를 찾을 수 있게 구성해야 할 것이다.

 

 

 

 

 

 

 

아이디 찾기 화면, 전화번호 또는 이메일로 찾을 수 있다.

 

 

 

 

비밀번호 찾기 화면

 

 

 

간단하게 아이디와 비밀번호 찾기 화면을 구성한 후에 로직을 작성하였다.

아래의 소스코드는 find.js의 일부이다.

	$(document).on("click","#FIND_BY_PHONE, #FIND_BY_EMAIL, #GET_QUESTION_BUTTON, #FIND_PASSWORD_BUTTON",function(){
		var flag = $(this).attr("id");
		
		var CUSTOMER_ID = $("#CUSTOMER_ID").val();
		var CUSTOMER_PHONE = $("#CUSTOMER_PHONE").val();
		var CUSTOMER_EMAIL = $("#CUSTOMER_EMAIL").val();
		var PASSWORD_HINT_ANSWER = $("#PASSWORD_HINT_ANSWER").val();
		
		$.ajax({
			"url":"/LibraryService/customer/find.do",
			"type":"POST",
			"dataType":"text",
			"data":{
				"flag":flag,
				"CUSTOMER_ID":CUSTOMER_ID,
				"CUSTOMER_PHONE":CUSTOMER_PHONE,
				"CUSTOMER_EMAIL":CUSTOMER_EMAIL,
				"PASSWORD_HINT_ANSWER":PASSWORD_HINT_ANSWER
			},
			"success":function(result){
				if(result=="logon"){
    				alert("이미 로그인 상태입니다.");
    				location.href = "/LibraryService/resource/jsp/main.jsp";
				}else if(flag=="FIND_BY_PHONE"){
					if(result=="null"){
						alert("해당 전화번호로 가입된 아이디가 없습니다.");
					}else{
						alert("해당 전화번호로 가입된 아이디는 "+result+"입니다.");
					}
				}else if(flag=="FIND_BY_EMAIL"){
					if(result=="null"){
						alert("해당 이메일로 가입된 아이디가 없습니다.");
					}else{
						alert("해당 이메일로 가입된 아이디는 "+result+"입니다.");
					}
				}else if(flag=="GET_QUESTION_BUTTON"){
					if(result=="null"){
						alert("해당 아이디 정보가 존재하지 않습니다.");
					}else{
						$("#PASSWORD_QUESTION_LIST_CONTENT").text(result);
					}
				}else{
					if(result=="null"){
						alert("비밀번호를 찾을 아이디를 설정해야합니다.");
					}else if(result == "false"){
						alert("비밀번호 찾기 질문에 대한 답이 틀렸습니다.");
					}else{
						alert(CUSTOMER_ID+"의 비밀번호는 "+result+"입니다.");
					}
				}
			},
			"error":function(){
				
			}
		});
	});

 

우선 아이디를 이메일 또는 전화번호로 찾고자 한다면 먼저 MVC패턴으로 구성되어 있으므로

사용자들에 관한 처리를 담당하는 컨트롤러에게 find.do 요청을 수행한다.

 

 

if(action.equals("/find.do")) {
			PrintWriter out = response.getWriter();
			FindDAO dao = new FindDAO();
			String flag = request.getParameter("flag");
			
			if(flag.equals("FIND_BY_PHONE")) {
				String result = dao.findByPhone(request.getParameter("CUSTOMER_PHONE"));
				out.print(result);
			}else if(flag.equals("FIND_BY_EMAIL")) {
				String result = dao.findByEmail(request.getParameter("CUSTOMER_EMAIL"));
				out.print(result);
			}else if(flag.equals("GET_QUESTION_BUTTON")) {
				String result = dao.getQuestion(request.getParameter("CUSTOMER_ID"));
				out.print(result);
			}else {
				String result = dao.findPassword(request.getParameter("CUSTOMER_ID"),request.getParameter("PASSWORD_HINT_ANSWER"));
				out.print(result);
			}
			out.flush();
			out.close();
		}else if(action.equals("/getPasswordQuestionList.do")) {
			PrintWriter out = response.getWriter();
			PasswordQuestionListDAO dao = new PasswordQuestionListDAO();
			
			Queue<PasswordQuestionListVO> queue = dao.getPasswordQuestionList();
			JSONArray arr = new JSONArray();
			while(!queue.isEmpty()) {
				PasswordQuestionListVO vo = queue.poll();
				JSONObject json = new JSONObject();
				json.put("PASSWORD_QUESTION_LIST_ID", vo.getPASSWORD_QUESTION_LIST_ID());
				json.put("PASSWORD_QUESTION_LIST_CONTENT", vo.getPASSWORD_QUESTION_LIST_CONTENT());
				arr.add(json);
			}
			out.print(arr);
			out.flush();
			out.close();
		}

 

 

 

그렇게 되면 컨트롤러는 find처리임을 구분하고, 이에 따라 적절한 모델인 FindDAO의 객체를 생성한다.

이 객체를 통해 모델이 실제 데이터베이스에 값을 추가하거나 변경, 질의할 수 있게 된다.

 

 

 

 

 

	public String findByPhone(String CUSTOMER_PHONE) {
		Connection connection;
		PreparedStatement pstmt;
		ResultSet rs;
		String result=null;
		try {
			String query ="";
			query += "SELECT CUSTOMER_ID ";
			query += "FROM CUSTOMER ";
			query += "WHERE CUSTOMER_PHONE = '"+CUSTOMER_PHONE+"'";
			
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(query);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				result = rs.getString("CUSTOMER_ID");
			}
			rs.close();
			pstmt.close();
			connection.close();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	public String findByEmail(String CUSTOMER_EMAIL) {
		Connection connection;
		PreparedStatement pstmt;
		ResultSet rs;
		String result=null;
		try {
			String query ="";
			query += "SELECT CUSTOMER_ID ";
			query += "FROM CUSTOMER ";
			query += "WHERE CUSTOMER_EMAIL = '"+CUSTOMER_EMAIL+"'";
			
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(query);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				result = rs.getString("CUSTOMER_ID");
			}
			rs.close();
			pstmt.close();
			connection.close();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public String getQuestion(String CUSTOMER_ID) {
		Connection connection;
		PreparedStatement pstmt;
		ResultSet rs;
		String result=null;
		try {
			String query ="";
			query += "SELECT Q.PASSWORD_QUESTION_LIST_CONTENT QUESTION ";
			query += "FROM PASSWORD_HINT H, PASSWORD_QUESTION_LIST Q ";
			query += "WHERE H.PASSWORD_QUESTION_LIST_ID = Q.PASSWORD_QUESTION_LIST_ID AND H.CUSTOMER_ID = '"+CUSTOMER_ID+"'";
			
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(query);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				result = rs.getString("QUESTION");
			}
			rs.close();
			pstmt.close();
			connection.close();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public String findPassword(String CUSTOMER_ID, String PASSWORD_HINT_ANSWER) {
		Connection connection;
		PreparedStatement pstmt;
		ResultSet rs;
		String result=null;
		try {
			String query ="";
			query += "SELECT H.PASSWORD_HINT_ANSWER, C.CUSTOMER_PW ";
			query += "FROM PASSWORD_HINT H, CUSTOMER C ";
			query += "WHERE H.CUSTOMER_ID = C.CUSTOMER_ID AND H.CUSTOMER_ID = '"+CUSTOMER_ID+"'";
			
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(query);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				String answer1 = rs.getString("PASSWORD_HINT_ANSWER").replaceAll(" ", "");
				String answer2 = PASSWORD_HINT_ANSWER.replaceAll(" ","");
				if(answer1.equalsIgnoreCase(answer2)) {
					result = rs.getString("CUSTOMER_PW");
				}else {
					result = "false";
				}
				rs.close();
				pstmt.close();
				connection.close();
				return result;
			}else {
				rs.close();
				pstmt.close();
				connection.close();
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

FindDAO.java의 일부 소스코드

 

 

 

 

모델은 실제로 질의를 수행하고 이에 따라 적절한 결과를 리턴한다. 

컨트롤러가 결과를 전달받은 후 그에 따라 사용자 VIEW에 alert( )를 통해서 결과를 전달한다.

비밀번호를 찾을 때는 먼저 찾을 아이디를 설정하고 그에 대한 질문을 얻어야만 수행할 수 있는 것에 유의하자.

 

 

 

 

 

 

 

 

 

 

 

사실 처음에는 MVC패턴이 아니라 각 요청마다 특정 서블릿들을 일일이 분리하여 구현했었다.

그러다 보니 비밀번호를 찾을 때, 아이디를 찾을 때, 로그인할 때, 로그아웃할 때 등등

서블릿의 종류가 너무 난잡하게 만들어지는 문제가 생겼다.

이에 따라 당연히 유지보수도 힘들어질 수밖에 없었다.

실제로 서블릿들의 이름이 복잡해지고, 길어지며, 종류가 많아 헷갈릴 때가 있었다.

 

 

 

 

앞으로 서비스의 기능을 추가하다 보면 서블릿과, DAO, VO클래스들이 기하급수적으로 늘어나

관리가 어려워질 것으로 판단하여 미리 패키지로 분리하고, MVC패턴을 적용했다.

즉 서비스를 MVC패턴으로 변경함으로써 유지보수성을 높였다.

 

 

 

 

 

MVC패턴으로 변경한 후의 모습

 

MVC패턴으로 변경전의 소스코드들

MVC패턴으로 변경하기 전에 서블릿이 여러 개 정의되어있는 것에 유의하자.

간결함의 차이가 너무나 잘 느껴진다...

 

 

 

 

 

 

박재현 사용자가 아이디, 비밀번호를 찾는 모습

 

 

 

 

 

 

로그아웃은 상당히 심플하다.

else if(action.equals("/logout.do")) {
	request.getSession().invalidate();
	response.sendRedirect("/LibraryService/resource/jsp/main.jsp");
}

다음과 같이 컨트롤러에서 세션을 종료하면 된다.

Comments