YJ의 새벽

JDBC 활용 연습용4. ( 롬복 + 회원가입(이메일인증) ) 본문

SelfStudy/JDBC

JDBC 활용 연습용4. ( 롬복 + 회원가입(이메일인증) )

YJDawn 2023. 3. 28. 15:43

 

 

 

jbufdyvxikcbxutr

 

 

lombok.jar  이클립스 경로에 넣기

 

관리자권한 cmd 실행후. 이클립스 경로 --> 롬복 실행.

 

이클립스 경로 찾아서 . 이클립스 Select .

 

 

 

 

  • Lombok  라이브러리

-- VO (Value Object)  또는 DTD (Data Transfer Object)

    에 작성되는 공통코드 ( getter/ setter/ 생성자 ) 를 자동 추가해주는 라이브러리

@Getter         // getter 자동추가
@Setter          // setter 자동추가
@ToString        // toString 자동추가
@NoArgsConstructor          // 기본생성자
@AllArgsConstructor        // 모든필드 초기화하는 매개변수 생성자

@Getter   // getter 자동추가
@Setter   // setter 자동추가
@ToString // toString 자동추가
@NoArgsConstructor   // 기본생성자
@AllArgsConstructor  // 모든필드 초기화하는 매개변수 생성자
public class Member {
	private int memberNo;
	private String memberEmail;
	private String memberPw;
	private String memberNickname;
	private String memberTel;
	private String memberAddress;
	private String profileImage;
	private String enrollDate;
	private String secessionFlag;
}

간단하게 작성할수있다 . 

 

 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

-- WEB-INF 폴터는 외부로 부터 직접적으로 요청할수 없는 폴더.

   왜 ?   중요한코드 (설정관련,자바 .. ) 가 위치하는 폴더로써

             외부로부터 접근을 차단하기 위해서.

  -->  대신 Servlet을 이용해서 내부접근 (forward) 가능

 

 

 

-- index.jsp      

                <%-- 회원가입 / ID/PW 찾기 --%>
                <article id="signUp-find-area">
                    <a href="${contextPath}/member/signUp">회원가입</a> 
                    <span>|</span> 
                    <a href="#">ID/PW찾기</a>
                </article>

 

-- SignUpServlet. class    ( /member/signUp )   --> jsp 로 forward .

@WebServlet("/member/signUp")
public class SignUpServlet extends HttpServlet{
	
	// GET 방식 요청 시 ( a 태그는 무조건 Get) 
	// JSP 로 바로 응답할수 있도록 요청 위임.
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		String path = "/WEB-INF/views/member/signUp.jsp";
		req.getRequestDispatcher(path).forward(req, resp);
	}
}

 

