Java EE 정리 17
21 Mar 2019
Reading time ~8 minutes
Java EE 정리 17 - 일정관리 만들기(2)
일정관리 만들기(2)
- 진행중인 프로젝트 패키지
- Java Calendar는 없는 날짜가 입력되면 다음달 1일이 된다.
- 3월 31일로 날짜를 변경한 경우
- 4월로 이동하면 31이 존재하지 않기 때문에 5월달로 이동해버린다.
...
int nowYear = 0;
int nowMonth = 0;
int nowDay = cal.get(Calendar.DAY_OF_MONTH);
String paramMon = request.getParameter("param_month");
// 요청했을 때 해당 달에 없는 일이 존재한다면
// 다음 달 1일로 설정되기 때문에 모든 달에 존재하는 날짜로 일을 설정
cal.set(Calendar.DAY_OF_MONTH, 1);
...
- 글작성 jsp 끼워넣기
<!-- diary.jsp -->
...
#diaryContent {
position: relative;
}
#diaryJob {
position: absolute; top: 10px; left: 160px;
}
#writeFrm {
background-color: #FFFFFF;
border:1px solid #333;
box-shadow: 5px 5px 5px #333;
padding: 10px;
}
...
</table>
...
<div id="diaryJob">
<c:import url="write_form.jsp"></c:import>
</div>
<!-- write_form.jsp -->
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<div id="writeFrm">
<form action="write_process" method="post">
<table id="writeTab">
<tr>
<th colspan="2"><span>이벤트 작성</span></th>
</tr>
<tr>
<td style="width:80px;">제목</td>
<td style="width:400px">
<input type="text" name="subject" class="inputBox"
style="width:350px;"/>
</td>
</tr>
<tr>
<td style="width:80px;">내용</td>
<td style="width:400px">
<textarea name="content" id="summernote"></textarea>
</td>
</tr>
<tr>
<td style="width:80px;">이벤트 일</td>
<td style="width:400px">
<input type="text" name="e_year" class="inputBox"
style="width:40px;" readonly="readonly"/>-
<input type="text" name="e_month" class="inputBox"
style="width:20px;" readonly="readonly"/>-
<input type="text" name="e_day" class="inputBox"
style="width:20px;" readonly="readonly"/>
</td>
</tr>
<tr>
<td style="width:80px;">작성자</td>
<td style="width:400px">
<input type="text" name="writer" class="inputBox"
style="width:120px;"/>
</td>
</tr>
<tr>
<td style="width:80px;">비밀번호</td>
<td style="width:400px">
<input type="password" name="writer" class="inputBox"
style="width:200px;"/>
</td>
</tr>
<tr>
<td style="width:80px;">작성일</td>
<td style="width:400px">
<fmt:formatDate value="<%= new Date() %>" pattern="yyyy-MM-dd a EEEE HH:mm"/>
</td>
</tr>
<tr>
<td style="width:80px;">작성IP</td>
<td style="width:400px">
<%= request.getRemoteAddr() %>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="button" value="이벤트 작성" class="btn" id="btnWrite"/>
<input type="button" value="닫기" class="btn" id="btnWriteClose"/>
</td>
</tr>
</table>
</form>
</div>
- summernote 적용을 위해 diary.jsp에 스크립트 추가
<!-- diary.jsp -->
...
<!-- summernote 관련 library 시작 -->
<link href="../common/summernote/bootstrap.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="../common/summernote/bootstrap.js"></script>
<link href="../common/summernote/summernote-lite.css" rel="stylesheet">
<script src="../common/summernote/summernote-lite.js"></script>
<!-- include summernote-ko-KR -->
<script src="../common/summernote/lang/summernote-ko-KR.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#summernote').summernote({
placeholder: '이벤트를 작성해주세요',
tabsize: 2,
height: 100,
width: 380,
lang: 'ko-KR'
});
});
</script>
<!-- summernote 관련 library 끝 -->
</head>
...
- 이제 pageFlag Parameter에 따라 jsp를 include 처리
<!-- diary.jsp -->
...
<div id="diaryJob">
<c:if test="${ not empty param.pageFlag }">
<c:import url="${ param.pageFlag }.jsp"></c:import>
</c:if>
</div>
...
<!-- diary.jsp -->
...
<!-- 날짜를 선택하면 글쓰기 jsp가 뜨도록 param 설정 -->
<!-- param_year, param_month, param_date를 같이 전달, 다른 달에서 글쓰기 해도 화면 유지 -->
<td class="diaryTd ${ todayClass }">
<a href="diary.jsp?param_year=${ nowYear }¶m_month=${ nowMonth }¶m_date=<%= tempDay %>&pageFlag=write_form">
<span class="${ dayClass }">
<%= tempDay %>
</span>
</a>
</td>
...
<!-- write_form.jsp -->
...
<td style="width:80px;">이벤트 일</td>
<td style="width:400px">
<input type="text" name="e_year" class="inputBox"
style="width:40px;" readonly="readonly" value="${ param.param_year }"/>-
<input type="text" name="e_month" class="inputBox"
style="width:20px;" readonly="readonly" value="${ param.param_month }"/>-
<input type="text" name="e_day" class="inputBox"
style="width:20px;" readonly="readonly" value="${ param.param_day }"/>
</td>
...
- CSS 수정
- th들 가운데정렬
- 이벤트작성 가운데 크게
- X버튼 우측상단에 붙이기
<th colspan="2" style="text-align: center;">
<span style="font-size: 20px;">이벤트 작성</span>
<span style="float:right; padding:5px;">
<img src="images/btn_close.png"/>
</span>
</th>
- 다른 달에서 글쓰기 창 띄웠다가 닫는 순간 3월달로 돌아온는 문제 해결
// diary.jsp
$(document).ready(function() {
$("#btnCloseFrm").click(function() {
// location.href="diary.jsp?param_year=${ param.param_year }¶m_month=${ param.param_month }"; 또는
// 히든을 갖고 submit
$("[name='param_year']").val(${ param.param_year });
$("[name='param_month']").val(${ param.param_month });
$("[name='diaryFrm']").submit();
});
});
- 기존에 있던 moveMonth를 사용해도 종료처리가 가능
function moveMonth(month,year) {
$("[name='param_year']").val(year);
$("[name='param_month']").val(month);
$("[name='diaryFrm']").submit();
}
$(document).ready(function() {
$("#btnCloseFrm").click(function() {
// 엑스버튼 눌렀을 때 글쓰는 창 닫기
moveMonth("${ param.param_month }","${ param.param_year }");
});
$("#btnWriteClose").click(function() {
// 닫기 버튼 눌렀을 때 글쓰는 창 닫기
moveMonth("${ param.param_month }", "${ param.param_year }");
});
})
- 글작성 처리
- 제목, 내용, 작성자, 비밀번호에 id부여 후 검증 수행 후 요청처리
// 글 작성 처리, diary.jsp
$("#btnWrite").click(function() {
if($("#subject").val() == "") {
alert("이벤트 제목은 필수입력 항목입니다.")
$("#subject").focus();
return;
}
if($("#summernote").val() == "") {
alert("이벤트 내용은 필수입력 항목입니다.");
return;
}
if($("#writer").val() == "") {
alert("작성자는 필수입력 항목입니다.");
$("#writer").focus();
return;
}
if($("#pass").val() == "") {
alert("비밀번호는 필수입력 항목입니다.");
$("#pass").focus();
return;
}
$("[name='writeFrm']").submit(); // 검증 후 submit
});
- 날짜 이동한 후 submit했을 때도 날짜 상태가 유지처리
<!--write_form.jsp -->
...
<div id="writeFrm">
<form action="diary.jsp" method="post" name="writeFrm">
<!-- post방식이므로 쿼리스트링에 pageFlag 파라미터를 전송 못함!! -->
<!-- 히든으로 해결 -->
<input type="hidden" name="pageFlag" value="write_process"/>
<input type="hidden" name="param_year" value="${ param.param_year }"/>
<input type="hidden" name="param_month" value="${ param.param_month }"/>
...
- useBean, setProperty를 이용 insert할 값들을 VO에 넣는다.
<!-- write_process.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
// request.setCharacterEncoding("UTF-8"); // POST 한글처리
// 근데 여기서 하면 body태그 밑에서 include, 한글화가 안된다.
// 따라서 diary.jsp 맨 위에서 적용 시켜줘야 함.
%>
<jsp:useBean id="dvo" class="kr.co.sist.vo.DiaryVO" scope="page" />
<jsp:setProperty name="dvo" property="*"/>
<jsp:setProperty name="dvo" property="ip" value="<%= request.getRemoteAddr() %>"/>
<%
dvo.setPass(ShaUtil.shaEncoding(dvo.getPass())); // 암호화
%>
- DBCP 사용 설정
- Severs/…/server.xml에서 설정
...
<Context docBase="jsp_prj" path="/jsp_prj" reloadable="true" source="org.eclipse.jst.jee.server:jsp_prj"/>
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver"
maxIdle="10" maxTotal="20" maxWaitMillis="-1"
name="jdbc/jsp_dbcp" password="tiger" type="javax.sql.DataSource"
url="jdbc:oracle:thin:@127.0.0.1:1521:orcl" username="scott"/>
</Context>
</Host>
...
- DiaryDAO에서 DBCP를 이용 Connection을 얻는 getConn과 새로운 글을 넣는 insertEvent메소드를 만듦
// DiaryDAO
...
private Connection getConn() throws SQLException {
Connection con = null;
try {
// 1. JNDI 사용 객체 생성
Context ctx = new InitialContext();
// 2. DBCP에 저장된 DataSource 얻기
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/jsp_dbcp");
// 3. Connection 얻기
con = ds.getConnection();
} catch (NamingException e) {
e.printStackTrace();
}
return con;
}
...
public void insertEvent(DiaryVO dvo) throws SQLException{
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConn();
StringBuilder insertEvent = new StringBuilder();
insertEvent
.append(" INSERT INTO diary(NUM,WRITER,SUBJECT,CONTENT,E_YEAR,E_MONTH,E_DATE,PASS,IP) ")
.append(" VALUES (seq_diary.nextval,?,?,?,?,?,?,?,?) ");
pstmt = con.prepareStatement(insertEvent.toString());
pstmt.setString(1, dvo.getWriter());
pstmt.setString(2, dvo.getSubject());
pstmt.setString(3, dvo.getContent());
pstmt.setString(4, dvo.getE_year());
pstmt.setString(5, dvo.getE_month());
pstmt.setString(6, dvo.getE_date());
pstmt.setString(7, dvo.getPass());
pstmt.setString(8, dvo.getIp());
pstmt.executeUpdate();
} finally {
if (pstmt != null) { pstmt.close(); }
if (con != null) { con.close(); }
}
}
- write_process.jsp에서 새글 등록처리
<script type="text/javascript">
// scriptlet 사이에 쓰이는 내용은 JS코드가 됨
<%
dvo.setPass(ShaUtil.shaEncoding(dvo.getPass())); // 암호화
DiaryDAO d_dao = DiaryDAO.getInstance();
try {
d_dao.insertEvent(dvo);
%>
alert("이벤트가 정상적으로 등록 되었습니다.");
<%
} catch (SQLException se) {
// _jspService에서 컴파일 예외를 처리해주지만 우리가 직접하는게 더 좋다
se.printStackTrace();
%>
alert("이벤트가 정상적으로 등록되지 않았습니다.");
<%
}
%>
</script>
- JavaScript에서 alert은 코드 브레이커(정지) 기능을 한다.
- onload 처리를 해서 HTML을 모두 그린 뒤 JavaScript가 동작하도록 변경
<script type="text/javascript">
window.onload = function() {
//또는 jQuery로 $(window).load(function(){ ... });
<%
dvo.setPass(ShaUtil.shaEncoding(dvo.getPass())); // 암호화
DiaryDAO d_dao = DiaryDAO.getInstance();
try {
d_dao.insertEvent(dvo);
%>
alert("이벤트가 정상적으로 등록 되었습니다.");
<%
} catch (SQLException se) {
// _jspService에서 컴파일 예외를 처리해주지만 우리가 직접하는게 더 좋다
se.printStackTrace();
%>
alert("이벤트가 정상적으로 등록되지 않았습니다.");
<%
}
%>
}
</script>
- 글 등록 후 새로고침으로 발생하는 중복등록 막기
- session을 사용해서 막는다.
- session으로 새로고침 재등록을 막는다.
- writeFlag란 session 속성을 만들고 boolean형 값을 저장
- writeFlag속성값이 null이 아니고 false라면 글쓰기 수행
- null이거나 true(이미 쓴 상태)라면 글쓰기를 수행하지 않는다.
<!-- write_form.jsp -->
<% // 글 쓰기 전, false로 writeFlag 세션속성 값 설정
session.setAttribute("writeFlag",false);
%>
<!-- write_process.jsp -->
<%
// 새로고침(F5)로 중복 실행 방지 세션
Boolean flag = (Boolean)session.getAttribute("writeFlag");
if (flag != null && !flag) {
dvo.setPass(ShaUtil.shaEncoding(dvo.getPass())); // 암호화
DiaryDAO d_dao = DiaryDAO.getInstance();
try {
d_dao.insertEvent(dvo);
%>
alert("이벤트가 정상적으로 등록 되었습니다.");
<%
session.setAttribute("writeFlag", true); // 읽음처리
} catch (SQLException se) {
// _jspService에서 컴파일 예외를 처리해주지만 우리가 직접하는게 더 좋다
se.printStackTrace();
%>
alert("이벤트가 정상적으로 등록되지 않았습니다.");
<%
}
}
%>
- 달력에 이벤트가 존재하는지 표시 * DiaryDAO에서 selectMonthEvent 메소드를 만듦
// DiaryDAO
public MonthVO[][] selectMonthEvent(String year, String month) throws SQLException {
MonthVO[][] mvo = new MonthVO[31][];
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConn();
StringBuilder selectMonthData = new StringBuilder();
selectMonthData
.append(" SELECT num, subject ")
.append(" FROM diary ")
.append(" WHERE e_year=? and e_month=? and e_date=? ");
MonthVO temp = null;
// DB에 두번 접속하지 않기 위해 정보를 담을 List
List<MonthVO> list = new ArrayList<MonthVO>();
pstmt = con.prepareStatement(selectMonthData.toString());
pstmt.setString(1, year);
pstmt.setString(2, month);
for(int i=1; i<32; i++) { // 해당 년월의 1일부터 31일까지 쿼리 수행
pstmt.setString(3, String.valueOf(i));
rs = pstmt.executeQuery();
int idx = 0;
while(rs.next()) { // 실행 결과가 존재한다면
// 해당 일자의 글이 존재하므로 글의 값을 저장한다.
list.add(new MonthVO(rs.getInt("num"),
rs.getString("subject")));
}
rs.close();
if (list.size() != 0) {
// 글을 저장할 배열 생성
MonthVO[] mvoArr = new MonthVO[list.size()];
// 리스트에 존재하는 값을 일차원 배열에 복사
list.toArray(mvoArr);
// 일차원 배열의 값을 가변배열의 i행에 추가
mvo[i-1] = mvoArr;
}
list.clear(); // 리스트 초기화
}
} finally {
if (rs != null) { rs.close(); }
if (pstmt != null) { pstmt.close(); }
if (con != null) { con.close(); }
}
return mvo;
}
<!-- diary.jsp -->
...
<%
String dayClass="";
String todayClass=""; // 요일별 색
// 요청되는 년,월의 모든 이벤트를 조회
DiaryDAO d_dao = DiaryDAO.getInstance();
try {
// 해당 년월에 이벤트 정보를 담는 가변배열
MonthVO[][] monthEvtData = d_dao.selectMonthEvent(
String.valueOf(nowYear), String.valueOf(nowMonth));
MonthVO[] dayEvt = null; // 해당 일에 글이 존재한다면 저장할 배열
...
<td class="diaryTd ${ todayClass }">
<%
dayEvt = monthEvtData[tempDay-1];
%>
<div>
<a href="diary.jsp?param_year=${ nowYear }¶m_month=${ nowMonth }¶m_day=<%= tempDay %>&pageFlag=write_form">
<span class="${ dayClass }">
<%= tempDay %>
</span>
</a>
</div>
<div>
<%
if (dayEvt != null) {
for(int i=0; i<dayEvt.length; i++) {
%>
<img src="images/evtflag.png"
title="<%=dayEvt[i].getSubject()%>"/>
<%
}
}
%>
</div>
</td>
...
} catch (SQLException se) { // SQLException 예외처리
se.printStackTrace();
%>
<tr>
<td colspan="7" style="text-align:center; height:400px;">
<img src="images/construction.jpg" title="죄송합니다."/>
</td>
</tr>
<%
}
%>
- 이벤트를 등록하고 페이지를 갱신
<!-- write_process.jsp -->
...
alert("이벤트가 정상적으로 등록 되었습니다.");
location.href="diary.jsp?param_year=${ param.param_year }¶m_month=${ param.param_month }";
...
- 이벤트 제목이 20자 이상이면 …으로 대체
String tempSubject = "";
...
<%
if (dayEvt != null) {
for(int i=0; i<dayEvt.length; i++) {
tempSubject = dayEvt[i].getSubject();
if (tempSubject.length() > 21) {
tempSubject = tempSubject.substring(0,20)+"...";
}
%>
<img src="images/evtflag.png" title="<%=tempSubject%>"/>
<%
}
}
%>
- 이벤트를 5개 이상 등록 막기
<!-- diary.jsp -->
<script type="text/javascript">
...
function writeEvt(year, month, date, pageFlag, evtCnt) {
if(evtCnt > 4) {
alert("하루에 작성 가능한 이벤트의 수는 5개 입니다.");
return;
}
$("[name='param_year']").val(year);
$("[name='param_month']").val(month);
$("[name='param_date']").val(date);
$("[name='pageFlag']").val(pageFlag);
$("[name='diaryFrm']").submit();
}
</script>
...
<form action="diary.jsp" name="diaryFrm" method="post" >
<input type="hidden" name="param_month"/>
<input type="hidden" name="param_year"/>
<input type="hidden" name="param_date"/>
<input type="hidden" name="pageFlag"/>
</form>
...
int evtCnt = 0; // 이벤트 건수를 제한하기 위한 변수
...
<div><a href="#void" onclick="writeEvt(${ nowYear },${ nowMonth },<%= tempDay %>,'write_form',<%=evtCnt%>)"><span class="${ dayClass }">
<%= tempDay %></span>
</a>