본문 바로가기
Spring/study

spring 20강 게시판 만들기1(목록, 글쓰기)

by avvin 2019. 7. 3.

spring 20강 게시판 만들기1(목록, 글쓰기)



게시판.sql

create table board(


bno number not null, --게시물 번호

title varchar2(200) not null, --제목

content varchar2(4000), --본문

writer varchar2(50) not null, --작성자 아이디( member테이블과 join할 것 )

regdate date default sysdate, --작성 날짜

viewcnt number default 0, -- 조회수

primary key(bno) -- 게시물 번호가 PK


)


//다시 seq로 바꿈

insert into board (bro, title, content, writer) values -- 이번엔 no를 시퀀스 말고 서브쿼리로 생성  

( ( select nvl ( max ( bno ) + 1 , 1 ) from board ), '제목', '내용', 'park' ); -- 가장 큰 bno + 1값이 null이면 default는 1


select * from board;


commit;





menu.jsp

1
<a href="${path}/board/list.do">게시판</a>
cs





BoardDTO.java 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.spring02.model.board.dto;
 
import java.util.Arrays;
import java.util.Date;
 
public class BoardDTO {
    private int bno;
    private String title;
    private String content;
    private String writer; //작성자 id
    private Date regdate; //java.util.Date
    private int viewcnt;
    private String name; //작성자 이름
    private int cnt; //댓글 갯수
    private String show; //화면 표시 여부
    private String[] files; //첨부파일 이름 배열
 
    //getter,setter,toString() 
    ...생략...
}
 





BoardDAO.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
package com.example.spring02.model.board.dao;
 
import java.util.List;
 
import com.example.spring02.model.board.dto.BoardDTO;
 
public interface BoardDAO {
 
    public void deleteFile(String fullName); //첨부파일 삭제
 
    public List<String> getAttach(int bno); //첨부파일 정보
 
    public void addAttach(String fullName); //첨부파일 저장
    
    public void updateAttach(String fullName, int bno); //첨부파일 수정
--------------------------------------------------------------------------------------- 
    public void create(BoardDTO dto) throws Exception; //글쓰기 
 
    public void update(BoardDTO dto) throws Exception; //글수정
 
    public void delete(int bno) throws Exception; //글삭제
 
    //목록
    public List<BoardDTO> listAll(
            String search_option, String keyword,int start, int end) throws Exception;
 
    //조회수 증가 처리
    public void increateViewcnt(int bno) throws Exception;
 
    //레코드 갯수 계산 
    public int countArticle(
            String search_option, String keyword) throws Exception;
 
    //레코드 조회
    public BoardDTO read(int bno) throws Exception;
}




글 목록보기


boardMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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와 중복되지 않도록 네임스페이스 기재 -->
<mapper namespace="board">
 
    <select id = "listAll" resultType="com.example.spring02.model.board.dto.BoardDTO">
        
        select bno, writer, title, regdate, viewcnt from board order by bno desc
 
    </select>
 
</mapper>





