MyBatis

8. SpringBoot로 페이징과 CRUD 작성 - 패스트캠퍼스 백엔드 부트캠프 3기

gkss2tpt 2025. 3. 5. 20:06

1. 페이징

  • boardList.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>BoardList</title>
</head>
<body>
<h2>게시물 목록</h2>
<table border="1px">
    <tr>
        <td>번호</td>
        <td>제목</td>
        <td>내용</td>
        <td>조회수</td>
    </tr>
    <tr th:each="item : ${list}">
        <td th:text="${item.bno}"></td>
        <td th:text="${item.title}"></td>
        <td th:text="${item.content}"></td>
        <td th:text="${item.view_cnt}"></td>
    </tr>
</table>
<div>
    <th:block th:if="${ph.beginPage}>1">
        <a th:href="@{'/board/list?page='+${ph.beginPage+-1}}">&lt;</a>
    </th:block>
    <th:block th:each="i : ${#numbers.sequence(ph.beginPage, ph.endPage)}">
        <a th:href="@{'/board/list?page=' + ${i} + '&pageSize=' + ${ph.pageSize}}"><span th:text="${i}">1</span></a>
    </th:block>
    <th:block th:if="${ph.endPage}<${ph.page_cnt}">
        <a th:href="@{'/board/list?page='+${ph.endPage+1}}">&gt;</a>
    </th:block>
</div>
</body>
</html>
  • BoardController
@Controller
@RequestMapping("/board")
public class BoardController {

    @Autowired
    BoardDao boardDao;

    // 게시판 목록
    @GetMapping("/list")
    public String list(Integer page, Integer pageSize, Model m){
        if(page==null)
            page=1;
        if(pageSize==null)
            pageSize=10;

        PageHandler ph = new PageHandler(boardDao.count(), page, pageSize);
        Map map = new HashMap();
        map.put("offset", (page-1)*pageSize);
        map.put("pageSize", pageSize);
        List<BoardDto> list = boardDao.selectPage(map);
        m.addAttribute("list", list);
        m.addAttribute("page",page);
        m.addAttribute("pageSize",pageSize);
        m.addAttribute("ph",ph);

        return "boardList";
    }
}

2. 게시물 삭제 

 // 게시물 삭제
    @PostMapping("/delete")
    public String delete(Integer page, Integer pageSize, BoardDto boardDto, Model m, RedirectAttributes rattr, HttpSession session){
        Map map = new HashMap();
        map.put("bno", boardDto.getBno());
        map.put("writer", boardDto.getWriter());

        System.out.println("boardDto.getWriter() = " + boardDto.getWriter());

        try{
            int rowCnt=0;
//            if(session.getAttribute("id").equals(boardDto.getWriter()))
                rowCnt = boardDao.delete(map);

            if(rowCnt==0){
                m.addAttribute("msg", "DEL_ERR");
                throw new Exception("delete Error");
            }

            rattr.addFlashAttribute("msg", "DEL_OK");
            rattr.addFlashAttribute("page", page);
            rattr.addFlashAttribute("pageSize", pageSize);

            System.out.println("boardDto = " + boardDto);

            return "redirect:/board/list";

        }catch(Exception e){
            e.printStackTrace();
            return "board";
        }
    }
  • post방식으로 Mapping된 delete라서 board.html에서도 post방식으로 데이터를 전송해줘야한다.
  • a태그 사용시 Get방식으로 데이터가 전송되기때문에 a태그를 사용하지 않고 자바스크립트를 사용해줬다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <title>Board</title>
</head>
<body>
<h2>게시물 읽기</h2>
<form id="form">
    <input name="bno" type="hidden" th:value="${boardDto.bno}"/>
    <input name="title" type="text" th:value="${boardDto.title}"/>
    <textarea name="content" th:text="${boardDto.content}"></textarea>
    <button>글쓰기</button>
    <button>수정</button>
<!--    <button type="submit" name="delBtn"><a th:href="@{'/board/delete?page='+${page}+'&pageSize='+${pageSize}+'&bno='+${boardDto.bno}+'&writer='+${boardDto.writer}}">삭제</a></button>-->
    <button type="button" id="delBtn">삭제</button>
    <button type="button" name="listBtn"><a th:href="@{'/board/list?page='+${page}+'&pageSize='+${pageSize}}">목록</a></button>
