370 likes | 758 Views
JSP BBS (Bulletin Board System). Internet Computing Laboratory @ KUT Youn-Hee Han. Design. 게시물에 할당해야 할 값 일렬 번호 ( 테이블 필드명 : THEME_MESSAGE_ID) 게시물마다 고유의 값을 가지면 글이 추가될 때 마다 1 씩 증가 그룹 번호 (GROUP_ID) 연관된 게시물들이 공유하는 번호 주 게시물 및 그 게시물의 모든 답글은 같은 그룹 번호를 가짐 부모 번호 (PARENT_ID) 주 게시물은 항상 0 을 지님
E N D
JSP BBS (Bulletin Board System) Internet Computing Laboratory @ KUT Youn-Hee Han
Design • 게시물에 할당해야 할 값 • 일렬 번호 (테이블 필드명: THEME_MESSAGE_ID) • 게시물마다 고유의 값을 가지면 글이 추가될 때 마다 1씩 증가 • 그룹 번호 (GROUP_ID) • 연관된 게시물들이 공유하는 번호 • 주 게시물 및 그 게시물의 모든 답글은 같은 그룹 번호를 가짐 • 부모 번호 (PARENT_ID) • 주 게시물은 항상 0을 지님 • 답변 게시물은 해당 주 게시물의 일렬 번호를 부모 번호로 가짐 • 출력 순서 (ORDER_NO) • 주 게시물은 항상 0을 지님 • 같은 그룹 내의 답변 게시물 들에 대한 출력 순서를 저장 • 단계값 (LEVEL) • 주 게시물은 항상 1을 지님 • 얼마나 중첩이 된 답변 게시물인지를 나타냄 Web Programming
Design 게시물에 할당해야 할 값 Primary Key 입력 순서 1 2 입력 순서 1 3 2 Web Programming
Design 게시물에 할당해야 할 값 입력 순서 1 4 3 2 입력 순서 1 3 5 4 2 Web Programming
Design 게시물에 할당해야 할 값 입력 순서 1 3 5 6 4 2 입력 순서 1 3 5 6 7 4 2 Web Programming
DB Schema for BBS • 데이터베이스 Table 목록 • THEME_MESSAGE(, NOTICE_MESSAGE, STUDY_MESSAGE…) • THEME_CONTENT(, NOTICE_CONTENT, STUDY_CONTENT…) Web Programming
DB Schema for BBS • 데이터베이스 Table 목록 • ID_SEQUENCE • 인덱스 • THEME_MESSAGE_INDEX Web Programming
컴파일러준비 • sjc.bat • 다음과 같이 재정비 할 필요 • 기본 위치는 java 설치 디렉토리의 bin 디렉토리 밑 • 하지만, \2006777888\WEB-INF\src 밑에 두고 써도 상관없음 • 그러나, 시스템 전체에 sjc.bat는 하나만 존재해야 함! set CLASSPATH= set CLASSPATH=%CLASSPATH%;D:\jakarta-tomcat-5.0.19\webapps\2006777888\WEB-INF\classes;D:\jakarta-tomcat-5.0.19\common\lib\servlet-api.jar javac -d D:\jakarta-tomcat-5.0.19\webapps\2006777888\WEB-INF\classes %1 Web Programming
DB 준비 • 데이터베이스 준비 • MySQL 4.0.18 설치 • 압축 해제 디렉토리: d:\install_mysql • 설치 디렉토리: d:\mysql • d:\mysql\bin\winmysqladmin.exe 을실행한다. • User ID는 root로 PASSWD는 적절하게 넣어준다. • 도스창에서 mysqladmin -p create theme • d:\mysql\bin\ 에서수행 • 패스워드는 아무것도 넣지 말고 엔터 누름 • theme.sql 다운받기 • d:\mysql\bin\ 에 다운받는다. • 도스창에서 mysql -p theme < theme.sql • d:\mysql\bin\ 에서수행 • 패스워드는 아무것도 넣지 말고 엔터 누름 패스워드는디폴트로 없이 수행 Web Programming
WEB Application 준비 • kut.ime.jdbcdriver.DBCPInit 서블릿 클래스 • DBCPInit.java 다운로드 • 다운로드 디렉토리: \2006777888\WEB-INF\src • 패키지명 유의 • 컴파일 Web Programming
Connection Pool 및 JDBC 드라이버 로드 • Connection Pool 설정 • pool.jocl 다운로드: /2006777888/WEB-INF/classes 밑에 저장 • 교재 474 페이지 참고 • JDBC Driver 사용을 위한 web.xml 수정 • 교재 475 페이지 참고 • web.xml 내에 다음을 추가 … <servlet> <servlet-name>DBCPInit</servlet-name> <servlet-class>kut.ime.jdbcdriver.DBCPInit</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>jdbcdriver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> </servlet> … Web Programming
Connection Pool 및 JDBC 드라이버 로드 • DB 이용을 위한 라이브러리 다운로드 • 다운로드 위치: /2006777888/WEB-INF/lib • 다운로드할 파일 • classes12.jar • commons-collections-3.1.jar • commons-dbcp-1.2.1.jar • commons-pool-1.2.jar • mysql-connector-java-3.0.14-production-bin.jar • Tomcat 재시작 • DB 테스트 2006777888/bbs/test.jsp <%@ page import="java.sql.*"%> <% Connection conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:/"+"pool"); ResultSet rs = conn.createStatement().executeQuery("show tables;"); while(rs.next()) { out.println(rs.getString(1) + "<BR>"); } %> pool.jocl Web Programming
보조 클래스 • kut.ime.util.DBUtil 클래스 2006777888/WEB-INF/src/DBUtil.java package kut.ime.util; import java.sql.DriverManager; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { public static Connection getConnection(String poolName) throws SQLException { return DriverManager.getConnection( "jdbc:apache:commons:dbcp:/"+poolName); } } 이후모든 .java 소스는 다운 받아서 sjc 로컴파일 필요
보조 클래스 • kut.ime.bbs.Sequencer 클래스 (교재 p.476) • 다음의 1개 메소드 제공 • public synchronized static int nextID(Connection conn, String tableName) • ID_SEQUENCE 테이블에 저장된 게시물들의 가장 큰 일련 번호인 MESSAGE_ID 필드 값을 얻어와 하나 증가된 값을 리턴해줌 • 사용법 • intid = Sequencer.nextId(conn, “THEME_MESSAGE”); • kut.ime.bbs.Theme 클래스 (교재 p.477~479) • 자바빈 클래스 • THEME_MESSAGE 테이블과 THEME_CONTENT 테이블의 내용을 모두 포함 • 용도 • 새로운 게시물을 입력할 때 • 게시물을 얻어올 때 • 게시물을 업데이트 할 때 • kut.ime.bbs.ThemeManagerException 클래스 (교재 p.480) • 본 JSP BBS 관련 작업 처리 중 발생하는 예외를다른 예외들과 구별하기 위하여 필요함
주클래스: ThemeManager • ThemeManager 클래스 • JSP BBS의 대부분의 기능을 수행하는 클래스 • 메소드 목록 • publicstatic ThemeManager getInstance(); • ThemeManager의 인스턴스를 얻어옴 • public void insert(Theme theme) throws ThemeManagerException; • 게시판에 새로운 글을 삽입할 때 사용 • public int count(List whereCond, Map valueMap) throws ThemeManagerException; • 특정 조건에 만족하는 게시물의 총 개수를 구함 • public List selectList(List whereCond, Map valueMap, int startRow, int endRow) throws ThemeManagerException; • 특정 조건에 만족하는 게시물을 목록을 가져 올때 사용 • public int select(int id) throws ThemeManagerException; • 하나의게시물을 읽어 올 때 사용 • public int update(Theme theme) throws ThemeManagerException; • 게시물 하나를 업데이트 할 때 사용 • public int delete(int id) throws ThemeManagerException; • 게시물 하나를 삭제 할 때 사용
클래스 배치 현재까지 클래스 컴파일 결과 모습
이미지 업로드&핸들링을 대비한 사전 준비 작업 • 폴더 생성 • 업로드 이미지 임시 저장소 • D:\jakarta-tomcat-5.0.19\webapps\2006777888\bbs\temp • 업로드 이미지 저장소 • D:\jakarta-tomcat-5.0.19\webapps\2006777888\bbs\image • ImageUtil 클래스 배치 • FileUploadRequestWrapper 클래스 배치
이미지 업로드&핸들링을 대비한 사전 준비 작업 • 라이브러리 재확인 • TOMCAT 재시작!!!
화면 출력용 JSP 구성 • BBS.zip 다운로드 • 다운로드 위치: 2006777888/bbs/ • write.jsp 에서 2006777888을 자신의 폴더 이름으로 변경 • 화면 레이아웃과 모듈화 • 교재 494 페이지 참조 2006777888/bbs/template/template.jsp 2006777888/bbs/module/top.jsp 2006777888/bbs/list_view.jsp 2006777888/bbs/writeForm_view.jsp 2006777888/bbs/read_view.jsp 2006777888/bbs/updateForm_view.jsp 2006777888/bbs/deleteForm_view.jsp 2006777888/bbs/module/bottom.jsp
화면 출력용 JSP 구성 • JSP 구성 및 연결도 (아래파일들은 2006777888/bbs 아래에 위치함) • 게시물 목록 출력 • 새 게시물 입력 /module/top.jsp list.jsp /template/template.jsp list_view.jsp /module/bottom.jsp /module/top.jsp writeForm.jsp /template/template.jsp writeForm_view.jsp /module/bottom.jsp form action javascript location.href = “list.jsp” write.jsp
화면 출력용 JSP 구성 • JSP 구성 및 연결도 • 게시물 읽기 • 게시물 수정 /module/top.jsp read.jsp /template/template.jsp read_view.jsp /module/bottom.jsp /module/top.jsp updateForm.jsp /template/template.jsp updateForm_view.jsp /module/bottom.jsp form action javascript document.move.action = "list.jsp"; document.move.submit(); list.jsp update.jsp
화면 출력용 JSP 구성 • JSP 구성 및 연결도 • 게시물 삭제 /module/top.jsp deleteForm.jsp /template/template.jsp deleteForm_view.jsp /module/bottom.jsp form action javascript location.href = “list.jsp” list.jsp delete.jsp
BBS 수행 모습 • 수행 모습 • 데이터 베이스 모습
게시물 목록 출력 코드 • 게시물 목록 출력 코드설명 (검색조건 없을 경우) • 관련 코드 리스트 • 코드 리스트 18.18, 18.8, 18.9 • 출력할페이지 번호 설정 • currentPage 변수 설정 • searchCond, searchKey 는 null 이라고 가정 • 따라서, whereCond, whereValue, searchCondName, searchCondTitle 모두 null • 글의 전체 개수를 얻어옴 • intcount = manager.count(whereCond, whereValue); • 수행되는 Query 문 • select count(*) from THEME_MESSAGE • 전체 페이지 개수와 읽어 올 시작행, 끝행을 구한다. • totalPageCount, startRow, endRow 설정
게시물 목록 출력 코드 • 게시물 목록 출력 코드설명 (검색조건 없을 경우) – 계속 • 글 목록을 읽어 옴 • Listlist = manager.selectList(whereCond, whereValue, startRow-1, endRow-1); • 수행되는 Query 문 • select * from THEME_MESSAGE order by GROUP_ID desc, ORDER_NO asc limit ?, ? • Java 코딩에서 Query 구성 법 • EL과 JSTL을 이용해서 글 목록 출력 • 글목록 및 하나의 게시글 보기에 대한 링크 생성 • 자바 스크립트와 form을 활용 • 페이지 이동 링크 출력 ([이전], [1], [2], …., [다음]) • 검색 폼 출력 StringBuffer query = new StringBuffer(200); query.append(“select * from THEME_MESSAGE “); query.append(“order by GROUP_ID desc, ORDER_NO asc limit ?, ?”); Connection conn = DBUtil.getConnection(POOLNAME); PreparedStatement pstmtMessage = conn.prepareStatement(query.toString()); pstmtMessage.setInt(valueMap.size() + 1, startRow); pstmtMessage.setInt(valueMap.size() + 2, endRow- startRow + 1); ResultSet rsMessage = pstmtMessage.executeQuery(); 리스트 18.10
게시물 목록 출력 코드 value: title • 게시물 목록 출력 코드설명 (검색조건 있을 경우) • 관련 변수 선언 • 검색조건 구성 value: name name: search_cond name: search_key String[] searchCond = request.getParameterValues("search_cond"); String searchKey = request.getParameter("search_key"); boolean searchCondName = false; boolean searchCondTitle = false; List whereCond = new java.util.ArrayList(); Map whereValue = new java.util.HashMap(); 리스트 18.8 for (int i = 0 ; i < searchCond.length ; i++) { if (searchCond[i].equals("name")) { whereCond.add("NAME = ?"); whereValue.put(new Integer(1), searchKey); searchCondName = true; } else if (searchCond[i].equals("title")) { whereCond.add("TITLE LIKE '%"+searchKey+"%'"); searchCondTitle = true; } } 리스트 18.8 whereCond 구성 예 ( List) : < “NAME=?”, “TITLE LIKE ‘%공지%’ ”> whereValue 구성 예 ( Map) : < (new Integer(1), ”한연희”), >
게시물 목록 출력 코드 • 게시물 목록 출력 코드설명 (검색조건 있을 경우) – 계속 • 글의 전체 개수를 얻어옴 • intcount = manager.count(whereCond, whereValue); • 수행되는 Query 문 • select count(*) from THEME_MESSAGE whereNAME=한연희 • select count(*) from THEME_MESSAGE whereNAME=한연희 or title like ‘%공지%’ • Java 코딩에서 Query 구성 법 StringBuffer query = new StringBuffer(200); query.append("select count(*) from THEME_MESSAGE "); if (whereCond != null && whereCond.size() > 0) { query.append("where "); for (int i = 0 ; i < whereCond.size() ; i++) { query.append(whereCond.get(i)); if (i < whereCond.size() -1 ) { query.append(" or "); } } } pstmt = conn.prepareStatement(query.toString()); Iterator keyIter = valueMap.keySet().iterator(); Integer key = (Integer)keyIter.next(); Object obj = valueMap.get(key); pstmt.setString(key.intValue(), (String)obj); 리스트 18.9
게시물 목록 출력 코드 • 게시물 목록 출력 코드설명 (검색조건 있을 경우) – 계속 • 글 목록을 읽어 옴 • Listlist = manager.selectList(whereCond, whereValue, startRow-1, endRow-1); • 수행되는 Query 문 • select * from THEME_MESSAGE whereNAME=한연희 or title like ‘%공지%’ order by GROUP_ID desc, ORDER_NO asc limit ?, ? • Java 코딩에서 Query 구성 법 • 글전체 개수를 구하는 코드에서 Query 구성법과 비슷 • 검색 조건 없을 때의 Query 구성법과 병합하여 최종 Query를구성!!! • 새 게시물 입력 링크 • 관련 코드 <td colspan="4" align="right"><a href="writeForm.jsp">[이미지등록]</a></td>
게시물 목록 출력 코드 • 파라미터 값 갖는 링크 구성 • 링크 구성 시 활용하는 폼 • 게시물 링크 리스트 18.8 <form name="move" method="post"> <input type="hidden" name="id" value=""> <input type="hidden" name="page" value="${currentPage}"> <c:if test="<%= searchCondTitle %>"> <input type="hidden" name="search_cond" value="title"> </c:if> <c:if test="<%= searchCondName %>"> <input type="hidden" name="search_cond" value="name"> </c:if> <c:if test="${! empty param.search_key}"> <input type="hidden" name="search_key" value="${param.search_key}"> </c:if> </form> <a href="javascript:goView(${theme.id})">${theme.title}</a> . . <scriptlanguage=“JavaScript”> function goView(id) { document.move.action = "read.jsp"; document.move.id.value = id; document.move.submit(); } </script> 리스트 18.8
게시물 목록 출력 코드 • 파라미터 값 갖는 링크 구성 • 페이지번호 링크 <c:set var="count" value="<%= Integer.toString(count) %>" /> <c:set var="PAGE_SIZE" value="<%= Integer.toString(PAGE_SIZE) %>" /> <c:set var="currentPage" value="<%= Integer.toString(currentPage) %>" /> <c:if test="${count > 0}"> <c:set var="pageCount" value="${count / PAGE_SIZE + (count % PAGE_SIZE == 0 ? 0 : 1)}" /> <c:set var="startPage" value="${currentPage - (currentPage % 10) + 1}" /> <c:set var="endPage" value="${startPage + 10}" /> <c:if test="${endPage > pageCount}"> <c:set var="endPage" value="${pageCount}" /> </c:if> <c:if test="${startPage > 10}"> <a href="javascript:goPage(${startPage - 10})">[이전]</a> </c:if> <c:forEach var="pageNo" begin="${startPage}" end="${endPage}"> <c:if test="${currentPage == pageNo}"><b></c:if> <a href="javascript:goPage(${pageNo})">[${pageNo}]</a> <c:if test="${currentPage == pageNo}"></b></c:if> </c:forEach> <c:if test="${endPage < pageCount}"> <a href="javascript:goPage(${startPage + 10})">[다음]</a> </c:if> </c:if> 리스트 18.8 <scriptlanguage=“JavaScript”> function goPage(pageNo) { document.move.action = "list.jsp"; document.move.page.value = pageNo; document.move.submit(); } </script>
게시물 보기 코드 • 게시물 보기 코드 설명 • 게시물 읽어 오기 • 사용하는 SQL Query 문 • select * from THEME_MESSAGE where THEME_MESSAGE_ID = ? • select CONTENT from THEME_CONTENT where THEME_MESSAGE_ID = ? • Theme 자바빈 구성 String themeId = request.getParameter("id"); ThemeManager manager = ThemeManager.getInstance(); Theme theme = manager.select(Integer.parseInt(themeId)); 리스트 18.23 Theme theme = new Theme(); theme.setId(rsMessage.getInt("THEME_MESSAGE_ID")); theme.setGroupId(rsMessage.getInt("GROUP_ID")); . . theme.setContent(buffer.toString()); . . return theme;
게시물 보기 코드 • 게시물 보기 코드 설명 • 링크 구성 시 활용하는 폼 <form name="move" method="post"> <input type="hidden" name="id" value=“${theme.id}"> <input type="hidden" name=“parentId" value=“${theme.id}"> <input type="hidden" name=“groupId" value=“${theme.groupId}"> <input type="hidden" name="page" value="${param.page}"> <c:forEach var="searchCond" items="${paramValues.search_cond}"> <c:if test="${searchCond == 'title'}"> <input type="hidden" name="search_cond" value="title"> </c:if> <c:if test="${searchCond == 'name'}"> <input type="hidden" name="search_cond" value="name"> </c:if> </c:forEach> <c:if test="${! empty param.search_key}"> <input type="hidden" name="search_key" value="${param.search_key}"> </c:if> </form> 리스트 18.23
게시물 보기 코드 • 게시물 보기 코드 설명 • 다양한링크구성 <tr> <td colspan="2"> <a href="javascript:goReply()">[답변]</a> <a href="javascript:goModify()">[수정]</a> <a href="javascript:goDelete()">[삭제]</a> <a href="javascript:goList()">[목록]</a> </td> </tr> <script language="JavaScript"> function goReply() { document.move.action = "writeForm.jsp"; document.move.submit(); } function goModify() { document.move.action = "updateForm.jsp"; document.move.submit(); } function goDelete() { document.move.action = "deleteForm.jsp"; document.move.submit(); } function goList() { document.move.action = "list.jsp"; document.move.submit(); } function viewLarge(imgUrl) { } </script> 리스트 18.23
게시물 입력 코드 • 게시물 입력 코드 설명 • writeForm_view.jsp write.jsp • 답변글일 경우 parentId 및 부모글의 제목 읽음 • 부모글은 theme 변수에 저장 • 폼 입력시 hidden 필드 구성 • 폼 입력 내용에 대한 validation <input type="hidden" name="level" value="${theme.level + 1}"> <c:if test="${! empty param.groupId}"> <input type="hidden" name="groupId" value="${param.groupId}"> </c:if> <c:if test="${! empty param.parentId}"> <input type="hidden" name="parentId" value="${param.parentId}"> </c:if> 리스트 18.20 <form action="write.jsp" method="post" enctype="multipart/form-data" onSubmit="return validate(this)"> 리스트 18.20
게시물 입력 코드 • 게시물 입력 코드 설명 • writeForm_view.jsp이 전달해준 입력 폼 내용을 write.jsp에서 받기 • 업로드 이미지 저장 및 썸네일 이미지 저장 • theme 빈 내용 추가 및 DB 저장 <jsp:useBean id="theme" class="kut.ime.bbs.Theme"> <jsp:setProperty name="theme" property="*" /> </jsp:useBean> 리스트 18.21 theme.setRegister(new Timestamp(System.currentTimeMillis())); theme.setImage(image); ThemeManager manager = ThemeManager.getInstance(); manager.insert(theme); 리스트 18.21
게시물 입력 코드 • 게시물 입력 코드 설명 • 답변글이 아닌 경우 제일 큰 그룹번호를 구하는 SQL Query • select max(GROUP_ID) from THEME_MESSAGE • 답변글인 경우 같은 부모를 지닌 답글 중 제일 큰 순서 번호를 구하는 SQL Query • select max(ORDER_NO) from THEME_MESSAGE where PARENT_ID = [theme.getParentId()] or THEME_MESSAGE_ID = [theme.getParentId()] • 답변글을저장할 경우 새롭게 저장될 순서 번호에 맞추어 기존글들의 순서 번호 갱신하는 SQL Query • update THEME_MESSAGE set ORDER_NO = ORDER_NO + 1 where GROUP_ID = [theme.getGroupId()] and ORDER_NO >= [theme.getOrderNo()] • 새로운글의 일련 번호를 구함 • 새로운글을 삽입하는 SQL Query • insert into THEME_MESSAGE values (?,?,?,?,?,?,?,?,?,?,?)"); • insert into THEME_CONTENT values (?,?) theme.setId(Sequencer.nextId(conn, "THEME_MESSAGE")); 리스트 18.8