ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 27. Model2 방식 - 게시판
    개발자 수업/JSP 2021. 12. 22. 23:24

    1. 답변형 게시판 테이블 (t_board)
            컬럼이름          속성         자료형      크기    Null 여부   Key     기본값
        1) articleNo         글 번호        number      10        N        PK
           parentNo       부모 글 번호      number      10        N                  0
           title            글 제목        varchar2    100        N       
           content          글 내용        varchar2    4000       Y               
           imageFileName 이미지 파일 이름   varchar2    100       Y
           writeDate        작성일          date                  N               sysdate
           id              작성자 ID       varchar2     20        N        FK

    2. 단위 기능 (논리적인 기능)
        1) 예
            - 게시판 글 조회시 해당 글을 조화하는 기능과 조회수를 갱신하는 기능
            - 쇼핑몰에서 상품 주문시 주문 상품을 테이블에 등록 후 주문자의 포인트 갱신하는 기능
            - 은행에서 송금시 송금자의 잔고를 갱신하는 기능과 수신자의 잔고를 갱신하는 기능
        2) View             Controller              Model
                                                ----------------------
                                                    Service     DAO

    3. 계층형 질의 (Hierarchical query)
        1) 테이블에 계층형 데이터가 존재하는 경우 조회하기 위해 계층형 질의를 사용
        2) 계층형 데이터
            - 동일 테이블에 계층적으로 상위와 하위 데이터가 포함된 데이터를 말함
        3) 오라클 계층형 SQL
            - SELECT
              FROM table
              WHERE
              START WITH 조건               -> 계층 구조 전개의 시작 위치를 지정하는 구문. 루트 데이터 지정함
              CONNECT BY PRIOR a AND b      -> CONNECT BY : 전개될 자식 데이터를 지정하는 구문
                                               PRIOR (PK, 자식) = (FK, 부모) : 계층 구조에서 부모 데이터에서 자식 데이터 방향으로 전개
                                               PRIOR (FK, 부모) = (PK, 자식) : 계층 구조에서 자식 데이터에서 부모 데이터 방향으로 전개
              ORDER SIBLINGS BY 컬럼        -> 형제 노드(동일 level) 사이에서 정렬 수행
            
            - LEVEL : 계층형 질의에서 사용되는 가상 컬럼
            - CONNECT_BY_ISLEAF : 전개 과정에서 해당 데이터가 리프 데이터이면 1, 그렇지 않으면 0

    4. 게시글 목록 보기 

    5. 게시판 글쓰기 
        1) 글쓰기를 요청
        2) 글쓰기창에서 글을 입력하고 Controller에 /board/addArticle.do 요청
        3) Controller에서 Service 클래스로 입력한 글 정보를 전달 -> 테이블에 글 추가
        4) 새글 추가하고 Controller에게 다시 /board/listArticles.do로 요청, 전체글 표시
        
    6. 글번호에 해당하는 폴더를 생성 후에 해당 이미지 파일 저장
        1) upload()
            - 첨부한 파일은 임시로 temp 폴더에 업로드함
        2) 컨트롤러는 서비스(Service) 클래스의 addNewArticle() 호출시 새 글 정보를 인자로 전달해 테이블에 추가한 후 새 글 번호 반환
        3) 컨트롤러에서 반환받은 새글 번호를 이용해 해당 폴더 생성 수 temp 폴더의 파일을 새글 번호 폴더로 이동

    7. 글 상세보기 기능
        1) 글 목록창에서 글 제목 클릭
            - 컨트롤러에 /board2/viewArticle.do?articleNo=글번호로 요청
        2) 컨트롤러는 전송된 글 번호로 글 정보를 조회(select) 후 viewArticle.jsp(상세보기창)로 포워딩
        3) viewArticle.jsp에 글과 이미지 파일이 표시

    8. 글 수정하기 기능
        1) 글 상세보기(viewArticle.jsp)창에서 수정하기 버튼 클릭해 입력창을 활성화함
        2) 글 정보와 이미지를 수정한 후 수정 반영하기 버튼 클릭해서 컨트롤러에게 /board2/modArticle.do로 요청
        3) 수정된 데이터를 Map에 저장하고 반환 -> 테이블에 반영
        4) 원래 이미지 파일을 삭제

    9. 글 삭제 기능
        1) 글 상세보기(viewArticle.jsp)창에서 삭제하기 클릭 -> /board2/removeArticle.do 요청
        2) 컨트롤러에서는 1)에서 전달받은 글번호에 대한 글과 이와 관련된 자식 글들을 삭제
        3) 삭제된 글에 대한 이미지 파일 저장 폴더도 삭제

    10. 답글 쓰기 기능
        1) 글 상세보기(viewArticle.jsp)창에서 답글쓰기 클릭 -> /board2/replyForm.do 요청하면서 부모글 번호(parentNo)를 전송
        2) 답글쓰기창(replyForm.jsp)에서 글을 작성한 후 요청 -> /board2/addReply.do
        3) 테이블에 부모 글 번호와 함께 게시글 추가

    11. 페이징 기능
        1) 하단에 보이는 숫자는 페이지 번호 (pageNum)
        2) 한 페이지마다 10개 글이 표시됨, 페이지 10개가 모여 한 개의 섹션(sectionNum)됨
        3) 섹션 하나는 1페이지부터 10페이지까지임
        4) 두 번째 섹션은 11페이지부터 20페이지까지임
            - 사용자가 [2] 클릭 -> section = 1, pageNum = 2
        
        서브쿼리문 실행 순서
        1) 기존 계층형 구조로 글 목록을 일단 조회함
        2) 이 결과에 대해 다시 ROWNUM(recNum)이 표시되도록 서브 쿼리문 이용해서 다시 조회함
        3) ROWNUM이 표시된 2)번 결과에서 section과 pageNum으로 계산된 where절의 between 연산자 사이의 값에 해당 ROWNUM이 있는 레코드들만 최종 조회함

     

     


    BoardController.java

    import java.io.File;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    import org.apache.commons.io.FileUtils;
    
    /**
     * Servlet implementation class BoardController
     */
    @WebServlet("/board2/*")
    public class BoardController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	private static String ARTICLE_IMAGE_REPO = "C:\\workspace-jsp\\FileRepo";	//이미지 저장 위치
    	
    	BoardService boardService;
    	ArticleVO articleVO;
    	
    
    	/**
    	 * @see Servlet#init(ServletConfig)
    	 */
    	public void init(ServletConfig config) throws ServletException {
    		boardService = new BoardService();		//서블릿 초기화시 BoardService 객체 생성
    		articleVO = new ArticleVO();
    	}
    
    	/**
    	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doHandle(request, response);
    	}
    
    	/**
    	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doHandle(request, response);
    	}
    
    	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		String forwardPage = "";
    		request.setCharacterEncoding("utf-8");
    		response.setContentType("text/html; charset=utf-8");
    		HttpSession session;
    		
    		String action = request.getPathInfo();		//요청명 가져옴
    		System.out.println("getPathInfo() : " + action);
    		try {
    			//List<ArticleVO> articlesList = new ArrayList<>();
    			
    			if(action == null || action.equals("/listArticles.do")) {	//전체글 조회
    				
    				//section 값과 pageNum 값을 구함
    				String _section = request.getParameter("section");
    				String _pageNum = request.getParameter("pageNum");
    				
    				//최초 요청시 section 값과 pageNum 값이 없으면 각각 1로 초기화함
    				int section = Integer.parseInt(((_section==null) ? "1" : _section));
    				int pageNum = Integer.parseInt(((_pageNum==null) ? "1" : _pageNum));
    				
    				//section 값과 pageNum 값을 HashMap에 저장
    				Map<String, Integer> pagingMap = new HashMap<>();
    				pagingMap.put("section", section);
    				pagingMap.put("pageNum", pageNum);
    				
    				/* articlesList = boardService.listArticles(); */
    				Map<String, Integer> articleMap = boardService.listArticles(pagingMap);
    				
    				articleMap.put("section", section);
    				articleMap.put("pageNum", pageNum);
    				
    				request.setAttribute("articleMap", articleMap); //조회된 글 목록을 articlesList 이름으로 바인딩
    				forwardPage = "/board/listArticles.jsp";			//listArticles.jsp로 포워딩
    				
    			} else if(action.equals("/articleForm.do")) {	//새글 추가창	
    				forwardPage = "/board/articleForm.jsp";			
    				
    			} else if(action.equals("/addArticle.do")) {	//새글 추가(insert) 작업 수행
    				int articleNo = 0;
    				Map<String, String> articleMap = upload(request, response);
    				String title = articleMap.get("title");
    				String content = articleMap.get("content");
    				String imageFileName = articleMap.get("imageFileName");
    				
    				articleVO.setParentNo(0);
    				articleVO.setId("ezenac");
    				articleVO.setTitle(title);
    				articleVO.setContent(content);
    				articleVO.setImageFileName(imageFileName);
    				
    				//테이블에 새 글을 추가한 후 새글 번호를 가져옴
    				articleNo = boardService.addNewArticle(articleVO);
    				
    				//파일을 첨부한 경우에만 수행함
    				if(imageFileName != null && imageFileName.length() != 0) {
    					//temp 폴더에 임시로 업로드된 파일 객체 생성
    					File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
    					//ARTICLE_IMAGE_REPO의 하위 경로에 글 번호로 폴더를 생성함
    					File destFile = new File(ARTICLE_IMAGE_REPO + "\\" + articleNo);
    					//temp 폴더의 파일을 글 번호 폴더로 이동시킴
    					FileUtils.moveFileToDirectory(srcFile, destFile, true);
    				}
    				
    				//새글 등록 메세지를 출력 후 자바스크립트의 location 객체의 href 속성 이용해 글 목록 요청함
    				PrintWriter pw = response.getWriter();
    				pw.print("<script>"
    						+ " alert('새 글을 추가했습니다.');"
    						+ " location.href='" + request.getContextPath() + "/board2/listArticles.do'"
    						+ "</script>");
    				return;
    				
    			} else if(action.equals("/viewArticle.do")) {
    				//글 상세보기를 요청시 articleNo값 가져옴
    				String articleNo = request.getParameter("articleNo");
    				//articleNo에 대한 글정보 조회
    				articleVO = boardService.viewArticle(Integer.parseInt(articleNo));
    				//article 속성으로 바인딩함
    				request.setAttribute("article", articleVO);
    				forwardPage = "/board/viewArticle.jsp";
    				
    			} else if(action.equals("/modArticle.do")) {
    				Map<String, String> articleMap = upload(request, response);
    				
    				int articleNo = Integer.parseInt(articleMap.get("articleNo"));
    				articleVO.setArticleNo(articleNo);
    				
    				String title = articleMap.get("title");
    				String content = articleMap.get("content");
    				String imageFileName = articleMap.get("imageFileName");
    				
    				articleVO.setParentNo(0);
    				articleVO.setId("ezenac");
    				articleVO.setTitle(title);
    				articleVO.setContent(content);
    				articleVO.setImageFileName(imageFileName);
    				
    				//전송된 글 정보를 이용해 글 수정함
    				boardService.modArticle(articleVO);
    				
    				if(imageFileName != null && imageFileName.length() != 0) {
    					String originalFileName = articleMap.get("originalFileName");
    					
    					//수정된 이미지 파일을 폴더로 이동함
    					//temp 폴더에 임시로 업로드된 파일 객체 생성
    					File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
    					//ARTICLE_IMAGE_REPO의 하위 경로에 글 번호로 폴더를 생성함
    					File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNo);
    					destDir.mkdirs();
    					FileUtils.moveFileToDirectory(srcFile, destDir, true);
    					
    					//기존 파일을 삭제
    					File oldFile = new File(ARTICLE_IMAGE_REPO + "\\" + articleNo + "\\" + originalFileName);
    					oldFile.delete();
    				}
    				
    				//글 수정 메세지를 출력 후 자바스크립트의 location 객체의 href 속성 이용해 글 상세보기 요청함
    				PrintWriter pw = response.getWriter();
    				pw.print("<script>"
    						+ " alert('글을 수정했습니다.');"
    						+ " location.href='" + request.getContextPath() + "/board2/viewArticle.do?articleNo=" + articleNo + "';"
    						+ "</script>");
    				return;
    				
    			} else if(action.equals("/removeArticle.do")) {
    				int articleNo = Integer.parseInt(request.getParameter("articleNo"));
    				//articleNo 값에 대한 글 삭제 후 삭제된 부모글과 자식글의 articleNo 목록을 가져옴
    				List<Integer> articleNoList = boardService.removeArticle(articleNo);
    				
    				for(int _articleNo : articleNoList) {
    					File imgDir = new File(ARTICLE_IMAGE_REPO + "\\" + _articleNo);
    					if(imgDir.exists()) {
    						FileUtils.deleteDirectory(imgDir);
    					}
    				}
    				
    				//글 삭제 메세지를 출력 후 자바스크립트의 location 객체의 href 속성 이용해 글 상세보기 요청함
    				PrintWriter pw = response.getWriter();
    				pw.print("<script>"
    						+ " alert('글을 삭제했습니다.');"
    						+ " location.href='" + request.getContextPath() + "/board2//listArticles.do';"
    						+ "</script>");
    				return;
    				
    			} else if(action.equals("/replyForm.do")) {
    				int parentNo = Integer.parseInt(request.getParameter("parentNo"));
    				session = request.getSession();
    				session.setAttribute("parentNo", parentNo);
    				forwardPage = "/board/replyForm.jsp";
    				
    			} else if (action.equals("/addReply.do")) {
    				session = request.getSession();
    				//답글창 요청시 세션에 저장된 부모 글 번호 parentNo 가져옴
    				int parentNo = (Integer)session.getAttribute("parentNo");
    				session.removeAttribute("parentNo");
    				
    				Map<String, String> articleMap = upload(request, response);
    				String title = articleMap.get("title");
    				String content = articleMap.get("content");
    				String imageFileName = articleMap.get("imageFileName");
    				
    				articleVO.setParentNo(parentNo);	//답글의 부모글 번호를 설정함
    				articleVO.setId("lee");
    				articleVO.setTitle(title);
    				articleVO.setContent(content);
    				articleVO.setImageFileName(imageFileName);
    				
    				int articleNo = boardService.addReply(articleVO);
    				
    				//파일을 첨부한 경우에만 수행함
    				if(imageFileName != null && imageFileName.length() != 0) {
    					//temp 폴더에 임시로 업로드된 파일 객체 생성
    					File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
    					//ARTICLE_IMAGE_REPO의 하위 경로에 글 번호로 폴더를 생성함
    					File destFile = new File(ARTICLE_IMAGE_REPO + "\\" + articleNo);
    					//temp 폴더의 파일을 글 번호 폴더로 이동시킴
    					FileUtils.moveFileToDirectory(srcFile, destFile, true);
    				}
    				
    				//새글 등록 메세지를 출력 후 자바스크립트의 location 객체의 href 속성 이용해 글 목록 요청함
    				PrintWriter pw = response.getWriter();
    				pw.print("<script>"
    						+ " alert('답글을 추가했습니다.');"
    						+ " location.href='" + request.getContextPath() + "/board2/viewArticle.do?articleNo=" + articleNo + "';"
    						+ "</script>");
    				return;
    				
    			} else {
    			
    				forwardPage = "/board/listArticles.jsp";
    			}
    			
    			RequestDispatcher dispatcher = request.getRequestDispatcher(forwardPage);
    			dispatcher.forward(request, response);
    			
    		} catch(Exception e) {
    			System.out.println("프론트 컨트롤러 처리 중 오류");
    			e.printStackTrace();
    		}
    		
    	}
    	
    	//파일 업로드 기능
    	private Map<String, String> upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		Map<String, String> articleMap = new HashMap<>();
    		
    		String encoding = "utf-8";
    		File currenDirPath = new File(ARTICLE_IMAGE_REPO);
    		
    		DiskFileItemFactory factory = new DiskFileItemFactory();
    		factory.setRepository(currenDirPath);		//파일 경로 설정
    		factory.setSizeThreshold(1024*1024); 		//최대 업로드 파일 크기 설정
    		
    		ServletFileUpload upload = new ServletFileUpload(factory);
    		String fileName = null;
    		
    		try {
    			List<FileItem> items = upload.parseRequest(request);	//request객체에서 매개변수를 List로 가져옴
    			for(int i=0; i<items.size(); i++) {
    				FileItem fileItem = items.get(i);		//파일 업로드창에서 업로드된 항목들을 하나씩 가져옴
    				
    				if(fileItem.isFormField()) {
    					//폼 필드이면 전송된 매개변수 값을 출력함
    					System.out.println(fileItem.getFieldName() + " = " + fileItem.getString(encoding));
    					//파일 업로드와 함께 전송된 관련 매개변수(title, content)를 Map에 저장
    					articleMap.put(fileItem.getFieldName(), fileItem.getString(encoding));
    					
    				} else {
    					System.out.println("파라미터명 : " + fileItem.getFieldName());
    					System.out.println("파일명 : " + fileItem.getName());
    					System.out.println("파일크기 : " + fileItem.getSize() + "bytes");
    					
    					if(fileItem.getSize() > 0) {
    						int idx = fileItem.getName().lastIndexOf("\\");
    						if(idx == -1) {
    							idx = fileItem.getName().lastIndexOf("/");
    						}
    						
    						fileName = fileItem.getName().substring(idx + 1);
    						articleMap.put(fileItem.getFieldName(), fileName);
    						
    						File uploadFile = new File(currenDirPath + "\\temp\\" + fileName);
    						fileItem.write(uploadFile);		//업로드한 파일 이름으로 저장소에 파일을 업로드함
    					}
    				}
    			}
    		} catch(Exception e) {
    			System.out.println("파일 업로드 중 오류 발생");
    			e.printStackTrace();
    		}
    		
    		return articleMap;
    	}
    }

     

    listArticles.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <c:set var="contextPath" value="${pageContext.request.servletContext.contextPath }"/>
    <c:set var="articlesList" value="${articleMap.articlesList }"/>
    <c:set var="totArticles" value="${articleMap.totArticles }"/>
    <c:set var="section" value="${articleMap.section }"/>
    <c:set var="pageNum" value="${articleMap.pageNum }"/>
    <%
    	request.setCharacterEncoding("utf-8");
    %>
    
    <!DOCTYPE html>
    <html>
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>게시글 목록</title>
    	<style type="text/css">
    		.cls1{text-decoration: none;}
    		.cls2{text-align: center; font-size: 20px;}
    		.no-uline{text-decoration: none;}
    		.sel-page{text-decoration: none; color: red;}
    	</style>
    </head>
    <body>
    	<table align="center" border="1" width="80%">
    		<tr height="10" align="center" bgcolor="lightblue">
    			<td>글번호</td>
    			<td>작성자</td>
    			<td>제목</td>
    			<td>작성일</td>
    		</tr>
    		<c:choose>
    			<c:when test="${empty articlesList }">
    				<tr height="10">
    					<td colspan="4">
    						<p>
    							<b><span style="font-size: 9pt;">등록된 글이 없습니다.</span></b>
    						</p>
    					</td>
    				</tr>
    			</c:when>
    			
    			<c:when test="${! empty articlesList }">
    				<!-- articlesList로 포워딩된 글목록을 c:forEach 태그를 이용해서 표시함 -->
    				<c:forEach var="article" items="${articlesList }" varStatus="articleNum">
    					<tr align="center">
    						<td width="5%">${articleNum.count }</td>
    						<td width="10%">${article.id }</td>
    						<td width="35%" align="left">
    							<span style="padding-right: 30px;"></span>
    							<c:choose>
    								<c:when test="${article.level > 1 }">	<!-- 자식글임 -->
    									<c:forEach begin="1" end="${article.level }" step="1">
    										<span style="padding-left: 20px"></span>
    									</c:forEach>
    									<span style="font-size: 12px;">[답변]</span>
    									<a class="cls1" href="${contextPath }/board2/viewArticle.do?articleNo=${article.articleNo }">${article.title }</a>
    								</c:when>
    								<c:otherwise>
    									<a class="cls1" href="${contextPath }/board2/viewArticle.do?articleNo=${article.articleNo }">${article.title }</a>
    								</c:otherwise>
    							</c:choose>
    						</td>
    						<td width="10%">
    							<fmt:formatDate value="${article.writeDate }"/>
    						</td>
    					</tr>
    				</c:forEach>			
    			</c:when>
    		</c:choose>
    	</table>
    	
    	<div class="cls2">
    		<c:if test="${totArticles != null }">
    			<c:choose>
    				<c:when test="${totArticles > 100 }">	<!-- 글 개수가 100개 초과인 경우 -->
    					<c:forEach var="page" begin="1" end="10" step="1">
    						<c:if test="${section > 1 && page == 1 }">
    							<a class="no-uline" href="${contextPath }/board2/listArticles.do?section=${section-1 }&pageNum=${(section-1)*10 +1 }">%nbsp;이전</a>
    						</c:if>
    							<a class="no-uline" href="${contextPath }/board2/listArticles.do?section=${section }&pageNum=${page }">${(section-1)*10 + page }</a>
    						<c:if test="${page == 10 }">
    							<a class="no-uline" href="${contextPath }/board2/listArticles.do?section=${section-1 }&pageNum=${section*10 +1 }">%nbsp;다음</a>
    						</c:if>
    					</c:forEach>
    				</c:when>
    				<c:when test="${totArticles == 100 }">		<!-- 등록된 글 개수가 100개인 경우 -->
    					<c:forEach var="page" begin="1" end="10" step="1"> 
    						<a class="no-uline" href="#">${page }</a>
    					</c:forEach>
    				</c:when>
    				<c:when test="${totArticles < 100 }">	<!-- 등록된 글 개수가 100개 미만인 경우 -->
    					<c:forEach var="page" begin="1" end="${totArticles/10 + 1 }" step="1">
    						<c:choose>
    							<c:when test="${page==pageNum }">
    								<a class="sel-page" href="${contextPath }/board2/listArticles.do?section=${section }&pageNum=${page }">${page }</a>
    							</c:when>
    							<c:otherwise>
    								<a class="no-uline" href="${contextPath }/board2/listArticles.do?section=${section }&pageNum=${page }">${page }</a>
    							</c:otherwise>
    						</c:choose>
    					</c:forEach>
    				</c:when>
    			</c:choose>
    		</c:if>
    	</div>
    	<a class="cls1" href="${contextPath }/board2/articleForm.do"><p class="cls2">글쓰기</p></a>
    </body>
    </html>

     

     

     

    ArticleVO.java

    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    import java.sql.Date;
    
    /*
     	articleNo		NUMBER(10)		PRIMARY KEY,
    	parentNo		NUMBER(10)		DEFAULT 0,
    	title			VARCHAR2(500)	NOT NULL,
    	content			VARCHAR2(4000),
    	imageFileName 	VARCHAR2(100),
    	writeDate		DATE			DEFAULT SYSDATE NOT NULL,
    	id				VARCHAR2(20),
    	CONSTRAINT FK_T_BOARD_ID FOREIGN KEY(ID) REFERENCES T_MEMBER(ID)
     */
    
    public class ArticleVO {
    	private int level;
    	private int articleNo;
    	private int parentNo;
    	private String title;
    	private String content;
    	private String imageFileName;
    	private String id;
    	private Date writeDate;
    	
    	
    	public ArticleVO() {
    		// TODO Auto-generated constructor stub
    	}
    
    	public ArticleVO(int level, int articleNo, int parentNo, String title, String content, String imageFileName,
    			String id) {
    		super();
    		this.level = level;
    		this.articleNo = articleNo;
    		this.parentNo = parentNo;
    		this.title = title;
    		this.content = content;
    		this.imageFileName = imageFileName;
    		this.id = id;
    	}
    
    	public int getLevel() {
    		return level;
    	}
    
    	public void setLevel(int level) {
    		this.level = level;
    	}
    
    	public int getArticleNo() {
    		return articleNo;
    	}
    
    	public void setArticleNo(int articleNo) {
    		this.articleNo = articleNo;
    	}
    
    	public int getParentNo() {
    		return parentNo;
    	}
    
    	public void setParentNo(int parentNo) {
    		this.parentNo = parentNo;
    	}
    
    	public String getTitle() {
    		return title;
    	}
    
    	public void setTitle(String title) {
    		this.title = title;
    	}
    
    	public String getContent() {
    		return content;
    	}
    
    	public void setContent(String content) {
    		this.content = content;
    	}
    
    	public String getImageFileName() {
    		if(imageFileName != null && imageFileName.length() != 0) {
    			try {
    				imageFileName = URLDecoder.decode(imageFileName, "utf-8");
    				
    			} catch (UnsupportedEncodingException e) {
    				e.printStackTrace();
    			}
    		}
    		return imageFileName;
    	}
    
    	public void setImageFileName(String imageFileName) {
    		try {
    			if(imageFileName != null && imageFileName.length() != 0) {
    				this.imageFileName = URLEncoder.encode(imageFileName, "utf-8");
    			}
    			
    		} catch (UnsupportedEncodingException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public Date getWriteDate() {
    		return writeDate;
    	}
    
    	public void setWriteDate(Date writeDate) {
    		this.writeDate = writeDate;
    	}
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    }

     

     

    BoardService.java

    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class BoardService {
    	BoardDAO boardDAO;
    	
    	public BoardService() {
    		boardDAO = new BoardDAO();
    	}
    	
    	public List<ArticleVO> listArticles() {
    		List<ArticleVO> articlesList = boardDAO.selectAllArticles();
    		return articlesList;
    	}
    	
    	public Map<String, Integer> listArticles(Map<String, Integer> pagingMap) {
    		Map articlesMap = new HashMap<>();
    		
    		List<ArticleVO> articlesList = boardDAO.selectAllArticles(pagingMap);
    		int totArticles = boardDAO.selectTotArticles();
    		
    		articlesMap.put("articlesList", articlesList);
    		articlesMap.put("totArticles", totArticles);
    		//articlesMap.put("totArticles", 170);
    		
    		return articlesMap;
    	}
    	
    	public void addArticle(ArticleVO articleVO) {
    		boardDAO.insertNewArticle(articleVO);
    	}
    	
    	//폴더 생성 후 이미지 추가
    	public int addNewArticle(ArticleVO articleVO) {
    		return boardDAO.insertArticle(articleVO);
    	}
    	
    	public ArticleVO viewArticle(int articleNo) {
    		ArticleVO article = null;
    		article = boardDAO.seleceArticle(articleNo);
    		
    		return article;
    	}
    	
    	public void modArticle(ArticleVO article) {
    		boardDAO.updateArticle(article);
    	}
    	
    	public List<Integer> removeArticle(int articleNo) {
    		//글을 삭제하기 전 글 번호들을 ArrayList 객체에 저장함
    		List<Integer> articleNoList = boardDAO.selectRemovedArticles(articleNo);
    		
    		boardDAO.deleteArticle(articleNo);
    		
    		return articleNoList;	//삭제한 글 번호
    	}
    	
    	public int addReply(ArticleVO article) {
    		
    		return boardDAO.insertArticle(article);	//새 글 추가시 사용한 메소드를 이용해 답글을 추가함
    	}
    }

     

    BoardDAO.java

    import java.net.URLEncoder;
    import java.sql.Connection;
    import java.sql.Date;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    
    public class BoardDAO {
    	private DataSource dataFactory;
    	Connection conn;
    	PreparedStatement pstmt;
    	
    	public BoardDAO() {
    		try {
    			Context ctx = new InitialContext();
    			Context envContext = (Context)ctx.lookup("java:/comp/env");		//JNDI에 접근하기 위해 기본경로를 지정
    			//톰캣 context.xml에 설정한 name 값인 jdbc/oracle을 이용해 톰캣이 미리 연결한 DataSource를 받아옴
    			dataFactory = (DataSource)envContext.lookup("jdbc/oracle");		
    		} catch (NamingException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public List<ArticleVO> selectAllArticles() {
    		List<ArticleVO> articlesList = new ArrayList<>();
    		
    		try {
    			conn = dataFactory.getConnection();
    			String query = "SELECT LEVEL, ARTICLENO, PARENTNO, TITLE, CONTENT, ID, WRITEDATE "
    							+ " FROM T_BOARD "
    							+ " START WITH PARENTNO=0 "
    							+ " CONNECT BY PRIOR ARTICLENO = PARENTNO "
    							+ " ORDER siblings BY ARTICLENO DESC";	//오라클 계층형 SQL문
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			ResultSet rs = pstmt.executeQuery();
    			while(rs.next()) {
    				int level = rs.getInt("level");
    				int articleNo = rs.getInt("articleNo");
    				int parentNo = rs.getInt("parentNo");
    				String title = rs.getString("title");
    				String content = rs.getString("content");
    				String id = rs.getString("id");
    				Date writeDate = rs.getDate("writeDate");
    				
    				ArticleVO article = new ArticleVO();
    				article.setLevel(level);				//게시글 정보를 ArticleVO 객체의 속성에 설정
    				article.setArticleNo(articleNo);
    				article.setParentNo(parentNo);
    				article.setTitle(title);
    				article.setContent(content);
    				article.setId(id);
    				article.setWriteDate(writeDate);
    				
    				articlesList.add(article);
    
    			}
    			
    		} catch (Exception e) {
    			System.out.println("게시물 조회 중 오류");
    			e.printStackTrace();
    		}
    		
    		return articlesList;
    	}
    	
    	public List<ArticleVO> selectAllArticles(Map<String, Integer> pagingMap) {
    		List<ArticleVO> articlesList = new ArrayList<>();
    		int section = pagingMap.get("section");	//전송된 section과 pageNum 가져옴
    		int pageNum = pagingMap.get("pageNum");
    		
    		try {
    			conn = dataFactory.getConnection();
    			String query = "SELECT * "
    						+ "FROM (SELECT rownum AS recNum, "
    						+ "		LVL, "
    						+ "		ARTICLENO, "
    						+ "		PARENTNO, "
    						+ "		TITLE, "
    						+ "		ID, "
    						+ "		WRITEDATE "
    						+ "		FROM ( "
    						+ "			SELECT LEVEL AS LVL ,	 "
    						+ "				ARTICLENO , "
    						+ "				PARENTNO , "
    						+ "				TITLE , "
    						+ "				ID , "
    						+ "				WRITEDATE "
    						+ "			FROM T_BOARD "
    						+ "			START WITH parentNo = 0 "
    						+ "			CONNECT BY PRIOR ARTICLENO = PARENTNO "
    						+ "			ORDER siblings BY ARTICLENO DESC) "
    						+ ") "
    						+ "WHERE recNum BETWEEN (?-1)*100 + (?-1)*10+1 AND (?-1)*100 + (?)*10";	
    						// section과 pageNum 값으로 레코드 번호 범위를 조건으로 정함 (각각 1로 전송되었으면 between 1 and 10)
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, section);
    			pstmt.setInt(2, pageNum);
    			pstmt.setInt(3, section);
    			pstmt.setInt(4, pageNum);
    			
    			ResultSet rs = pstmt.executeQuery();
    			while(rs.next()) {
    				int level = rs.getInt("LVL");
    				int articleNo = rs.getInt("articleNo");
    				int parentNo = rs.getInt("parentNo");
    				String title = rs.getString("title");
    				String id = rs.getString("id");
    				Date writeDate = rs.getDate("writeDate");
    				
    				ArticleVO article = new ArticleVO();
    				article.setLevel(level);				//게시글 정보를 ArticleVO 객체의 속성에 설정
    				article.setArticleNo(articleNo);
    				article.setParentNo(parentNo);
    				article.setTitle(title);
    				article.setId(id);
    				article.setWriteDate(writeDate);
    				
    				articlesList.add(article);
    
    			}
    			
    			rs.close();
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("게시물 조회 중 오류");
    			e.printStackTrace();
    		}
    		
    		return articlesList;
    	}
    	
    	//전달받은 글 번호를 이용해 글 상세 정보 조회
    	public ArticleVO seleceArticle(int articleNo) {
    		ArticleVO article = new ArticleVO();
    		
    		try {
    			conn = dataFactory.getConnection();
    			String query = "SELECT ARTICLENO , PARENTNO , TITLE , CONTENT , NVL(IMAGEFILENAME, 'null') , ID , WRITEDATE "
    						 + "FROM T_BOARD WHERE ARTICLENO = ?";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, articleNo);
    			ResultSet rs = pstmt.executeQuery();
    			rs.next();
    			int _articleNo = rs.getInt("articleNo");
    			int parentNo = rs.getInt("parentNo");
    			String title = rs.getString("title");
    			String content = rs.getString("content");
    			
    			//파일 이름에 특수문자가 있을 경우 인코딩함
    			String imageFileName = URLEncoder.encode(rs.getString(5), "utf-8");
    			if(imageFileName.equals("null")) {
    				imageFileName = null;
    			}
    			String id = rs.getString("id");
    			Date writeDate = rs.getDate("writeDate");
    			
    			article.setArticleNo(_articleNo);
    			article.setParentNo(parentNo);
    			article.setTitle(title);
    			article.setContent(content);
    			article.setImageFileName(imageFileName);
    			article.setId(id);
    			article.setWriteDate(writeDate);
    			
    			rs.close();
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("글 상세보기 중 오류");
    			e.printStackTrace();
    		}
    		
    		return article;
    	}
    	
    	public int selectTotArticles() {
    		try {
    			conn = dataFactory.getConnection();
    			String query = "SELECT COUNT(ARTICLENO) FROM T_BOARD";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			ResultSet rs = pstmt.executeQuery();
    			if(rs.next())
    				return (rs.getInt(1));
    			
    			rs.close();
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("전체 게시글 수 리턴 중 오류");
    			e.printStackTrace();
    		}
    		return 0;
    	}
    	
    	private int getNewArticleNo() {
    		try {
    			conn = dataFactory.getConnection();
    			String query = "SELECT MAX(ARTICLENO) FROM T_BOARD";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			ResultSet rs = pstmt.executeQuery();
    			
    			if(rs.next()) 
    				return (rs.getInt(1) + 1);
    			
    			rs.close();
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("articleNo 값 획득 중 오류");
    			e.printStackTrace();
    		}
    		return 0;
    	}
    	
    	public void insertNewArticle(ArticleVO articleVO) {
    		try {
    			
    			conn = dataFactory.getConnection();
    			int articleNo = getNewArticleNo();
    			int parentNo = articleVO.getParentNo();
    			String title = articleVO.getTitle();
    			String content = articleVO.getContent();
    			String id = articleVO.getId();
    			String imageFileName = articleVO.getImageFileName();
    			String query = "INSERT INTO T_BOARD (ARTICLENO, PARENTNO, TITLE, CONTENT, IMAGEFILENAME, ID) " 
    							+ "VALUES (?, ?, ?, ?, ?, ?)";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, articleNo);
    			pstmt.setInt(2, parentNo);
    			pstmt.setString(3, title);
    			pstmt.setString(4, content);
    			pstmt.setString(5, imageFileName);
    			pstmt.setString(6, id);
    			
    			pstmt.executeUpdate();
    			
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("게시물 쓰기 중 오류");
    			e.printStackTrace();
    		} 
    	}
    	
    	public int insertArticle(ArticleVO articleVO) {
    		int articleNo = getNewArticleNo();
    		try {
    			conn = dataFactory.getConnection();
    			int parentNo = articleVO.getParentNo();
    			String title = articleVO.getTitle();
    			String content = articleVO.getContent();
    			String id = articleVO.getId();
    			String imageFileName = articleVO.getImageFileName();
    			String query = "INSERT INTO T_BOARD (ARTICLENO, PARENTNO, TITLE, CONTENT, IMAGEFILENAME, ID) " 
    							+ "VALUES (?, ?, ?, ?, ?, ?)";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, articleNo);
    			pstmt.setInt(2, parentNo);
    			pstmt.setString(3, title);
    			pstmt.setString(4, content);
    			pstmt.setString(5, imageFileName);
    			pstmt.setString(6, id);
    			
    			pstmt.executeUpdate();
    			
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("게시물 쓰기 중 오류");
    			e.printStackTrace();
    		} 
    		
    		return articleNo;
    	}
    	
    	public void updateArticle(ArticleVO article) {
    		int articleNo = article.getArticleNo();
    		String title = article.getTitle();
    		String content = article.getContent();
    		String imageFileName = article.getImageFileName();
    		
    		try {
    			conn = dataFactory.getConnection();
    			String query = "UPDATE T_BOARD SET TITLE = ?, CONTENT = ?";
    			
    			if(imageFileName != null && imageFileName.length() != 0) {
    				query += ", IMAGEFILENAME = ?";
    			}
    			
    			query += " WHERE ARTICLENO = ?";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setString(1, title);
    			pstmt.setString(2, content);
    			
    			//이미지 파일을 수정하는 경우 설정
    			if(imageFileName != null && imageFileName.length() != 0) {
    				pstmt.setString(3, imageFileName);
    				pstmt.setInt(4, articleNo);
    				
    			} else {	//수정하지 않는 경우 설정
    				pstmt.setInt(3, articleNo);
    			}
    			
    			pstmt.executeUpdate();
    			
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("게시글 수정 등록 중 오류");
    			e.printStackTrace();
    		}
    	}
    	
    	public List<Integer> selectRemovedArticles(int articleNo) {
    		List<Integer> articleNoList = new ArrayList<>();
    		
    		try {
    			conn = dataFactory.getConnection();
    			
    			String query = "SELECT ARTICLENO FROM T_BOARD "
    						+ "	START WITH ARTICLENO = ? "
    						+ "	CONNECT BY PRIOR ARTICLENO = PARENTNO ";
    			System.out.println(query);
    			
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, articleNo);
    			ResultSet rs = pstmt.executeQuery();
    			
    			while(rs.next()) {
    				articleNo = rs.getInt("articleNo");
    				articleNoList.add(articleNo);
    			}
    			
    			pstmt.close();
    			rs.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("삭제할 글의 articleNo 조회 중 오류");
    			e.printStackTrace();
    		}
    		
    		return articleNoList;
    	}
    	
    	public void deleteArticle(int articleNo) {
    		try {
    			conn = dataFactory.getConnection();
    			
    			String query = "DELETE FROM T_BOARD "
    						+ " WHERE ARTICLENO IN ( "
    						+ "	SELECT ARTICLENO "
    						+ "	FROM T_BOARD "
    						+ "	START WITH ARTICLENO = ? "
    						+ "	CONNECT BY PRIOR ARTICLENO = PARENTNO "
    						+ " ) ";
    			System.out.println(query);
    
    			pstmt = conn.prepareStatement(query);
    			pstmt.setInt(1, articleNo);
    			pstmt.executeUpdate();
    			
    			pstmt.close();
    			conn.close();
    			
    		} catch (Exception e) {
    			System.out.println("게시글 삭제(관련 자식글까지) 중 오류");
    			e.printStackTrace();
    		}
    	}
    }

     

     

    articleForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <c:set var="contextPath" value="${pageContext.request.servletContext.contextPath }"/>
    <%
    	request.setCharacterEncoding("utf-8");
    %>
    <!DOCTYPE html>
    <html>
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>글쓰기</title>
    	<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    	<script type="text/javascript">
    		function readURL(input) {
    			if(input.files && input.files[0]) {
    				var reader = new FileReader();
    				reader.onload = function(e) {
    					$('#preview').attr('src', e.target.result);
    				}
    				reader.readAsDataURL(input.files[0]);
    			}
    		}
    		function backToList(obj) {
    			obj.action = "${contextPath }/board2/listArticles.do"
    			obj.submit();
    		}
    	</script>
    </head>
    <body>
    	<h1 style="text-align: center;">새 글 쓰기</h1>
    											<!-- /addArticle.do로 새글 등록 요청 -->			<!-- 파일 업로드 기능 -->
    	<form name="articleForm" method="post" action="${contextPath }/board2/addArticle.do" enctype="multipart/form-data">
    		<table border="0" align="center">
    			<tr>
    				<td align="right">글제목 : </td>
    				<td colspan="2"><input type="text" size="69" maxlength="500" name="title"></td>
    			</tr>
    			<tr>
    				<td align="right" valign="top"><br>글내용 : </td>
    				<td colspan="2"><textarea rows="10" cols="66" maxlength="4000" name="content"></textarea></td>
    			</tr>
    			<tr>
    				<td align="right"><br>이미지 파일 첨부 : </td>
    				<td colspan="2"><input type="file" name="imageFileName" onchange="readURL(this)"></td>
    				<!-- 첨부한 이미지를 미리보기로 표시함 -->
    				<td><img id="preview" alt="이미지" src="#" width="200" height="200"></td>
    			</tr>
    			<tr>
    				<td align="right"></td>
    				<td colspan="2">
    					<input type="submit" value="글쓰기">
    					<input type="button" value="목록보기" onclick="backToList(this.form)">
    				</td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>

     

     

     

    viewArticle.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <c:set var="contextPath" value="${pageContext.request.servletContext.contextPath }"/>
    <%
    	request.setCharacterEncoding("utf-8");
    %>
    <!DOCTYPE html>
    <html>
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>글 상세보기</title>
    	<style type="text/css">
    		#tr_btn_modify{
    			display: none;
    		}
    	</style>
    	<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    	<script type="text/javascript">
    		function backToList(obj) {
    			obj.action="${contextPath}/board2/listArticles.do";
    			obj.submit();
    		}
    		function fn_enable(obj) {	/* 수정하기 클릭시 텍스트 박스를 활성화 시킴 */
    			document.getElementById("i_title").disabled=false;
    			document.getElementById("i_content").disabled=false;
    			document.getElementById("i_imageFileName").disabled=false;
    			document.getElementById("tr_btn_modify").style.display="block";
    			document.getElementById("tr_btn").style.display="none";
    		}
    		function fn_modify_article(obj) {	/* 수정등록 클릭시 컨트롤러에게 수정 데이터를 전송함 */
    			obj.action="${contextPath}/board2/modArticle.do";
    			obj.submit();
    		}
    		function readURL(input) {
    			if(input.files && input.files[0]) {
    				var reader = new FileReader();
    				reader.onload = function(e) {
    					$('#preview').attr('src', e.target.result);
    				};
    				reader.readAsDataURL(input.files[0]);
    			}
    		}
    		function fn_remove_article(url, articleNo) {
    			var form = document.createElement("form");		/* js 이용해 동적으로 <form> 태그 생성 */
    			form.setAttribute("method", "post");
    			form.setAttribute("action", url);
    			var articleNoInput = document.createElement("input"); /* js 이용해 동적으로 <input> 태그 생성 후 name, value 설정 */
    			articleNoInput.setAttribute("type", "hidden");
    			articleNoInput.setAttribute("name", "articleNo");
    			articleNoInput.setAttribute("value", articleNo);
    			form.appendChild(articleNoInput);	/* 동적으로 생성된 <input> 태그를 동적으로 생성한 <form>에 append */
    			document.body.appendChild(form);	/* <form> 태그를 <body> 태그에 추가 후 서버에 요청함 */
    			form.submit();
    		}
    		function fn_reply_form(url, parentNo) {
    			var form = document.createElement("form");
    			form.setAttribute("method", "post");
    			form.setAttribute("action", url);
    			var parentNoInput = document.createElement("input");
    			parentNoInput.setAttribute("type", "hidden");
    			parentNoInput.setAttribute("name", "parentNo");
    			parentNoInput.setAttribute("value", parentNo);
    			form.appendChild(parentNoInput);
    			document.body.appendChild(form);
    			form.submit();
    		}
    	</script>
    </head>
    <body>
    	<form action="#" name="frmArticle" method="post" enctype="multipart/form-data">
    	 	<table border="0" align="center">
    	 		<tr>
    	 			<td width="150" align="center" bgcolor="#add3f7">
    	 				글번호
    	 			</td>
    	 			<td>
    	 				<input type="text" value="${article.articleNo }" disabled>
    	 				<!-- 글 수정시 글 번호를 컨트롤러에게 전송하기 위해 글 번호를 저장함 -->
    	 				<input type="hidden" name="articleNo" value="${article.articleNo }"> 
    	 			</td>
    	 		</tr>
    	 		<tr>
    	 			<td width="150" align="center" bgcolor="#add3f7">
    	 				작성자 아이디
    	 			</td>
    	 			<td>
    	 				<input type="text" value="${article.id }" name="id" disabled>
    	 			</td>
    	 		</tr>
    	 		<tr>
    	 			<td width="150" align="center" bgcolor="#add3f7">
    	 				제목
    	 			</td>
    	 			<td>
    	 				<input type="text" value="${article.title } " name="title" id="i_title" disabled>
    	 			</td>
    	 		</tr>
    	 		<tr>
    	 			<td width="150" align="center" bgcolor="#add3f7">
    	 				내용
    	 			</td>
    	 			<td>
    	 				<textarea rows="20" cols="60" name="content" id="i_content" disabled>${article.content }</textarea>
    	 			</td>
    	 		</tr>
    	 		<c:if test="${not empty article.imageFileName && article.imageFileName != 'null' }">
    	 			<tr>
    		 			<td width="150" align="center" bgcolor="#add3f7" rowspan="2">
    		 				이미지
    		 			</td>
    		 			<td>
    		 				<!-- 이미지 수정시 미리 원래 이미지 파일 이름을 저장함 -->
    		 				<input type="hidden" name="originalFileName" value="${article.imageFileName }">
    		 				<img alt="이미지" src="${contextPath }/download.do?imageFileName=${article.imageFileName }&articleNo=${article.articleNo }" 
    		 				id="preview" width="450px" height="450px"><br>
    		 			</td>
    	 			</tr>
    	 			<tr>
    	 				<td>
    	 					<!-- 수정된 이미지 파일 이름을 전송함 -->
    	 					<input type="file" name="imageFileName" id="i_imageFileName" disabled onchange="readURL(this)">
    	 				</td>
    	 			</tr>
    	 		</c:if>
    	 		<tr>
    	 			<td width="150" align="center" bgcolor="#add3f7">
    	 				등록일자
    	 			</td>
    	 			<td>
    	 				<input type="text" value='<fmt:formatDate value="${article.writeDate }"/>' disabled>
    	 			</td>
    	 		</tr>
    	 		<tr id="tr_btn_modify">
    	 			<td colspan="2" align="center">
    	 				<input type="button" value="등록" onclick="fn_modify_article(frmArticle)">
    	 				<input type="button" value="취소" onclick="backToList(frmArticle)">
    	 			</td>
    	 		</tr>
    	 		<tr id="tr_btn">
    	 			<td colspan="2" align="center">
    	 				<input type="button" value="수정하기" onclick="fn_enable(this.form)">
    	 				<!-- 삭제하기 클릭시 fn_remove_article() 호출하면서 articleNo를 전달 -->
    	 				<input type="button" value="삭제하기" onclick="fn_remove_article('${contextPath }/board2/removeArticle.do', ${article.articleNo })">
    	 				<input type="button" value="목록보기" onclick="backToList(frmArticle)">
    	 				<!-- 답글쓰기 클릭시 fn_reply_form() 호출하면서 articleNo를 전달 -->
    	 				<input type="button" value="답글쓰기" onclick="fn_reply_form('${contextPath }/board2/replyForm.do', ${article.articleNo })">
    	 			</td>
    	 		</tr>
    	 	</table>
    	</form>
    </body>
    </html>

     

     

     

    FileDownlodeController.java

    package kr.co.ezenac.common;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Servlet implementation class FileDownloadController
     */
    @WebServlet("/download.do")
    public class FileDownloadController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	private static String ARTICLE_IMAGE_REPO = "C:\\workspace-jsp\\FileRepo";
    
    	/**
    	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doHandle(request, response);
    	}
    
    	/**
    	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doHandle(request, response);
    	}
    
    	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		request.setCharacterEncoding("utf-8");
    		response.setContentType("text/html; charset=utf-8");
    		
    		//이미지 파일이름과 글번호를 가져옴
    		String imageFileName = request.getParameter("imageFileName");
    		String articleNo = request.getParameter("articleNo");
    		System.out.println("imageFileName = " + imageFileName);
    		
    		OutputStream out = response.getOutputStream();
    		
    		//글번호에 대한 파일 경로 설정함
    		String path = ARTICLE_IMAGE_REPO + "\\" + articleNo + "\\" + imageFileName;
    		File imageFile = new File(path);
    		FileInputStream in = new FileInputStream(imageFile);
    		
    		response.setHeader("Cache-Control", "no-cache");
    		//이미지 파일을 내려받는데 필요한 response에 헤더 정보 설정함
    		response.addHeader("Content-disposition", "attachment;filename=" + imageFileName);
    		
    		//버퍼를 이용해 한번에 8kb씩 전송함
    		byte[] buffer = new byte[1024 * 8];
    		while(true) {
    			int count = in.read(buffer);
    			if(count == -1)
    				break;
    			out.write(buffer, 0, count);
    		}
    		in.close();
    		out.close();
    	}
    
    }

     

     

     

     

    replyForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <c:set var="contextPath" value="${pageContext.request.servletContext.contextPath }"/>
    <%
    	request.setCharacterEncoding("utf-8");
    %>
    <!DOCTYPE html>
    <html>
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>글쓰기</title>
    	<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
    	<script type="text/javascript">
    		function readURL(input) {
    			if(input.files && input.files[0]) {
    				var reader = new FileReader();
    				reader.onload = function(e) {
    					$('#preview').attr('src', e.target.result);
    				}
    				reader.readAsDataURL(input.files[0]);
    			}
    		}
    		function backToList(obj) {
    			obj.action = "${contextPath }/board2/listArticles.do"
    			obj.submit();
    		}
    	</script>
    </head>
    <body>
    	<h1 style="text-align: center;">답글 쓰기</h1>
    											<!-- 답글 입력 후 /addReply.do로 답글 등록 요청 -->		<!-- 파일 업로드 기능 -->
    	<form name="articleForm" method="post" action="${contextPath }/board2/addReply.do" enctype="multipart/form-data">
    		<table border="0" align="center">
    			<tr>
    				<td align="right">글제목 : </td>
    				<td colspan="2"><input type="text" size="69" maxlength="500" name="title"></td>
    			</tr>
    			<tr>
    				<td align="right" valign="top"><br>글내용 : </td>
    				<td colspan="2"><textarea rows="10" cols="66" maxlength="4000" name="content"></textarea></td>
    			</tr>
    			<tr>
    				<td align="right"><br>이미지 파일 첨부 : </td>
    				<td colspan="2"><input type="file" name="imageFileName" onchange="readURL(this)"></td>
    				<!-- 첨부한 이미지를 미리보기로 표시함 -->
    				<td><img id="preview" alt="이미지" src="#" width="200" height="200"></td>
    			</tr>
    			<tr>
    				<td align="right"></td>
    				<td colspan="2">
    					<input type="submit" value="답글작성하기">
    					<input type="button" value="취소" onclick="backToList(this.form)">
    				</td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>

     

     

     

    '개발자 수업 > JSP' 카테고리의 다른 글

    28. WebSocket  (0) 2022.02.03
    26. Model2 방식 - 회원  (0) 2021.12.22
    25. Model2 방식  (0) 2021.12.21
    24. jQuery Ajax  (0) 2021.12.20
    23. 파일 업로드 다운로드  (0) 2021.12.16

    댓글