spring mybatis 셋팅이 끝났다면 이제 게시판을 만들 차례이다.

공지사항 게시판을 예로 들어 먼저 공지사항 리스트 페이지를 만들어 보자.


가장 먼저 게시판을 위한 controller, dao, mapper, vo를 생성하자.


<프로젝트 패키지 구성>

필자는 위와 같이 패키지와 클래스를 생성하였다.


Controller 소스를 구성하기 전에 기존에 pom.xml에 등록했던 lombok 라이브러리의 기능을 사용하기 위해 lombok.config라는 파일을 생성하자.

경로 : 프로젝트 하위(pom.xml과 동일한 경로)


<lombok.config 소스 코드>

1
lombok.log.fieldName=logger

이 설정은 @Slf4j 어노테이션 사용 시 static logger를 생성하지 않고 바로 log를 찍을 수 있게 해준다.(아래의 소스들을 보면 확인이 가능하다.)


<게시판 table 생성>

1
2
3
4
5
6
7
8
CREATE TABLE `push_notice` (
  `notice_id` int(11) NOT NULL AUTO_INCREMENT,
  `notice_title` varchar(32) NOT NULL,
  `notice_coments` varchar(4000) NOT NULL,
  `mod_date` timestamp NULL DEFAULT NULL,
  `use_yn` varchar(2) NOT NULL DEFAULT 'Y',
  PRIMARY KEY (`notice_id`)
)

필자는 위와같이 테이블을 생성하였다.


1. Controller 단 구성


<NoticeController.java 소스 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.spring.test.controller;
 
import java.util.List;
 
import javax.annotation.Resource;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.spring.test.dao.NoticeDao;
import com.spring.test.vo.NoticeVo;
import com.spring.test.vo.PagingVo;
 
import lombok.extern.slf4j.Slf4j;
 
@Controller
@Slf4j
public class NoticeController {
 
    @Resource(name = "noticeDao")
    private NoticeDao noticeDao;
     
    @RequestMapping(value = "/noticeList")
    public String noticeList(@ModelAttribute("Notice") NoticeVo notice,
                            @RequestParam(defaultValue="1") int curPage,
                            Model model
                                ) {
        logger.info("noticeList page");
        int listCnt = noticeDao.getNoticeListCount();
        PagingVo paging = new PagingVo(listCnt, curPage);
        notice.setStartIndex(paging.getStartIndex());
        notice.setCntPerPage(paging.getPageSize());
             
        List<NoticeVo> noticeList=noticeDao.getNoticeList(notice);
        model.addAttribute("noticeList", noticeList);
        model.addAttribute("listCnt", listCnt);
        model.addAttribute("paging", paging);
        return "notice/noticeList";
    }
     
    @RequestMapping(value = "/noticeRegi")
    public String noticeRegi() {
        logger.info("notice Regi page");
            return "notice/noticeDetail";
    }
     
    @RequestMapping(value = "/noticeDetail/{notice_id}")
    public String noticeEdit(@PathVariable String notice_id, Model model) {
        logger.info("notice detail page [notice_id = {}]",notice_id);
        model.addAttribute("notice",noticeDao.getNoticeOne(notice_id));
        return "notice/noticeDetail";
    }
     
    @ResponseBody
    @RequestMapping(value = "/noticeInsert", method=RequestMethod.POST)
    public int noticeInsert(NoticeVo notice) {
        logger.info("notice insert");
        return noticeDao.NoticeInsert(notice);
    }
     
    @ResponseBody
    @RequestMapping(value = "/noticeUpdate", method=RequestMethod.POST)
    public int noticeUpdate(NoticeVo notice) {
        logger.info("notice update {} ", notice.getNotice_id());
        return noticeDao.NoticeUpdate(notice);
    }
     
    @ResponseBody
    @RequestMapping(value = "/noticeDelete", method=RequestMethod.POST)
    public int noticeDelete(NoticeVo notice) {
        logger.info("notice delete {} ", notice.getNotice_id());
        return noticeDao.NoticeDelete(notice.getNotice_id());
    }  
}

컨트롤러 구성은 위에서 부터 게시판 리스트 페이지, 등록페이지, 수정페이지, 게시판 정보 insert, 게시판 정보 update, 게시판 정보 delete 로 구성하였다.

하나의 메소드에서 여러개의 기능을 하도록 소스를 구성할 수 있으나, 보기 쉽게 할 수 있도록 메소드를 나뉘었다.

또한 noticeList()에서 페이징 처리를 할 수 있도록 소스를 구성하였다.


