본문 바로가기
개발/Java

[Spring] 스프링 입문 5 - 게시판 제작하기

by m_.9m 2023. 1. 5.

 

 

1. 글 목록 보기, 새로운 글쓰기, 페이징, 글 검색 기능 구현.

 

 

2.소스코드

 

 

board.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thyleaf.org">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>main page</title>
    <link href="../css/main.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<style>
table{
    width:100%;
    height:70%;
    border: 1px solid;
    border-collapse: collapse;
    text-align: center;
}

tr, td{
    border: 1px solid;
    border-collapse: collapse;
    padding: 3px 3px;
}

button {
    left: 85%;
  background-color: #fff;
  border: 1px solid #d5d9d9;
  border-radius: 8px;
  box-shadow: rgba(213, 217, 217, .5) 0 2px 5px 0;
  box-sizing: border-box;
  color: #0f1111;
  cursor: pointer;
  display: inline-block;
  font-family: "Amazon Ember",sans-serif;
  font-size: 13px;
  line-height: 29px;
  padding: 0 10px 0 11px;
  position: relative;
  text-align: center;
  text-decoration: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  vertical-align: middle;
  width: 80px;
}

button:hover {
  background-color: #f7fafa;
}

button:focus {
  border-color: #008296;
  box-shadow: rgba(213, 217, 217, .5) 0 2px 5px 0;
  outline: 0;
}

.pagination {
  display: inline-block;
}

.pagination a {
  color: black;
  float: left;
  padding: 8px 16px;
  text-decoration: none;
  border: 1px solid #ddd;
}

.pagination a.active {
  background-color: #4CAF50;
  color: white;
  border: 1px solid #4CAF50;
}

