게시판 읽기, 쓰기, 수정, 삭제 구현

 

1. 기능별 URI 정의

URL : 리소스 경로(전체 경로)

URI : URL의 일부분

 

 

2. 게시물 읽기 기능의 구현

 

package com.fastcampus.ch4.controller;

import com.fastcampus.ch4.domain.BoardDto;
import com.fastcampus.ch4.domain.PageHandler;
import com.fastcampus.ch4.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/board")
public class BoardController {
    @Autowired
    BoardService boardService;

    @GetMapping("/read")
    public String read(Integer bno, Integer page, Integer pageSize, Model m) {
        try {
            BoardDto boardDto = boardService.read(bno);

//            m.addAttribute("boardDto", boardDto); // 아래 문장과 동일
            m.addAttribute(boardDto); // 이름을 따로 지정하지 않으면 타입의 첫 글자를 소문자로 해서 저장함
            m.addAttribute("page", page);
            m.addAttribute("pageSize", pageSize);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "board";
    }

    @GetMapping("/list")
    public String list(Integer page, Integer pageSize, Model m, HttpServletRequest request) {
        if(!loginCheck(request))
            return "redirect:/login/login?toURL="+request.getRequestURL();  // 로그인을 안했으면 로그인 화면으로 이동

        if(page==null) page=1;
        if(pageSize==null) pageSize=10;

        try {
            int totalCnt = boardService.getCount();
            PageHandler pageHandler = new PageHandler(totalCnt, page, pageSize);


            Map map = new HashMap();
            map.put("offset", (page-1)*pageSize);
            map.put("pageSize", pageSize);

            List<BoardDto> list = boardService.getPage(map);
            m.addAttribute("list", list);
            m.addAttribute("ph", pageHandler);
            m.addAttribute("page", page);
            m.addAttribute("pageSize", pageSize);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
    }

    private boolean loginCheck(HttpServletRequest request) {
        // 1. 세션을 얻어서
        HttpSession session = request.getSession();
        // 2. 세션에 id가 있는지 확인, 있으면 true를 반환
        return session.getAttribute("id")!=null;
    }
}

먼저 list 메소드 쪽을 보면,

로그인 후 Board를 클릭했을 때 index.jsp에서 http://localhost/ch4/board/list로 이동하도록 작성이 되어있으므로

쿼리 스트링 값이 없다.

그래서 page와 pageSize가 null이면 초기값을 설정하도록 헀다.

그리고 페이지 구성에 필요한 글들(list), 페이지 핸들러(pageHandler),

목록 버튼을 눌렀을 때 첫 목록이 아닌 기존에 있었던 목록으로 돌아가기 위해 필요한

page와 pageSize를 model에 저장하여 read 메소드 쪽으로 넘겨준다.

 

[boardList.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
</head>
<body>
<div id="menu">
    <ul>
        <li id="logo">fastcampus</li>
        <li><a href="<c:url value='/'/>">Home</a></li>
        <li><a href="<c:url value='/board/list'/>">Board</a></li>
        <li><a href="<c:url value='/login/login'/>">login</a></li>
        <li><a href="<c:url value='/register/add'/>">Sign in</a></li>
        <li><a href=""><i class="fas fa-search small"></i></a></li>
    </ul>
</div><div style="text-align:center">
    <table border="1">
        <tr>
            <th>번호</th>
            <th>제목</th>
            <th>이름</th>
            <th>등록일</th>
            <th>조회수</th>
        </tr>
        <c:forEach var="boardDto" items="${list}">

        <tr>
            <td>${boardDto.bno}</td>
            <td><a href="<c:url value='/board/read?bno=${boardDto.bno}&page=${page}&pageSize=${pageSize}'/>">${boardDto.title}</a></td>
            <td>${boardDto.writer}</td>
            <td>${boardDto.reg_date}</td>
            <td>${boardDto.view_cnt}</td>
        </tr>
        </c:forEach>
    </table>
    <br>
    <div>
        <c:if test="${ph.showPrev}">
            <a href="<c:url value='/board/list?page=${ph.beginPage-1}&pageSize=${ph.pageSize}'/>">&lt;</a>
        </c:if>
        <c:forEach var="i" begin="${ph.beginPage}" end="${ph.endPage}">
            <a href="<c:url value='/board/list?page=${i}&pageSize=${ph.pageSize}'/>">${i}</a>
        </c:forEach>
        <c:if test="${ph.showNext}">
            <a href="<c:url value='/board/list?page=${ph.endPage+1}&pageSize=${ph.pageSize}'/>">&gt;</a>
        </c:if>
    </div>
</div>
</body>
</html>

 

 

다음으로 read쪽을 보면,

bno을 받아서 그 bno에 해당하는 boardDto 객체를 얻는다.

또, 목록 버튼을 눌렀을 때 첫 목록이 아닌 기존에 있었던 목록으로 돌아가기 위해 page와 pageSize를 model에 저장했다.

 

    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
</head>
<body>
<div id="menu">
    <ul>
        <li id="logo">fastcampus</li>
        <li><a href="<c:url value='/'/>">Home</a></li>
        <li><a href="<c:url value='/board/list'/>">Board</a></li>
        <li><a href="<c:url value='/login/login'/>">login</a></li>
        <li><a href="<c:url value='/register/add'/>">Sign in</a></li>
        <li><a href=""><i class="fas fa-search small"></i></a></li>
    </ul>
</div>
<div style="text-align:center">
    <h2>게시물 읽기</h2>
    <form action="" id="form">
        <input type="text" name="bno" value="${boardDto.bno}" readonly="readonly">
        <input type="text" name="title" value="${boardDto.title}" readonly="readonly">
        <textarea name="content" id="" cols="30" rows="10" readonly="readonly">${boardDto.content}</textarea>
        <button type="button" id="writeBtn" class="btn">등록</button>
        <button type="button" id="modifyBtn" class="btn">수정</button>
        <button type="button" id="removeBtn" class="btn">삭제</button>
        <button type="button" id="listBtn" class="btn">목록</button>
    </form>
</div>
<script>
    $(document).ready(function() { // main()
        $('#listBtn').on("click", function(){
           location.href = "<c:url value='/board/list'/>?page=${page}&pageSize=${pageSize}";
        });
    });
</script>
</body>
</html>

목록 버튼을 누르면 list 메소드에서 넘겨 받은 page와 pageSize를 이용해서 

바로 전에 있었던 목록으로 돌아갈 수 있게 된다.

 

 

3. 게시물 삭제 기능의 구현

 

 

 

 

@PostMapping("/remove")
public String remove(Integer bno, Integer page, Integer pageSize, Model m, HttpSession session, RedirectAttributes rattr) {
    String writer = (String)session.getAttribute("id");
    try {
        m.addAttribute("page", page);
        m.addAttribute("pageSize", pageSize);
        // 모델에 담으면 redirect 할 때 쿼리 스트링에 ?page=i&pageSize=i 으로 붙음

        int rowCnt = boardService.remove(bno, writer);

        if(rowCnt==1) {
            // 한번만 에러메세지 띄우려면 RedirectAttributes 객체의 addFlashAttribute 메소드를 사용하면 된다(jsp 파일에 msg 출력부분 수정필요)
            rattr.addFlashAttribute("msg", "DEL_OK"); 
            return "redirect:/board/list";
        } else {
            throw new Exception("board remove error");
        }

    } catch (Exception e) {
        e.printStackTrace();
        rattr.addFlashAttribute("msg", "DEL_ERR");
    }


    return "redirect:/board/list";
}

다음과 같이 remove 메소드를 추가한다.

중간에 RedirectAttributes 객체와 addFlashAttribute 메소드를 이용하여 에러메세지를 저장했는데

이렇게 하면

성공 또는 실패 메세지를 한 번 띄운 후에, URL에 붙은 쿼리 스트링이 전부 사라지게 되어 더 이상 메세지가 표시되지 않는다.

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
</head>
<body>
<div id="menu">
    <ul>
        <li id="logo">fastcampus</li>
        <li><a href="<c:url value='/'/>">Home</a></li>
        <li><a href="<c:url value='/board/list'/>">Board</a></li>
        <li><a href="<c:url value='/login/login'/>">login</a></li>
        <li><a href="<c:url value='/register/add'/>">Sign in</a></li>
        <li><a href=""><i class="fas fa-search small"></i></a></li>
    </ul>
</div>
<div style="text-align:center">
    <h2>게시물 읽기</h2>
    <form action="" id="form">
        <input type="text" name="bno" value="${boardDto.bno}" readonly="readonly">
        <input type="text" name="title" value="${boardDto.title}" readonly="readonly">
        <textarea name="content" id="" cols="30" rows="10" readonly="readonly">${boardDto.content}</textarea>
        <button type="button" id="writeBtn" class="btn">등록</button>
        <button type="button" id="modifyBtn" class="btn">수정</button>
        <button type="button" id="removeBtn" class="btn">삭제</button>
        <button type="button" id="listBtn" class="btn">목록</button>
    </form>
</div>
<script>
    $(document).ready(function() { // main()
        $('#listBtn').on("click", function(){
           location.href = "<c:url value='/board/list'/>?page=${page}&pageSize=${pageSize}";
        });
    });
    $(document).ready(function() { // main()
        $('#removeBtn').on("click", function(){
            if(!confirm("정말로 삭제하시겠습니까?")) return;
            let form = $('#form');
            form.attr("action", "<c:url value='/board/remove'/>?page=${page}&pageSize=${pageSize}");
            form.attr("method", "post");
            form.submit();
        });
    });
</script>
</body>
</html>

board.jsp의 삭제 버튼 스크립트에

"정말로 삭제하시겠습니까?" 라는 메세지를 띄울 수 있게 추가하였다.

취소 버튼을 누르면 조건문이 true가 되어 삭제를 취소할 수 있고

확인 버튼을 누르면 삭제가 된다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
</head>
<body>
<div id="menu">
    <ul>
        <li id="logo">fastcampus</li>
        <li><a href="<c:url value='/'/>">Home</a></li>
        <li><a href="<c:url value='/board/list'/>">Board</a></li>
        <li><a href="<c:url value='/login/login'/>">login</a></li>
        <li><a href="<c:url value='/register/add'/>">Sign in</a></li>
        <li><a href=""><i class="fas fa-search small"></i></a></li>
    </ul>
</div>
<script>
    let msg = "${msg}"
    if(msg=="DEL_OK") alert("성공적으로 삭제되었습니다.");
    if(msg=="DEL_ERR") alert("삭제에 실패했습니다.");
</script>
<div style="text-align:center">
    <table border="1">
        <tr>
            <th>번호</th>
            <th>제목</th>
            <th>이름</th>
            <th>등록일</th>
            <th>조회수</th>
        </tr>
        <c:forEach var="boardDto" items="${list}">

        <tr>
            <td>${boardDto.bno}</td>
            <td><a href="<c:url value='/board/read?bno=${boardDto.bno}&page=${page}&pageSize=${pageSize}'/>">${boardDto.title}</a></td>
            <td>${boardDto.writer}</td>
            <td>${boardDto.reg_date}</td>
            <td>${boardDto.view_cnt}</td>
        </tr>
        </c:forEach>
    </table>
    <br>
    <div>
        <c:if test="${ph.showPrev}">
            <a href="<c:url value='/board/list?page=${ph.beginPage-1}&pageSize=${ph.pageSize}'/>">&lt;</a>
        </c:if>
        <c:forEach var="i" begin="${ph.beginPage}" end="${ph.endPage}">
            <a href="<c:url value='/board/list?page=${i}&pageSize=${ph.pageSize}'/>">${i}</a>
        </c:forEach>
        <c:if test="${ph.showNext}">
            <a href="<c:url value='/board/list?page=${ph.endPage+1}&pageSize=${ph.pageSize}'/>">&gt;</a>
        </c:if>
    </div>
</div>
</body>
</html>

boardList.jsp 파일도 코드가 추가되어야 한다.
성공과 실패 메세지를 띄우기 위해 중간에 

<script>
    let msg = "${msg}"
    if(msg=="DEL_OK") alert("성공적으로 삭제되었습니다.");
    if(msg=="DEL_ERR") alert("삭제에 실패했습니다.");
</script>

다음과 같이 스크립트를 추가하였다.

(RedirectAttributes 객체를 사용하게 되면 ${msg} 라고 써야 하고, 그냥 Model 객체의 addAttribute 메소드를 쓰면

${param.msg}라고 써야 한다) 

 

* RedirectAttributes는 session을 이용한 일회성 저장임

 

4. 게시물 쓰기 기능의 구현

 

 

 

 

먼저 board.jsp 파일을 수정한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>fastcampus</title>
    <link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
</head>
<body>
<div id="menu">
    <ul>
        <li id="logo">fastcampus</li>
        <li><a href="<c:url value='/'/>">Home</a></li>
        <li><a href="<c:url value='/board/list'/>">Board</a></li>
        <li><a href="<c:url value='/login/login'/>">login</a></li>
        <li><a href="<c:url value='/register/add'/>">Sign in</a></li>
        <li><a href=""><i class="fas fa-search small"></i></a></li>
    </ul>
</div>
<script>
    let msg="${msg}";
    if(msg=="WRT_ERR") alert("게시물 등록에 실패했습니다. 다시 시도해 주세요.");
</script>
<div style="text-align:center">
    <h2>게시물 ${mode=="new" ? "글쓰기" : "읽기"}</h2>
    <form action="" id="form">
        <input type="hidden" name="bno" value="${boardDto.bno}"}>
        <input type="text" name="title" value="${boardDto.title}" ${mode=="new" ? '' : 'readonly="readonly"'}>
        <textarea name="content" id="" cols="30" rows="10" ${mode=="new" ? '' : 'readonly="readonly"'}>${boardDto.content}</textarea>
        <button type="button" id="writeBtn" class="btn">등록</button>
        <button type="button" id="modifyBtn" class="btn">수정</button>
        <button type="button" id="removeBtn" class="btn">삭제</button>
        <button type="button" id="listBtn" class="btn">목록</button>
    </form>
</div>
<script>
    $(document).ready(function() { // main()
        $('#listBtn').on("click", function(){
           location.href = "<c:url value='/board/list'/>?page=${page}&pageSize=${pageSize}";
        });
    });
    $(document).ready(function() { // main()
        $('#removeBtn').on("click", function(){
            if(!confirm("정말로 삭제하시겠습니까?")) return;
            let form = $('#form');
            form.attr("action", "<c:url value='/board/remove'/>?page=${page}&pageSize=${pageSize}");
            form.attr("method", "post");
            form.submit();
        });
    });
    $(document).ready(function() { // main()
        $('#writeBtn').on("click", function(){
            let form = $('#form');
            form.attr("action", "<c:url value='/board/write'/>");
            form.attr("method", "post");
            form.submit();
        });
    });
</script>
</body>
</html>

게시물 등록 실패 메세지 추가,

model에 "mode", "new"를 추가하여 글 제목과 내용을 작성할 수 있게 함,

(read와 write에 둘 다 view로 board.jsp를 사용하는데, mode에 따라 읽기전용 여부를 결정해서 사용하기 위함)

<script> 태그에 POST 메소드 방식으로 /board/write에 <form> 태그 안의 input 값들을 넘겨준다.

 

@GetMapping("/write")
public String write(Model m) {
    m.addAttribute("mode", "new");
    return "board"; // 읽기와 쓰기에 사용, 쓰기에 사용할 때는 mode=new
}
@PostMapping("/write")
public String write(BoardDto boardDto, Model m, HttpSession session, RedirectAttributes rattr) {
    String writer = (String)session.getAttribute("id");
    boardDto.setWriter(writer);

    try {
        int rowCnt = boardService.write(boardDto);

        if(rowCnt!=1)
            throw new Exception("Write failed");

        rattr.addFlashAttribute("msg", "WRT_OK");

        return "redirect:/board/list";
    } catch (Exception e) {
        e.printStackTrace();
        m.addAttribute("boardDto", boardDto);
        m.addAttribute("msg", "WRT_ERR");
        return "board";
    }

}

 

GET 메소드와 POST 메소드 방식의 write 메소드를 각각 추가한다.

 

글쓰기 버튼을 누르면 GET 메소드로 write 메소드를 호출한다.

model에 "mode", "new"를 저장하여 board.jsp로 보내면

글쓰기 화면이 나오게 된다.

 

제목과 내용을 입력하고 등록 버튼을 누르면, 입력한 제목과 내용이 POST 메소드의 write 메소드로 전달된다.

호출된 write 메소드에서, 매개변수에 있는 boardDto 객체를 만들어 setter 메소드에 의해 제목과 내용의 값들이

입력된다. 

세션에서 id를 얻어와서 boardDto 객체에 저장하여 온전한 boardDto 객체를 완성하게 된다(제목, 내용, 아이디)

 

완성된 boardDto 객체를 boardService.write 메소드로 데이터베이스에 등록하게 된다.

등록이 잘 되었으면 성공 메세지를 나타내고 글 목록의 첫 페이지로 이동하며,

실패했으면 실패 메세지가 나타나고 다시 글 작성화면으로 돌아간다. 

이 때 catch 블럭에서 model에 boardDto 객체를 저장해주면, 글 작성화면에 돌아갔을 때 아까 작성했던 내용들이 다시 나타나게 된다.

 

 

 

5. 게시물 수정 기능의 구현

$(document).ready(function() { // main()
    $('#modifyBtn').on("click", function(){
        // 1. 읽기 상태이면 수정 상태로 변경
        let form = $('#form');
        let isReadOnly = $("input[name=title]").attr('readonly');

        if(isReadOnly=='readonly') {
            $("input[name=title]").attr('readonly', false); // title
            $("textarea").attr('readonly', false); // content
            $("#modifyBtn").html("등록");
            $("h2").html("게시물 수정");
            return;
        }

        // 2. 수정 상태이면 수정된 내용을 서버로 전송
        form.attr("action", "<c:url value='/board/modify'/>?page=${page}&pageSize=${pageSize}");
        form.attr("method", "post");
        form.submit();
    });
});

board.jsp에 다음과 같은 코드를 추가

 

@PostMapping("/modify")
public String modify(BoardDto boardDto, Model m, Integer page, Integer pageSize, HttpSession session, RedirectAttributes rattr) {
    String writer = (String)session.getAttribute("id");
    boardDto.setWriter(writer);

    try {
        int rowCnt = boardService.modify(boardDto);

        if(rowCnt!=1)
            throw new Exception("Modify failed");

        rattr.addFlashAttribute("msg", "MOD_OK");
        rattr.addAttribute("page", page);
        rattr.addAttribute("pageSize", pageSize);

        return "redirect:/board/list";
    } catch (Exception e) {
        e.printStackTrace();
        m.addAttribute("boardDto", boardDto);
        m.addAttribute("msg", "MOD_ERR");
        return "board";
    }

}

boardController에 modify 메소드를 추가한다.

 

boardService의 modify 메소드를 호출하여 boardDto 객체를 전달하고

DAO의 title, content 를 update하게 된다.

 

 


 

+ Recent posts