상세 컨텐츠

본문 제목

팀 프로젝트 - 나의 파트

Project/Team

by 영공쁘이 2021. 11. 22. 00:21

본문

1. 팀 프로젝트 홈페이지: 호텔 델루나 홈페이지

 

2. 호텔로 선정한 이유:

 코로나19로 인한 침체된 관광 산업에 속한 분야 중 하나인데, 시간이 지나면 회복될 것을 생각하여 전보다 호텔을 찾는 경우가 늘어날 것을 예상했습니다.

 그래서 앞으로는 더 이러한 산업군이 활발해지지 않을까 하여 정하게 되었습니다. 

 

3. 기술 스택:

java jre 1.8버전, tomcat 8.5, myBatis 3.4, spring 5.0.2, Mysql-jdbc 8.0.24, junit mockito-junit-jupiter 3.8.0

 

4. 담당: Q&A, 로그인, FAQ, 이벤트 기능을 담당

 

5. ERD로 설계하기

 

qna erd
로그인은 기존의 member를 이용한 것

 

6. starUML로 설계하기

  1) 로그인 UML

    1-1) 로그인 요소 간 uml

      1-2) 프로세스 uml

 

 2) Q&A UML

     2-1) 요소 간 uml

      2-2) 프로세스 uml

 

 

로그인

1. 비밀번호 찾기 때, 이메일 라이브러리를 적용하였습니다. 

비밀번호 찾기 파트

 1) pom.xml에 이메일 library

<!-- mail전송을 위한 API : javax.mail, spring-context-support 2개 -->
	<!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api -->
	<dependency>
		<groupId>com.sun.mail</groupId>
		<artifactId>javax.mail</artifactId>
		<version>1.6.1</version>
	</dependency>

2. service 항목으로 email 관련 service 넣기

1. EmailToType은 VO를 나타냄

2. EmailMessageUtil은 이메일에 넣을 내용 나타내기

3. EmailCodeUtil은 이메일 코드를 작성 및 가져오기 할 수 있는 getter & setter를 나타냄

 

 2-1) VO

public class EmailToType {
	String email;
	String type;
	String id;
	String pw;
	String name;
	
    이 아래는 각각의 getter 와 setter를 나타냄
    
    }

 2-2) 이메일에 넣을 내용 나타내기(html로 작성함)

public class EmailMessgeUtil {
	public static EmailHash emailHash;
	
	public static String getFindPwEmailMessage(int authNo) {
		emailHash = new EmailHash();		
		return emailHash.getFindPwMessage(authNo);
	}

}
여기서 authNo는 곧, 이메일로 넘길 인증번호를 나타냄(authNo는 이메일을 수행하는 비즈니스 파트에서 구현한다)
class EmailHash{
	String html;
	
String getFindPwMessage(int authNo) {
		html = "<html lang=\"ko\">";
		html += "<head><title>Emogrifier Example</title></head>";
		html += "<body>";
		html += "<div class=\"wrap\" style=\"margin: 0 auto;width: 900px;background-color: #fff;border: 1px solid #c4c4c4;box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3);font-size: 16px;line-height: 22px;color: #333;\">";
		html += "<div class=\"inner_wrap\" style=\"padding: 30px;\">";
		html += "<div class=\"box\">";
		html += "<h1 class=\"title\" style=\"padding-bottom: 20px;\">HOTEL DEULLA</h1>";
		html +=	"<p class=\"desc\" style=\"margin: 40px 0;\">";
		html += "호텔 델루나 로그인을 위한 인증번호입니다.<br> 아래 인증번호로 재 로그인 하세요";
		html += "</p>";
		html += "</div>";
		html += "<div class=\"box2\" style=\"border-bottom: 1px solid #c4c4c4;height: 150px;\">";
		html += "<h2 class=\"title\" style=\"padding-bottom: 20px;\">&#51064;&#51613;&#53076;&#46300;</h2>";
		html += "<p class=\"desc\" style=\"margin: 40px 0;\">"+authNo+"</p>";
		html +=	"</div>";
		html += "<p class=\"small\" style=\"font-size: 12px;color: rgba(0, 0, 0, .6);\">";
		html += "본 메일은 발신전용입니다.<br> Copyright 2021 HOTEL DELLUNA All Rights Reserved";
		html += "</div></div></body></html>";
		return html;
        }
       }

 2-3) EmailCodeUtil