</form>
<script>
    $(document).ready(function(){
        $('#delBtn').on("click",function(){
            if(!confirm("정말로 삭제하시겠습니까?")) return;
            let form = $('#form');
            form.attr("action", "/board/delete?page=[[${page}]]&pageSize=[[${pageSize}]]&bno=[[${boardDto.bno}]]&writer=[[${boardDto.writer}]]");
            form.attr("method", "post");
            form.submit();
        });
    });
</script>
</body>
</html>
  • button태그를 input태그로 바꿔도 보고, type을 submit으로도 바꿔보았지만 a태그가 더 우선적인건지 무조건 Get방식으로 보내져서 자바스크립트로 delete를 작성해야했다.

3. 게시물 읽기

// 게시물 읽어오기
    @GetMapping("/read")
    public String read(Integer page, Integer pageSize, BoardDto boardDto, Model m){
        // boardList.html 에서 title 을 누르면 /board/read 페이지로 넘어온다.

        BoardDto boardDto2 = boardDao.select(boardDto.getBno());

        m.addAttribute("page", page);
        m.addAttribute("pageSize", pageSize);
        m.addAttribute("boardDto", boardDto2);
        System.out.println("read/page = " + page);
        System.out.println("read/pageSize = " + pageSize);
        System.out.println("read/boardDto = " + boardDto2);

        return "board";
    }
  • 게시물 선택 쿼리
<select id="select" resultType="BoardDto" parameterType="int">
    SELECT *
    FROM board
    WHERE bno=#{bno}
</select>
  • page와 pageSize bno(게시물번호)를 read메서드로 넘겨준다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>BoardList</title>
</head>
<body>
<h2>게시물 목록</h2>
<table border="1px">
    <tr>
        <td>번호</td>
        <td>제목</td>
        <td>내용</td>
        <td>작성자</td>
        <td>조회수</td>
    </tr>
    <tr th:each="item : ${list}">
        <td th:text="${item.bno}"></td>
        <td><a th:href="@{'/board/read?page='+${page}+'&pageSize='+${pageSize}+'&bno='+${item.bno}}"><span th:text="${item.title}"></span></a></td>
        <td th:text="${item.content}"></td>
        <td th:text="${item.writer}"></td>
        <td th:text="${item.view_cnt}"></td>
    </tr>
</table>
<div>
    <th:block th:if="${ph.beginPage}>1">
        <a th:href="@{'/board/list?page='+${ph.beginPage+-1}}">&lt;</a>
    </th:block>
    <th:block th:each="i : ${#numbers.sequence(ph.beginPage, ph.endPage)}">
        <a th:href="@{'/board/list?page=' + ${i} + '&pageSize=' + ${ph.pageSize}}"><span th:text="${i}">1</span></a>
    </th:block>
    <th:block th:if="${ph.endPage}<${ph.page_cnt}">
        <a th:href="@{'/board/list?page='+${ph.endPage+1}}">&gt;</a>
    </th:block>
</div>
</body>
</html>

 

 

4. 게시물 수정

  • 게시물을 수정하기 버튼을 누르기 전에는 readonly속성으로 글을 변경할 수 없어야한다.
<form id="form">
    <input id="bno" type="hidden" th:value="${boardDto.bno}" readonly="readonly"/>
    <input id="title" type="text" th:value="${boardDto.title}" th:attr="readonly=${menu != 'write'}"/>
    <textarea id="content" th:text="${boardDto.content}" th:attr="readonly=${menu != 'write'}"></textarea>
    <button>새글쓰기</button>
    <button type="button" id="wrtBtn" th:text="${menu eq 'write' ? '수정완료' : '수정'}"></button>
    <button type="button" id="delBtn">삭제</button>
    <button type="button" id="listBtn"><a th:href="@{'/board/list?page='+${page}+'&pageSize='+${pageSize}}">목록</a></button>
</form>
  • th:attr을 사용해서 menu가 write가 아니면 readonly속성을 추가하도록 만들었다.
  • update 쿼리문
<update id="update" parameterType="BoardDto">
    UPDATE board
    SET title=#{title}, content=#{content}
    WHERE bno=#{bno} AND writer=#{writer}
</update>
  • update메서드는 PostMapping으로 만들것이다.
