• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

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

Java EE 정리 19

25 Mar 2019

Reading time ~11 minutes

Java EE 정리 19 - 일정관리 만들기(4)


일정관리 만들기(4)

  • 진행중인 프로젝트 패키지

  • 게시판 만드는 방법

// 1. 총 게시물의 수를 구함
int totalCnt = " SELECT COUNT(*) cnt FROM diary ";
// 또는
totalCnt = " SELECT num cnt FROM diary "; // PK로 조회
// 검색할 값이 존재하면
// 검색 조건(제목, 내용, 작성자)에 따라 동적 쿼리를 생성해야 한다.
totalCnt += " WHERE 컬럼명 LIKE '%'||?||'%' "

// 2. 한 화면에 보여질 게시물 수 설정
int pageScale = 10;

// 3. 게시물을 나눠서 보여주기 위한 총 페이지 수를 구한다
int totalPage = totalCnt/pageScale;

if (totalCnt%pageScale != 0) { // 딱 나눠 떨어지지 않으면
    totalPage++; // +1
}

// 4. 시작 게시글 번호 구하기
// 사용자가 클릭한 index list 번호를 가지고 시작 게시글 번호를 얻는다.
String currentPage = request.getParameter("currentPage");

int startNum = 1;
if (currentPage != null) {
    int tempPage = Integer.parseInt(currentPage);
    // 시작번호 = 선택된 페이지 * 스케일 - 스케일 + 1
    startNum = tempPage*pageScale - pageScale + 1;
}

// 5. 끝 게시글 번호 구하기
int endNum = 0;
endNum = startNum + pageScale - 1;
  • 검색 조건에 따라 동적 쿼리 생성
    • 총 게시물의 수를 구하는 메소드
// DiaryDAO
...
public int selectEvtCnt(SearchDataVO sdvo) throws SQLException {
      int cnt = 0;
      
      System.out.println(sdvo);
      
      Connection con = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      try {
            con = getConn();
            
            StringBuilder selectEvtCnt = new StringBuilder();
            selectEvtCnt
            .append(" SELECT count(*) cnt ")
            .append(" FROM diary ");
            // 검색조건에 따라 Count할 총 게시글의 수가 달라진다(Dynamic  Query)
            if (sdvo != null) {
                  selectEvtCnt
                  .append(" WHERE  ").append(sdvo.getFieldName()).append(" LIKE '%'||?||'%' ");
                  pstmt = con.prepareStatement(selectEvtCnt.toString());
                  pstmt.setString(1, sdvo.getKeyword());
            } else {
                  pstmt = con.prepareStatement(selectEvtCnt.toString());
            }
            
            rs = pstmt.executeQuery();
            
            if(rs.next()) {
                  cnt = rs.getInt("cnt");
            }
            
      } finally {
            if (rs != null) { rs.close(); }
            if (pstmt != null) { pstmt.close(); }
            if (con != null) { con.close(); }
      }
      
      return cnt;
}
...
  • 게시판 만들기 위한 정보 얻기
<!-- list.jsp -->
...
<%
      DiaryDAO d_dao = DiaryDAO.getInstance();
      SearchDataVO sdvo = null;
      // 1. 전체 페이지 수를 얻기
      int totalCnt = d_dao.selectEvtCnt(sdvo);
      
      // 2. 한 화면에 보여질 게시물의 수
      int pageScale = 10;
      
      // 3. 총 페이지 수 구하기
      // int totalPage = (int)Math.ceil(totalCnt/(double)pageScale);

      int totalPage = totalCnt/pageScale;
      
      if (totalCnt%pageScale != 0) {
            totalPage++;
      }
      
      // 4. 시작 게시글 번호 구하기
      // current_page에 따라 시작번호는 달라진다.
      // 1->1, 2->11, 3->21, 4->31

      String currentPage = request.getParameter("current_page");
      int startNum = 1;

      if (currentPage != null) {
            int tempPage = Integer.parseInt(currentPage);
            startNum = tempPage*pageScale - pageScale + 1;
      }
      
      // 5. 끝 게시글 번호 구하기
      int endNum = startNum + pageScale - 1;
      
