프로그래밍 언어/자바 웹

글 삭제 기능 구현

· 코딩마이데이

어제 게시판의 글을 삭제하는 과정을 구현할 차례입니다. 글을 삭제할 때는 테이블의 글뿐만 아니라 그 글의 자식 글과 이미지 파일도 함께 삭제해야 합니다.

글 삭제 과정은 다음과 같습니다.

① 글 상세창(viewArticle.jsp)에서 삭제하기를 클릭하면 /board/removeArticle.do로 요청합니다.

② 컨트롤러에서는 글 상세창에서 전달받은 글 번호에 대한 글과 이에 관련된 자식 글들을 삭제합니다.

③ 삭제된 글에 대한 이미지 파일 저장 폴더도 삭제합니다.

 

그럼 이름 BoardDAO 클래스에 추가하여 삭제 기능을 구현해 보겠습니다.

 

1. sec04.brd06 패키지를 만들고 다음과 같이 삭제 기능 자바 클래스와 JSP를 추가합니다.

실습 파일 위치

 

2. BoardController 클래스를 다음과 같이 작성합니다. 브라우저에서 삭제를 요청하면 글 번호를 메서드로 전달해 글 번호에 대한 글과 그 자식 글을 삭제하기 전에 먼저 삭제한 글 번호와 자식 글 번호를 목록으로 가져옵니다. 그리고 글을 삭제한 후 글 번호로 이루어진 이미지 저장 폴더까지 모두 삭제합니다.

package sec03.brd06;

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.Servlet;
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 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("/board/*") */
public class BoardController extends HttpServlet {
    private static String ARTICLE_IMAGE_REPO = "C:\\board\\article_image";
    BoardService boardService;
    ArticleVO articleVO;

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        boardService = new 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 nextPage = "";
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");
        String action = request.getPathInfo();
        System.out.println("action:" + action);
        try {
            List<ArticleVO> articlesList = new ArrayList<ArticleVO>();
            if (action == null) {
                articlesList = boardService.listArticles();
                request.setAttribute("articlesList", articlesList);
                nextPage = "/board05/listArticles.jsp";
            } else if (action.equals("/listArticles.do")) {
                articlesList = boardService.listArticles();
                request.setAttribute("articlesList", articlesList);
                nextPage = "/board05/listArticles.jsp";
            } else if (action.equals("/articleForm.do")) {
                nextPage = "/board05/articleForm.jsp";
            } else if (action.equals("/addArticle.do")) {
                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("hong");
                articleVO.setTitle(title);
                articleVO.setContent(content);
                articleVO.setImageFileName(imageFileName);
                articleNO = boardService.addArticle(articleVO);
                if (imageFileName != null && imageFileName.length() != 0) {
                    File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
                    File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
                    destDir.mkdirs();
                    FileUtils.moveFileToDirectory(srcFile, destDir, true);
                }
                PrintWriter pw = response.getWriter();
                pw.print("<script>" + "  alert('새글을 추가했습니다.');" + " location.href='" + request.getContextPath()
                        + "/board/listArticles.do';" + "</script>");

                return;
            } else if (action.equals("/viewArticle.do")) {
                String articleNO = request.getParameter("articleNO");
                articleVO = boardService.viewArticle(Integer.parseInt(articleNO));
                request.setAttribute("article", articleVO);
                nextPage = "/board05/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("hong");
                articleVO.setTitle(title);
                articleVO.setContent(content);
                articleVO.setImageFileName(imageFileName);
                boardService.modArticle(articleVO);
                if (imageFileName != null && imageFileName.length() != 0) {
                    String originalFileName = articleMap.get("originalFileName");
                    File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
                    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();
                }
                PrintWriter pw = response.getWriter();
                pw.print("<script>" + "  alert('글을 수정했습니다.');" + " location.href='" + request.getContextPath()
                        + "/board/viewArticle.do?articleNO=" + articleNO + "';" + "</script>");
                return;
            } else if (action.equals("/removeArticle.do")) {
                int articleNO = Integer.parseInt(request.getParameter("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);
                    }
                }

                PrintWriter pw = response.getWriter();
                pw.print("<script>" + "  alert('글을 삭제했습니다.');" + " location.href='" + request.getContextPath()
                        + "/board/listArticles.do';" + "</script>");
                return;

            }else {
                nextPage = "/board05/listArticles.jsp";
            }

            RequestDispatcher dispatch = request.getRequestDispatcher(nextPage);
            dispatch.forward(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Map<String, String> upload(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Map<String, String> articleMap = new HashMap<String, String>();
        String encoding = "utf-8";
        File currentDirPath = new File(ARTICLE_IMAGE_REPO);
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setRepository(currentDirPath);
        factory.setSizeThreshold(1024 * 1024);
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            List items = upload.parseRequest(request);
            for (int i = 0; i < items.size(); i++) {
                FileItem fileItem = (FileItem) items.get(i);
                if (fileItem.isFormField()) {
                    System.out.println(fileItem.getFieldName() + "=" + fileItem.getString(encoding));
                    articleMap.put(fileItem.getFieldName(), fileItem.getString(encoding));
                } else {
                    System.out.println("파라미터명:" + fileItem.getFieldName());
                    //System.out.println("파일명:" + fileItem.getName());
                    System.out.println("파일크기:" + fileItem.getSize() + "bytes");
                    //articleMap.put(fileItem.getFieldName(), fileItem.getName());
                    if (fileItem.getSize() > 0) {
                        int idx = fileItem.getName().lastIndexOf("\\");
                        if (idx == -1) {
                            idx = fileItem.getName().lastIndexOf("/");
                        }

                        String fileName = fileItem.getName().substring(idx + 1);
                        System.out.println("파일명:" + fileName);
                        articleMap.put(fileItem.getFieldName(), fileName);  //익스플로러에서 업로드 파일의 경로 제거 후 map에 파일명 저장
                        File uploadFile = new File(currentDirPath + "\\temp\\" + fileName);
                        fileItem.write(uploadFile);

                    } // end if
                } // end if
            } // end for
        } catch (Exception e) {
            e.printStackTrace();
        }
        return articleMap;
    }

}

 

3. BoardService 클래스를 다음과 같이 작성합니다. 컨트롤러에서 removeArticle() 메서드 호출 시 매개변수 articleNO로 글 번호를 전달받아 BoardDAO의 selectRemovedArticles()를 먼저 호출헤 글 번호에 대한 글과 그 자식 글의 번호를 articleNOList에 저장합니다. 그런 다음 deleteArticle() 메서드를 호출해 글 번호에 대한 글과 저식 글을 삭제하고 글 번호를 반환합니다.

package sec03.brd06;

import java.util.List;

public class BoardService {
    BoardDAO boardDAO;

