팀프로젝트/JAVA

JAVA 팀플) 2024012_DTO/Filter/Redirect/스피너/이메일 아이디 제약/질문 별 제약/메일 시스템

일일일코_장민기 2024. 3. 13. 13:23
728x90

 

이 날은 정말 여러가지 작업이 진행되었다.
아무래도 주말 분량이 합쳐졌다보니...


우선 1. 한 명 정보를 받아오는 경우, List<DTO>에서 DTO타입으로 정보를 받아 오도록 변경

List로 통째로 받아오던 것을 member 1명 단위로 받아오도록 바꾸었다.

 

 

2. 한글 Filter 처리
MyEncodingFilter
package com.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyEncodingFilter implements Filter{

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		//System.out.println("doFilter");			//web.xml등록 필수
		request.setCharacterEncoding("utf-8");
		chain.doFilter(request, response);
		
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}

}

 

Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>Culture</display-name>
  
  <welcome-file-list>
    <welcome-file>login_main.jsp</welcome-file>
  </welcome-file-list>
  
  <filter>
  <filter-name>MyEncodingFilter</filter-name>
  <filter-class>com.filter.MyEncodingFilter</filter-class>
  </filter>
  <filter-mapping>
  <filter-name>MyEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
  </filter-mapping>
  
</web-app>

 

 

3. Redirect 처리가 유효한 경우, Redirect로 변경

redirect 처리를 해야 하는 경우에는 redirect로 처리하도록 수정했다.

 

 

 

4. 회원가입 페이지 스피너 추가
.loadingSpinner {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            border: 4px solid rgba(0, 0, 0, 0.1);
            border-left: 4px solid #3498db;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
        }

@keyframes spin {
    0% {
        transform: translate(-50%, -50%) rotate(0deg);
    }

    100% {
        transform: translate(-50%, -50%) rotate(360deg);
    }
}

 

당시 왜인지 모르게 ajax 출력과 회원가입 처리가 매우 늦었다.

이때 당시의 내 능력으로는 이벤트 처리를 빠르게 하지 못했기 때문에

스피너를 출력시켜 이벤트 처리 중임을 알리고자 했다.

그런데 부트스트랩을 알기 전이기 떄문에 이걸 css로 만들었다;;;

 

 //핸드폰 번호를 모두 입력했을 때 발동
		    if (userPhoneNum1 && userPhoneNum2 && userPhoneNum3) {
		        $.ajax({
		            type: "POST",
		            url: "<%=request.getContextPath()%>/Ajax_check_PhoneNum_duplicate_for_register", 
		            
		            beforeSend: function () {
	                    // AJAX 요청 전에 로딩 표시 보여주기
	                	$("#loadingSpinner_for_PhoneNum").show();
	                	// 가입 버튼 비활성화
	                	$("#register_button").prop("disabled", true);
	                	$("#userIdButton").prop("disabled", true);
	                },
		            
		            data: {
		            	userPhoneNum1: userPhoneNum1,
		            	userPhoneNum2: userPhoneNum2,
		            	userPhoneNum3: userPhoneNum3
		            },
		            success: function(response) {
		            	//핸드폰 번호 전체가 DB에 저장된 핸드폰 번호와 일치하는 데이터가 있을 경우, ajax 출력
		                if (response === "duplicate") {
		                	errorSpan.text("이미 사용 중인 핸드폰 번호입니다.");
		                } else {
		                	errorSpan.text("");
		                }
		            },
		            error: function(error) {
		                console.error("핸드폰 번호 중복 검사 에러:", error);
		            },
		            
		            complete: function () {
	                    // AJAX 요청 완료 후에 로딩 표시 숨기기
	                	$("#loadingSpinner_for_PhoneNum").hide();
	                	// 가입 버튼 활성화
		               	$("#register_button").prop("disabled", false);
		               	$("#userIdButton").prop("disabled", false);
	                }
		        });
		    } else {
		    	errorSpan.text("");
			}

스피너 출력을 위한 ajax 코드 수정

 

 

ajax가 출력 중임을 표시해주고 있다.

 

 

// 이메일 아이디에 영어와 숫자만 입력되도록 제한
	    $("#userEmailId").on("input", function() {
	        var userEmailId = $(this).val();
	        var errorSpan = $("#confirmUserEmailIdError");

	        var pattern = /^[a-zA-Z0-9]+$/;		// 영어와 숫자만 허용

	        if (!pattern.test(userEmailId)) {	// 영어나 숫자 외 다른 것이 입력될 경우
	            errorSpan.text("영어와 숫자만 입력 가능합니다.");
	        } else {
	            errorSpan.text("");
	        }
	    });

 