public class EmailCodeUtil {
	String code;

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}
	
}

** 컨트롤러에서 사용 법*business result 층에서 동작 수행 및 컨트롤러에서는 구현

@ResponseBody
	@RequestMapping(value="/member/mailcode")
	public String sendCodeMail(@RequestBody EmailToType toType, HttpSession session) {
		BusinessResult br = joinService.sendCodeMail(toType, session);
		if(br.getCode() !=RESULTCODE.정상) {}//비정상처리
		return (String)br.getValue();
	}

현재는 joinService에서 회원 가입 시에도 적용시켜야 했기 때문에 한 군데에서 수행함

아래는 BusinessResult의 모습을 나타낸 것

package com.delluna.hotels.util;

public class BusinessResult {
	RESULTCODE code = RESULTCODE.정상;
	String message = null;//던져줄 메시지
	Object value = null;//DB에서 요청오는 모든 것들은 여기에 담기.
	
	public BusinessResult() {//값은 없는 성공
		code=RESULTCODE.정상;
		message=null;
		value=null;
	}
	public BusinessResult(Object value) {//값이 있는 성공
		code=RESULTCODE.정상;
		message=null;
		this.value=value;
	}
	
	public BusinessResult(RESULTCODE code, String message) {//code와 메시지 직접 지정(주로 실패시)
		this.code=code;
		this.message=message;
	}
	
	public RESULTCODE getCode() {
		return code;
	}
	public void setCode(RESULTCODE code) {
		this.code = code;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}
	
}

이는, 실무에서 어떠한 처리가 오갈지 모르기 때문에 비즈니스 계층을 따로 만들어 값을 처리하거나, 해당 코드에 대한 메세지를 표현하기 위해 나타낸 것

 

아래는, joinservice에서 실현한 email 코드

//회원이메일
	@Override
	public BusinessResult sendCodeMail(EmailToType toType, HttpSession session) {
		
		try {
			MimeMessage mailMessage = javaMailSenderImpl.createMimeMessage();
			MimeMessageHelper message;
			message = new MimeMessageHelper(mailMessage,true,"utf-8");
			message.setTo(toType.getEmail());
			String html = null;
			
			//회원가입 - 인증코드 보내기 (AJAX)
			if(toType.getType().equals("verify")) {
				//중복아닐 시 메일 보내기
				int authNo = (int)(Math.random() * (99999 - 10000 + 1)) + 10000;
				System.out.println(String.valueOf(authNo));
				//이메일중복확인
				boolean isinEmail = false;
				try {
					isinEmail = memberDAO.isinEmail(toType.getEmail());
				} catch (Exception e) {
					if(e instanceof ConnectException) {
						return new BusinessResult(RESULTCODE.NETWORK_ERROR,"네트워크 통신이 원활하지 않습니다.");
					}
					TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				}
				
				if(isinEmail) {
					return new BusinessResult("No");
				}

				message.setFrom("dojh555@gmail.com");
				message.setSubject("HOTEL DELLUNA 회원가입 인증 메일입니다.");
				session.setAttribute("authNum", authNo);
				session.setMaxInactiveInterval(60*20);
				
				html = EmailMessgeUtil.getVerifyEmailMessge(authNo);
				
			}else if(toType.getType().equals("findPw")) {
				int authNo = (int)(Math.random() * (99999 - 10000 + 1)) + 10000;
				//비교를 해야해 (type으로 들어온 데이터를 비교할 것)
				String id = toType.getId();
				//비교를 할 id를 불러와야 해(memberDAO의 데이터를 가져올 것)
				String name = toType.getName();
				System.out.println(toType.getName());
				String email = toType.getEmail();
				
				if(!memberDAO.gomail(id, name, email,"가입","가입",1))
				{
				return  new BusinessResult("No");
				
				}
				message.setFrom("yeong973@gmail.com");
				message.setSubject("HOTEL DELLUNA 비밀번호 찾기 메일입니다.");
				html = EmailMessgeUtil.getFindPwEmailMessage(authNo);
				Member member = new Member();
				String i = Integer.toString(authNo);
				session.setAttribute("id", id);
				session.setAttribute("pw", i);
				
			}else if(toType.getType().equals("findAdmPw")) {//관리자 페이지 패스워드 찾기
				
				int authNo = (int)(Math.random() * (99999 - 10000 + 1)) + 10000;
				
				String pw = Integer.toString(authNo);
				String id = toType.getId();
				String name = toType.getName();
				String email = toType.getEmail();
				
				if(!memberDAO.gomail(id, name, email,"대기","가입",8)) {
					return new BusinessResult("No");
				}
				message.setFrom("dojh555@gmail.com");
				message.setSubject("HOTEL DELLUNA 비밀번호 찾기 메일입니다.");
				//메일 메시지
				html = EmailMessgeUtil.getFindPwEmailMessage(authNo);
				//패스워드 바꾸기
				memberDAO.uppw(id, pw);
			}			
			
			message.setText(html, true); 
			// 메일발송
			javaMailSenderImpl.send(mailMessage);
			System.out.println("success to send email");
		}catch (MessagingException e) {
			e.printStackTrace();
		}
			
		return new BusinessResult("Yes");
	}