    public BoardService() {
        boardDAO = new BoardDAO();
    }

    public List<ArticleVO> listArticles() {
        List<ArticleVO> articlesList = boardDAO.selectAllArticles();
        return articlesList;
    }

    public int addArticle(ArticleVO article) {
        return boardDAO.insertNewArticle(article);
    }

    public ArticleVO viewArticle(int articleNO) {
        ArticleVO article = null;
        article = boardDAO.selectArticle(articleNO);
        return article;
    }

    public void modArticle(ArticleVO article) {
        boardDAO.updateArticle(article);
    }

    public List<Integer> removeArticle(int  articleNO) {
        List<Integer> articleNOList = boardDAO.selectRemovedArticles(articleNO);
        boardDAO.deleteArticle(articleNO);
        return articleNOList;
    }

}

 

4. BoardDAO 클래스를 다음과 같이 작성합니다. selectRemovedArticles() 메서드는 삭제할 글에 대한 글 번호를 가져옵니다. deleteArticle() 메서드는 전달된 articleNO에 대한 글을 삭제합니다.

package sec03.brd06;

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 javax.naming.Context;
import javax.naming.InitialContext;
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");
            dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<ArticleVO> selectAllArticles() {
        List<ArticleVO> articlesList = new ArrayList<ArticleVO>();
        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";
            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);
                article.setArticleNO(articleNO);
                article.setParentNO(parentNO);
                article.setTitle(title);
                article.setContent(content);
                article.setId(id);
                article.setWriteDate(writeDate);
                articlesList.add(article);
            }
            rs.close();
            pstmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return articlesList;
    }

    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(query);
            if (rs.next())
                return (rs.getInt(1) + 1);
            rs.close();
            pstmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public int insertNewArticle(ArticleVO article) {
        int articleNO = getNewArticleNO();
        try {
            conn = dataFactory.getConnection();
            int parentNO = article.getParentNO();
            String title = article.getTitle();
            String content = article.getContent();
            String id = article.getId();
            String imageFileName = article.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) {
            e.printStackTrace();
        }

        return articleNO;
    }

    public ArticleVO selectArticle(int articleNO) {
        ArticleVO article = new ArticleVO();
        try {
            conn = dataFactory.getConnection();
            String query = "select articleNO,parentNO,title,content, NVL(imageFileName, 'null') as imageFileName, 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("imageFileName"), "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) {
            e.printStackTrace();
        }
        return article;
    }

    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) {
            e.printStackTrace();
        }
    }

    public void deleteArticle(int  articleNO) {
        try {
            conn = dataFactory.getConnection();
            String query = "DELETE FROM t_board ";
            query += " WHERE articleNO in (";
            query += "  SELECT articleNO FROM  t_board ";
            query += " START WITH articleNO = ?";
            query += " 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) {
            e.printStackTrace();
        }
    }

    public List<Integer> selectRemovedArticles(int  articleNO) {
        List<Integer> articleNOList = new ArrayList<Integer>();
        try {
            conn = dataFactory.getConnection();
            String query = "SELECT articleNO FROM  t_board  ";
            query += " START WITH articleNO = ?";
            query += " 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();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return articleNOList;
    }

}

 