-> 여기서 @ResponseBody 어노테이션을 사용한 메소드는 view의 ajax로 부터의 요청에 대해 view 즉 jsp의 경로를 돌려주는게 아닌 문자열만 리턴하도록 처리된다.

-> Spring에서 제공하는 @Autowired 어노테이션 대신 java에서 제공하는 @Resource 어노테이션을 사용하여 가용성을 높였다.


2. model 단 구성


<PagingVo.java 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.spring.test.vo;
 
import lombok.Data;
 
@Data
public class PagingVo {
    /** 한 페이지당 게시글 수 **/
    private int pageSize = 10;
     
    /** 한 블럭(range)당 페이지 수 **/
    private int rangeSize = 10;
     
    /** 현재 페이지 **/
    private int curPage = 1;
     
    /** 현재 블럭(range) **/
    private int curRange = 1;
     
    /** 총 게시글 수 **/
    private int listCnt;
     
    /** 총 페이지 수 **/
    private int pageCnt;
     
    /** 총 블럭(range) 수 **/
    private int rangeCnt;
     
    /** 시작 페이지 **/
    private int startPage = 1;
     
    /** 끝 페이지 **/
    private int endPage = 1;
     
    /** 시작 index **/
    private int startIndex = 0;
     
    /** 이전 페이지 **/
    private int prevPage;
     
    /** 다음 페이지 **/
    private int nextPage;
     
    public PagingVo(int listCnt, int curPage){
         
        /**
         * 페이징 처리 순서
         * 1. 총 페이지수
         * 2. 총 블럭(range)수
         * 3. range setting
         */
         
        // 총 게시물 수와 현재 페이지를 Controller로 부터 받아온다.
        /** 현재페이지 **/
        setCurPage(curPage);
        /** 총 게시물 수 **/
        setListCnt(listCnt);
         
        /** 1. 총 페이지 수 **/
        setPageCnt(listCnt);
        /** 2. 총 블럭(range)수 **/
        setRangeCnt(pageCnt);
        /** 3. 블럭(range) setting **/
        rangeSetting(curPage);
         
        /** DB 질의를 위한 startIndex 설정 **/
        setStartIndex(curPage);
    }
  
    public void setPageCnt(int listCnt) {
        this.pageCnt = (int) Math.ceil(listCnt*1.0/pageSize);
    }
    public void setRangeCnt(int pageCnt) {
        this.rangeCnt = (int) Math.ceil(pageCnt*1.0/rangeSize);
    }
    public void rangeSetting(int curPage){
         
        setCurRange(curPage);       
        this.startPage = (curRange - 1) * rangeSize + 1;
        this.endPage = startPage + rangeSize - 1;
         
        if(endPage > pageCnt){
            this.endPage = pageCnt;
        }
         
        this.prevPage = curPage - 1;
        this.nextPage = curPage + 1;
    }
    public void setCurRange(int curPage) {
        this.curRange = (int)((curPage-1)/rangeSize) + 1;
    }
    public void setStartIndex(int curPage) {
        this.startIndex = (curPage-1) * pageSize;
    }
}


<NoticeVo.java 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.spring.test.vo;
 
import lombok.Data;
 
@Data
public class NoticeVo {
    private String notice_id;
    private String notice_title;
    private String notice_coments;
    private String use_yn;
    private String mod_date;
    private int startIndex;
    private int cntPerPage;
}

->lombok의 @Data 어노테이션을 사용하여 setter, getter 생성을 생략할 수 있다.


<NoticeMapper.java 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.spring.test.dao;
 
import java.util.List;
 
import org.springframework.stereotype.Repository;
 
import com.spring.test.vo.NoticeVo;
 
@Repository(value = "noticeMapper")
public interface NoticeMapper {
    public int noticeListCount();
    public List<NoticeVo> noticeList(NoticeVo notice);
    public NoticeVo noticeOne(String notice_id);
    public int noticeInsert(NoticeVo notice);
    public int noticeUpdate(NoticeVo notice);
    public int noticeDelete(String notice_id);
}


<NoticeDao.java 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.spring.test.dao;
 
import java.util.List;
 
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
 
import com.spring.test.vo.NoticeVo;
 
@Service(value = "noticeDao")
public class NoticeDao{
 
    @Resource(name = "noticeMapper")
    private NoticeMapper noticeMapper;
     
    public int getNoticeListCount() {
        return noticeMapper.noticeListCount();
    }
     
    public List<NoticeVo> getNoticeList(NoticeVo notice){
        return noticeMapper.noticeList(notice);
    }
     
