• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java EE 정리 14

18 Mar 2019

Reading time ~9 minutes

Java EE 정리 14 - JSTL(2), 달력 만들기


JSTL

  • redirect
    • 비정상적인 요청이 발생했을 때 페이지 이동 기술
<c:redirect url="이동할 페이지 URL"/>
  • import
    • 공통 코드가 아닌 디자인인 경우 사용
    • 지시자의 충돌이 발생하지 않는다
    • 변수나 method가 공유되지 않는다
<c:import url="끼워넣을 JSP URL">

01

  • import와 동일 기능을 가진, jsp include 액션태그
    • 공통코드를 가진 JSP를 분리하여 작성하고 필요한 곳에서 사용하기 위해 사용되는 기술
    • include directive는 공통 코드를 합칠 때, jsp include 액션태그, JSTL import는 디자인을 합칠 때 사용
<%@ include file="..." %> <!-- 공통 코드인 경우, ex) 세션체크 -->

<jsp:include page="..." > <!-- 디자인인 경우 -->
  • 예외처리 catch
<c:catch var="예외객체명">
    예외발생 예상코드
</c:catch>

<c:if test="${ 예외객체명 ne null }"> <!-- 'not empty 객체명'으로 조건바꿀 수 있다 -->
    예외발생 시 제공할 코드
</c:if>
  • redirect 사용 예
<!-- jstl_redirect_a.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
정상적인 경우 제공할 서비스<br/>
<img src="../common/images/img2.jpg"/>
<%
      boolean flag = new Random().nextBoolean();
      pageContext.setAttribute("flag", flag);
%>
<c:if test="${ flag }">
      <!-- 비정상적인 경우의 요청이 있을 때 페이지 이동 -->
      <c:redirect url="jstl_redirect_b.jsp"></c:redirect>
</c:if>    
<!-- jstl_redirect_b.jsp -->
...
비정상적일 때 응답할 페이지<br/>
<img src="../common/images/img.png"/>
...

02

  • import 사용 예
<!-- jstl_import.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
외부페이지<br/>
<div>
      <c:import url="jstl_import_b.jsp"/>
</div>
외부페이지<br/>
내부 페이지 변수의 사용 : ${ week }
<!-- jstl_import_b.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"
    info="지시자의 충돌이 발생하지 않는다."%>

공통 디자인 정의<br/>
<%
      String week = "오늘은 월요일입니다.";
      pageContext.setAttribute("week", week);
%>
<strong>${ week }</strong>

03

  • catch 사용 예 * scriptlet내용은 Java파일로 변경되면서 _jspService안에 코드로 생성된다. * scrriptlet안에 내용은 try~catch문으로 감싸여 있어서 코드 에러가 발생하지 않는다. * 반면에 declaration으로 메소드를 생성 후 에러코드를 넣으면 예외처리를 안했기 때문에 에러가 발생한다.

04

<!-- jstl_catch.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<c:catch var="e">
<%
      Class.forName("kk"); 
      // 예외를 throws하는 method를 호출하더라도  _jspService()가
      // 코드를 try~catch로 감싸고 있기 때문에 코드 에러가 발생하지 않는다.
%>
</c:catch>
<c:if test="${ not empty e }">
      서비스도중 문제가 발생하였습니다. ${ e }
</c:if>

05

JSTL - fmt

  • 형식을 가진 문자열을 만드는 Tag Library
  • 지시자
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"/>
  • 날짜형식 변경
    • 브라우저로 출력된다.
    • fmt - prefix/ formatDate - suffix
<fmt:formatDate value="date 객체" pattern="SimpleDateFormat Pattern과 동일"/>
  • 숫자형식 변경
<fmt:formatNumber value="숫자" pattern="DecimalFormat Pattern과 동일" />
<!--
    pattern값은 DecimalFormat에서 제공하는 pattern을 사용
    데이터가 없을 때 0을 출력하고자 한다면 '0'(오라클은 0)
    데이터가 없을 때 출력하지 않고자 한다면 '#'(오라클은 9)