5. viewArticle.jsp에서 삭제하기를 클릭하면 fn_remove_article() 자바스크립트 함수를 호출해 글 번호인 articleNO를 컨트롤러로 전송하도록 구현합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"
         isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
    request.setCharacterEncoding("UTF-8");
%>
<c:set var="contextPath"  value="${pageContext.request.contextPath}"  />
<head>
    <meta charset="UTF-8">
    <title>글보기</title>
    <style>
        #tr_btn_modify{
            display:none;
        }

    </style>
    <script  src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script type="text/javascript" >
        function backToList(obj){
            obj.action="${contextPath}/board/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}/board/modArticle.do";
            obj.submit();
        }

        function fn_remove_article(url,articleNO){
            var form = document.createElement("form");
            form.setAttribute("method", "post");
            form.setAttribute("action", url);
            var articleNOInput = document.createElement("input");
            articleNOInput.setAttribute("type","hidden");
            articleNOInput.setAttribute("name","articleNO");
            articleNOInput.setAttribute("value", articleNO);

            form.appendChild(articleNOInput);
            document.body.appendChild(form);
            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();
        }

        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]);
            }
        }
    </script>
</head>
<body>
<form name="frmArticle" method="post"  action="${contextPath}"  enctype="multipart/form-data">
    <table  border=0  align="center">
        <tr>
            <td width=150 align="center" bgcolor=#FF9933>
                글번호
            </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="#FF9933">
                작성자 아이디
            </td>
            <td >
                <input type=text value="${article.id }" name="writer"  disabled />
            </td>
        </tr>
        <tr>
            <td width="150" align="center" bgcolor="#FF9933">
                제목
            </td>
            <td>
                <input type=text value="${article.title }"  name="title"  id="i_title" disabled />
            </td>
        </tr>
        <tr>
            <td width="150" align="center" bgcolor="#FF9933">
                내용
            </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="#FF9933"  rowspan="2">
                    이미지
                </td>
                <td>
                    <input  type= "hidden"   name="originalFileName" value="${article.imageFileName }" />
                    <img src="${contextPath}/download.do?articleNO=${article.articleNO}&imageFileName=${article.imageFileName}" id="preview"  /><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="#FF9933">
                등록일자
            </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)">
                <input type=button value="삭제하기" onClick="fn_remove_article('${contextPath}/board/removeArticle.do', ${article.articleNO})">
                <input type=button value="리스트로 돌아가기"  onClick="backToList(this.form)">
                <input type=button value="답글쓰기"  onClick="fn_reply_form('${contextPath}/board/replyForm.do', ${article.articleNO})">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

 

6. 글 상세창에서 삭제하기를 클릭해 /board/removeArticle.do?articleNO=8로 요청합니다.

 

7. 글을 삭제한 후 다시 글 목록을 표시합니다.

 

8. 로컬 PC를 확인해보면 8번 글에 해당하는 저장소 폴더가 삭제된 것을 확인할 수 있습니다.