@Controller
@RequestMapping("/board")
public class BoardController {

    @Autowired
    BoardDao boardDao;

    // 게시물 수정
    @PostMapping("/update")
    public String update(Integer page, Integer pageSize, BoardDto boardDto, RedirectAttributes rattr){

        System.out.println("update/boardDto = " + boardDto);

        int rowCnt = boardDao.update(boardDto);

        rattr.addFlashAttribute("boardDto",boardDto);

        System.out.println("update/page = " + page);
        System.out.println("update/pageSize = " + pageSize);
        System.out.println("update/boardDto.getBno() = " + boardDto.getBno());

        return "redirect:/board/read?page="+page+"&pageSize="+pageSize;
    }

    // 게시물 삭제
    @PostMapping("/delete")
    public String delete(Integer page, Integer pageSize, BoardDto boardDto, Model m, RedirectAttributes rattr, HttpSession session){
        Map map = new HashMap();
        map.put("bno", boardDto.getBno());
        map.put("writer", boardDto.getWriter());

        System.out.println("delete/boardDto.getWriter() = " + boardDto.getWriter());

        try{
            int rowCnt=0;
//            if(session.getAttribute("id").equals(boardDto.getWriter()))
                rowCnt = boardDao.delete(map);

            if(rowCnt==0){
                m.addAttribute("msg", "DEL_ERR");
                throw new Exception("delete Error");
            }

            rattr.addFlashAttribute("msg", "DEL_OK");
            rattr.addFlashAttribute("page", page);
            rattr.addFlashAttribute("pageSize", pageSize);

            System.out.println("delete/boardDto = " + boardDto);

            return "redirect:/board/list";

        }catch(Exception e){
            e.printStackTrace();
            return "board";
        }
    }

    // 게시물 읽어오기
    @GetMapping("/read")
    public String read(Integer page, Integer pageSize, BoardDto boardDto, Model m){
        // boardList.html 에서 title 을 누르면 /board/read 페이지로 넘어온다.

        BoardDto boardDto2 = boardDao.select(boardDto.getBno());

        m.addAttribute("page", page);
        m.addAttribute("pageSize", pageSize);
        m.addAttribute("boardDto", boardDto2);
        System.out.println("read/page = " + page);
        System.out.println("read/pageSize = " + pageSize);
        System.out.println("read/boardDto = " + boardDto2);

        return "board";
    }

    // 게시판 목록
    @GetMapping("/list")
    public String list(Integer page, Integer pageSize, Model m){
        if(page==null)
            page=1;
        if(pageSize==null)
            pageSize=10;

        PageHandler ph = new PageHandler(boardDao.count(), page, pageSize);
        Map map = new HashMap();
        map.put("offset", (page-1)*pageSize);
        map.put("pageSize", pageSize);
        List<BoardDto> list = boardDao.selectPage(map);
        m.addAttribute("list", list);
        m.addAttribute("page",page);
        m.addAttribute("pageSize",pageSize);
        m.addAttribute("ph",ph);

        return "boardList";
    }
}
  • board.html에서 데이터가 update메서드로 들어오지 않는것을 확인 후 수정된 클래스
  • board.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <title>Board</title>
</head>
<body>
<h2>게시물 읽기</h2>

<form id="form">
    <input name="bno" type="hidden" th:value="${boardDto.bno}" readonly/>
    <input name="title" type="text" th:value="${boardDto.title}" readonly/>
    <textarea name="content" th:text="${boardDto.content}" readonly></textarea>
    <input name="writer" type="text" th:value="${boardDto.writer}" readonly/>
    <input name="page" type="hidden" th:value="${page}" readonly/>
    <input name="pageSize" type="hidden" th:value="${pageSize}" readonly/>
    <button>새글쓰기</button>
    <button type="button" id="wrtBtn">수정</button>
    <button type="button" id="delBtn">삭제</button>
    <button type="button" id="listBtn"><a th:href="@{'/board/list?page='+${page}+'&pageSize='+${pageSize}}">목록</a></button>