ajax가 출력되는 중

 

ajax 출력 중일 때는 경고창이 이벤트 진행을 저지한다.

 

 

 

 

6. 전체 비밀번호 찾기 질문 별 제약 설정
현재 전체 비밀번호 찾기의 질문은 총 3가지이다.
1. 닉네임 / 2. 핸드폰 번호 / 3. 이메일
이때 핸드폰 번호는 11자리의 숫자만 입력할 수 있도록 제약
이메일은 반드시 @가 들어갈 수 있도록 제약
이런 제약이 질문에 맞게 부여되어야 했다.

 

$("#confirmUserInfo").change(function () {
			var userInfo = $(this).val();
			var userAnswerInput = $("#userAnswer");
																		//디버그 코드**********			
																		console.log(userInfo)
																		//디버그 코드**********			
			userAnswerInput.val("");
			userAnswerInput.focus();

			// 질문이 핸드폰 번호에 대한 것일 경우, 숫자 11자리로 제한, 숫자만 허용
			if (userInfo == "userPhoneNum") {
				
				// 입력값이 숫자가 아닌 경우 경고창을 띄움
				userAnswerInput.on("input", function () {
					if (!/^\d*$/.test($(this).val())) {
						alert("숫자만 입력 가능합니다.");
						$(this).val("");
						$(this).focus();
					}
				});
				
				userAnswerInput.attr({
					"maxlength": "11",
					"pattern": "\\d*"
				});
			 } else {
                 // userInfo가 userPhoneNum이 아닌 경우에는 이벤트 제거
                 userAnswerInput.off("input");
                 userAnswerInput.removeAttr("maxlength pattern");
             }
		});
	});

 

이벤트를 선택했을 때, 어떤 이벤트인지에 따라 제약을 부과하고
다른 이벤트로 바꾸면 부과한 제약을 제거하는 방식이다.

 

 

7. 전체 비밀번호 찾기 통과하면 비밀번호를 메일로 보내는 메일 시스템 구축

 

메일 시스템을 위한 라이브러리 추가

 

 

Send_Mail
package com.mail;

import javax.mail.PasswordAuthentication;

public class Send_Email extends javax.mail.Authenticator {
	public PasswordAuthentication getPasswordAuthentication() {
		
		System.out.println("PasswordAuthentication");
	
		return new PasswordAuthentication("", "");
//		return new PasswordAuthentication("아이디", "2차_인증_패스워드");
	
	}
}
Send_EmailServlet
package com.mail;

import java.io.IOException;
import java.util.Properties;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.dto.memberDTO;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

@WebServlet("/Send_EmailServlet")
public class Send_EmailServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		memberDTO dto = (memberDTO)request.getAttribute("foundUserInfo") ;
		
		String userEmail = dto.getUserEmailId()+"@"+dto.getUserEmailDomain();
		String userName = dto.getUserName();
		String userid = dto.getUserId();
		String userPW = dto.getUserPw();
																							//디버그 코드********************
																							System.out.println(userEmail);
																							//디버그 코드********************
		// 보내는 사람,제목, 내용, 받는 사람 설정
		String host = "smtp.naver.com";
		String subject = "AcornTP 비밀번호 전체 발송 메일";		 	// 메일제목
		String from = "@naver.com";				 	// 보내는 메일주소(2차 인증한 아이디 메일)