--signUp.jsp  추가 .  ( 회원가입 폼 )

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>KH 커뮤니티</title>
 
     <link rel="stylesheet" href="../resources/css/main-style.css">
     
     <link rel="stylesheet" href="../resources/css/signUp-style.css">
 
     <script src="https://kit.fontawesome.com/a2e8ca0ae3.js" crossorigin="anonymous"></script>
 </head>
 <body>
     <main>
        <!-- hedaer include -->
        <jsp:include page="/WEB-INF/views/common/header.jsp" />


         <!-- 회원가입  -->
         <section class="signUp-content">
 
            <!-- 회원가입 화면 전환 주소(GET)와 같은 주소로 
                실제 회원가입을 요청(POST)
                -> 요청 주소가 같아도 데이터 전달 방식이 다르면 중복 허용
            -->

            <!-- 
                절대경로 : /community/member/signUp
                상대경로 : signUp
             -->

            <form action="signUp" method="POST" name="signUp-form" onsubmit="return signUpValidate()">
 
                 <label for="memberEmail">
                     <span class="required">*</span> 아이디(이메일)
                 </label>
                 
                 <div class="signUp-input-area">
                     <input type="text" id="memberEmail" name="memberEmail"
                             placeholder="아이디(이메일)" maxlength="30"
                             autocomplete="off" required>
 
                     <!-- autocomplete="off" : 자동완성 미사용 -->
                     <!-- required : 필수 작성 input 태그 -->
                     
                     <!-- 자바스크립로 코드 추가 예정 -->
                     <button type="button" id="sendBtn">인증번호 받기</button>
                 </div>
 
                 <span class="signUp-message" id="emailMessage">메일을 받을 수 있는 이메일을 입력해주세요.</span>
 
 
 
                 <label for="emailCheck">
                     <span class="required">*</span> 인증번호
                 </label>
                 
                 <div class="signUp-input-area">
                     <!-- cNumber -->
                     <input type="text" id="cNumber"  
                             placeholder="인증번호 입력" maxlength="6"
                             autocomplete="off">
 
                     <button type="button" id="cBtn">인증하기</button>
                 </div>
 
                 <!-- 5:00 타이머 / 인증되었습니다(녹색) / 인증 시간이 만료되었습니다.(빨간색) -->
                 <span class="signUp-message" id="cMessage" ></span>
 
 
 
 
                 <label for="memberPw">
                     <span class="required">*</span> 비밀번호
                 </label>
                 
                 <div class="signUp-input-area">
                     <input type="text" id="memberPw" name="memberPw"
                             placeholder="비밀번호" maxlength="30">
                 </div>
 
                 <div class="signUp-input-area">
                     <input type="text" id="memberPwConfirm"
                             placeholder="비밀번호 확인" maxlength="30">
                 </div>
 
                 <span class="signUp-message" id="pwMessage">영어, 숫자, 특수문자(!,@,#,-,_) 6~30글자 사이로 작성해주세요.</span>
 
 
 
 
                 <label for="memberNickname">
                     <span class="required">*</span> 닉네임
                 </label>
                 
                 <div class="signUp-input-area">
                     <input type="text" id="memberNickname" name="memberNickname"
                             placeholder="닉네임" maxlength="10">
                 </div>
 
                 <span class="signUp-message" id="nicknameMessage">영어/숫자/한글 2~10글자 사이로 작성해주세요.</span>
 
 
 
                 <label for="memberTel">
                     <span class="required">*</span> 전화번호
                 </label>
                 
                 <div class="signUp-input-area">
                     <input type="text" id="memberTel" name="memberTel"
                             placeholder="(- 없이 숫자만 입력)" maxlength="11">
                 </div>
 
                 <span class="signUp-message" id="telMessage">전화번호를 입력해주세요.(- 제외)</span>
              
              
              
                 <label for="memberAddress">
                     주소
                 </label>
                 
                 <div class="signUp-input-area">
                     <input type="text" id="memberAddress" name="memberAddress"
                             placeholder="우편번호" maxlength="6">
                     
                     <button type="button">검색</button>
                 </div>
 
                 <div class="signUp-input-area">
                     <input type="text" name="memberAddress" placeholder="도로명주소">
                 </div>
 
                 <div class="signUp-input-area">
                     <input type="text" name="memberAddress" placeholder="상세주소">
                 </div>
 
                 <button type="submit" id="signUp-btn">가입하기</button>
 
             </form>
             
         </section>
 
 
     </main>
 
    <!-- footer include -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />


    <!-- jQuery 라이브러리 추가(CDN) -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

    <!-- signUp.js 연결 -->
    <script src="${contextPath}/resources/js/member/signUp.js"></script>

 </body>
 </html>

 

 

 

--- signUp.js   만들기 .  ( 회원가입 폼 관리 )  (   ajax 이용  emailDupCheck,  sendEmail  )  url 생성

   ** emailDupCheck   :  EmailDupCheckServlet.class  ( /member/emailDupCheck )

                                        에서  int result   값을 받아옴 ,   

  **  sendEmail SendEmailServlet.class  (  / member / sendEmail )

                                에서 int result 값을 받아옴.

// 유효성 검사 여부를 기록할 객체 생성
const checkObj = {
    "memberEmail" : false,
    "memberPw" : false,
    "memberPwConfirm" : false,
    "memberNickname" : false,
    "memberTel" : false,
    "sendEmail" : false,

};

// 이메일 유효성 검사
const memberEmail = document.getElementById("memberEmail");
const emailMessage = document.getElementById("emailMessage");

memberEmail.addEventListener("input",function(){  // 이메일박스값이 작성될때마다 검사

    //입력이 되지 않은경우
    if ( memberEmail.value.length == 0 ){
        emailMessage.innerText = "메일을 받을 수 있는 이메일을 입력하세요!!";
        emailMessage.classList.remove("confirm","error");

        checkObj.memberEmail = false;             // 기록할객체에 유효X 기록X  
        return;
    }
    //입력이 된 경우
    const regExp = /^[\w\-\_]{4,}@[\w\-\_]+(\.\w+){1,3}$/;
    if ( regExp.test(memberEmail.value) ){   // 정규표현식 유효한경유
        // 이메일 중복검사 (ajax) 진행****************
        
        $.ajax( {
            url : "emailDupCheck",  // 필수속성 url , 
                                    // 현재주소 : /community/member/signUp
                                    // 상대경로 : /community/member/emailDupCheck
            data : {"memberEmail" : memberEmail.value}, 
                                    // data속성 : 비동기통신시 서버로 전달할값을 작성(JS객체 형식)
                                    //  --> 비동기통신시 input에 입력된 값을
                                    // "memberEmail" 이라는 key값(파라미터) 으로 전달
            success : function(result){
                                    // 비동기통신(.ajax)가 오류없이 요청/응답 성공한경우
                                    // 매개변수 result : Servlet에서 출력된 result값이 담겨있음
                console.log(result);
                if (result == 1){    // 중복 O
                    emailMessage.innerText = "이미 사용중인 이메일입니다.";
                    emailMessage.classList.add("error");
                    emailMessage.classList.remove("confirm");
                    checkObj.memberEmail = false;
                } else {     // 중복 X
                    emailMessage.innerText = "사용가능한 이메일입니다.";
                    emailMessage.classList.add("confirm");
                    emailMessage.classList.remove("error");
                    checkObj.memberEmail = true;          // 사용가능함. true 세팅
                }             
            },                        
            error : function(){
                                    // 비동기통신(.ajax)중 오류가 발생한 경우
                console.log("에러발생");
            }

        });
    }else{            // 정규표현식 유효하지 않은경우
        emailMessage.innerText = "이메일 형식이 유효하지 않습니다!!";
        emailMessage.classList.add("error");
        emailMessage.classList.remove("confirm");

        checkObj.memberEmail = false;           // 기록할객체에 유효X 기록X  
    }

});


// 정규표현식 으로 이메일형식검사


// 인증번호 보내기
const sendBtn = document.getElementById("sendBtn");

sendBtn.addEventListener("click",function(){

    if( checkObj.memberEmail ){  // 유효한 이메일이 작성되어 있을경우에만 메일보내기
        $.ajax({ 
            url : "sendEmail",
            data : {"inputEmail" : memberEmail.value},
            success : function(result){
                console.log("이메일 발송 성공");
                console.log(result);

                // 인증버튼이 클릭되어 정상적으로 메일이 보내졌음을
                checkObj.sendEmail = true;
            },
            error : function(){
                console.log("이메일 발송 실패");
            }
         })
    }
});

 

 

 

-- member-sql.xml   ( emailDupCheck ,  updateCertification  , insertCertification) sql문 추가

                                            

 

	<entry key="emailDupCheck">
	SELECT COUNT(*) FROM MEMBER
	WHERE MEMBER_EMAIL= ?
	AND SECESSION_FL = 'N'
	</entry>
	
	<entry key="updateCertification">
	UPDATE CERTIFICATION SET
	C_NUMBER = ?,
	ISSUE_DT = SYSDATE
	WHERE EMAIL = ? 
	</entry>
	
	<entry key="insertCertification">
	INSERT INTO CERTIFICATION VALUES(?,?,SYSDATE)
	</entry>

 

 

-- EmailDupCheckServlet.class  ( /member/emailDupCheck )

 

@WebServlet("/member/emailDupCheck")
public class EmailDupCheckServlet extends HttpServlet{

	// 이메일 중복검사 (비동기통신) 
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		// 비동기 통신으로 전달된 파라미터 ( data 속성의 key값 ) 얻어오기
		String memberEmail = req.getParameter("memberEmail");
		
		try {
			// 이메일 중복 검사 서비스 호출 후 결과받기
			MemberService service = new MemberService();
			
			int result = service.emailDupCheck(memberEmail);   // 받아온 값 0 / 1 
			
			// 보통 동기식코드 작성시 
			// forward 또는 redirect 를 이용하여 새로운페이지가 보이게 동작함.
			
			// *** 비동기통신시 응답은 화면이 아닌 데이터 ( String, JSON , XML , int ...)
			//   --> 응답용 스트림을 이용해서 단순 데이터 전달만 하면 된다.
			
			resp.getWriter().print(result);
			//응답용 스트림을 이용해 result 출력
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

 

 

 

-- MemberService.class  이메일중복검사서비스 추가  ,  인증번호 DB추가서비스 . 

	/** 이메일 중복검사 서비스
	 * @param memberEmail
	 * @return
	 * @throws Exception 
	 */
	public int emailDupCheck(String memberEmail) throws Exception {
		
		Connection conn = getConnection();
		
		int result= dao.emailDupCheck(conn,memberEmail);
		
		close(conn);
		
		return result;
	}
    
    	/** 인증번호 DB 추가 서비스
	 * @param inputEmail
	 * @param cNumber
	 * @return
	 */
	public int insertCertification(String inputEmail, String cNumber) throws Exception {
		
		Connection conn = getConnection();
		
		//1 ) 입력한 이메일과 일치하는 값이 있으면 수정(UPDATE)
		int result = dao.updateCertification(conn,inputEmail,cNumber);
		//2 ) 일치하는 이메일이 없는경우--> 처음인증번호발급-> 삽입(INSERT)
		if ( result ==0 ) {
		    result = dao.insertCertification(conn,inputEmail,cNumber);
		}
		// 트랜잭션 제어 !! (DML구문)
		if(result > 0 ) commit(conn);
		else            rollback(conn);
		
		close(conn);
		
		return result;
	}

 

-- MemberDAO.class   이메일 중복검사 DAO 실행.  ,  인증번호,발급일 수정DAO ,   인증번호 생성DAO

	/**  이메일 중복검사 DAO
	 * @param conn
	 * @param memberEmail
	 * @return
	 */
	public int emailDupCheck(Connection conn, String memberEmail) throws Exception{
		// 결과 저장용 변수선언
		int result = 0;
		
		try {
			//sql 얻어오기
			String sql = prop.getProperty("emailDupCheck");
			
			pstmt = conn.prepareStatement(sql);  //pstmt 생성
			pstmt.setString(1, memberEmail);   // 위치홀더 값세팅
			rs = pstmt.executeQuery();       // sql 실행후 결과받기
			
			if ( rs.next()) {
				result = rs.getInt(1);  // count(*) 값.
			}
		}finally {
			close(rs);
			close(pstmt);
		}
		return result;
	}
    
    	/**   인증번호, 발급일 수정 DAO
	 * @param conn
	 * @param inputEmail
	 * @param cNumber
	 * @return
	 */
	public int updateCertification(Connection conn, String inputEmail, String cNumber) throws Exception{
		int result = 0;
		try {
			String sql = prop.getProperty("updateCertification");
			
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, cNumber);
			pstmt.setString(2, inputEmail);
			
			result = pstmt.executeUpdate();
			
		}finally {
			close(pstmt);
		}
		return result;
	}

	/**   인증번호 생성 DAO
	 * @param conn
	 * @param inputEmail
	 * @param cNumber
	 * @return
	 * @throws Exception
	 */
	public int insertCertification(Connection conn, String inputEmail, String cNumber) throws Exception{
		int result = 0;
		try {
			String sql = prop.getProperty("insertCertification");
			
			pstmt=conn.prepareStatement(sql);
			
			pstmt.setString(1, inputEmail);
			pstmt.setString(2, cNumber);
			
			result = pstmt.executeUpdate();
					
		}finally {
			close(pstmt);
		}
		return result;
	}

 

 

 

--SendEmailServlet.class  (  / member / sendEmail )


@WebServlet("/member/sendEmail")
public class SendEmailServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		String inputEmail = req.getParameter("inputEmail"); // 입력받은 이메일

		String subject = "[Commnity 프로젝트] 회원 가입 이메일 인증번호"; // 제목

		String fromEmail = "내이메일@gmail.com"; // 보내는 사람으로 표시될 이메일 (이메일 따라서 안될수도 있음)
		String fromUsername = "관리자"; // 보내는 사람 이름
		String toEmail = inputEmail; // 받는사람, 콤마(,)로 여러개 나열 가능

		// 구글 이메일을 이용한 메일 보내기 (SMTP)
		// 1. 구글 계정 생성(기존 이메일 사용해도됨)
		// 2. 계정 -> 보안 설정 진행
		// 1) 2단계 인증 추가
		// 2) 앱 비밀번호 생성(메일, 서버컴퓨터 OS) -> 저장해두기 ( queapyfpwuqdpisq )

		final String smtpEmail = "내이메일@gmail.com"; // 이메일
		final String password = "발급받은키"; // 발급 받은 비밀번호
		// 메일 옵션 설정
		Properties props = new Properties();

		// 중요
		props.put("mail.transport.protocol", "smtp");
		props.put("mail.smtp.host", "smtp.gmail.com");
		props.put("mail.smtp.port", "587"); // 465, 587
		props.put("mail.smtp.auth", "true");
		// 추가 옵션
		props.put("mail.smtp.quitwait", "false");
		props.put("mail.smtp.socketFactory.port", "587");
		props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.socketFactory.fallback", "true");
		props.put("mail.smtp.starttls.enable", "true");

		try {
			// 메일 세션 생성
			Session session = Session.getDefaultInstance(props);
			// 메일 송/수신 옵션 설정(1명 보내기)
			Message message = new MimeMessage(session);

			message.setFrom(new InternetAddress(fromEmail, fromUsername)); // 송신자(보내는 사람) 지정

			message.addRecipient(RecipientType.TO, new InternetAddress(toEmail)); // 수신자(받는사람) 지정

			message.setSubject(subject); // 이메일 제목 지정

			// 메일 콘텐츠 설정
			Multipart mParts = new MimeMultipart();
			MimeBodyPart mTextPart = new MimeBodyPart();

			// 인증번호 6자리 생성코드(영어 대/소문 + 숫자)
			String cNumber = "";
			for (int i = 0; i < 6; i++) {

				int sel1 = (int) (Math.random() * 3); // 0:숫자 / 1,2:영어

				if (sel1 == 0) {

					int num = (int) (Math.random() * 10); // 0~9
					cNumber += num;

				} else {

					char ch = (char) (Math.random() * 26 + 65); // A~Z

					int sel2 = (int) (Math.random() * 2); // 0:소문자 / 1:대문자

					if (sel2 == 0) {
						ch = (char) (ch + ('a' - 'A')); // 대문자로 변경
					}

					cNumber += ch;
				}

			}

			// 메일에 출력할 텍스트
			StringBuffer sb = new StringBuffer(); // 가변성 문자열 저장 객체
			sb.append("<h3>[Community 프로젝트] 회원 가입 인증 번호입니다.</h3>\n");
			sb.append("<h3>인증 번호 : <span style='color:red'>" + cNumber + "</span></h3>\n");

			// sb.append("<img
			// src='https://cdn.wikifarmer.com/wp-content/uploads/2022/02/%ED%94%8C%EB%9F%BC%EB%B0%94%EA%B3%A0.jpg'>");

			String mailContent = sb.toString(); // 문자열로 반환

			// 메일 콘텐츠 - 내용 , 메일인코딩, "html" 추가 시 HTML 태그가 해석됨
			mTextPart.setText(mailContent, "UTF-8", "html");
			mParts.addBodyPart(mTextPart);

			// 메일 콘텐츠 지정
			message.setContent(mParts);

			// MIME 타입 설정 (이메일 내용이 깨질 때 사용)
			/*
			 * MailcapCommandMap MailcapCmdMap = (MailcapCommandMap)
			 * CommandMap.getDefaultCommandMap(); MailcapCmdMap.
			 * addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html"
			 * ); MailcapCmdMap.
			 * addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"
			 * ); MailcapCmdMap.
			 * addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain"
			 * ); MailcapCmdMap.
			 * addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed"
			 * ); MailcapCmdMap.
			 * addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822"
			 * ); CommandMap.setDefaultCommandMap(MailcapCmdMap);
			 */

			// 메일 발송
			Transport t = session.getTransport("smtp");
			t.connect(smtpEmail, password);
			t.sendMessage(message, message.getAllRecipients());
			t.close();

			// sysdate
			// 인증번호를 받은 이메일, 인증번호, 인증번호 발급 시간 -> DB 삽입
			int result = new MemberService().insertCertification(inputEmail, cNumber);
			resp.getWriter().print(result);

		} catch (Exception e) {
			e.printStackTrace();

			// ajax error 속성 활용을 위한 500에러를 응답으로 전달.
			resp.setStatus(500);
			resp.getWriter().print(e.getMessage());
		}

	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Comments