mimemessageHelper를 이용한 메세지 전송

1. 파일 인코딩 형식

2. 받는 이, 보내는 이 주소

3. 보내는 이메일 제목, 내용(html)

 

* 이메일로 인증번호를 받음(이는 곧 새로운 비밀번호로 업데이트 됨)

최종 화면(이메일 적용 후의 화면)

 

 

 

Q&A 

 1. 사용자 파트: 질문 등록하기

사용자가 질문을 등록하도록 함
정상적으로 등록된 후의 화면

2. 관리자 파트: 

 1-1) 체크리스트 동작 화면

* 위의 사진은, 현재 클라이언트 단에서 질문을 등록한게 올라온 것임

체크리스트, 등록을 위해서는 해당 번호를 하나만 선택해야 함을 나타냄
여러 개 선택해서 삭제가 가능

1-2) 체크리스트 구현 코드

  <script>
    	var clck = [];
    	function getCheckboxValue(event)
    	{
    		$('#chk').attr("readonly",false);
    		console.log(event);
    		console.log(event.target.value);
    		
    		if(event.target.checked)
    		{clck.push(event.target.value);}
    		else{
    			clck=clck.filter(v=>v!==event.target.value);} 
    	}
  	function submitHandler(event){
    		
    		const fd = new FormData();
    		fd.append("idlist",clck);
    	
    		event.preventDefault(); //event를 눌렀을 때 submit하면서 reload가 되면서! 이벤트를 막는다.(on submit 시에만!!!!) url에 ?를 막아주는 거다!!(강제로)
    		if(clck=="")
    		{
				alert("질문을 선택해주십시오");
    			$('#chk').attr("readonly",false);	    			
			}
		
		else{	
	   		$.ajax({
	    			
	    			url : "/adm/adm-qna-delete"
	    			, type : "post"
	    			// , data : JSON.stringify({idlist:clck}) 
	    			, data : fd
	    			, processData: false
	    			, contentType: false
	    			, success : function(result) {
	    						console.log(result.data);
	    						window.location.reload(true);	
	    						alert("삭제되었습니다.");
	    				}
	    			, error : function(result){
	    				console.log(result);		
	    			}	
	    		});
    	 	 }
    	}
    	function bHandler(event){
    		var rdata=0;			
  		if(clck=="")
	    		{
    				alert("질문을 선택해주십시오");
	    			$('#chk').attr("readonly",false);	    			
    			}
    		
    		else{
	    			
	    		if(clck.length>1)	
	    			{
	    				alert("하나만 선택하십시오");
	    				 $("input[type=checkbox]").prop("checked", false);
	    				 clck.length=0;
	    			}
	    		else{ 
			    		//버튼의 상태를 가져오기(답변관련상태)
			 			$.ajax({
			    			url:"/adm/adm-qna-state",
			    			type:"post",
			    			data: JSON.stringify({'clck':clck}),
			    			contentType:"application/json; charset=utf-8",
			    			dataType:"json"
			    			,success:function(result){
			    				
			    			if(result.rno==-1){
			    					location.href="/adm/adm-rqna-write/"+result.bno; 
			    					
			    				}
			    				else{//답변완료다 
			    					location.href="/adm/adm-rqna-edit/"+result.bno;	 
			    				
			    				}
		       				}
		    			}); 
	    			}
    			}	
    	   }
    	
    	function selectAll(event)  {
    		  const checkboxes 
    		       = document.getElementsByName('chk');
    		  
    		  checkboxes.forEach((checkbox) => {
    		    checkbox.checked = selectAll.checked;
    		  })
    		 
      		clck.push(selectAll.target.value);
      		console.log(clck);
    		}
    </script>
 
  <form onsubmit="return submitHandler(event)" method="post" >
			
			<table>
				<thead>
				
				<tr class="headline">
					<td></td>
					<td>번호</td>
					<td>이름</td>
					<td>질문 유형</td>
					<td>제목</td>
					<td>날짜</td>
					<td>답변상태</td>
				</tr>
			</thead><br>
			<tbody>
				
				<c:forEach var="qna" items="${qlist}" varStatus="status">
				<tr>
				
				<th scope="col" class="chk_all_box1">
                		 <input type="checkbox" id="chk" name="chk" value="${qna.no}" onclick="getCheckboxValue(event)" readonly>
                         
                         
                <td class="delbtn"><input type="submit" id="del" value="질문삭제" ></td>
			
				<td class="savebtn"><input type="button" value="답변등록" onclick="bHandler(event)">
		
				</td>
	   </tr>
		</table>

 

  1. 먼저 체크리스트 된 값들을 읽어서 각각의 값을 추출한다

  2. form에서 script의 이벤트 중,  'submitHandler'를 동작시킴

     -> 체크된 데이터들이 clck라는 배열에 입력이 된 것을 ajax 통신으로 adm-qna-delete라는 곳으로 서버에 전송

     -> 그리고 수행이 되었으면(success면,) 해당 글은 삭제가 되는 것.

 * 배열로 체크된 값이 들어간 것이라서 체크된 것들이 선택되어 삭제 및 선택되는 것이다.

 

