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==null) return; //첨부파일이 없으면 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로 받은 값 )을 넣는다.
'Spring > study' 카테고리의 다른 글
selectOne / selectList 쓰임 차이 (0) | 2019.07.04 |
---|---|
spring 21강 게시판 만들기2( 페이지 나누기, 검색 기능 ) (0) | 2019.07.04 |
spring 19강 Smart Editor(CKEditor, SummerNote) (0) | 2019.07.03 |
Spring Framework root-context.xml과 servlet-context.xml의 차이점 (0) | 2019.07.03 |
spring 17강 드래그앤드롭, ajax 방식의 파일업로드 (0) | 2019.07.01 |