-->
  • fmt formatDate 사용 예
<!-- jstl_formatdate.jsp -->
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
...
<%
      Date date = new Date();
      pageContext.setAttribute("pageDate", date);
%>

<!-- EL 사용 -->
<fmt:formatDate value="${ pageDate }" pattern="yyyyMMdd EEEE  HH:mm:ss"/><br/>

<!-- Expression 사용 -->
<fmt:formatDate value="<%= date %>" pattern="yyyyMMdd EEEE  HH:mm:ss"/><br/>

06

  • fmt formatNumber 사용 예
<!-- jstl_formatnumber.jsp -->
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
...
<%
      int i = 20190318;
      int j = 2019;
      
      pageContext.setAttribute("i", i);
      pageContext.setAttribute("j", j);
%>
<fmt:formatNumber value="${ i }" pattern="#,###"/><br/>
<fmt:formatNumber value="${ j }" pattern="#,###"/><br/>

07

  • #은 있는 데이터만 출력, 0은 없는 데이터도 출력
<fmt:formatNumber value="${ i }" pattern="###,###,###,###"/><br/>
<fmt:formatNumber value="${ j }" pattern="0,000,000,000"/><br/>

08

달력 만들기

  • 지금까지 배운 내용 총정리
  • 다음 캘린더와 비슷하게 만들 것

09

  • 초기 디자인
<!-- diary.jsp -->
...
<style type="text/css">
      #diaryTab { margin: 0px auto; border-spacing: 0px;    }
      
      .sunTitle {
            width: 80px;
            height:25px;
            border:1px solid #CECECE;
            background-color: #e84118;
            font-weight:bold;
            color: white;     
      }
      .weekTitle { width: 80px; height:25px; border:1px solid #CECECE; }
      .satTitle {
            width: 80px;
            height:25px;
            border:1px solid #CECECE;
            background-color: #273c75;
            color: white;
      }
      
      #diaryTitle {
            text-align: center;
            margin-bottom: 10px;
            margin-tom: 20px;
      }
      
      #diaryToday {
            width:100px;
            font-family: 고딕체;
            font-size:24px;
            font-weight: bold;
            vertical-align: bottom;
      }
      
      .diaryTd {
            width: 100px;
            height: 60px;
            border: 1px solid #CECECE;
            text-align: right;
            vertical-align: top;
            font-size: 14px;
            font-weight: bold;
            padding-right: 10px;
      }
      
      
      
      /* jQuery tooltip 플러그인 */
      .ui-tooltip, .arrow:after {
        background: black;
        border: 2px solid white;
      }
      .ui-tooltip {
        padding: 10px 20px;
        color: white;
        border-radius: 20px;
        font: bold 14px "Helvetica Neue", Sans-Serif;
        text-transform: uppercase;
        box-shadow: 0 0 7px black;
      }
      .arrow {
        width: 70px;
        height: 16px;
        overflow: hidden;
        position: absolute;
        left: 50%;
        margin-left: -35px;
        bottom: -16px;
      }
      .arrow.top {
        top: -16px;
        bottom: auto;
      }
      .arrow.left {
        left: 20%;
      }
      .arrow:after {
        content: "";
        position: absolute;
        left: 20px;
        top: -20px;
        width: 25px;
        height: 25px;
        box-shadow: 6px 5px 9px -9px black;
        -webkit-transform: rotate(45deg);
        -ms-transform: rotate(45deg);
        transform: rotate(45deg);
      }
      .arrow.top:after {
        bottom: -20px;
        top: auto;
      }