    public NoticeVo getNoticeOne(String notice_id) {
        return noticeMapper.noticeOne(notice_id);
    }
     
    public int NoticeInsert(NoticeVo notice) {
        return noticeMapper.noticeInsert(notice);
    }
     
    public int NoticeUpdate(NoticeVo notice) {
        return noticeMapper.noticeUpdate(notice);
    }
     
    public int NoticeDelete(String notice_id) {
        return noticeMapper.noticeDelete(notice_id);
    }
}

-> NoticeDao.java에서 현재 소스에는 없지만 추후 트랜잭션을 처리하기에 용이할 것 같다.


<noticeMapper.xml 소스 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 <mapper namespace="com.spring.test.dao.NoticeMapper">
    <resultMap id="NoticeVo" type="com.spring.test.vo.NoticeVo">
        <result property="notice_id" column="notice_id"/>
        <result property="notice_title" column="notice_title"/>
        <result property="notice_coments" column="notice_coments"/>
        <result property="mod_date" column="mod_date"/>
        <result property="use_yn" column="use_yn"/>
    </resultMap>
     
    <select id="noticeListCount" resultType="Integer">
        select count(*)
        from push_notice;
    </select>
     
    <select id="noticeList" resultMap = "NoticeVo">
        select  notice_id,
                notice_title,
                notice_coments,
                use_yn,
                DATE_FORMAT(mod_date, '%Y-%m-%d') mod_date
        from push_notice
        order by notice_id
        limit #{startIndex}, #{cntPerPage}
         
    </select>
    <select id="noticeOne" parameterType="String" resultMap = "NoticeVo">
        select  notice_id,
                notice_title,
                notice_coments
        from push_notice
        where notice_id = #{notice_id}
    </select>
    <insert id="noticeInsert" parameterType="com.spring.test.vo.NoticeVo">
        insert into push_notice
        (
            notice_title,
            notice_coments,
            mod_date
        )
        values
        (
            #{notice_title},
            #{notice_coments},
            NOW()
        )
    </insert>
    <update id="noticeUpdate" parameterType="com.spring.test.vo.NoticeVo">
        update push_notice set
            notice_title = #{notice_title},
            notice_coments = #{notice_coments},
            mod_date = NOW()
        where notice_id = #{notice_id}
    </update>
    <delete id="noticeDelete" parameterType="String">
        delete from push_notice
        where notice_id = #{notice_id}
    </delete>
 </mapper>

비즈니스 로직을 다양한 방법으로 구성할 수 있으나 필자는 위와 같이 구성하는 것이 추후 유지보수 하거나 트랜잭션을 구성하는데 용이하다고 판단되었다.


3. View 단 구성

View단은 noticeList.jsp와 noticeDetail.jsp로 구성하였으며, jquery는 3.3.1버전을 사용하였다.


<noticeList.jsp 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<p><%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="/test/resources/css/noticeList.css">
<script type="text/javascript" src="/test/resources/js/jquery-3.3.1.min.js"></script>
<script>
    $(document).ready(function(){
        $("#notice_regi").on("click",function(){
            location.href="/test/noticeRegi"
        });
    });
    function fn_paging(curPage){
        location.href="/test/noticeList?curPage="+curPage;
    }
    function notice_push(notice_id){
        alert(notice_id);
    }