%>
전체 페이지 수 : <%= totalCnt %><br/>
한 화면에 보여질 게시물의 수 : <%= pageScale %><br/>
총 페이지 수 : <%= totalPage %><br/>
현재 페이지 번호 : <%= currentPage %><br/>
시작 번호 : <%=startNum %><br/>
끝 번호 : <%=endNum %><br/>
...
<div id="diaryIndexList">
<%
      for(int i=1; i<=totalPage; i++) {
%>
      [<a href="list.jsp?current_page=<%=i %>"><%=i %></a>]
<%          
      }
%>
</div>
...

01

  • pageScale만큼의 수를 가져오는 SQL 쿼리
SELECT num, subject, writer, e_year, e_month, e_date, w_date, ip
FROM (SELECT rownum r, num, subject, writer, e_year, e_month, e_date, w_date, ip
      FROM (SELECT num, subject, writer, e_year, e_month, e_date, w_date, ip
            FROM diary
            ORDER BY w_date DESC))
WHERE r BETWEEN 1 AND 10;

-- 랭크함수 ROW_NUMBER() OVER()를 사용하면 짧게 줄일 수 있다.
SELECT num, subject, writer, e_year, e_month, e_date, w_date, ip
FROM (SELECT num, subject, writer, e_year, e_month, e_date, w_date, ip,                        ROW_NUMBER() OVER(ORDER BY w_date DESC) r_num
      FROM diary)
WHERE r_num BETWEEN 1 AND 10;
  • 시작, 끝 번호를 담는 VO 생성
public class ListRangeVO {
      private int startNum, endNum;
      // 기본생성자, setters, getters, toString 생성
      ...
  • 게시글 목록을 가져오는 메소드
// DiraryDAO
public List<DiaryListVO> selectList(SearchDataVO sdvo, ListRangeVO lrvo)  throws SQLException{
      List<DiaryListVO> list = new ArrayList<DiaryListVO>();
      
      Connection con = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      try {
            con = getConn();
            
            StringBuilder selectList = new StringBuilder();
            
            selectList
            .append(" SELECT num, subject, writer, e_year, e_month,  e_date, w_date")
            .append(" FROM (SELECT num, subject, writer, e_year,  e_month,  ")
            .append("       e_date, w_date, ROW_NUMBER()  OVER(ORDER BY w_date DESC) r_num ")
            .append("       FROM diary) ")
            .append(" WHERE r_num BETWEEN ? AND ? ");
            
            pstmt = con.prepareStatement(selectList.toString());
            pstmt.setInt(1, lrvo.getStartNum());
            pstmt.setInt(2, lrvo.getEndNum());
            
            rs = pstmt.executeQuery();
            
            DiaryListVO dlvo = null;
            while(rs.next()) {
                  dlvo = new DiaryListVO(rs.getShort("num"),
                              rs.getString("subject"),  rs.getString("writer"),
                              rs.getString("e_year"),  rs.getString("e_month"),
                              rs.getString("e_date"),  rs.getString("w_date"));
                  
                  list.add(dlvo);
            }
            
      } finally {
            if (rs != null) { rs.close(); }
            if (pstmt != null) { pstmt.close(); }
            if (con != null) { con.close(); }
      }
      
      return list;
}
  • selectEvtCnt, selectList메소드 호출 예외처리
    • JSTL catch 사용
<c:catch var="e">
<%
      DiaryDAO d_dao = DiaryDAO.getInstance();
      SearchDataVO sdvo = null;
      // 1. 전체 페이지 수를 얻기
      int totalCnt = d_dao.selectEvtCnt(sdvo);
      
      // 2. 한 화면에 보여질 게시물의 수
      int pageScale = 10;
      
      // 3. 총 페이지 수 구하기
      // int totalPage = (int)Math.ceil(totalCnt/(double)pageScale);
      int totalPage = totalCnt/pageScale;
      
      if (totalCnt%pageScale != 0) {
            totalPage++;
      }
      
      // 4. 시작 게시글 번호 구하기
      // current_page에 따라 시작번호는 달라진다.
      // 1->1, 2->11, 3->21, 4->31
      String currentPage = request.getParameter("current_page");
      int startNum = 1;
      if (currentPage != null) {
            int tempPage = Integer.parseInt(currentPage);
            startNum = tempPage*pageScale - pageScale + 1;
      }
      
      // 5. 끝 게시글 번호 구하기
      int endNum = startNum + pageScale - 1;
      