</style>
<link rel="stylesheet"  href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script  src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
  $( document ).tooltip({
    position: {
      my: "center bottom-20",
      at: "center top",
      using: function( position, feedback ) {
        $( this ).css( position );
        $( "<div>" )
          .addClass( "arrow" )
          .addClass( feedback.vertical )
          .addClass( feedback.horizontal )
          .appendTo( this );
      }
    }
  });
} );
</script>
...
<div id="diaryWrap">
<%
      Calendar cal = Calendar.getInstance();
      int nowYear = cal.get(Calendar.YEAR);
      int nowMonth = cal.get(Calendar.MONTH)+1;
      int nowDay = cal.get(Calendar.DAY_OF_MONTH);
      
      pageContext.setAttribute("nowYear", nowYear);
      pageContext.setAttribute("nowMonth", nowMonth);
      pageContext.setAttribute("nowDay", nowDay);
%>
<div id="diaryTitle">
      <img src="images/btn_prev.png" title="이전 월"/>
      <span id="diaryToday" title="${ nowYear }년 ${ nowMonth }월">
            <c:out value="${ nowYear }"/>
            .<c:out value="${ nowMonth }"/></span>
      <img src="images/btn_next.png" title="다음 월"/>
      <img src="images/btn_today.png" title="오늘"/>
      
</div>
<div id="diaryContent">
      <table id="diaryTab">
            <tr>
                  <th class="sunTitle">일</th>
                  <th class="weekTitle">월</th>
                  <th class="weekTitle">화</th>
                  <th class="weekTitle">수</th>
                  <th class="weekTitle">목</th>
                  <th class="weekTitle">금</th>
                  <th class="satTitle">토</th>
            </tr>
            <tr>
                  <td class="diaryTd">1</td>
            </tr>
      </table>
</div>
</div>
  • Calendar 사용
    • Singleton 패턴, 인스턴스 얻어 사용
Calendar cal = Calendar.getInstance();
  • 마지막 날 얻기
    • 이번 달에 없는 날짜가 입력되면 달력은 다음달 1일로 설정이 된다.
for(int i=1; ; i++) {
    cal.set(Calendar.DAY_OF_MONTH, i);
    if(cal.get(Calendar.DAY_OF_MONTH) != i) { 
        // 예로 32일이 들어가면 없는 날이라 첫날(1)로 돌아감, 반복문을 빠져나온다.
        break;
    }
    // 마지막 날을 저장해서 사용
}
  • 현재 날짜랑 tempDay랑 같지 않음으로 마지막 날을 알 수 있다
<tr>
      <%
            for(int tempDay = 1; tempDay < 33;tempDay++) {
                  cal.set(Calendar.DAY_OF_MONTH, tempDay);  
                  out.println(cal.get(Calendar.DAY_OF_MONTH)+"/"+tempDay+"<br/>");
            }
      %>
</tr>

10

  • 토요일이면 줄을 바꿔줘야 한다.
    • 딱 떨어지는 수를 비교할 땐 switch case문을 사용
switch (cal.get(Calendar.DAY_OF_WEEK)) {
case Calendar.SATURDAY :
    out.println("</tr><tr>");                                          
}
<div id="diaryContent">
      <table id="diaryTab">
            <tr>
                  <th class="sunTitle">일</th>
                  <th class="weekTitle">월</th>
                  <th class="weekTitle">화</th>
                  <th class="weekTitle">수</th>
                  <th class="weekTitle">목</th>
                  <th class="weekTitle">금</th>
                  <th class="satTitle">토</th>
            </tr>
            <tr>
                  <%
                        // 매월마다 끝나는 날짜가 다르기 때문에
                        for(int tempDay = 1; ;tempDay++) {
                              // 임시일자를 설정
                              cal.set(Calendar.DAY_OF_MONTH, tempDay);      
                              if (cal.get(Calendar.DAY_OF_MONTH) !=  tempDay) {
                                    // 설정된 날짜가 현재일자가 아니라면 다음 달 1일
                                    // 다음달이면 반복문을 빠져나간다.
                                    break;
                              }
                  %>
                  <td class="diaryTd"><%= tempDay %></td>
                  <%
                              switch (cal.get(Calendar.DAY_OF_WEEK)) {
                              case Calendar.SATURDAY :
                                    out.println("</tr><tr>");
                              }
                        }
                  %>
            </tr>
      </table>