※ 삭제를 실제로 구현한 코드

//질문 삭제
	@RequestMapping(value="/adm/adm-qna-delete", method=RequestMethod.POST)
	public String deleteqlist(@RequestParam("idlist") List<String> idlist) {
		
		for(int i=0;i<idlist.size();i++)
		{
			Qna qna = qnaDAO.findByNo(Integer.parseInt(idlist.get(i)));
			qnaDAO.delete(qna.getNo());
			qnaDAO.deleteR(qna.getNo());
			qnaDAO.udState("답변대기","답변대기",Integer.parseInt(idlist.get(i)));
			System.out.println(Integer.parseInt(idlist.get(i)));
		}
		
		return "redirect:/adm/adm-qna-list";
	}

 -> 여기서 어려운 점이 있었다면, 현재 배열된 값을 읽어야 하는 것이고, 해당 리스트 값은 문자열 형태로 되어있어서 값으로 넘어온(JSON형태의 값) 값을 @RequestParam으로 읽는 것이다.

   그리고 직접 하나씩 읽어서 삭제하는 쿼리를 실행시킴.

 

2) 글자수 실시간 조회

이는, fn이라는 taglib의 prefix로 형식을 쓴다. 그리고 실제 값의 길이를 나타낸다.

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<script type="text/javascript">
$(document).ready(function(){
	$('#content').on('keyup',function(){
		$('#result').html("("+$(this).val().length+"/200)");
		
		if($(this).val().length>200){
			$(this).val($(this).val().substring(0,200));
			$('#result').html("(200/200)");	
		}
	});
});
</script>


<div id="result">(<c:out value="${fn:length(response)}"/>/200)</div>

즉각적으로 실행이 될 수 있도록 script의 제일 첫번째에 등록한다. 

그리고 키보드의 이벤트 중, keyup이라는 이벤트를 통해 키보드 동작이 on이 될 때마다, 해당 길이를 나타내는 로직을 작성하였다.

 

3) 질문 등록부터 답변 등록, 수정, 삭제까지 MySQL을 이용한 쿼리를 작성하여 구현하였다.

 

FAQ

HTML의 아코디언 형식을 활용하였습니다.

* w3school의 html에서 아코디언 파트를 적용하여 구현하였습니다.

 

7. 응용(안드로이드에 적용)

안드로이드 스튜디오에서 해당 url을 입력하여 안드로이드 앱으로 나타내는 실습을 진행함

@Override
public void onClick(View v)
{
 Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("localhost:192.168.0.76:9000/index"));
 startActivity(intent);
}

해당 주소를 괄호 안에 넣어서 실행시킴.

'Project > Team' 카테고리의 다른 글

svn 사용하기  (0) 2021.08.24

관련글 더보기