      ListRangeVO lrvo = new ListRangeVO(startNum, endNum);
      List<DiaryListVO> list = d_dao.selectList(sdvo, lrvo);
                  
      pageContext.setAttribute("list", list);
      pageContext.setAttribute("totalPage", totalPage);
%>
</c:catch>
...
<c:if test="${ not empty e }">
<tr>
      <td colspan="5">서비스가 월활하지 못한점 죄송합니다.</td>
</tr>
</c:if>
<c:if test="${ empty e and empty list }">
<tr>
      <td colspan="5">
            이벤트가 존재하지 않습니다.<br/>
            <a href="diary.jsp">이벤트 작성</a>
      </td>
</tr>
</c:if>
<c:forEach var="data" items="${ list }">
<c:set var="i" value="${ i+1 }"/>
<tr>
      <td><c:out value="${ i }"/></td>
      <td><c:out value="${ data.subject }"/></td>
      <td><c:out value="${ data.writer }"/></td>
      <td><c:out value="${ data.e_year }-${ data.e_month }-${  data.e_date }"/></td>
      <td><c:out value="${ data.w_date }"/></td>
</tr>
</c:forEach>
...
<div id="diaryIndexList">
<c:forEach var="i" begin="1" end="${ totalPage }" step="1">       
      [<a href="list.jsp?current_page=${ i }">${ i }</a>]
</c:forEach>
...

02

  • 게시글을 조회하는 걸 클래스로 빼서 만들자
    • 업무 로직을 처리하는 service 패키지
    • jsp에서는 가급적이면 업무로직을 처리하지 않는다.
// kr.co.sist.diary.service.ListService
// 게시판 리스트에 관한 업무 처리 클래스
public class ListService {
      
      private static ListService ls; // 싱글톤 패턴
      
      private ListService() {}
      
      public static ListService getInstance() {
            if (ls == null) {
                  ls = new ListService();
            }
            
            return ls;
      }
      
      public int totalCount(SearchDataVO sdvo) {
            int totalCnt = 0;
            DiaryDAO d_dao = DiaryDAO.getInstance();
            
            // 1. 전체 페이지 수를 얻기
            try {
                  totalCnt = d_dao.selectEvtCnt(sdvo);
            } catch (SQLException e) {
                  e.printStackTrace();
            }
            
            return totalCnt;
      }
      
      public int pageScale() {
            int pageScale = 10;
            
            return pageScale;
      }
      
      public int totalPage(int totalCnt) {
            int totalPage = totalCnt/pageScale();
            
            if (totalCnt%pageScale() != 0) {
                  totalPage++;
            }
            
            return totalPage;
      }
      
      public int startNum(String currentPage) {
            int startNum = 1;
            
            if (currentPage != null) {
                  int tempPage = Integer.parseInt(currentPage);
                  startNum = tempPage*pageScale() - pageScale() + 1;
            }
            
            return startNum;
      }
      
      public int endNum(int startNum) {
            int endNum = startNum + pageScale() - 1;
            return endNum;
      }
      
      public List<DiaryListVO> searchList(SearchDataVO sdvo, ListRangeVO  lrvo) {
            List<DiaryListVO> list = new ArrayList<DiaryListVO>();
            DiaryDAO d_dao = DiaryDAO.getInstance();
            
            try {
                  list = d_dao.selectList(sdvo, lrvo);
                  
                  DiaryListVO dlvo = null;
                  String subject = "";
                  // 글의 제목은 24자 까지만 보여준다.
                  for(int i=0; i<list.size(); i++) {
                        dlvo = list.get(i);
                        subject= dlvo.getSubject();
                        
                        if( subject.length() > 25 ) {
                              subject = subject.substring(0, 24)+"...";
                              dlvo.setSubject(subject);
                        }
                  }
                  
            } catch (SQLException e) {
                  e.printStackTrace();
            }
            
            return list;
      }
}
<!-- list.jsp -->
...
<%
      // 업무 로직을 클래스로 빼서 JSP가 깔끔해졌다.
      ListService ls = ListService.getInstance();

      request.setCharacterEncoding("UTF-8"); // POST 한글처리

      // 1. 전체 페이지 수를 얻기
      int totalCnt = ls.totalCount(null);
      
      // 2. 한 화면에 보여질 게시물의 수
      int pageScale = ls.pageScale();
      