</div>

11

  • 1일을 출력하기 전, 시작요일에 맞게 공백을 출력해야 함. * 1일인 경우에 공백을 넣어주는 코드를 앞에 넣어줘야 함 * 월마다 찍는 공백의 수는 다름
// for 반복문 안에서
if ( i == 1 ) { // 이번에 찍을 날짜가 1일이라면 1일의 요일까지 공백넣기
    for(int j=1; j < cal.get(Calendar.DAY_OF_WEEK); j++) {
        out.println("<td></td>");
    }
}
...
<%! // declaration, 필드에 상수 생성
      public static final int START_DAY = 1;
%>
...
      <tr>
            <%
                  for(int tempDay = 1; ;tempDay++) {
                        cal.set(Calendar.DAY_OF_MONTH, tempDay);
                        if (cal.get(Calendar.DAY_OF_MONTH) != tempDay) {
                              break;
                        }
                        // 1일을 출력하기 전 1일 요일까지 공백 출력
                        switch(tempDay) {
                        case START_DAY:
                              for(int blankTd=1; blankTd < cal.get(Calendar.DAY_OF_WEEK); blankTd++) {
                                    out.println("<td  class='diaryTd'></td>");
                              }
                        }
            %>
            <td class="diaryTd"><%= tempDay %></td>
            <%
                        switch (cal.get(Calendar.DAY_OF_WEEK)) {
                        case Calendar.SATURDAY :
                              out.println("</tr><tr>");
                        }
                  }
            %>
      </tr>
</table>

12

  • 마지막날 이후에 공백 만들기 * 빠져나가기 전에 만들어야 한다.
if(cal.get(Calendar.DAY_OF_MONTH) != tempDay) { // 마지막 날짜를 넘고서
    for(int j=cal.get(Calendar.DAY_OF_WEEK); j<8; j++) { // SAT(7)까지 공백넣기
        out.println("<td></td>");
    }
    break;
}
...
      <tr>
            <%
                  // 매월마다 끝나는 날짜가 다르기 때문에
                  for(int tempDay = 1; ;tempDay++) {
                        cal.set(Calendar.DAY_OF_MONTH, tempDay);  //  임시일자를 설정
                        if (cal.get(Calendar.DAY_OF_MONTH) != tempDay) {
                              // 설정된 날짜가 현재일자가 아니라면  마지막 일자 다음달 1일이므로 반복문을 빠져나간다.
                              
                             for(int blankTd=cal.get(Calendar.DAY_OF_WEEK); blankTd<8; blankTd++) {
                                    out.println("<td  class='blankTd'></td>");
                              }
                              break;
                        }
                        
                        // 1일을 출력하기 전 공백 출력
                        switch(tempDay) {
                        case START_DAY:
                              for(int blankTd=1;  blankTd<cal.get(Calendar.DAY_OF_WEEK); blankTd++) {
                                    out.println("<td  class='blankTd'></td>");
                              }
                        }
            %>
            <td class="diaryTd"><%= tempDay %></td>
            <%
                        switch (cal.get(Calendar.DAY_OF_WEEK)) {
                        case Calendar.SATURDAY :
                              out.println("</tr><tr>");
                        }
                  }
            %>
      </tr>
</table>

13

  • 토요일, 일요일 글자색을 바꾸기
...
.sunColor {
      color: #e84118;
      font-size:15px;
}
.satColor {
      color: #273c75;
}
.weekColor {
      color: #333;
}
</style>
...
      String dayClass="";
...
            // 요일별 색 설정
            switch(cal.get(Calendar.DAY_OF_WEEK)) {
            case Calendar.SUNDAY:
                  dayClass="sunColor";
                  break;
            case Calendar.SATURDAY:
                  dayClass="satColor";
                  break;
            default:
                  dayClass="weekColor";
            }
            
            pageContext.setAttribute("dayClass", dayClass);
%>
<td class="diaryTd"><span class="${ dayClass }"><%= tempDay  %></span></td>
<%
...