//		String from = "아이디";				 					// 보내는 메일주소(2차 인증한 아이디 메일)
		String fromName = "AcornTP 문화 관리자"; 				 	// 보내는 사람이름
		String to = userEmail; 									// 받는 메일주소		
		String content = userName+"님! 회원님의 "
				+ "아이디는 "+ userid
				+ ", 비밀번호는 " + userPW + "입니다. 감사합니다."; 	// 메일 내용

		try {
			// 프로퍼티 값 인스턴스 생성과 기본세션(SMTP 서버 호스트 지정)
			Properties props = new Properties();
			// 네이버 SMTP 사용시

			props.put("mail.transport.protocol", "smtp");
			props.put("mail.smtp.host", host);

			// 2단계 인증**************************************
			props.put("mail.smtp.starttls.enable", "true");
			props.put("mail.smtp.starttls.trust", "*");
			props.put("mail.smtp.ssl.enable", "false");
			props.put("mail.smtp.ssl.protocols", "TLSv1.2");
			// 2단계 인증**************************************

			props.put("mail.smtp.port", "465"); // 보내는 메일 포트 설정(도메인 별로 포트번호가 다름)
			props.put("mail.smtp.user", from);
			props.put("mail.smtp.auth", "true");
			props.put("mail.smtp.debug", "true");
			props.put("mail.smtp.socketFactory.port", "465");
			props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			props.put("mail.smtp.socketFactory.fallback", "false");

			Authenticator auth = new Send_Email();
			Session mailSession = Session.getDefaultInstance(props, auth);

			Message msg = new MimeMessage(mailSession);
			msg.setFrom(new InternetAddress(from, MimeUtility.encodeText(fromName, "UTF-8", "B"))); // 보내는 사람 설정
			InternetAddress[] address = { new InternetAddress(to) };

			msg.setRecipients(Message.RecipientType.TO, address); 	// 받는 사람설정
			msg.setSubject(subject); 								// 제목설정
			msg.setSentDate(new java.util.Date()); 					// 보내는 날짜 설정
			msg.setContent(content, "text/html; charset=EUC-KR"); 	// 내용 설정(MIME 지정-HTML 형식)

			Transport.send(msg); // 메일 보내기

		} catch (MessagingException ex) {
			System.out.println("mail send error : " + ex.getMessage());
			ex.printStackTrace();
		} catch (Exception e) {
			System.out.println("error : " + e.getMessage());
			e.printStackTrace();
		}
		
		RequestDispatcher dis = request.getRequestDispatcher("Find_Info/view_all_PW.jsp");
		dis.forward(request, response);

	}
}

 

기존의 전체 비밀번호를 알려주는 페이지 대신, 메일을 전송했다는 안내 페이지로 바뀌었다.

 

실제 네이버 메일로 잘 전송되었다.

 

 

 

번외
Site_link.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>


<%-- 각 페이지에 입력할 내용(현재 보류)

<%
// 페이지마다 다르게 설정
boolean show_Login_Link = false;
boolean show_Search_IDLink = true;
boolean show_Search_PWLink = true;
boolean show_Register_Link = true;
%>

<jsp:include page="/Include/site_Link.jsp">
    <jsp:param name="show_Login_Link" value="<%= show_Login_Link %>" />
    <jsp:param name="show_Search_IDLink" value="<%= show_Search_IDLink %>" />
    <jsp:param name="show_Search_PWLink" value="<%= show_Search_PWLink %>" />
    <jsp:param name="show_Register_Link" value="<%= show_Register_Link %>" />
</jsp:include> 

--%>


<%
boolean show_Login_Link = Boolean.parseBoolean(request.getParameter("show_Login_Link"));
boolean show_Search_IDLink = Boolean.parseBoolean(request.getParameter("show_Search_IDLink"));
boolean show_Search_PWLink = Boolean.parseBoolean(request.getParameter("show_Search_PWLink"));
boolean show_Register_Link = Boolean.parseBoolean(request.getParameter("show_Register_Link"));
%>

<div id="sitesShortCut">

<% if (show_Login_Link){ %>
<a href="<%=request.getContextPath()%>/LoginForm_Active" class="links">로그인</a>
<% } %>

<% if (show_Search_IDLink){ %>
<a href="<%=request.getContextPath()%>/connect_to_search_ID_by_Name_SSN" class="links">아이디 찾기</a> 
<% } %>

<% if (show_Search_PWLink){ %>
<a href="<%=request.getContextPath()%>/connect_to_search_PW_by_ID_Name_SSN" class="links">비밀번호 찾기</a> 
<% } %>

<% if (show_Register_Link){ %>
<a href="<%=request.getContextPath()%>/connect_to_register_agree_terms" class="links">회원가입</a>
<% } %>

</div>

이 jsp는 여러 페이지에서 반복되는 사이트 링크를 include해보려고 만들었던 페이지다.

그런데 각 페이지 별로 include하는 링크가 다르고, 그 다르게 링크를 출력시키려고 만들어보고자 했더니

기존의 링크 출력보다 훨씬 긴 코드를 필요하다는 것을 알게 되었다.

그래서 폐기하게 되었는데, 이렇게 다시 보니 반갑긴 하다.