      // 3. 총 페이지 수 구하기
      int totalPage = ls.totalPage(totalCnt);
      
      // 4. 시작 게시글 번호 구하기
      String currentPage = request.getParameter("current_page");
      int startNum = ls.startNum(currentPage);
      
      // 5. 끝 게시글 번호 구하기
      int endNum = ls.endNum(startNum);
      
      ListRangeVO lrvo = new ListRangeVO(startNum, endNum);
      List<DiaryListVO> list = ls.searchList(null, lrvo);
                  
      pageContext.setAttribute("list", list);
      pageContext.setAttribute("totalPage", totalPage);
%>
...

03

  • 인덱스리스트도 메소드로 구현
    • c:out은 EL을 태그로 인식안해서 EL만 써줘야 함
// ListService
public String indexList(String url, int totalPage) {
      StringBuilder indexList = new StringBuilder();
      
      for(int i=1; i<= totalPage; i++) {
            indexList.append("[ ").append("<a href='")
            .append(url).append("?current_page=")
            .append(i).append("'>").append(i).append("</a> ]");
      }
      
      return indexList.toString();
}
<!-- list.jsp -->
...
      String indexList = ls.indexList("list.jsp", totalPage);
      pageContext.setAttribute("indexList", indexList);
%>
</c:catch>
...
<div id="diaryIndexList">
${ indexList }
</div>

04

  • 검색 기능 추가
    • 제목, 내용, 작성자(fieldName) 선택
    • 버튼 이벤트 처리
$(function() {
      $("#keyword").keydown(function(evt) {
            if(evt.which == 13) { // 엔터가 입력되면
                  if($("#keyword").val() == "") {
                        $("#keyword").focus();
                        alert("검색할 키워드를 입력해주세요.");
                        return;
                  }
            }
      });
      
      $("#searchBtn").click(function() {
            if($("#keyword").val() == "") {
                  alert("검색할 키워드를 입력해주세요.");
                  $("#keyword").focus();
                  return;
            }
            
            $("#searchFrm").submit();
      });
});

05

<%    // list.jsp
      ListService ls = ListService.getInstance();
      String keyword = request.getParameter("keyword");
      String fieldName = request.getParameter("fieldName");
      
      SearchDataVO sdvo = null;
      if (keyword != null && !"".equals(keyword)) { // 사용자가 검색값을  넣었을 때
            sdvo = new SearchDataVO(fieldName, keyword);
      }