14

  • a태그 href 속성값 #void와 #의 차이 * #void는 눌러도 제자리(아무반응없이) * #은 페이지를 갱신해서 스크롤이 아래로 내려가 있었다면 페이지 위로 올라옴
  • 이전, 다음 달로 이동
    • 쿼리스트링에 파라미터로 이동할 달 페이지 요청
<a href="diary.jsp?param_month=${ nowMonth-1 }"><img  src="images/btn_prev.png" title="이전 월"/></a>

15

<%
      Calendar cal = Calendar.getInstance();
      int nowYear = cal.get(Calendar.YEAR);
      int nowMonth = 0;
      int nowDay = cal.get(Calendar.DAY_OF_MONTH);
      
      String paramMon = request.getParameter("param_month");
      if (paramMon != null) { // 파라미터 월이 존재한다면 현재 켈린더 객체의 월을 변경
            cal.set(Calendar.MONTH, Integer.parseInt(paramMon)-1);
      }
      
      nowMonth = cal.get(Calendar.MONTH)+1;
      
      pageContext.setAttribute("nowYear", nowYear);
      pageContext.setAttribute("nowMonth", nowMonth);
      pageContext.setAttribute("nowDay", nowDay);
%>
  • 문제는 2019년으로만 유지됨 * parameter로 ‘년’도 추가 * 1월일 때 12월 이동할 때 상태바로 파라미터가 제대로 전달되는지 확인
int nowYear = 0;
...
String paramYear = request.getParameter("param_year");
if (paramYear != null) {
      cal.set(Calendar.YEAR, Integer.parseInt(paramYear));
}
nowYear = cal.get(Calendar.YEAR);
...
<a href="diary.jsp?param_month=${ nowMonth-1 eq 0 ? 12 : nowMonth-1  }&param_year=${ nowMonth-1 eq 0 ? nowYear-1 : nowYear }"><img  src="images/btn_prev.png" title="이전 월"/></a>

16

  • 이번엔 JavaScript로 월 이동 구현
<script type="text/javascript">
      function moveMonth(month, year) { // JS로 페이지 이동
            location.href="diary.jsp?param_month="+month+"&param_yaer="+year;
      }
</script>
...
<a onclick="moveMonth(${ nowMonth-1 eq 0 ? 12 : nowMonth-1 },${  nowMonth-1 eq 0 ? nowYear-1 : nowYear })"><img src="images/btn_prev.png"  title="이전 월"/></a>
  • 폼으로 넘기기
    • hidden태그로 post방식으로 전달
<script type="text/javascript">
      function moveMonth(month, year) {
            var obj = document.diaryFrm;
            obj.param_month.value = month;
            obj.param_year.value = year;
            
            obj.submit();
      }
</script>
...
<form action="diary.jsp" name="diaryFrm" method="post">
      <input type="hidden" name="param_month"/>
      <input type="hidden" name="param_year"/>
</form>
...
<a href="#void" onclick="moveMonth(${ nowMonth == 13 ? 1 : nowMonth+1  },${ nowMonth == 13 ? nowYear+1 : nowYear })"><img  src="images/btn_next.png" title="다음 월"/></a>
  • 위 함수를 jQuery로 바꾸면
<script type="text/javascript">
      function moveMonth(month,year) {
            $("[name='param_year']").val(year);
            $("[name='param_month']").val(month);
            $("[name='diaryFrm']").submit();
      }
</script>
  • 오늘로 이동
    • jsp 요청 시 파라미터 없으면 오늘로 이동한다.
<a href="diary.jsp"><img src="images/btn_today.png" title="오늘"/></a>
  • 오늘로 이동 - JS함수로 이동
    • 함수에 empty값 전달해서 이동 시 empty일 때 예외처리를 해줘야 에러가 없다
