728x90
반응형
Board.java
package logic;
import java.util.Date;
import javax.validation.constraints.NotEmpty;
import org.springframework.web.multipart.MultipartFile;
public class Board {
private int num;
private String boardid;
@NotEmpty(message = "글쓴이를 입력하세요.")
private String writer;
@NotEmpty(message = "비밀번호를 입력하세요.")
private String pass;
@NotEmpty(message = "제목을 입력하세요.")
private String subject;
@NotEmpty(message = "내용을 입력하세요.")
private String content;
private MultipartFile file1; //파일의 정보 저장하는 변수. 파일의 이름과 프로퍼티 이름이 같아야 함.
private String fileurl;
private String ip;
private Date regdate;
private int readcnt;
private int grp;
private int grplevel;
private int grpstep;
//setter, getter, toString
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getBoardid() {
return boardid;
}
public void setBoardid(String boardid) {
this.boardid = boardid;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public MultipartFile getFile1() {
return file1;
}
public void setFile1(MultipartFile file1) {
this.file1 = file1;
}
public String getFileurl() {
return fileurl;
}
public void setFileurl(String fileurl) {
this.fileurl = fileurl;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public int getReadcnt() {
return readcnt;
}
public void setReadcnt(int readcnt) {
this.readcnt = readcnt;
}
public int getGrp() {
return grp;
}
public void setGrp(int grp) {
this.grp = grp;
}
public int getGrplevel() {
return grplevel;
}
public void setGrplevel(int grplevel) {
this.grplevel = grplevel;
}
public int getGrpstep() {
return grpstep;
}
public void setGrpstep(int grpstep) {
this.grpstep = grpstep;
}
@Override
public String toString() {
return "Board [num=" + num + ", boardid=" + boardid + ", writer=" + writer + ", pass=" + pass + ", subject="
+ subject + ", content=" + content + ", file1=" + file1 + ", fileurl=" + fileurl + ", ip=" + ip
+ ", regdate=" + regdate + ", readcnt=" + readcnt + ", grp=" + grp + ", grplevel=" + grplevel
+ ", grpstep=" + grpstep + "]";
}
}
BoardDao.java
package dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import logic.Board;
@Repository
public class BoardDao {
private NamedParameterJdbcTemplate template;
private Map<String,Object> param = new HashMap<String,Object>();
private RowMapper<Board> mapper = new BeanPropertyRowMapper<>(Board.class);
@Autowired
public void setDateSource(DataSource dataSource) {
template = new NamedParameterJdbcTemplate(dataSource);
}
public int count(String boardid) {
param.clear();
param.put("boardid", boardid);
return template.queryForObject("select count(*) from board where boardid=:boardid", param, Integer.class);
}
public List<Board> list(Integer pageNum, int limit, String boardid) {
param.clear();
/*
* pageNum startrow endrow
* 1 1 10
* 2 11 10
*/
int startrow = (pageNum - 1) * limit + 1;
int endrow = startrow + limit - 1;
param.put("startrow", startrow);
param.put("endrow", endrow);
param.put("boardid", boardid);
String sql = "select * from "
+ "(select rownum rnum, num, writer, subject, content, file1 fileurl, regdate, "
+ " grp, grplevel, grpstep, pass, readcnt from "
+ "(select * from board where boardid =:boardid order by grp desc, grpstep asc))"
+ " where rnum >= :startrow and rnum <= :endrow";
return template.query(sql, param, mapper);
}
public void write(Board board) {
int num = maxNum() + 1; //db에 등록된 num 컬럼 중 최대한 + 1
board.setNum(num);
board.setGrp(num); //원글 grp 값은 num 값과 같다.
SqlParameterSource param = new BeanPropertySqlParameterSource(board);
String sql = "insert into board "
+ "(num, writer, pass, subject, content, file1, boardid, regdate, readcnt, grp, grplevel, grpstep, ip)"
+ "values "
+ "(:num, :writer, :pass, :subject, :content, :fileurl, :boardid, sysdate, 0, :grp, :grplevel, :grpstep, :ip)";
template.update(sql, param);
}
private int maxNum() {
return template.queryForObject("select nvl(max(num),0) from board", param, Integer.class);
}
public Board selectOne(Integer num) {
param.clear();
param.put("num", num);
String sql = "select num, writer, subject, content, file1 as fileurl, regdate, "
+ " grp, grplevel, grpstep, pass, readcnt, boardid from board where num=:num";
return template.queryForObject(sql, param, mapper);
}
public void readcntadd(Integer num) {
param.clear();
param.put("num", num);
String sql = "update board set readcnt = readcnt + 1 where num=:num";
template.update(sql, param);
}
public void update(Board board) {
SqlParameterSource param = new BeanPropertySqlParameterSource(board);
String sql = "update board set writer=:writer, subject=:subject, content=:content, "
+ " file1=:fileurl where num=:num";
template.update(sql, param);
}
public void grpStepAdd(Board board) {
SqlParameterSource param = new BeanPropertySqlParameterSource(board);
String sql = "update board set grpstep = grpstep+1 "
+ " where grp=:grp and grpstep >:grpstep";
template.update(sql, param);
}
public void reply(Board board) {
//board : -원글정보 : num, grp, grplevel, grpstep
// -답글정보 : writer, pass, subject, content
/*
* - db에 insert => boardDao.reply()
* num : maxNum() + 1
* grp : 원글과 동일
* grplevel : 원글의 grplevel + 1
* grpstep : 원글의 grpstep + 1
*/
board.setNum(maxNum() + 1); //답글의 num 저장
//board.setGrp(...) => 원글 정보와 답글 정보가 동일
board.setGrplevel(board.getGrplevel()+1); //답글의 grplevel로 저장 : 원글 level + 1
board.setGrpstep(board.getGrpstep()+1); //답글의 grpstep로 저장 : 원글 step + 1
SqlParameterSource param = new BeanPropertySqlParameterSource(board);
String sql = "insert into board "
+ "(num, writer, pass, subject, content, file1, boardid, regdate, readcnt, grp, grplevel, grpstep, ip)"
+ "values "
+ "(:num, :writer, :pass, :subject, :content, :fileurl, :boardid, sysdate, 0, :grp, :grplevel, :grpstep, :ip)";
template.update(sql, param);
}
public void delete(int num) {
param.clear();
param.put("num",num);
template.update("delete from board where num=:num", param); //db에 데이터 삭제하기
}
}
BoardController.java
package controller;
import java.io.File;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import exception.BoardException;
import logic.Board;
import logic.ShopService;
@Controller
@RequestMapping("board")
public class BoardController {
@Autowired
ShopService service;
@RequestMapping("list")
public ModelAndView list(Integer pageNum, String boardid, HttpSession session) {
ModelAndView mav = new ModelAndView();
if(pageNum == null || pageNum.toString().equals("")) {
pageNum = 1; //pageNum 파라미터가 없는 경우 1로 초기화
}
if(boardid == null || boardid.equals("")) {
boardid = "1"; //boardid 파라미터가 없는 경우 "1"로 초기화
}
//boardid값 session에 등록하기
session.setAttribute("boardid", boardid);
String boardName=null;
switch(boardid) {
case "1" : boardName = "공지사항"; break;
case "2" : boardName = "자유게시판"; break;
case "3" : boardName = "QnA"; break;
}
int limit = 10; //한 페이지에 보여질 게시물의 건수
int listcount = service.boardcount(boardid); //게시판 구분별 전체 게시물 등록 건수
List<Board> boardlist = service.boardlist(pageNum, limit, boardid); //페이지에 출력한 게시물
//페이징처리를 위한 데이터
int maxpage = (int)((double)listcount/limit + 0.95); //최대 필요한 페이지 수
/*
* listcount maxpage
* 5 1
* 10 (int)(10.0/10 + 0.95) => 1.95 => 1
* 11 (int)(11.0/10 + 0.95) => 2.05 => 2
* 500 (int)(500.0/10 + 0.95) => 50.95 => 50
* 501 (int)(501.0/10 + 0.95) => 50.1 => 51
*/
int startpage = (int)((pageNum/10.0 + 0.9) - 1) * 10 + 1; //화면에 표시할 페이지의 시작번호
/*
* pageNum startpage
* 1 (int)((1/10.0 + 0.9) - 1) * 10 + 1 => 1
* 5 (int)((5/10.0 + 0.9) - 1) * 10 + 1 => 1
* 11 (int)((11/10.0 + 0.9) - 1) * 10 + 1 => 11
* 20 (int)((20/10.0 + 0.9) - 1) * 10 + 1 => 11
*/
int endpage = startpage + 9; //10개의 페이지로 표시
if(endpage > maxpage) endpage = maxpage;
//화면에 보여주기 위한 게시물 번호
int boardno = listcount - (pageNum - 1) * limit;
mav.addObject("boardid", boardid); //게시판 종류
mav.addObject("boardName", boardName); //게시판 이름
mav.addObject("pageNum", pageNum); //현재 페이지값
mav.addObject("maxpage", maxpage); //등록된 게시물의 건수에 따른 최대 페이지 갯수
mav.addObject("startpage", startpage); //화면에 표시될 시작 페이지번호
mav.addObject("endpage", endpage); //화면에 표시될 종료 페이지번호. 한 화면에 10개의 페이지 표시
mav.addObject("listcount", listcount); //게시판 종류별 등록된 게시물 건수
mav.addObject("boardlist", boardlist); //화면에 출력할 게시물 데이터 목록. 최대 10개씩
mav.addObject("boardno", boardno); //페이지에 보여줄 게시물 시작 번호
return mav;
}
@GetMapping("write")
public ModelAndView getBoard(HttpSession session) {
ModelAndView mav = new ModelAndView();
String boardid = (String)session.getAttribute("boardid");
if(boardid == null) boardid = "1";
String boardName = null;
switch(boardid) {
case "1" : boardName = "공지사항"; break;
case "2" : boardName = "자유게시판"; break;
case "3" : boardName = "QnA"; break;
}
mav.addObject("board", new Board());
mav.addObject("boardName", boardName);
return mav;
}
/*
* Post 방식 : write
* 1. 유효성 검증
* 2. 파일 업로드, db의 board 테이블에 내용 저장
* 3. 등록 성공 => list로 이동
* 등록 실패 => write로 이동
*/
@PostMapping("write")
public ModelAndView postBoard(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
// 1. 유효성 검증. @Valid annotation 사용시 board 객체의 어노테이션을 체크하여 bresult 로 결과를 내려준다.
if(bresult.hasErrors()) {
System.out.println("bindingResult = " + bresult.getModel());
mav.getModel().putAll(bresult.getModel());
return mav;
}
// 2. 세션에서 boardid 를 가져온다. boardid가 빈값일 경우, 1로 셋팅해준다.
String boardid = (String) request.getSession().getAttribute("boardid");
if(boardid == null) boardid = "1";
// board 객체에 boardid 다시 셋팅
board.setBoardid(boardid);
board.setIp(request.getRemoteAddr());
// 게시물 등록
service.boardwrite(board,request); //2. 파일 업로드, db 저장
// 뷰를 list로 재요청하기
mav.setViewName("redirect:list?boardid=" + boardid);
return mav;
}
//CKEDITOR 모듈에서 이미지 파일 업로드.
@RequestMapping("imgupload")
public String imgupload(MultipartFile upload, String CKEditorFuncNum, HttpServletRequest request, Model model) {
/*
* upload : CKEditor 에서 전송해주는 file 이름
* <input type="file" name="upload"> 형태
* 업로드 되는 이미지 파일의 내용 저장
* CKEditorFuncNum : CKEditor에서 전송해주는 파라미터 이름.
* model : 뷰에 전달한 데이터 저장 객체
* path : 업로드 되는 파일의 위치
*/
String path = request.getServletContext().getRealPath("/") + "board/imgfile/";
File f = new File(path);
if(!f.exists()) f.mkdirs(); //업로드 폴더가 없으면, 폴더 생성
if(!upload.isEmpty()) {
File file = new File(path, upload.getOriginalFilename());
try {
upload.transferTo(file); //파일 생성
} catch (Exception e) {
e.printStackTrace();
}
}
//업로드 된 웹 어플리케이션 기준 파일의 절대 경로
String fileName = request.getContextPath() + "/board/imgfile/" + upload.getOriginalFilename();
model.addAttribute("fileName", fileName);
model.addAttribute("CKEditorFuncNum", CKEditorFuncNum);
return "ckedit"; //뷰이름. /WEB-INF/view/ckedit.jsp
}
/*
* 게시물 상세보기
* 1. num 값에 해당하는 게시물 내용을 db에서 읽어오기
* 2. db의 조회수 증가하기
* 3. detail.jsp 뷰 호출하기
*/
@RequestMapping("detail")
public ModelAndView detail(Integer num) {
ModelAndView mav = new ModelAndView();
if(num != null) {
// 1. 게시판 상세 조회하기.
Board board = service.getBoard(num); //db에서 게시물 내용 조회
// 2. db 조회 수 증가
service.readcntadd(num);
mav.addObject("board", board);
}
return mav;
}
@GetMapping({"update", "reply"})
public ModelAndView update(Integer num) {
//num : update 요청시에는 수정 될 게시물 번호
// reply 요청시에는 답변을 작성할 원글의 게시물 번호
ModelAndView mav = new ModelAndView();
if(num != null) {
Board board = service.getBoard(num);
mav.addObject("board", board);
}
return mav;
}
/*
* 1. 유효성 검증
* 2. 입력된 비밀번호, db의 비밀번호 비교
* -일치 : db 수정 => detail.jsp 호출
* -불일치 : 예외 발생(BoardException) => "비밀번호가 틀렸습니다." 오류메세지 출력하고 update.jsp 호출
*/
@PostMapping("update")
public ModelAndView update(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
//1. 유효성 검증. @Valid annotation 사용시 board 객체의 어노테이션을 체크하여 bresult 로 결과를 내려준다.
if(bresult.hasErrors()) {
mav.getModel().putAll(bresult.getModel());
return mav;
}
//2. 비밀번호 비교, num 값에 해당하는 게시물데이터를 db에서 조회
Board dbBoard = service.getBoard(board.getNum());
//board.getPass() : 사용자가 입력한 비밀번호
//dbBoard.getPass() : db에 등록된 비밀번호
if(!board.getPass().equals(dbBoard.getPass())) { //비밀번호 오류.
throw new BoardException("비밀번호가 틀립니다.", "update?num=" + board.getNum());
}
try {
board.setFileurl(request.getParameter("file2")); //수정전 파일정보
service.boardUpdate(board, request);
mav.setViewName("redirect:detail?num=" + board.getNum());
} catch (Exception e) {
e.printStackTrace(); //수정 실패
throw new BoardException("게시글 수정을 실패했습니다.", "update?num="+board.getNum());
}
return mav;
}
/*
* 1. 유효성 검사하기-파라미터값 저장.
* -원글정보 : num, grp, grplevel, grpstep
* -답글정보 : writer, pass, subject, content
* 2. db에 insert => service.boardReply()
* - 원글의 grpstep 보다 큰 이미 등록된 답글의 grpstep 값을 +1 증가 => boardDao.grpStepAdd()
* - db에 insert => boardDao.reply()
* num : maxNum() + 1
* grp : 원글과 동일
* grplevel : 원글의 grplevel + 1
* grpstep : 원글의 grpstep + 1
* 3. 등록 성공 : list.jsp 페이지로 이동
* 등록 실패 : "답변등록시 오류 발생" 메세지 출력 후 reply.jsp 페이지로 이동
*/
@RequestMapping("reply")
public ModelAndView reply(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
//1. 유효성 검증. @Valid annotation 사용시 board 객체의 어노테이션을 체크하여 bresult 로 결과를 내려준다.
if(bresult.hasErrors()) {
// re 가 계속 붙는 현상 방지하기위한 로직
// board.getNum() : 원글의 num 값
// dbboard : 원글의 데이터 값
Board dbBoard = service.getBoard(board.getNum());
Map<String, Object> map = bresult.getModel();
Board b = (Board) map.get("board");
b.setSubject(dbBoard.getSubject()); //원글의 제목으로 변경
mav.getModel().putAll(bresult.getModel());
return mav;
}
try {
// 2. 세션에서 boardid 를 가져온다. boardid가 빈값일 경우, 에러처리 해준다.
String boardid = (String) request.getSession().getAttribute("boardid");
if(boardid == null) throw new BoardException("게시판을 선택하세요.", "list?boardid=1");
// board 객체에 boardid 다시 셋팅
board.setBoardid(boardid);
board.setIp(request.getRemoteAddr());
//2. db에 insert => service.boardReply()
service.boardReply(board);
//3. 등록성공
mav.setViewName("redirect:list?boardid=" + boardid);
} catch (Exception e) {
e.printStackTrace();
//3. 등록실패
throw new BoardException("답변등록시 오류 발생.", "reply?num=" + board.getNum());
}
return mav;
}
@GetMapping("*") //화면 출력
public String getView() {
return null;
}
/*
* 1. num, pass 파라미터 저장
* 2. db에서 num의 게시물을 읽기. 비밀번호 검증
* 비밀번호 오류시 error.login.password 입력 => 뷰에 출력
* 3. db에서 num의 게시물 삭제
* 삭제 성공 : list 페이지 이동
* 삭제 실패 : delete 페이지 이동
*/
@PostMapping("delete")
public ModelAndView delete(Board board, BindingResult bresult) {
ModelAndView mav = new ModelAndView();
try {
//1. 파라미터로 넘어온 num으로 보드 데이터 조회
Board dbBoard = service.getBoard(board.getNum());
// 2. 파라미터로 넘어온 비밀번호와 조회된 게시물 비밀번호 비교. 같지 않으면 오류 발생
if(!board.getPass().equals(dbBoard.getPass())) { //비밀번호 오류.
bresult.reject("error.login.password");
return mav;
}
// 3. 게시글 삭제
service.boardDelete(board.getNum());
// 4. 삭제 성공 후 리스트로 이동
mav.setViewName("redirect:list?boardid=" + dbBoard.getBoardid());
} catch (Exception e) {
e.printStackTrace();
throw new BoardException("게시물 삭제에 실패하였습니다.", "delete?num=" + board.getNum());
}
return mav;
}
}
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%-- /springmvc1/src/main/webapp/WEB-INF/view/board/list.jsp --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록 보기</title>
<script>
if('${param.msg}') {
alert('${param.msg}')
}
</script>
</head>
<body>
<table>
<tr>
<td colspan="4">${boardName}게시판</td>
<td>글개수:${listcount}</td>
</tr>
<c:if test="${listcount > 0 }">
<tr>
<th>번호</th>
<th>제목</th>
<th>글쓴이</th>
<th>날짜</th>
<th>조회수</th>
</tr>
<c:forEach var="board" items="${boardlist}">
<tr>
<td>${boardno}</td>
<c:set var="boardno" value="${boardno - 1 }" />
<td style="text-align: left;">
<c:if test="${!empty board.fileurl}">
<a href="file/${board.fileurl}">@</a>
</c:if>
<c:if test="${empty board.fileurl}"> </c:if>
<c:forEach begin="1" end="${board.grplevel}"> </c:forEach>
<c:if test="${board.grplevel > 0}">└</c:if>
<a href="detail?num=${board.num}">${board.subject}</a>
</td>
<td>${board.writer}</td>
<td>
<fmt:formatDate value="${board.regdate}" pattern="yyyy-MM-dd HH:mm:ss" />
</td>
<td>${board.readcnt}</td>
</tr>
</c:forEach>
<%-- 페이징 부분 --%>
<tr>
<td colspan="5">
<div class="w3-center w3-padding-32">
<div class="w3-bar">
<c:if test="${pageNum > 1}">
<a href="list?pageNum=${pageNum - 1}&boardid=${boardid}">[이전]</a>
</c:if>
<c:if test="${pageNum <= 1}">[이전]</c:if>
<c:forEach var="a" begin="${startpage}" end="${endpage}">
<c:if test="${a == pageNum}">
<a class="w3-butten w3-black" href="#">${a}</a>
</c:if>
<c:if test="${a != pageNum}">
<a class="w3-button w3-hover-black" href="list?pageNum=${a}&boardid=${boardid}">${a}</a>
</c:if>
</c:forEach>
<c:if test="${pageNum < maxpage}">
<a href="list?pageNum=${pageNum + 1}&boardid=${boardid}">[다음]</a>
</c:if>
<c:if test="${pageNum >= maxpage}">[다음]</c:if>
</div>
</div>
</td>
</tr>
</c:if>
<c:if test="${listcount == 0}">
<tr>
<td colspan="5">등록된 게시물이 없습니다.</td>
</tr>
</c:if>
<%-- boardid == 1 인 경우 관리자로 로그인 한 경우만 글쓰기 부분 출력하기 --%>
<c:if test="${!empty param.boardid && param.boardid != '1'}">
<tr>
<td colspan="5" align="right">
<a href="write">[글쓰기]</a>
</td>
</tr>
</c:if>
<c:if test="${empty param.boardid || param.boardid == '1'}">
<c:if test="${loginUser.userid == 'admin'}">
<tr>
<td colspan="5" align="right">
<a href="write">[글쓰기]</a>
</td>
</tr>
</c:if>
</c:if>
</table>
</body>
</html>
728x90
반응형
'study > Spring' 카테고리의 다른 글
[Spring] 24. Spring (공지사항 관리자만 작성가능하도록 Interceptor제어[BoardInterceptor.java]) (0) | 2022.05.16 |
---|---|
[Spring] 24. Spring (게시글 작성[write.jsp]) (0) | 2022.05.13 |
[Spring] 24. Spring (아이디찾기, 비밀번호찾기[idsearch.jsp, pwsearch.jsp, search.jsp]) (0) | 2022.05.12 |
[Spring] 24. Spring (관리자권한설정[AdminController.java, AdminLoginAspect.java]) (0) | 2022.05.12 |
[Spring] 24. Spring (회원목록[list.jsp]) (0) | 2022.05.12 |