      // 1. 전체 페이지 수를 얻기/ 검색에 맞는 게시물 수 얻기
      int totalCnt = ls.totalCount(sdvo); // 검색 시 sdvo가 null이 아님
...
  • 검색 조건에 따라 동적 쿼리를 수행
// DiaryDAO
...
public int selectEvtCnt(SearchDataVO sdvo) throws SQLException {
      int cnt = 0;
      
      Connection con = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      try {
            con = getConn();
            
            StringBuilder selectEvtCnt = new StringBuilder();
            selectEvtCnt
            .append(" SELECT COUNT(*) cnt ")
            .append(" FROM diary ");

            // 검색조건에 따라 Count할 총 게시글의 수가 달라진다(Dynamic Query)
            if (sdvo != null) {
                  selectEvtCnt
                  .append(" WHERE  ").append(sdvo.getFieldName()).append(" LIKE '%'||?||'%'" );
                  pstmt = con.prepareStatement(selectEvtCnt.toString());
                  pstmt.setString(1, sdvo.getKeyword());
            } else {
                  pstmt = con.prepareStatement(selectEvtCnt.toString());
            }
...
public List<DiaryListVO> selectList(SearchDataVO sdvo, ListRangeVO lrvo)  throws SQLException{
      List<DiaryListVO> list = new ArrayList<DiaryListVO>();
      
      Connection con = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      try {
            con = getConn();
            
            StringBuilder selectList = new StringBuilder();
            
            selectList
            .append(" SELECT num, subject, writer, e_year, e_month,  e_date, to_char(w_date, 'yyyy-mm-dd hh24:mi') w_date ")
            .append(" FROM (SELECT num, subject, writer, e_year,  e_month,  ")
            .append("       e_date, w_date, ROW_NUMBER()  OVER(ORDER BY w_date DESC) r_num ")
            .append("       FROM diary ");
            
            if (sdvo != null) {
                  selectList.append(" WHERE  ").append(sdvo.getFieldName())
                  .append(" LIKE '%'||?||'%' ");
            }
            selectList.append(") WHERE r_num BETWEEN ? AND ? ");
            
            pstmt = con.prepareStatement(selectList.toString());

            int bindIdx = 1; // 바인드 idx 변수로 중복코드를 줄일 수 있다.
            if (sdvo != null) {
                  pstmt.setString(bindIdx++, sdvo.getKeyword());
            }
            pstmt.setInt(bindIdx++, lrvo.getStartNum());
            pstmt.setInt(bindIdx++, lrvo.getEndNum());

            rs = pstmt.executeQuery();
...
  • ‘작성자’, ‘내용’ 검색을 수행하면 ‘제목’으로 초기화됨
    • 유지가 되도록 변경
<form action="list.jsp" method="post" id="searchFrm" name="searchFrm">
      <select name="fieldName" class="inputBox" id="fieldName">
            <option value="subject"${ param.fieldName eq 'subject' ?"  selected='selected'" : "" }>제목</option>
            <option value="content"${ param.fieldName eq 'content' ?"  selected='selected'" : "" }>내용</option>
            <option value="writer"${ param.fieldName eq 'writer' ?"  selected='selected'" : "" }>작성자</option>
      </select>
      <input type="text" name="keyword" class="inputBox"  style="width:150px" id="keyword" value="${ param.keyword }"/>
      <input type="button" value="검색" id="searchBtn" class="btn"  style="width:30px;"/>
</form>

06

  • 검색 후 인덱스 리스트를 누르면 초기화 되버린다.
    • 인덱스리스트 클릭 시 검색 정보도 같이 요청되어야 됨

07

<!-- list.jsp -->
...
      // indexList를 만들 때 sdvo도 전달
      String indexList = ls.indexList("list.jsp", sdvo, totalPage);
      pageContext.setAttribute("indexList", indexList);
%>
// ListService
public String indexList(String url, SearchDataVO sdvo, int totalPage) {
      StringBuilder indexList = new StringBuilder();
      
      for(int i=1; i<= totalPage; i++) {
            indexList.append("[ ").append("<a href='")
            .append(url).append("?current_page=")
            .append(i);
            if (sdvo != null) {
                  indexList.append("&fieldName=").append(sdvo.getFieldName())
                  .append("&keyword=").append(sdvo.getKeyword());
            }
            indexList.append("'>").append(i).append("</a> ]");
      }
      
      return indexList.toString();
}

08

  • ‘전체글’ 버튼을 추가해서 검색 결과에서 초기화
...
<div id="diary">  
      <div id="diaryHeader">
      <sapn style="float:left;"><a href="list.jsp"><img  src="images/btn_all.png"/></a></sapn>
      이벤트 목록
      </div>
...

09

  • 게시글 번호를 역순으로 나타내기
      pageContext.setAttribute("bbsIdx",  totalCnt-(Integer.parseInt(currentPage)-1)*pageScale);
%>
...
      <c:forEach var="data" items="${ list }">
      <c:set var="bbsIdx" value="${ bbsIdx }"/>
      <tr>
            <td><c:out value="${ bbsIdx }"/></td>
            <td><c:out value="${ data.subject }"/></td>
            <td><c:out value="${ data.writer }"/></td>
            <td><c:out value="${ data.e_year }-${ data.e_month }-${  data.e_date }"/></td>
            <td><c:out value="${ data.w_date }"/></td>
      </tr>
      <c:set var="bbsIdx" value="${ bbsIdx-1 }"/>
      </c:forEach>
</table>
  • indexList를 c:out으로 출력하고 싶을 때 “escapeXml”속성을 false로 준다.
<div id="diaryIndexList">
    <c:out value="${ indexList }" escapeXml="false"/>
</div>
  • 게시글 읽기
<td><a href="read.jsp?num=${ data.num }"><c:out value="${ data.subject  }"/></a></td>
  • read.jsp는 read.form 그대로 복 붙, form action속성만 변경
...
<form action="list.jsp" method="post" name="readFrm">
...
<!-- x버튼 삭제 -->
...
    <td style="width:400px">
        <%= ddvo.getContent() %>
    </td>
...
<!-- 비밀번호, 이벤트일 삭제 -->
...
    <td colspan="2" align="center">
        <a href="#void" onclick="history.back();">글읽기</a>
    </td>

10

  • 예외처리
<%
      DiaryDAO d_dao = DiaryDAO.getInstance();
      
      try{
            int num = Integer.parseInt(request.getParameter("num"));
            DiaryDetailVO ddvo = d_dao.selectDetailEvent(num);
            
            if (ddvo == null) {
                  throw new NullPointerException();
            }
%>
...
      <td style="width:400px">
          <div id="subject" ><strong><%= ddvo.getSubject()  %></strong></div>
      </td>
...
<%
      } catch (NumberFormatException nfe) {
%>
      유효하지 않은 파라미터가 입력되었습니다.<br/>   
<%
      } catch (NullPointerException npe) {
%>
      해당 글을 찾을 수 없습니다.<br/>    
<%
      } catch (SQLException se) {
%>
      <img src="images/construction.jpg" title="죄송합니다."/>
<%
      }
%>
</div>
  • 일정정보를 조회할 때 CLOB 데이터형을 getString으로 가져오는건 틀린 방법
    • CLOB은 getClob을 사용해서 처리한다.
public DiaryDetailVO selectDetailEvent(int num) throws SQLException,  IOException{
      DiaryDetailVO ddvo = null;
      
      Connection con = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      BufferedReader br = null;
      
      try {
            con = getConn();
            
            StringBuilder selectOneEvt = new StringBuilder();
            selectOneEvt
            .append(" SELECT WRITER, SUBJECT, CONTENT, TO_CHAR(W_DATE,  'YYYY-MM-DD DY HH24:MI') W_DATE, IP ")
            .append(" FROM DIARY ")
            .append(" WHERE NUM=? ");
            
            pstmt = con.prepareStatement(selectOneEvt.toString());
            pstmt.setInt(1, num);
            
            rs = pstmt.executeQuery();
            
            if(rs.next()) {
                  // CLOB(Character Large Object) 처리
                  Clob clob = rs.getClob("content");

                  // CLOB처리를 위해 별도의 스트림을 연결
                  br = new BufferedReader(clob.getCharacterStream());
                  
                  String temp = "";
                  StringBuilder content = new StringBuilder();
                  while((temp = br.readLine()) != null) {
                        content.append(temp);
                  }
                  
                  ddvo = new DiaryDetailVO(rs.getString("writer"),
                        rs.getString("subject"), content.toString(),
                        rs.getString("w_date"), rs.getString("ip"));
            }
            
      } finally {
            if (br != null) { br.close(); }
            if (rs != null) { rs.close(); }
            if (pstmt != null) { pstmt.close(); }
            if (con != null) { con.close(); }
      }
      
      return ddvo;
}
<!-- read_form.jsp -->
...
<div id="readFrm">
<%
      DiaryDAO d_dao = DiaryDAO.getInstance();
      
      try{
            int num = Integer.parseInt(request.getParameter("num"));
            DiaryDetailVO ddvo = d_dao.selectDetailEvent(num);
%>
...
</form>
<%
      } catch (IOException ie) {
%>
      <img src="images/construction.jpg" title="죄송합니다."/>
<%
      } catch (SQLException se) {
%>
      <img src="images/construction.jpg" title="죄송합니다."/>
<%
      }
%>
</div>
<!-- read.jsp -->
...
<%
      DiaryDAO d_dao = DiaryDAO.getInstance();
      
      try{
            int num = Integer.parseInt(request.getParameter("num"));
            DiaryDetailVO ddvo = d_dao.selectDetailEvent(num);
            
            if (ddvo == null) {
                  throw new NullPointerException();
            }
%>
...
<%
      } catch (IOException ie) {
%>
      글의 내용을 읽어들이지 못했습니다.<br/>   
<%
      } catch (NumberFormatException nfe) {
%>
      유효하지 않은 파라미터가 입력되었습니다.<br/>   
<%
      } catch (NullPointerException npe) {
%>
      해당 글을 찾을 수 없습니다.<br/>    
<%
      } catch (SQLException se) {
%>
      <img src="images/construction.jpg" title="죄송합니다."/>
<%
      }
%>
</div>


Java EEJSP