.pagination a:hover:not(.active) {background-color: #ddd;}

.pagination a:first-child {
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
}

.pagination a:last-child {
  border-top-right-radius: 5px;
  border-bottom-right-radius: 5px;
}


</style>

<div class="header">
    <h1>Spring 취약점 테스트</h1>
</div>

<div class="topnav">
    <a href="../login">로그인</a>
    <a href="board">게시판</a>
    <a href="#">SSTI 취약점</a>
    <a href="#">LFI, RFI</a>
    <a href="#">추가 예정</a>
</div>

<div class="row">
    <div class="column side">
        <h2>환경 구성</h2>
        <p>Spring boot 2.7.3 <br> Gradle <br> Thymeleaf <br> MySQL <br> Mybatis</p>
    </div>
    <div class="column middle">
        <h2>게시판</h2>
        검색어: <p th:text="${search}"></p>


        <head>
            <meta charset="UTF-8">
        </head>
        <body>
        <div class="container">
            <table>
                <thead>
                <td>번호</td>
                <td>제목</td>
                <td>작성자</td>
                </thead>
                <tbody>

                <tr th:each="board: ${boardlist}">
                    <td th:text="${board.id}"></td>
                    <td th:text="${board.title}"></td>
                    <td th:text="${board.writer}"></td>
                </tr>
                </tbody>
            </table>
            <button onclick="location.href='newpost'" style="margin-top:3px;=">글쓰기</button>
            <!-- pagination{s} -->
            <div id="paginationBox">

                <ul class="pagination">
                    <!--                    <th:if test="${pagination.prev}">-->
                    <li class="page-item" th:if="${pagination.prev}">
                        <!--                            <a class="page-link" href="#" onClick="fn_prev('${pagination.page}', '${pagination.range}', '${pagination.rangeSize}')">Previous</a>-->
                        <a class="page-link" href="#"
                           th:attr="onclick=|fn_prev('${pagination.page}', '${pagination.range}', '${pagination.rangeSize}')|">Previous</a>
                    </li>
                    <!--                    </th:if>-->
                    <!--                    <th:forEach begin="${pagination.startPage}" end="${pagination.endPage}" var="idx">-->
                    <!--&lt;!&ndash;                        <li class="page-item <th:out value=\"${pagination.page== idx ? 'active' : ''}\"/> ">&ndash;&gt;-->
                    <!--                        <li class="page-item">-->
                    <!--                            <a class="page-link" href="#" onClick="fn_pagination('${idx}', '${pagination.range}', '${pagination.rangeSize}')">-->
                    <!--                                ${idx}-->
                    <!--                            </a>-->
                    <!--                        </li>-->
                    <!--                    </th:forEach>-->

                    <th:block th:each="idx : ${#numbers.sequence(pagination.startPage, pagination.endPage+1)}">
                        <!--                        <a th:text="${idx}" class="page-link" href="#" onClick="fn_pagination('${idx}', '${pagination.range}', '${pagination.rangeSize}')"></a>-->
                        <a th:text="${idx}" class="page-link" href="#"
                           th:attr="onclick=|fn_pagination('${idx}', '${pagination.range}', '${pagination.rangeSize}')|"></a>
                    </th:block>

                    <!--                    <th:if test="${pagination.next}">-->
                    <li class="page-item" th:if="${pagination.next}">
                        <!--                            <a class="page-link" href="#" onClick="fn_next('${pagination.range}', '${pagination.range}', '${pagination.rangeSize}')">Next</a>-->
                        <a class="page-link" href="#"
                           th:attr="onclick=|fn_next('${pagination.page}', '${pagination.range}', '${pagination.rangeSize}')|">Previous</a>
                    </li>
                    <!--                    </th:if>-->
                </ul>
            </div>
            <!-- pagination{e} -->
            <div id="search_box" style="text-align: center;">
                <form action="search_result" mothod="get">
                    <select name="gubun">
                        <option value="title">제목</option>
                        <option value="writer">글쓴이</option>
                        <option value="content">내용</option>
                    </select>
                    <input type="text" name="search" size="40" required="required">
                    <button class="btn" style="position:relative; left:3px;">검색</button>
                </form>
            </div>
        </div>
        </body>

    </div>
    <div class="column side">
        <h2>Side</h2>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit..</p>
    </div>
</div>

<div class="footer">
    <p>Footer</p>
</div>
</body>
</html>

<script>

//이전 버튼 이벤트

function fn_prev(page, range, rangeSize) {
      var page = ((range - 2) * rangeSize) + 1;
      var range = range - 1;

      var url = "/Board/boardpage";
      url = url + "?page=" + page;

      url = url + "&range=" + range;



      location.href = url;

   }



  //페이지 번호 클릭

   function fn_pagination(page, range, rangeSize, searchType, keyword) {

      var url = "/Board/boardpage";

      url = url + "?page=" + page;

      url = url + "&range=" + range;

      location.href = url;

   }



   //다음 버튼 이벤트

   function fn_next(page, range, rangeSize) {

      var page = parseInt((range * rangeSize)) + 1;

      var range = parseInt(range) + 1;



      var url = "/Board/boardpage";

      url = url + "?page=" + page;

      url = url + "&range=" + range;



      location.href = url;

   }


</script>

 

 

 

css

* {
    box-sizing: border-box;
}
body {
  margin: 0;
}
/* Style the header */
.header {
    background-color: #f1f1f1;
    padding: 20px;
    text-align: center;
}
/* Style the top navigation bar */
.topnav {
    overflow: hidden;
    background-color: #333;
}
/* Style the topnav links */
.topnav a {
    float: left;
    display: block;
    color: #f2f2f2;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
}
/* Change color on hover */
.topnav a:hover {
    background-color: #ddd;
    color: black;
}
/* Create three unequal columns that floats next to each other */
.column {
    float: left;
    padding: 10px;
}
/* Left and right column */
.column.side {
    width: 25%;
}
/* Middle column */
.column.middle {
    width: 50%;
}
/* Clear floats after the columns */
.row:after {
    content: "";
    display: table;
    clear: both;
}
/* Responsive layout - makes the three columns stack on top of each other instead of next to each other */
@media screen and (max-width: 600px) {
    .column.side, .column.middle {
        width: 100%;
    }
}
/* Style the footer */
.footer {
    background-color: #f1f1f1;
    padding: 10px;
    text-align: center;
}

 

 

Controller

BoardController

package hello.hellospring.Controller;

import hello.hellospring.domain.Board;
import hello.hellospring.domain.Pagination;
import hello.hellospring.mapper.BoardMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

import static java.lang.System.out;

@Controller
@Slf4j
public class BoardController {

    private final BoardMapper boardMapper;

    public BoardController(BoardMapper boardMapper) {
        this.boardMapper = boardMapper;
    }


    @GetMapping("Board/board")
    public String getBoardList(@ModelAttribute Board board, Model model){
        Pagination pagination = new Pagination();
        int totalCount = boardMapper.getBoardTotal(board);
        int page = 1;
        pagination.pageInfo(page, 1, totalCount);

        board.setStartRownum(pagination.getStartList());
        board.setEndRownum(pagination.getEndList());

        List<Board> boardList = boardMapper.getBoardPage(board);

        model.addAttribute("boardlist", boardList);
        model.addAttribute("pagination", pagination);
        return "Board/board";
    }

    @GetMapping("Board/boardpage")
    public String getBoardListPage(@ModelAttribute Board board, Model model){
//        pageInfo(int page, int range, int listCnt);
        Pagination pagination = new Pagination();
        int totalCount = boardMapper.getBoardTotal(board);
        int page = 1;
        if ( board.getPage() > 1 ) page = board.getPage();
        pagination.pageInfo(page, 1, totalCount);

        out.println("------------------------");
        out.println(page);
        out.println(pagination.getStartList());
        out.println(pagination.getEndList());
        out.println("------------------------");

        board.setStartRownum(pagination.getStartList());
        board.setEndRownum(pagination.getEndList());

        List<Board> boardList = boardMapper.getBoardPage(board);

        model.addAttribute("boardlist", boardList);
        model.addAttribute("pagination", pagination);
        return "Board/board";
    }


    @GetMapping("Board/search_result")
    public String board_search(@ModelAttribute Board board ,Model model, HttpServletRequest request){
        Pagination pagination = new Pagination();
        int totalCount = boardMapper.getBoardTotal(board);
        int page = 1;
        pagination.pageInfo(page, 1, totalCount);

        board.setStartRownum(pagination.getStartList());
        board.setEndRownum(pagination.getEndList());
        String search1 = board.getSearch();

        List<Board> boardList = boardMapper.resultBoard(board);
        String search = request.getParameter("search");
//        model.addAttribute("search",search);
        model.addAttribute("search",search1);
        model.addAttribute("boardlist", boardList);
        model.addAttribute("pagination", pagination);
        return "Board/board";
    }

    @GetMapping("Board/newpost")
    public String board_new(@ModelAttribute Board board , Model model){
        return "Board/newpost";
    }

    @PostMapping("Board/newpost_ok")
    public String board_newpost(@ModelAttribute Board board , Model model){
        boardMapper.setBoard(board);
        model.addAttribute("board", board);

        out.println("====================" + board.getTitle());
        out.println("====================" + board.getContent());
        return "redirect:/Board/board";
    }



}

 

 

Domain

Board.java

package hello.hellospring.domain;


public class Board {
        private String title;
        private String content;
        private  String writer;
        private Long id;

        private String gubun;
        private String search;

        // paging
        private int startRownum;
        private int endRownum;
        private int page;

        public int getStartRownum() {
                return startRownum;
        }

        public void setStartRownum(int startRownum) {
                this.startRownum = startRownum;
        }

        public int getEndRownum() {
                return endRownum;
        }

        public void setEndRownum(int endRownum) {
                this.endRownum = endRownum;
        }

        public int getPage() {
                return page;
        }

        public void setPage(int page) {
                this.page = page;
        }

        public Board(Long id, String title, String content, String writer) {
                this.id = id;
                this.title = title;
                this.content = content;
                this.writer = writer;
        }
        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 getWriter() {
                return writer;
        }

        public void setWriter(String writer) {
                this.writer = writer;
        }

        public Long getId() {
                return id;
        }

        public void setId(Long id) {
                this.id = id;
        }

        public String getGubun() {
                return gubun;
        }

        public void setGubun(String gubun) {
                this.gubun = gubun;
        }

        public String getSearch() {
                return search;
        }

        public void setSearch(String search) {
                this.search = search;
        }
}

 

 

Pagination(페이징)

package hello.hellospring.domain;

public class Pagination {
    private int listSize = 7;  // **               //초기값으로 목록개수를 10으로 셋팅

    private int rangeSize = 10;            //초기값으로 페이지범위를 10으로 셋팅
    private int page; // *** current page
    private int range;  //  각 페이지 범위 시작 번호
    private int listCnt;  // ** total count
    private int pageCnt;
    private int startPage;
    private int startList;
    private int endList;
    private int endPage;
    private boolean prev;
    private boolean next;


    public int getRangeSize() {
        return rangeSize;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getRange() {
        return range;
    }

    public void setRange(int range) {
        this.range = range;
    }


    public int getStartPage() {
        return startPage;
    }


    public void setStartPage(int startPage) {
        this.startPage = startPage;
    }


    public int getEndPage() {
        return endPage;
    }


    public void setEndPage(int endPage) {
        this.endPage = endPage;
    }


    public boolean isPrev() {
        return prev;
    }


    public void setPrev(boolean prev) {
        this.prev = prev;
    }



    public boolean isNext() {
        return next;
    }


    public void setNext(boolean next) {
        this.next = next;
    }


    public int getListSize() {
        return listSize;
    }


    public void setListSize(int listSize) {
        this.listSize = listSize;
    }


    public int getListCnt() {
        return listCnt;
    }


    public void setListCnt(int listCnt) {
        this.listCnt = listCnt;
    }


    public int getStartList() {
        return startList;
    }
    
    public int getEndList() {
        return endList;
    }

    public void pageInfo(int page, int range, int listCnt) {
        this.page = page;
        this.range = range;
        this.listCnt = listCnt;

        //전체 페이지수
        this.pageCnt = (int) Math.ceil(listCnt/listSize);

        //시작 페이지
        this.startPage = (range - 1) * rangeSize + 1 ;

        //끝 페이지
        this.endPage = range * rangeSize;

        //게시판 시작번호
        this.startList = (page - 1) * listSize;
        this.endList = (page - 1) * listSize + listSize -1;

        //이전 버튼 상태
        this.prev = range == 1 ? false : true;

        //다음 버튼 상태
        this.next = endPage > pageCnt ? false : true;
        if (this.endPage > this.pageCnt) {
            this.endPage = this.pageCnt;
            this.next = false;
        }
    }
}

 

 

 

interface

BoardMapper

package hello.hellospring.mapper;

import hello.hellospring.domain.Board;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface BoardMapper {
    List<Board> getBoard(Board board);
    List<Board> getBoardPage(Board board);
    int getBoardTotal(Board board);

    List<Board> resultBoard(Board board);

    int setBoard(Board board);
    void deleteBoard(Board board);

}

 

 

 

board-mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="hello.hellospring.mapper.BoardMapper">

    <resultMap type="board" id="selectMap">
        <result column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="content" property="content"/>
        <result column="writer" property="writer"/>
    </resultMap>

    <select id="getBoard" resultMap="selectMap">
        select * from board
    </select>

    <select id="getBoardPage" parameterType="Board" resultMap="selectMap">
        <![CDATA[
            select *
            from
            (
            select @rownum := @rownum +1 AS ROWNUM
            , b.* from board b, (SELECT @ROWNUM:=0) R
            ) t
            where ROWNUM >= #{startRownum,jdbcType=NUMERIC}
            and ROWNUM <= #{endRownum,jdbcType=NUMERIC}
        ]]>
    </select>

    <select id="getBoardTotal" parameterType="Board" resultType="int">
        select count(*) as totalCnt from board
    </select>


    <!-- 리턴값이 존재하지않음! int 값으로 반환   -->
    <insert id="setBoard" parameterType="Board">
        INSERT INTO board (title, content,writer) VALUES (#{title,jdbcType=VARCHAR}, #{content,jdbcType=VARCHAR}, #{writer,jdbcType=VARCHAR});
    </insert>

    <delete id="deleteBoard" parameterType="Board">
        delete from board where id = #{id,jdbcType=NUMERIC}
    </delete>

    <select id="resultBoard" parameterType="Board" resultMap="selectMap">
        select * from board
        where 1 = 1
        <choose>
            <when test='gubun == "title"'>
<!--                AND title like #{search,jdbcType=VARCHAR}-->
                AND title like "${search}"
            </when>
            <when test='gubun == "content"'>
                AND content like #{search,jdbcType=VARCHAR}
            </when>
            <when test='gubun == "writer"'>
                AND writer like #{search,jdbcType=VARCHAR}
            </when>
        </choose>


    </select>

</mapper>