YJ의 새벽
JDBC 활용 연습용4. ( 롬복 + 회원가입(이메일인증) ) 본문
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());
}
}
}
'SelfStudy > JDBC' 카테고리의 다른 글
JDBC 활용 연습용6. ( 닉네임 중복확인 + 전화번호유효성검사 ) (0) | 2023.03.29 |
---|---|
JDBC 활용 연습용5. ( 인증번호 확인 + 비밀번호일치확인 ) (0) | 2023.03.29 |
JDBC 활용 연습용3. ( 암호화 (로그인)) (0) | 2023.03.27 |
JDBC 활용 연습용2. ( ajax 이용 화면에 정보 바로뿌리기 ) (0) | 2023.03.24 |
JDBC 활용 연습용. ( 이메일 받아서 정보출력 ) (0) | 2023.03.24 |