BoardDAOImpl.java [ listAll 메서드 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//게시물 목록 리턴
    @Override
    public List<BoardDTO> listAll(
String search_option, String keyword,int start, int end) 
                throws Exception {
        Map<String,Object> map=new HashMap<>();
        map.put("search_option", search_option);
        map.put("keyword""%"+keyword+"%");
        map.put("start", start); //맵에 자료 저장
        map.put("end", end);
    // mapper에는 2개 이상의 값을 전달할 수 없음(dto 또는 map 사용)    
    
        return sqlSession.selectList("board.listAll",map); 
    }




BoardService.java 는 BoardDAO와 같은 메서드 구성


BoardServiceImpl.java [ listAll 메서드 ]

1
2
3
4
5
6
    @Override
    public List<BoardDTO> listAll(
String search_option, String keyword,int start, int end)
            throws Exception {
        return boardDao.listAll(search_option,keyword,start,end); 
    }




BoardController.java [ list.do ]

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
package com.example.spring02.controller.board;
 
import java.util.HashMap;
import java.util.List;
 
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
 
import com.example.spring02.model.board.dto.BoardDTO;
import com.example.spring02.service.board.BoardService;
import com.example.spring02.service.board.Pager;
@Controller //controller bean
@RequestMapping("board/*"//공통적인 url pattern
public class BoardController {
    @Inject 
    BoardService boardService;
 
 
    @RequestMapping("list.do"//세부적인 url pattern
    public ModelAndView list() throws Exception{
 
        List<BoardDTO> list= boardService.listAll(0,0,"",""); //게시물 목록
 
        ModelAndView mav=new ModelAndView();
 
        HashMap<String,Object> map=new HashMap<>();
 
        map.put("list", list); // 맵에 자료 저장
 
        mav.setViewName("board/list"); //포워딩할 뷰의 이름
 
        mav.addObject("map", map); //ModelAndView에 map을 저장
 
        return mav; // board/list.jsp로 이동
    }
 
}




board/list.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
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<script>
$(function(){
    $("#btnWrite").click(function(){
        location.href="${path}/board/write.do";
    });
});
function list(page){
    location.href="${path}/board/list.do?curPage="+page;
}
</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>게시판</h2>
 
<!-- 검색폼 -->
<form name="form1" method="post"
    action="${path}/board/list.do">
    <select name="search_option">
        <option value="name"
<c:if test="${map.search_option == 'name'}">selected</c:if>
        >이름</option>
        <option value="title" 
<c:if test="${map.search_option == 'title'}">selected</c:if>
        >제목</option>
        <option value="content" 
<c:if test="${map.search_option == 'content'}">selected</c:if>
        >내용</option>
        <option value="all" 
<c:if test="${map.search_option == 'all'}">selected</c:if>
        >이름+내용+제목</option>
    </select>
    <input name="keyword" value="${map.keyword}">
    <input type="submit" value="조회">
</form>
 
<button type="button" id="btnWrite">글쓰기</button>
${map.count}개의 게시물이 있습니다.
<table border="1" width="600px">
    <tr>
        <th>번호</th>
        <th>제목</th>
        <th>이름</th>
        <th>날짜</th>
        <th>조회수</th>
    </tr>
    <!-- forEach var="개별데이터" items="집합데이터" -->
<c:forEach var="row" items="${map.list}">
    <tr>
        <td>${row.bno}</td>
        <td>
<a href="${path}/board/view.do?bno=${row.bno}">
${row.title}
</a>
            <c:if test="${row.cnt > 0}">
                <span style="color:red;">( ${row.cnt} )</span>
            </c:if>   
        </td>
        <td>${row.name}</td>
        <td><fmt:formatDate value="${row.regdate}"
            pattern="yyyy-MM-dd HH:mm:ss"/> </td>
        <td>${row.viewcnt}</td>
    </tr>
</c:forEach>    
<!-- 페이지 네비게이션 출력 -->
    <tr>
        <td colspan="5" align="center">
            <c:if test="${map.pager.curBlock > 1}">
                <a href="#" onclick="list('1')">[처음]</a>
            </c:if>
            <c:if test="${map.pager.curBlock > 1}">
                <a href="#" onclick="list('${map.pager.prevPage}')">
                [이전]</a>
            </c:if>
            <c:forEach var="num" 
                begin="${map.pager.blockBegin}"
                end="${map.pager.blockEnd}">
                <c:choose>
                    <c:when test="${num == map.pager.curPage}">
                    <!-- 현재 페이지인 경우 하이퍼링크 제거 -->
                        <span style="color:red;">${num}</span>
                    </c:when>
                    <c:otherwise>
                        <a href="#" onclick="list('${num}')">${num}</a>
                    </c:otherwise>
                </c:choose>
            </c:forEach>
            <c:if test="${map.pager.curBlock < map.pager.totBlock}">
                <a href="#" 
                onclick="list('${map.pager.nextPage}')">[다음]</a>
            </c:if>
            <c:if test="${map.pager.curPage < map.pager.totPage}">
                <a href="#" 
                onclick="list('${map.pager.totPage}')">[끝]</a>
            </c:if>
        </td>
    </tr>
</table>
</body>
</html>





글쓰기


board/write.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
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<script src="${path}/include/js/common.js"></script>
<!-- ckeditor의 라이브러리 -->
<script src="${path}/ckeditor/ckeditor.js"></script>
<script>
$(function(){
    $("#btnSave").click(function(){
        var str="";
        // uploadedList 내부의 .file 태그 각각 반복
        $("#uploadedList .file").each(function(i){
            console.log(i);
            //hidden 태그 구성
            str += 
"<input type='hidden' name='files["+i+"]'    value='"
    + $(this).val()+"'>";
        });
        //폼에 hidden 태그들을 붙임
        $("#form1").append(str);
        document.form1.submit();
    });
    $(".fileDrop").on("dragenter dragover",function(e){
        //기본 효과 막음
        e.preventDefault();
    });
    $(".fileDrop").on("drop",function(e){
        e.preventDefault();
        //첫번째 첨부파일
        var files=e.originalEvent.dataTransfer.files;
        var file=files[0];
        //폼 데이터에 첨부파일 추가
        var formData=new FormData();
        formData.append("file",file);
        $.ajax({
            url: "${path}/upload/uploadAjax",
            data: formData,
            dataType: "text",
            processData: false,
            contentType: false,
            type: "post",
            success: function(data){
                //console.log(data);
                //data : 업로드한 파일 정보와 Http 상태 코드
                var fileInfo=getFileInfo(data);
                //console.log(fileInfo);
                var html="<a href='"+fileInfo.getLink+"'>"+
                    fileInfo.fileName+"</a><br>";
                html += "<input type='hidden' class='file' value='"
                    +fileInfo.fullName+"'>";
                $("#uploadedList").append(html);
            }
        });
    });
});
</script>
 
<style>
.fileDrop {
    width: 600px;
    height: 100px;
    border: 1px dotted gray;
    background-color: gray;
}
</style>
 
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>글쓰기</h2>
<form id="form1" name="form1" method="post"
action="${path}/board/insert.do">
    <div>제목 <input name="title" id="title" size="80"
                    placeholder="제목을 입력하세요">
    </div>
    <div style="width:800px;">
        내용 <textarea id="content" name="content"
rows="3" cols="80" placeholder="내용을 입력하세요"></textarea>
<script>
// ckeditor 적용
CKEDITOR.replace("content",{
    filebrowserUploadUrl: "${path}/imageUpload.do"
});
</script>
    </div>
    <div> 첨부파일을 등록하세요
        <div class="fileDrop"></div>
        <div id="uploadedList"></div>
    </div>
    <div style="width:700px; text-align:center;">
        <button type="button" id="btnSave">확인</button>
    </div>
</form>
</body>
</html>





[Error]

TypeException : Error setting null for parameter #3 with JdbcType OTHER .


boardMapper.xml  id="insert"에서 받은 #{writer}


로그인하지 않은 상태라서 에러가 남


servlet-context.xml에 만들어뒀던 로그인 인터셉트를 board에도 적용  


1
2
3
    <beans:bean id="loginInterceptor"
class="com.example.spring02.interceptor.LoginInterceptor">
    </beans:bean>
cs


1
2
3
4
5
6
7
8
9
        <interceptor>
            <mapping path="/board/write.do" />
            <mapping path="/board/insert.do" />
            <mapping path="/board/update.do" />
            <mapping path="/board/delete.do" />
            <mapping path="/shop/cart/list.do"/>
            <mapping path="/shop/cart/insert.do"/>
            <beans:ref bean="loginInterceptor"/>
        </interceptor>
cs


loginInterceptor.java : session에 담긴 id값이 null이면 로그인 페이지로 이동하고 reture false ( 계속 진행하지 않음 )

        null이 아니면 (로그인 상태이면) return true ( 계속 진행 )








BoardController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    @RequestMapping("write.do")
    public String write() {
        // 글쓰기 폼 페이지로 이동
        return "board/write";
    }
// write.jsp에서 입력한 내용들이 BoardDTO에 저장됨    
    @RequestMapping("insert.do")
    public String insert(@ModelAttribute BoardDTO dto
            , HttpSession session)
        throws Exception {
        // 세션에서 사용자아이디를 가져옴
        String writer=(String)session.getAttribute("userid");
        dto.setWriter(writer); 
        //레코드 저장
        boardService.create(dto);
        //게시물 목록으로 이동
        return "redirect:/board/list.do";
    }




BoardService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1.글쓰기 - 게시물 번호 생성
// 2.첨부파일 등록-게시물 번호 사용    
    @Transactional
    @Override
    public void create(BoardDTO dto) throws Exception {
        //board 테이블에 레코드 추가
        boardDao.create(dto); 
        //attach 테이블에 레코드 추가
        String[] files=dto.getFiles(); //첨부파일 이름 배열
        if(files==nullreturn;  //첨부파일이 없으면 skip
        for(String name : files) {
            boardDao.addAttach(name);  //attach 테이블에 insert
        }
    }



board테이블에 게시글 레코드를 추가하고 attach테이블에도 첨부파일 레코드를 추가해야 한다.

두 작업 모두 수행되고 commit 돼야하기때문에 @Transactional 처리 필요


첨부파일 번호가 꼬일 경우 테이블을 비우고 해본다(drop말고 delete)



BoardDAO.java

1
2
3
4
5
6
7
8
9
    @Override
    public void create(BoardDTO dto) throws Exception {
        sqlSession.insert("board.insert", dto); 
    }
    
    @Override
    public void addAttach(String fullName) {
        sqlSession.insert("board.addAttach", fullName); 
    }




boardMapper.xml

1
2
3
4
5
6
7
8
9
10
<!-- 글 정보 저장 (board 테이블) -->    
    <insert id="insert">
        insert into board (bno,title,content,writer) values
        ( seq_board.nextval, #{title}, #{content}, #{writer} )
    </insert>
<!-- 첨부파일 정보 저장 (attach ) -->    
    <insert id="addAttach">
        insert into attach (fullName, bno) values
        ( #{fullName}, seq_board.currval ) 
    </insert>




seq_board.currval : board에 등록한 시퀀스 넘버 중 가장 최근에 등록된 값( id=insert 에서 시퀀스 nextval로 받은 값 )을 넣는다.