</script>
</head>
<body>
    <div class="title">공지사항</div>
    <div class="contents">
        <input type="button" id="notice_regi" value="등록">
        <div class="divTable greenTable">
            <div class="divTableHeading">
                <div class="divTableRow">
                    <div class="divTableHead"><input type="checkbox"></div>
                    <div class="divTableHead">NO</div>
                    <div class="divTableHead">제목</div>
                    <div class="divTableHead">등록일자</div>
                    <div class="divTableHead"></div>
                </div>
            </div>
            <c:forEach var="v" items="${noticeList}" varStatus="status">
                <div class="divTableBody">
                    <div class="divTableRow">
                        <div class="divTableCell"><input type="checkbox"></div>
                        <div class="divTableCell">${status.index+1+(paging.curPage-1)*10}</div>
                        <div class="divTableCell">
                            <a href="/test/noticeDetail/${v.notice_id}">${v.notice_title}</a>
                        </div>
                        <div class="divTableCell">${v.mod_date}</div>
                        <div class="divTableCell"><input type="button" onclick="notice_push(${v.notice_id})" value="전송"></div>
                    </div>
                </div>
            </c:forEach>
        </div>
         
        <div class="greenTable outerTableFooter">
            <div class="tableFootStyle">
                <div class="links">
                        <a href="#" onClick="fn_paging(1)">[처음]</a>
                    <c:if test="${paging.curPage ne 1}">
                        <a href="#" onClick="fn_paging(${paging.prevPage })">[이전]</a>
                    </c:if>
                    <c:forEach var="pageNum" begin="${paging.startPage }" end="${paging.endPage }">
                        <c:choose>
                            <c:when test="${pageNum eq  paging.curPage}">
                                <span style="font-weight: bold;">
                                    <a href="#" onClick="fn_paging(${pageNum })" style="font-weight: bold; color:red;">
                                        ${pageNum }
                                    </a>
                                </span>
                            </c:when>
                            <c:otherwise>
                                <a href="#" onClick="fn_paging(${pageNum })">${pageNum }</a>
                            </c:otherwise>
                        </c:choose>
                    </c:forEach>
                    <c:if test="${paging.curPage ne paging.pageCnt && paging.pageCnt > 0}">
                        <a href="#" onClick="fn_paging(${paging.nextPage })">[다음]</a>
                    </c:if>
                    <c:if test="${paging.curRange ne paging.rangeCnt && paging.rangeCnt > 0}">
                        <a href="#" onClick="fn_paging(${paging.pageCnt })">[끝]</a>
                    </c:if>
                </div>
            </div>
        </div>
         
        <div>
                    총 게시글 수 : ${paging.listCnt } /    총 페이지 수 : ${paging.pageCnt } / 현재 페이지 : ${paging.curPage } / 현재 블럭 : ${paging.curRange } / 총 블럭 수 : ${paging.rangeCnt }
        </div>
         
    </div>
     
     
</body>
</html>
</p>

<noticeDetail.jsp 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<p><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="/test/resources/css/noticeDetail.css">
<script type="text/javascript" src="/test/resources/js/jquery-3.3.1.min.js"></script>
<script>
    $(document).ready(function(){
         
        //공지사항 신규 등록
        $("#notice_regist").on("click",function(){
            var formData = $("#notice_form").serialize();
            $.ajax({
                type : "post",
                url : "/test/noticeInsert",
                data : formData,
                success : function(data){
                    if(data==1) alert("등록 완료");
                    else alert('등록 실패');
                         
                },
                error : function(error){
                    alert("등록 실패");
                    console.log("notice insert fail : "+error);
                }
            });
        });
         
        //공지사항 수정
        $("#notice_edit").on("click",function(){
            var formData = $("#notice_form").serialize();
            $.ajax({
                type : "post",
                url : "/test/noticeUpdate",
                data : formData,
                success : function(data){
                    if(data==1) alert("수정 완료");
                    else alert('수정 실패');
                },
                error : function(error){
                    alert("수정 실패");
                    console.log("notice update fail : "+error);
                }
            });
        });
         
        //공지사항 삭제
        $("#notice_delete").on("click",function(){
            var formData = $("#notice_form").serialize();
            alert(formData);
            $.ajax({
                type : "post",
                url : "/test/noticeDelete",
                data : formData,
                success : function(data){
                    if(data==1){
                        alert("삭제 완료");
                        location.href="/push/noticeList";
                    }else alert('삭제 실패');
                },
                error : function(error){
                    alert("삭제 실패");
                    console.log("notice delete fail : "+error);
                }
            });
        });
         
        $("#notice_backPage").on("click",function(){
            location.href="/test/noticeList";
        });
    })
</script>
</head>
<body>
    <div class="big_title">공지사항 수정/삭제/추가</div>
    <div class="big_contents">
    <form id="notice_form">
    <input type="hidden" id="notice_id" name="notice_id" value="${notice.notice_id }">
        <div class="title">
            <div>
                제목
            </div>
            <div>
                <input type="text" id="notice_title" name="notice_title" value="${notice.notice_title}">
            </div>
        </div>
         
        <div class="contents">
            <div>
                내용
            </div>
            <div>
                <textarea id="notice_coments" name="notice_coments">${notice.notice_coments}</textarea>
            </div>
        </div>
    </form>
         
        <div class="footer">
             
            <c:if test="${null eq notice }">
                <input type="button" id="notice_regist" value="등록">
            </c:if>
            <c:if test="${null ne notice }">
                <input type="button" id="notice_edit" value="수정">
            </c:if>      
            <input type="button" id="notice_backPage" value="뒤로">
            <input type="button" id="notice_delete" value="삭제">
        </div>
         
    </div>
     
     
