📌 reply 생성
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%-- /springmvc2/src/main/webapp/WEB-INF/view/board/reply.jsp --%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8">
<title>${boardName}게시판 답글 쓰기</title>
</head>
<body>
<form:form modelAttribute="board" action="reply" method="post" name="f">
<form:hidden path="num" /> <!-- 원글에 해당하는 게시글 번호(답변글에 대한 것이 아님.) -->
<form:hidden path="grp" /> <!-- 원글의 그룹번호 -->
<form:hidden path="grplevel" /> <!-- 원글의 답변글 여부 -->
<form:hidden path="grpstep" /> <!-- 원글의 그룹내의 순서값. -->
<form:hidden path="boardid" /> <!-- 원글의 게시판 구분 -->
<table class="w3-table-all">
<caption>${boardName} 답글 등록</caption>
<tr><td>글쓴이</td>
<td><input type="text" name="writer" class="w3-input w3-border">
<font color="red"><form:errors path="writer" /></font></td>
</tr>
<tr><td>비밀번호</td>
<td><form:password path="pass" class="w3-input w3-border"/>
<font color="red"><form:errors path="pass" /></font></td>
</tr>
<tr><td>제목</td>
<td><form:input path="subject" value="RE:${board.subject}" class="w3-input w3-border"/>
<font color="red"><form:errors path="subject" /></font></td>
</tr>
<tr><td>내용</td>
<td><textarea name="content" rows="15" cols="80"></textarea>
<script type="text/javascript">
CKEDITOR.replace("content",{ filebrowserImageUploadUrl : "imgupload"});
</script>
<font color="red"><form:errors path="content" /></font></td>
</tr>
<tr><td colspan="2" class="w3-center">
<a href="javascript:document.f.submit()">[답변글등록]</a></td>
</tr></table>
</form:form></body></html>
📌 BoardController 내용 추가
package controller;
import java.io.File;
import java.util.List;
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
private 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로 설정
}
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; //한 페이지에 출력을 해야하는 게시물의 개수. 한페이지에 10개씩 화면에 출력
//게시판 종류별 등록된 게시글 개수
int listcount = service.boardcount(boardid);
//boardlist : 한페이지에 출력될 게시물 목록
List<Board> boardlist = service.boardlist(pageNum,limit,boardid);
//페이징 처리를 위한 데이터
/* 최대 페이지
* listcount maxpage
* 1 1
* 10 1
* 11 2
*/
int maxpage = (int)((double)listcount/limit + 0.95);
//화면에 표시될 페이지의 시작번호
/*
* pageNum startpage
* 1 1
* 5 1
* 10 1
* 11 11
* 20 11
*/
int startpage = (int)((pageNum/10.0 + 0.9) -1) * 10 + 1;
//화면에 표시될 페이지의 끝번호, 시작번호부터 10개
int endpage = startpage + 9;
//endpage는 maxpage를 넘으면 안된다.
if(endpage > maxpage) endpage = maxpage;
//화면에 표시 될 순차적인 게시물 번호
/*
* listcount pageNum boardno
* 1 1 1
* 11 1 11
* 11 2 1
*/
int boardno = listcount - (pageNum - 1) * limit;
//list.jsp 뷰에 전달할 데이터 설정
mav.addObject("boardid",boardid);
mav.addObject("boardName",boardName);
mav.addObject("pageNum",pageNum);
mav.addObject("maxpage",maxpage);
mav.addObject("startpage",startpage);
mav.addObject("endpage",endpage);
mav.addObject("listcount",listcount);
mav.addObject("boardlist",boardlist);
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 write(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
if(bresult.hasErrors()) {
mav.getModel().putAll(bresult.getModel());
return mav;
}
String boardid = (String)request.getSession().getAttribute("boardid");
if(boardid == null) boardid="1";
board.setBoardid(boardid);
board.setIp(request.getRemoteAddr());
service.boardwrite(board,request);
mav.setViewName("redirect:list?boardid=" + boardid);
return mav;
}
@GetMapping("detail")
public ModelAndView detail(Integer num, HttpSession session) {
ModelAndView mav = new ModelAndView();
String boardid = (String)session.getAttribute("boardid");
Board board = service.getBoard(num); //num 게시판 내용 조회
service.readcntadd(num); //조회수 1 증가
mav.addObject("board",board);
if(boardid == null || boardid.equals("1"))
mav.addObject("boardName","공지사항");
else if(boardid.equals("2"))
mav.addObject("boardName","자유게시판");
else if(boardid.equals("3"))
mav.addObject("boardName","QNA");
return mav;
}
@RequestMapping("imgupload")
public String imgupload(MultipartFile upload, String CKEditorFuncNum, HttpServletRequest request, Model model) {
/*
* upload : CKEditor모듈에서 업로드 이미지의 이름을 지정함.
* 업로드된 이미지의 내용 저장
* CKEditorFuncNum : CKEditor모듈에서 이름 지정. 리턴이 필요한 값
* model : 뷰에 전달할 데이터의 집합.
* 뷰의 이름은 imgupload 함수의 리턴값
*/
//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();
}
}
//request.getContextPath() : /springmvc2. 프로젝트명
String fileName = request.getContextPath()
+ "/board/imgfile/"
+ upload.getOriginalFilename();
model.addAttribute("fileName", fileName);
model.addAttribute("CKEditorFuncNum",CKEditorFuncNum);
return "ckedit"; //뷰. /WEB-INF/view/ckedit.jsp
}
**@GetMapping({"update","reply"})
public ModelAndView getBoard(Integer num, HttpSession session) {
ModelAndView mav = new ModelAndView();
String boardid = (String)session.getAttribute("boardid");
Board board = service.getBoard(num); //num 게시판 내용 조회
mav.addObject("board",board);
if(boardid == null || boardid.equals("1"))
mav.addObject("boardName","공지사항");
else if(boardid.equals("2"))
mav.addObject("boardName","자유게시판");
else if(boardid.equals("3"))
mav.addObject("boardName","QNA");
return mav;
}**
@PostMapping("update")
public ModelAndView update(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
if(bresult.hasErrors()) {
mav.getModel().putAll(bresult.getModel());
return mav;
}
//비밀번호 검증
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"));
//파일 업로드 부분, db의 수정 부분 담당.
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로 페이지 이동
* 등록 실패 : "답변 등록시 오류 발생" reply 페이지 이동
*/
@PostMapping("reply")
public ModelAndView reply(@Valid Board board, BindingResult bresult, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
//1. 유효성 검사하기
if(bresult.hasErrors()) {
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;
}
//2. db에 insert
try {
board.setIp(request.getRemoteAddr());
service.boardReply(board);
mav.setViewName("redirect:list?boardid=" + board.getBoardid());
} catch (Exception e) {
e.printStackTrace();
throw new BoardException("답변 등록 시 오류 발생.","reply?num=" + board.getNum());
}
return mav;
}**
}
📌 ShopService 내용 추가
package logic;
import java.io.File;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import dao.BoardDao;
import dao.ItemDao;
import dao.SaleDao;
import dao.SaleItemDao;
import dao.UserDao;
/*
* @Component : 해당 클래스를 객체화
* - Service 기능 : Controller 와 Model사이의 중간 역할의 클래스
*/
@Service //@Component + Service 기능
public class ShopService {
@Autowired //현재 내 컨테이너(객체)들 중에 itemDao 객체를 주입.
private ItemDao itemDao;
@Autowired
private UserDao userDao;
@Autowired
private SaleDao saleDao;
@Autowired
private SaleItemDao saleItemDao;
@Autowired
private BoardDao boardDao;
public List<Item> itemList() {
return itemDao.list();
}
public Item getItem(Integer id) {
return itemDao.getItem(id);
}
//db에 내용 저장. 파일 업로드.
//item : 저장 정보. 입력된 파라미터 값 + 업로드 된 파일의 내용
public void itemCreate(@Valid Item item, HttpServletRequest request) {
//item.getPicture() : 업로드 된 파일의 내용
if(item.getPicture() != null && !item.getPicture().isEmpty()) { //업로드 된 파일이 있는 경우
String uploadPath = request.getServletContext().getRealPath("/") + "img/"; //업로드 위치
uploadFileCreate(item.getPicture(), uploadPath); //업로드 구현 완료
item.setPictureUrl(item.getPicture().getOriginalFilename()); //파일의 이름
}
//maxid : item테이블 중 최대 id 값
int maxid = itemDao.maxId();
item.setId(maxid+1);
itemDao.insert(item);
}
private void uploadFileCreate(MultipartFile file, String uploadPath) {
//uploadPath : 파일이 업로드 되는 폴더
String orgFile = file.getOriginalFilename(); //업로드 된 파일의 이름
File fpath = new File(uploadPath);
if(!fpath.exists()) fpath.mkdirs(); //업로드 폴더를 생성(없으면)
try {
//파일의 내용 => uploadPath + orgFile 로 파일 저장
file.transferTo(new File(uploadPath + orgFile)); //파일업로드
} catch(Exception e) {
e.printStackTrace();
}
}
public void itemUpdate(Item item, HttpServletRequest request) {
System.out.println(item);
//item.getPicture() : 업로드 된 파일의 내용
if(item.getPicture() != null && !item.getPicture().isEmpty()) { //업로드 된 파일이 있는 경우
String uploadPath = request.getServletContext().getRealPath("/") + "img/"; //업로드 위치
uploadFileCreate(item.getPicture(), uploadPath); //파일 업로드 : 업로드된 내용을 서버에 파일로 저장
item.setPictureUrl(item.getPicture().getOriginalFilename()); //파일의 이름을 db에 등록하기 위해 설정
}
itemDao.update(item);
}
public void itemDelete(Integer id) {
itemDao.delete(id);
}
public void userInsert(@Valid User user) {
userDao.insert(user);
}
public User getUser(String userid) {
return userDao.selectOne(userid);
}
public void userUpdate(@Valid User user) {
userDao.update(user);
}
public void userDelete(String userid) {
userDao.delete(userid);
}
public void userChgPass(String userid, String pass) {
userDao.chgpass(userid,pass);
}
public String getSearch(User user, String url) {
return userDao.search(user, url);
}
public List<User> userlist() {
return userDao.list();
}
/*
* 로그인 정보, 장바구니 정보에서 sale, saleitem 테이블에 데이터 저장
* 결과를 Sale 객체로 저장
* 1. sale 테이블의 saleid의 최대 값 조회
* 2. sale 테이블에 최대값+1, userid,sysdate 등록
* 3. Cart 데이터에서 saleitem 데이터 추출. insert
* 4. saleitem 정보를 sale객체에 모든 데이터 저장
* 5. sale 데이터 리턴
*/
public Sale checkend(User loginUser, Cart cart) {
//1. sale 테이블의 saleid의 최대 값 조회
int maxid = saleDao.getMaxSaleId();
//2. sale 테이블 등록
Sale sale = new Sale();
sale.setSaleid(maxid + 1);
sale.setUserid(loginUser.getUserid());
sale.setUser(loginUser);
saleDao.insert(sale); //sale 테이블에 데이터 저장
//3. Cart 데이터에서 saleitem 데이터 추출. insert
int seq = 0;
for(ItemSet is : cart.getItemSetList()) {
SaleItem saleItem = new SaleItem(sale.getSaleid(), ++seq, is);
sale.getItemList().add(saleItem);
saleItemDao.insert(saleItem); //saleitem 테이블에 저장
}
return sale;
}
public List<Sale> salelist(String id) {
//Sale 테이블의 내용 저장
List<Sale> list = saleDao.list(id); //sale 테이블에서 id에 해당하는 목록 조회
for(Sale sa : list) {
//주문 번호에 해당하는 주문상품 조회
List<SaleItem> saleitemlist = saleItemDao.list(sa.getSaleid());
//SaleItem 객체에 Item 객체 저장
for(SaleItem si : saleitemlist) {
Item item = itemDao.getItem(si.getItemid()); //주문상품의 상품데이터(item) 조회
si.setItem(item); //Item 객체를 SaleItem 객체에 추가
}
sa.setItemList(saleitemlist); //Sale 객체에 SaleItem 목록 추가
}
return list;
}
public int boardcount(String boardid) {
return boardDao.count(boardid);
}
public List<Board> boardlist(Integer pageNum, int limit, String boardid) {
return boardDao.list(pageNum,limit,boardid);
}
public void boardwrite(Board board, HttpServletRequest request) {
//첨부파일이 존재 : 파일 업로드
if(board.getFile1() != null && board.getFile1().isEmpty()) {
String path = request.getServletContext().getRealPath("/") +
"board/file/";
uploadFileCreate(board.getFile1(),path);
board.setFileUrl(board.getFile1().getOriginalFilename());
}
boardDao.insert(board);
}
public Board getBoard(Integer num) {
return boardDao.selectOne(num);
}
public void readcntadd(Integer num) {
boardDao.readcntadd(num);
}
//파일 업로드, db 수정
public void boardUpdate(Board board, HttpServletRequest request) {
//수정 시 파일 업로드가 발생 됨. 첨부파일 수정됨.
if(board.getFile1() != null && !board.getFile1().isEmpty()) {
String path = request.getServletContext().getRealPath("/") +
"board/file/";
uploadFileCreate(board.getFile1(), path);
board.setFileUrl(board.getFile1().getOriginalFilename());
}
boardDao.update(board);
}
**public void boardReply(@Valid Board board) {
boardDao.grpStepAdd(board);
boardDao.reply(board);
}**
}
📌 BoardDao.java 내용 추가
package dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import dao.mapper.BoardMapper;
import logic.Board;
@Repository
public class BoardDao {
@Autowired
private SqlSessionTemplate template;
private Map<String, Object> param = new HashMap<>();
private Class<BoardMapper> cls = BoardMapper.class;
public int count(String boardid) {
return template.getMapper(cls).count(boardid);
}
public List<Board> list(Integer pageNum, int limit, String boardid) {
param.clear();
int startrow = (pageNum - 1) * limit + 1;
int endrow = startrow + limit -1; //startrow에서 limit 만큼 가져오는
param.put("startrow", startrow);
param.put("endrow", endrow);
param.put("boardid", boardid);
return template.getMapper(cls).list(param);
}
/* 1. 게시판 테이블의 num 컬럼의 최대값
2. board 케이블에 저장
*/
public void insert(Board board) {
int num = maxNum() + 1;
board.setNum(num);
board.setGrp(num);
template.getMapper(cls).insert(board);
}
private int maxNum() {
return template.getMapper(cls).maxNum();
}
public void readcntadd(Integer num) {
template.getMapper(cls).readcntadd(num);
}
public Board selectOne(Integer num) {
return template.getMapper(cls).selectOne(num);
}
public void update(Board board) {
template.getMapper(cls).update(board);
}
**public void grpStepAdd(@Valid Board board) {
template.getMapper(cls).getStepAdd(board);
}
//board : 원글 정보
// num,grp,grplevel,grpstep,boardid
public void reply(@Valid Board board) {
board.setNum(maxNum()+1); //게시물 번호를 최대값 +1 변경 => 답글 번호
board.setGrplevel(board.getGrplevel() + 1); //원글의 grplevel보다 +1 => 답글의 grplevel
board.setGrpstep(board.getGrpstep() + 1); //원글의 grpstep보다 +1 => 답글의 grpstep
template.getMapper(cls).insert(board);
}**
}
📌 BoardMapper 내용 추가
package dao.mapper;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import logic.Board;
public interface BoardMapper {
@Select("select count(*) from board where boardid=#{value}")
int count(String 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 between #{startrow} and #{endrow}";
@Select(sql)
List<Board> list(Map<String, Object> param);
@Select("select nvl(max(num),0) from board")
int maxNum();
@Insert("insert into board"
+ "(num,writer,pass,subject,content,file1,regdate,readcnt,grp,grplevel,grpstep,boardid,ip)"
+ " values (#{num}, #{writer}, #{pass}, #{subject}, #{content}, #{fileurl}, sysdate, "
+ "0, #{grp}, #{grplevel}, #{grpstep},#{boardid} , #{ip})")
void insert(Board board);
String cols = "num,writer,pass,subject,content,file1 fileurl,"
+ "regdate,readcnt,grp,grplevel,grpstep,boardid,ip";
@Select("select " + cols+ " from board where num=#{num}")
Board selectOne(Integer num);
@Update("update board set readcnt = readcnt+1 where num = #{num}")
void readcntadd(Integer num);
@Update("update board set writer = #{writer},subject = #{subject}, content = #{content},"
+ " file1 = #{fileurl} where num =#{num}")
void update(Board board);
**@Update("update board set grpstep = grpstep +1"
+" where grp=#{grp} and grpstep > #{grpstep}")
void getStepAdd(@Valid Board board);**
}