<a href="#void" onclick="moveMonth('','')"><img  src="images/btn_today.png" title="오늘"/></a>
...
String paramMon = request.getParameter("param_month");
if (paramMon != null && !"".equals(paramMon)) { // "" empty 조건 추가
      cal.set(Calendar.MONTH, Integer.parseInt(paramMon)-1);
}
String paramYear = request.getParameter("param_year");
if (paramYear != null && !"".equals(paramYear)) { // "" empty 조건 추가
      cal.set(Calendar.YEAR, Integer.parseInt(paramYear));
}
...
  • 숙제1 - 오늘을 표현하기(색상)
Calendar cal = Calendar.getInstance();
Calendar todayCal = Calendar.getInstance();
int currYear = todayCal.get(Calendar.YEAR);
int currMon = todayCal.get(Calendar.MONTH);
int currDay = todayCal.get(Calendar.DAY_OF_MONTH);
...
if(currYear == cal.get(Calendar.YEAR) && currMon ==  cal.get(Calendar.MONTH) && currDay == tempDay) {
      todayClass = "today";
} else {
      todayClass = "";
}

pageContext.setAttribute("todayClass", todayClass);
<td class="diaryTd ${ todayClass }"><span class="${ dayClass }"><%=  tempDay %></span></td>

17

  • 숙제2 - 마지막 날이 토요일이면 다음 줄의 공백생성을 막아라
int lastDay=0;
...
      for(int tempDay = 1; ;tempDay++) {
            cal.set(Calendar.DAY_OF_MONTH, tempDay);  
            if (cal.get(Calendar.DAY_OF_MONTH) != tempDay) {
                  
                  for(int  blankTd=cal.get(Calendar.DAY_OF_WEEK); blankTd<8; blankTd++) {
                        if (lastDay == Calendar.SATURDAY) { 
                              // 마지막날이 토요일이면 빠져나옴
                              break; 
                        }
                        out.println("<td  class='blankTd'></td>");
                  }
                  break;
            }
...
            // 마지막날 저장
            lastDay = cal.get(Calendar.DAY_OF_WEEK); 
      }
%>

18

  • 숙제3 - 마지막 날 이후에 다음달의 일자를 1, 2, 3, 4 일 등 공백에 출력
...
if (cal.get(Calendar.DAY_OF_MONTH) != tempDay) {
      int nextCnt = 1; // 다음달 시작 날짜
      for(int blankTd=cal.get(Calendar.DAY_OF_WEEK); blankTd<8;  blankTd++) {
            if (lastDay == Calendar.SATURDAY) {
                  break;
            }
            out.print("<td class='blankTd'>");
            out.print(nextCnt); // 시작날짜를 찍고 
            out.println("</td>");
            nextCnt++; // 증가시킴
      }
      break;
}
...
  • 숙제4 - 1일 이전에 이전 달의 마지막 일자부터 출력.
// 1일을 출력하기 전 공백 출력
switch(tempDay) {
case START_DAY:
      Calendar prevCal = Calendar.getInstance(); // 지난 달을 저장할 Calendar 객체
      prevCal.set(Calendar.MONTH, cal.get(Calendar.MONTH)-1); //  지난달로 설정
      
      int prevLastDay = 0;
      for(int temp = 1; ;temp++) { // 지난 달 마지막 날까지 반복
            prevCal.set(Calendar.DAY_OF_MONTH, temp); 
            if(prevCal.get(Calendar.DAY_OF_MONTH) != temp) { // 다음달로 바뀔 때까지
                  break;
            }
            prevLastDay = prevCal.get(Calendar.DATE); // 마지막 날짜 저장
      }
      
      int cnt = -1;
      for(int blankTd=1; blankTd<cal.get(Calendar.DAY_OF_WEEK);  blankTd++) {
            cnt++; // 공백의 수를 카운트
      }                             
      
      for(int blankTd=1; blankTd<cal.get(Calendar.DAY_OF_WEEK);  blankTd++) {
            out.print("<td class='blankTd'>");
            out.print(prevLastDay - cnt); // 마지막날짜 - 공백 수
            out.println("</td>");
            cnt--;
      }
}


Java EEJSP