</body>
</html>
</p>

noticeDetail.jsp에서 ajax를 사용하여 등록, 수정, 삭제 기능을 처리하였다.

-> ajax에서 serialize()메소드를 통해 POST로 넘길 form data들을 객체 변수로 생성하여 controller에서 NoticeVo 변수로 받을 수 있다.


<noticeList.css 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
.title{
    position : relative;
    text-align : center;
    left : 50%;
    width : 200px;
    margin-left : -100px;
}
.contents{
    position : relative;
    top : 30px;
    left : 50%;
    width : 1000px;
    margin-left : -500px;
}      
div.greenTable {
  font-family: Georgia, serif;
  border: 0px solid #949494;
  background-color: #FFFFFF;
  width: 100%;
  border-collapse: collapse;
}
.divTable.greenTable .divTableCell, .divTable.greenTable .divTableHead {
  border: 1px solid #000000;
  padding: 3px 2px;
}
.divTable.greenTable .divTableBody .divTableCell {
  font-size: 13px;
}
.divTable.greenTable .divTableHeading .divTableHead {
  font-size: 15px;
  font-weight: bold;
  color: #000000;
  text-align: center;
}
.greenTable .tableFootStyle {
  font-size: 13px;
  font-weight: bold;
}
.greenTable .tableFootStyle {
  font-size: 13px;
}
.greenTable .tableFootStyle .links {
     text-align: right;
}
.greenTable .tableFootStyle .links a{
  display: inline-block;
  color: #101010;
  padding: 2px 8px;
  border-radius: 5px;
}
.greenTable.outerTableFooter {
  border-top: none;
}
.greenTable.outerTableFooter .tableFootStyle {
  padding: 3px 5px;
}
.divTableRow>div:first-child{                                       
    width : 5px;
    text-align : center;
}
.divTableRow>div:nth-child(2){
    width : 10px;
    text-align : center;
}
.divTableRow>div:nth-child(3){
    width : 700px;
}
.divTableRow>div:nth-child(4){
    width : 100px;
    text-align : center;
}
.divTableRow>div:nth-child(5){
    width : 10px;
    text-align : center;
}
 
 
/* DivTable.com */
.divTable{ display: table; }
.divTableRow { display: table-row; }
.divTableHeading { display: table-header-group;}
.divTableCell, .divTableHead { display: table-cell;}
.divTableHeading { display: table-header-group;}
.divTableFoot { display: table-footer-group;}
.divTableBody { display: table-row-group;}
 
.divTableBody a,.tableFootStyle a{
    text-decoration:none;
    color : black;
}


<noticeDetail.css 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
body{
    margin : 0px;
    padding : 0px;
}
.big_title{
    position : relative;
    text-align : center;
    left : 50%;
    width : 200px;
    margin-left : -100px;
}
.big_contents{
    position : relative;
    top : 30px;
    left : 50%;
    width : 800px;
    margin-left : -400px;
}
.title{
    position : absolute;
    border-top : 1px solid black;
    width : 100%;
}
.contents{
    position : absolute;
    top : 30px;
    width : 100%;
    border-top : 1px solid black;
    border-bottom : 1px solid black;
}
.footer{
    position : absolute;
    width : 140px;
    top : 560px;
    left: 50%;
    margin-left : -70px;
}
.title>div, .contents>div{
    display : inline-block;
    padding : 3px 0 3px 0;
    width : 100%;
}
.title>div:first-child, .contents>div:first-child{
    width : 100px;
    text-align : center;
}
.title>div:nth-child(2), .contents>div:nth-child(2){
    width : 600px;
}
.title input[type=text] {
    width : 602px;
}
.contents textarea {
    width : 600px;
    height : 500px;
}
.contents>div:first-child{
    position : relative;
    bottom : 250px;
}

페이징 처리 로직은 https://gangnam-americano.tistory.com/18 을 참고하였다.(참고 사이트에서는 일부 소스가 누락되어있음)


4. 모두 적용이 되었다면 tomcat에 올려 실행시켜 보자.(tomcat 7을 사용하였다.)


<공지사항 리스트 페이지>




<공지사항 상세 페이지>





<공지사항 등록 페이지>




'spring' 카테고리의 다른 글

spring quartz 스케쥴링 java config  (3) 2019.02.15
spring 스케쥴링 설정  (0) 2019.02.12
view에서 특정 함수 반복 실행 방지  (0) 2019.02.12
spring mybatis 셋팅  (0) 2019.02.11

+ Recent posts