</form>
<script>
    $(document).ready(function(){
        $('#delBtn').on("click",function(){
            if(!confirm("정말로 삭제하시겠습니까?")) return;
            let form = $('#form');
            form.attr("action", "/board/delete");
            form.attr("method", "post");
            form.submit();
        });
        $('#wrtBtn').on("click", function(){
            let form = $('#form');
            let isReadOnly = $("input[name=title]").attr('readonly');
            if(isReadOnly=='readonly'){
                $("textarea[name=content]").removeAttr("readonly");
                $("input[name=title]").removeAttr("readonly");
                $('h2').html('게시물 수정');
                $('#wrtBtn').html('수정완료');
                return;
            }

            if(!confirm("변경하시겠습니까?")) return;
            form.attr("action", "/board/update");
            form.attr("method", "post");
            form.submit();
        });
    });
</script>
</body>
</html>
  • form.submit()메서드를 실행하면 form태그 안에있는 데이터를 자동으로 매핑된 메서드로 전달해준다.
  • URL뒤에 붙여줄 값들이 적어졌고 중복이 줄어들었다.

  • read메서드와 update메서드에 데이터가 잘 전달된것을 볼 수 있었다.

5. 게시물 등록

  • board.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <title>Board</title>
</head>
<body>
<h2>게시물 읽기</h2>

<form id="form">
    <input name="bno" type="hidden" th:value="${boardDto.bno}" readonly/>
    <input name="title" type="text" th:value="${boardDto.title}" readonly/>
    <textarea name="content" th:text="${boardDto.content}" readonly></textarea>
    <input name="writer" type="text" th:value="${boardDto.writer}" readonly/>
    <input name="page" type="hidden" th:value="${page}" readonly/>
    <input name="pageSize" type="hidden" th:value="${pageSize}" readonly/>
    <input name="check" type="hidden" readonly />
    <button type="button" id="creBtn">새글쓰기</button>
    <button type="button" id="wrtBtn">수정</button>
    <button type="button" id="delBtn">삭제</button>
    <button type="button" id="listBtn">목록</button>
</form>
<script>
    $(document).ready(function(){
        $('#creBtn').on("click", function(){
            let form = $('#form');
            let isReadOnly = $("input[name=check]").attr('readonly');
            if(isReadOnly=='readonly'){
                $("input[name=check]").attr('readonly', false);
                $("input[name=title]").attr('readonly', false);
                $("textarea[name=content]").attr('readonly', false);
                $('#creBtn').html('등록');
                $('h2').html('게시물 등록');
                return;
            }

            form.attr("action", "/board/insert");
            form.attr("method", "post");
            form.submit();
        })
        $('#listBtn').on("click", function(){
            let form = $('#form');
            form.attr("action", "/board/list");
            form.attr("method", "get");
            form.submit();
        })
        $('#delBtn').on("click",function(){
            if(!confirm("정말로 삭제하시겠습니까?")) return;
            let form = $('#form');
            form.attr("action", "/board/delete");
            form.attr("method", "post");
            form.submit();
        });
        $('#wrtBtn').on("click", function(){
            let form = $('#form');
            let isReadOnly = $("input[name=title]").attr('readonly');
            if(isReadOnly=='readonly'){
                $("textarea[name=content]").removeAttr("readonly");
                $("input[name=title]").removeAttr("readonly");
                $('h2').html('게시물 수정');
                $('#wrtBtn').html('수정완료');
                return;
            }

            if(!confirm("변경하시겠습니까?")) return;
            form.attr("action", "/board/update");
            form.attr("method", "post");
            form.submit();
        });
    });
</script>
</body>
</html>
  • insert메서드
// 게시물 추가
    @PostMapping("/insert")
    public String insert(Integer page, Integer pageSize, BoardDto boardDto, RedirectAttributes rattr){
        System.out.println("insert/page = " + page);
        System.out.println("insert/pageSize = " + pageSize);
        System.out.println("insert/boardDto = " + boardDto);

//      BoardDto boardDto2 = new BoardDto(boardDto.getTitle(), boardDto.getContent(), (String)session.getAttribute("id"));
        BoardDto boardDto2 = new BoardDto(boardDto.getTitle(), boardDto.getContent(), boardDto.getWriter());

        boardDao.insert(boardDto);

        return "redirect:/board/list?page="+page+"&pageSize="+pageSize;
    }
  • 기본적인 로그인 체크나 아이디 체크 같은건 생략하고 CRUD에만 집중해서 만들었다.