day40 - 0921
계층형 게시판
- 게시글과 답글이 계층 관계(종속 관계)를 갖는 게시판
- 계층형 게시판의 모든 글은 게시글 또는 답글 중 하나이다
- 계층형 게시판의 모든 글은 기본적인 게시글의 필드 외에 그룹(group), 상위글(parent), 차수(depth)를 추가로 갖는다
- 계층형 게시판에 모든 글은 상위글(parent)과 차수(depth)에 의해 게시글과 답글로 구분된다
1) 그룹(group)
- 게시글일 경우 게시글의 번호
- 답글일 경우 답글이 달린 게시글의 번호
2) 상위글(parent)
- 게시글이라면 상위글이 없으므로 null
- 게시글에 달린 답글이라면 해당 게시글의 번호
- 답글에 달린 답글이라면 해당 답글의 번호
3) 차수(depth)
- 게시글이라면 상위글이 없으므로 0
- 게시글에 달린 답글이라면 1
- 답글에 달린 답글이라면 2 이상
ex) 게시글과 게시글에 달린 답글의 글 번호(no), 그룹(group), 상위글(parent), 차수(depth)
- 상위글(parent)에 들어갈 값은 int(상위글 번호)와 String(상위글 번호 없음, null)이다
- JDBC에서 필드의 값이 null일 경우 0으로 자동 변환하므로 상위글(parent)의 자료형은 int로 한다
계층형 게시판 - 게시글 목록
BOARD 테이블
- 게시글일 경우 상위글(parent)이 없으므로 not null 조건을 부여하지 않는다
- 계층 관계를 위해 게시판 테이블을 자기참조 한다
- 게시판 테이블 자기참조시 기본키인 게시글 번호(board_no)의 참조 컬럼이 외래키로 추가된다
- 해당 외래키 컬럼을 상위글 번호(board_parent)로 한다
- 계층 관계 : 게시글 번호(board_no)가 자식 컬럼, 상위글 번호(board_parent)가 부모 컬럼이 된다
(모든 게시글 번호는 상위글 번호에 포함되지만 상위글 번호에 들어갈 수 있는 null은 게시글 번호에 들어갈 수 없다)
-- board 테이블 생성 (수정된 테이블 생성 구문)
create table board (
board_no number primary key,
board_writer references member(member_id) on delete set null,
board_title varchar2(300) not null,
board_content varchar2(4000) not null,
board_writetime date default sysdate not null,
board_updatetime date,
board_read number default 0 not null check(board_read >= 0),
board_like number default 0 not null check(board_like >= 0),
board_head varchar2(6) check(board_head in('정보', '유머', '공지')),
board_group number not null,
board_parent references board(board_no),
board_depth number not null
);
** 계층형 구조에서의 조회 구문 : start with [조건1] ~ connect by [조건2]
select * from 테이블명 start with [조건1] connect by [조건2] order siblings by [정렬기준];
1) start with [조건1]
- 최상위 노드(루트 노드)가 되는 행의 조건을 명시
** 상위글 번호(board_parent)가 없는 경우(null)가 최상위 노드가 되므로 [조건1]은 board_parent is null이 된다
2) connect by [조건2]
- start with [조건1]을 만족하는 행과 연결된 행을 조회하기 위한 연결 조건을 명시
- prior 연산자를 이용하여 조회할 행의 우선 순위를 정할 수 있다
- prior 연산자가 붙은 행 바로 뒤에 해당 행과 특정한 조건으로 연결된 행이 나오도록 조회한다
(prior이 붙은 열에서 prior이 붙지 않은 행 방향으로 조회)
- connect by prior 자식 컬럼 = 부모 컬럼 : 부모에서 자식으로 트리 구성 (Top Down)
- connect by prior 부모 컬럼 = 자식 컬럼 : 자식에서 부모로 트리 구성 (Bottom Up)
3) order siblings by [정렬기준]
- 계층형 구조에서의 정렬 구문
ex1) connect by prior board_no = board_parent 의 결과
1) 그룹 내림차순 정렬한다
2) 아홉 번째 글의 글 번호(board_no)에 대하여
아홉 번째 글의 글 번호(board_no)인 9를 상위글 번호(board_parent)로 갖는 열 번째 글을 조회한다
열 번째 글의 글 번호(board_no)인 10을 상위글 번호(board_parent)로 갖는 글은 없다
아홉 번째 글의 글 번호(board_no)인 9를 상위글 번호(board_parent)로 갖는 열 한번째 글을 조회한다
열 한번째 글의 글 번호(board_no)인 11을 상위글 번호(board_parent)로 갖는 글은 없다
3) 네 번째 글의 글 번호(board_no)인 4를 상위글 번호(board_no)로 갖는 글은 없다
4) 세 번째 글의 글 번호(board_no)에 대하여
세 번째 글의 글 번호(board_no)인 3를 상위글 번호(board_parent)로 갖는 다섯 번째 글을 조회한다
다섯 번째 글의 글 번호(board_no)인 5를 상위글 번호(board_parent)로 갖는 일곱 번째 글을 조회한다
일곱 번째 글의 글 번호(board_no)인 7을 상위글 번호(board_parent)로 갖는 글은 없다
세 번째 글의 글 번호(board_no)인 3을 상위글 번호(board_parent)로 갖는 여섯 번째 글을 조회한다
여섯 번째 글의 글 번호(board_no)인 6를 상위글 번호(board_parent)로 갖는 여덟 번째 글을 조회한다
여덟 번째 글의 글 번호(board_no)인 8을 상위글 번호(board_parent)로 갖는 글은 없다
5) 두 번째 글의 글 번호(board_no)인 2를 상위글 번호(board_no)로 갖는 글은 없다
6) 첫 번째 글의 글 번호(board_no)인 1를 상위글 번호(board_no)로 갖는 글은 없다
ex2) connect by prior board_parent = board_no 의 결과
1) 그룹 내림차순 정렬한다
2) 아홉 번째 글의 상위글 번호(board_parent)인 null과 같은 글 번호(board_no)를 갖는 글은 없다
3) 네 번째 글의 상위글 번호(board_parent)인 null과 같은 글 번호(board_no)를 갖는 글은 없다
4) 세 번째 글의 상위글 번호(board_parent)인 null과 같은 글 번호(board_no)를 갖는 글은 없다
5) 두 번째 글의 상위글 번호(board_parent)인 null과 같은 글 번호(board_no)를 갖는 글은 없다
6) 첫 번째 글의 상위글 번호(board_parent)인 null과 같은 글 번호(board_no)를 갖는 글은 없다
ex) 계층형 구조에서 조회 구문
select * from board
-- 상위글 번호(board_parent)의 값이 null인 행(그룹 내 최상위 노드)부터 시작
start with board_parent is null
-- 해당 행(그룹 내 최상위 노드)과 다음의 조건으로 연결된 모든 하위 행 출력
-- 글 번호(board_no)와 상위글 번호(board_parent)가 같은 하위 행
-- 여기서 글 번호(board_no)가 상위글 번호(board_parent)보다 상위 열이다
connect by prior board_no = board_parent
-- 가장 먼저 그룹(board_group) 내림차순 정렬하며
-- 같은 그룹 내에서는 글 번호(board_no) 오름차순 정렬
order siblings by board_group desc, board_no asc;
BoardDto
- 계층형 게시판을 위해 그룹(group), 상위글(parent), 차수(depth) 필드 추가
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BoardDto {
// 필드
private int boardNo;
private String boardWriter;
private String boardTitle;
private String boardContent;
private Date boardWritetime;
private Date boardUpdatetime;
private int boardRead;
private int boardLike;
private String boardHead;
// 계층형 게시판을 위한 필드 추가
private int boardGroup;
private int boardParent;
private int boardDepth;
}
BoardDao
public interface BoardDao {
// 추상 메소드 - 게시글 조회
// 3) BoardListSearchVO를 이용한 통합 조회
List<BoardDto> selectList(BoardListSearchVO vo);
// - 전체 조회
List<BoardDto> list(BoardListSearchVO vo);
// - 검색 조회
List<BoardDto> search(BoardListSearchVO vo);
}
BoardDaoImpl
- BoardDto에 대한 RowMapper 및 SQL문 수정
- ResultSetExtractor 수정
@Repository
public class BoardDaoImpl implements BoardDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// BoardDto에 대한 RowMapper (수정)
private RowMapper<BoardDto> mapper = new RowMapper<BoardDto>() {
@Override
public BoardDto mapRow(ResultSet rs, int rowNum) throws SQLException {
return BoardDto.builder()
.boardNo(rs.getInt("board_no"))
.boardTitle(rs.getString("board_title"))
.boardContent(rs.getString("board_content"))
.boardWriter(rs.getString("board_writer"))
.boardHead(rs.getString("board_head"))
.boardRead(rs.getInt("board_read"))
.boardLike(rs.getInt("board_like"))
.boardWritetime(rs.getDate("board_writetime"))
.boardUpdatetime(rs.getDate("board_updatetime"))
.boardGroup(rs.getInt("board_group"))
.boardParent(rs.getInt("board_parent"))
.boardDepth(rs.getInt("board_depth"))
.build();
}
};
// 추상 메소드 오버라이딩 - 게시글 조회
// 3) BoardListSearchVO를 이용한 통합 조회
@Override
public List<BoardDto> selectList(BoardListSearchVO vo) {
// 조회 유형
if(vo.isSearch()) { // 검색 조회일 경우
return search(vo);
}
else { // 그렇지 않다면
return list(vo);
}
}
// - 전체 조회
@Override
public List<BoardDto> list(BoardListSearchVO vo) {
String sql = "select * from ("
+ "select rownum rn, TMP.* from ("
+ "select * from board "
+ "connect by prior board_no=board_parent "
+ "start with board_parent is null "
+ "order siblings by board_group desc, board_no asc "
+ ")TMP"
+ ") where rn between ? and ?";
Object[] param = {vo.startRow(), vo.endRow()};
return jdbcTemplate.query(sql, mapper, param);
}
// - 검색 조회
@Override
public List<BoardDto> search(BoardListSearchVO vo) {
String sql = "select * from ("
+ "select rownum rn, TMP.* from ("
+ "select * from board "
+ "where instr(#1, ?) > 0 "
+ "connect by prior board_no=board_parent "
+ "start with board_parent is null "
+ "order siblings by board_group desc, board_no asc "
+ ")TMP"
+ ") where rn between ? and ?";
sql = sql.replace("#1", vo.getType());
Object[] param = {
vo.getKeyword(), vo.startRow(), vo.endRow()
};
return jdbcTemplate.query(sql, mapper, param);
}
// BoardDto에 대한 ResultSetExtractor (수정)
private ResultSetExtractor<BoardDto> extractor = new ResultSetExtractor<>() {
@Override
public BoardDto extractData(ResultSet rs) throws SQLException, DataAccessException {
if(rs.next()) {
return BoardDto.builder()
.boardNo(rs.getInt("board_no"))
.boardWriter(rs.getString("board_writer"))
.boardTitle(rs.getString("board_title"))
.boardContent(rs.getString("board_content"))
.boardWritetime(rs.getDate("board_writetime"))
.boardUpdatetime(rs.getDate("board_updatetime"))
.boardRead(rs.getInt("board_read"))
.boardLike(rs.getInt("board_like"))
.boardHead(rs.getString("board_head"))
.boardGroup(rs.getInt("board_group"))
.boardParent(rs.getInt("board_parent"))
.boardDepth(rs.getInt("board_depth"))
.build();
}
else {
return null;
}
}
};
}
BoardController
@Controller
@RequestMapping("/board")
public class BoardController {
// 의존성 주입
@Autowired
BoardDao boardDao;
// 2. 게시글 목록
// 게시글 목록 Mapping
@GetMapping("/list")
public String selectList(Model model, @ModelAttribute(name = "vo") BoardListSearchVO vo) {
// 조회 유형 판정과 실행시킬 메소드를 BoardDaoImpl에서 결정하도록 변경
// 조회 유형에 따른 조회 결과의 총 갯수를 반환
int count = boardDao.count(vo);
// 반환한 조회 결과의 총 갯수(count)를 vo의 count 필드의 값으로 설정
vo.setCount(count);
// model에 조회 유형에 따른 조회 결과를 첨부
model.addAttribute("list", boardDao.selectList(vo));
// 게시글 목록(list.jsp)로 연결
return "board/list";
}
}
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" %>
<%-- 오늘 날짜를 구해서 today라는 변수로 만든다 --%>
<jsp:useBean id = "now" class = "java.util.Date"></jsp:useBean>
<c:set var = "today">
<fmt:formatDate value = "${now}" pattern = "yyyy-MM-dd"/>
</c:set>
<%-- 테스트용 데이터 출력 --%>
<h3>${vo}</h3>
<jsp:include page = "/WEB-INF/views/template/header.jsp">
<jsp:param value = "게시글 목록" name = "title"/>
</jsp:include>
<div align = "center">
<h1>게시글 목록</h1>
<table border = "1" width = "900">
<thead>
<%-- 로그인 상태에서만 글쓰기 항목이 보이도록 설정 --%>
<c:if test = "${loginId != null}">
<tr>
<td align = "right" colspan = "5">
<a href = "write">글쓰기</a>
</td>
</tr>
</c:if>
<tr>
<th>번호</th>
<th width = "45%">제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회수</th>
</tr>
</thead>
<tbody align = "center">
<c:forEach var = "boardDto" items = "${list}">
<tr>
<td>${boardDto.boardNo}</td>
<td align = "left">
<%-- 차수만큼 띄어쓰기 반복 --%>
<c:forEach var="i" begin="1" end="${boardDto.boardDepth}" step="1">
</c:forEach>
<%-- 말머리가 있을 경우에만 출력 --%>
<c:if test = "${boardDto.boardHead != null}">
[${boardDto.boardHead}]
</c:if>
<%-- 제목을 누르면 해당 게시글의 상세 페이지로 이동하도록 --%>
<a href = "detail?boardNo=${boardDto.boardNo}">
${boardDto.boardTitle}
</a>
</td>
<td>${boardDto.boardWriter}</td>
<td>
<%-- 작성 날짜를 current라는 변수로 만든다 --%>
<c:set var = "current">
<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "yyyy-MM-dd"/>
</c:set>
<c:choose>
<%-- today(오늘 날짜)와 current(작성 날짜)가 같다면 시간과 분만 표시--%>
<c:when test = "${today == current}">
<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "HH:mm"/>
</c:when>
<%-- 그렇지 않다면 년도-월-일로 표시 --%>
<c:otherwise>
<fmt:formatDate value = "${boardDto.boardWritetime}" pattern = "yyyy-MM-dd"/>
</c:otherwise>
</c:choose>
</td>
<td>${boardDto.boardRead}</td>
</tr>
</c:forEach>
</tbody>
<c:if test = "${loginId != null}">
<tfoot>
<tr>
<td align = "right" colspan = "5">
<a href = "write">글쓰기</a>
</td>
</tr>
</tfoot>
</c:if>
</table>
<%-- 페이지 네비게이터 --%>
<h4>
<%-- 맨 처음 페이지로 이동 --%>
<c:choose>
<c:when test = "${not vo.isFirst()}"> <%-- 맨 처음 페이지가 아니라면 --%>
<a href = "list?p=${vo.firstBlock()}">«</a> <%-- 첫 번째 페이지로 이동 --%>
</c:when>
<c:otherwise> <%-- 그렇지 않다면 --%>
<a href = "">«</a> <%-- 아무런 페이지 변화가 없도록 --%>
</c:otherwise>
</c:choose>
<%-- 이전 구간의 마지막 페이지로 이동 --%>
<c:choose>
<c:when test = "${vo.hasPrev()}"> <%-- 이전 페이지가 있다면 --%>
<a href = "list?p=${vo.prevBlock()}"><</a> <%-- 이전 구간의 마지막 페이지로 이동 --%>
</c:when>
<c:otherwise> <%-- 그렇지 않다면 --%>
<a href = ""><</a> <%-- 아무런 페이지 변화가 없도록 --%>
</c:otherwise>
</c:choose>
<%-- 현재 구간의 페이지 이동 --%>
<%-- 변수명을 i로 하며 시작과 끝은 vo의 startBlock(), endBlock()의 반환값으로, 간격은 1로 한다 --%>
<c:forEach var = "i" begin = "${vo.startBlock()}" end = "${vo.endBlock()}" step = "1">
<a href = "list?p=${i}">${i}</a>
</c:forEach>
<%-- 다음 구간의 첫 번째 페이지로 이동 --%>
<c:choose>
<c:when test = "${vo.hasNext()}"> <%-- 다음 페이지가 있다면 --%>
<a href = "list?p=${vo.nextBlock()}">></a> <%-- 다음 구간의 첫 번째 페이지로 이동 --%>
</c:when>
<c:otherwise> <%-- 그렇지 않다면 --%>
<a href = "">></a> <%-- 아무런 페이지 변화가 없도록 --%>
</c:otherwise>
</c:choose>
<%-- 맨 마지막 페이지로 이동 --%>
<c:choose>
<c:when test = "${not vo.isLast()}"> <%-- 맨 마지막 페이지가 아니라면 --%>
<a href = "list?p=${vo.lastBlock()}">»</a> <%-- 맨 마지막 페이지로 이동 --%>
</c:when>
<c:otherwise>
<a href = "">»</a>
</c:otherwise>
</c:choose>
</h4>
<%--검색창 --%>
<form action = "list" method = "get">
<select name = "type" required>
<option value = "board_title" <c:if test = "${vo.type == 'board_title'}">selected</c:if>>제목</option>
<option value = "board_content" <c:if test = "${vo.type == 'board_content'}">selected</c:if>>작성자</option>
<option value = "board_writer" <c:if test = "${vo.type == 'board_writer'}">selected</c:if>>작성자</option>
</select>
<input type = "search" name = "keyword" placeholder = "검색어" required value = "${vo.keyword}">
<button type = "submit">검색</button>
</form>
</div>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
- 제목(boardTitle)에 치수(board_depth)만큼 띄어쓰기를 반복을 추가한다
<%-- 차수만큼 띄어쓰기 반복 --%>
<c:forEach var="i" begin="1" end="${boardDto.boardDepth}" step="1">
</c:forEach>
계층형 게시판 - 게시글 작성
- 게시글과 답글은 prior board_no = board_parent 관계로 연결되어 있다
detail.jsp
- 게시글 상세(detail.jsp)에서 답글 작성시 하이퍼링크를 통해 boardParent(key)와 boardNo(value)를 전달한다
<%-- 로그인 상태에서만 게시글 및 답글 작성이 가능하도록 설정 --%>
<c:if test = "${loginId != null}">
<a href = "write">글쓰기</a>
<a href = "write?boardParent=${boardDto.boardNo}">답글쓰기</a>
</c:if>
write.jsp
- 게시글인지 답글인지에 따라 제목이 달라지도록 설정
- 답글(isReply == true)이라면 <form>에서 상위글 번호를 추가로 전달하도록 처리
<%@ 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" %>
<jsp:include page="/WEB-INF/views/template/header.jsp">
<jsp:param value="자유 게시판" name="title"/>
</jsp:include>
<%-- 게시글인지 답글인지에 따라 제목이 달라지도록 설정 --%>
<c:set var = "isReply" value = "${param.boardParent != null}"></c:set>
<c:choose>
<c:when test = "${isReply}">
<h1>답글 작성</h1>
</c:when>
<c:otherwise>
<h1>게시글 작성</h1>
</c:otherwise>
</c:choose>
<div align = "center">
<form action="write" method="post">
<table border="1" width="500">
<tbody>
<tr>
<td>
<%-- 답글이라면 상위글 번호를 추가로 전송하도록 설정 --%>
<c:if test="${isReply}">
<input type = "hidden" name = "boardParent" value = "${param.boardParent}">
</c:if>
</td>
</tr>
<tr>
<th>말머리</th>
<td>
<select name="boardHead">
<option value="">선택</option>
<option>정보</option>
<option>유머</option>
<%-- 관리자만 공지글을 작성할 수 있도록 설정 --%>
<c:if test="${mg == '관리자'}">
<option>공지</option>
</c:if>
</select>
</td>
</tr>
<tr>
<th>제목</th>
<td>
<input type="text" name="boardTitle" required>
</td>
</tr>
<tr>
<th>내용</th>
<td>
<textarea name="boardContent" rows="10" cols="50" required></textarea>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td align="right" colspan="2">
<a href="list">목록으로</a>
<button type="submit">작성하기</button>
</td>
</tr>
</tfoot>
</table>
</form>
</div>
<jsp:include page="/WEB-INF/views/template/footer.jsp"></jsp:include>
BoardDao
public interface BoardDao {
// 추상 메소드 - 게시글 번호를 위한 다음 시퀀스 번호 반환
int sequence();
// 추상 메소드 - 게시글 작성
void write(BoardDto boardDto);
}
BoardDaoImpl
@Repository
public class BoardDaoImpl implements BoardDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 - 게시글 번호를 위한 다음 시퀀스 번호 반환
@Override
public int sequence() {
String sql = "select board_seq.nextval from dual";
int boardNo = jdbcTemplate.queryForObject(sql, int.class);
return boardNo;
}
// 추상 메소드 오버라이딩 - 게시글 작성
@Override
public void write(BoardDto boardDto) {
// 게시글을 작성하는 SQL문으로 변경
String sql = "insert into board("
+ "board_no, "
+ "board_title, "
+ "board_content,"
+ "board_writer, "
+ "board_head, "
+ "board_group, "
+ "board_parent, "
+ "board_depth"
+ ") values(?, ?, ?, ?, ?, ?, ?, ?)";
// 바인드 변수에 들어갈 매개변수 배열 생성
Object[] param = {boardDto.getBoardNo(),
boardDto.getBoardTitle(),
boardDto.getBoardContent(),
boardDto.getBoardWriter(),
boardDto.getBoardHead(),
boardDto.getBoardGroup(),
boardDto.getBoardParentInteger(),
boardDto.getBoardDepth()
};
// JdbcTemplate의 update 메소드 실행
jdbcTemplate.update(sql, param);
}
}
BoardController
- boardDto : View에서 글 작성을 위해 전달받은 값(데이터), 상위글 번호(boardParent)는 하이퍼링크를 통해 전달받은 값
- parentDto : View에서 전달받은 boardParent로 단일 조회를 실행한 결과값(데이터)
- 새로 글을 작성하기 전에 작성하려는 글이 게시글인지 답글인지 판정해야 한다
(상위글 번호(boardParent)이 0(null)이면 게시글, 그렇지 않으면 답글)
i) 게시글일 경우 그룹은 게시글 번호(= 반환한 다음 시퀀스 번호), 상위글은 0, 차수는 0이 된다
ii) 답글일 경우 전달받은 상위글 번호(boardParent)를 게시글 번호로 하여 단일 조회를 실행하여 parentDto를 얻는다
- boardDto의 그룹(boardGroup)은 parentDto의 그룹과 같다
- boardDto의 차수(boardDepth)는 parentDto의 차수에 1을 더한 값이다
- boardDto의 상위글 번호(boardParent)는 하이퍼링크를 통해 전달받은 값인 boardNo가 된다
@Controller
@RequestMapping("/board")
public class BoardController {
// 의존성 주입
@Autowired
BoardDao boardDao;
// 게시글 작성
// 게시글 작성 Mapping
@GetMapping("/write")
public String write() {
return "board/write";
}
// 게시글 작성 Mapping에 DTO 전달 및 DB 처리
@PostMapping("/write")
public String write(HttpSession session, @ModelAttribute BoardDto boardDto, RedirectAttributes attr) {
// 게시글 작성자(boardWriter)가 로그인중인 아이디가 되도록 session에서 회원 아이디를 반환
String boardWriter = (String) session.getAttribute("loginId");
// setter 메소드로 게시글 작성자(boardWriter)를 session에서 반환한 로그인 아이디로 설정
boardDto.setBoardWriter(boardWriter);
// 게시글 번호(boardNo)를 위한 다음 시퀀스 번호 반환
int boardNo = boardDao.sequence();
// 등록 전 '게시글'인지 '답글'인지 판정
if(boardDto.getBoardParent() == 0) { // 게시글이라면
// 그룹(boardGroup)은 게시글 번호(= 반환한 다음 시퀀스 번호)
boardDto.setBoardGroup(boardNo);
// 상위글(boardParent)은 0 (없다)
boardDto.setBoardParent(0);
// 차수(boardDepth)는 0
boardDto.setBoardDepth(0);
}
else { // 답글이라면
// View에서 하이퍼링크를 통해 전달받은 boardParent(key)의 값인 boardNo(value)로 단일 조회 실행
BoardDto parentDto = boardDao.selectOne(boardDto.getBoardParent());
// 그룹(boardGroup)은 단일 조회의 결과로 얻은 parentDto의 그룹과 같다
boardDto.setBoardGroup(parentDto.getBoardGroup());
// 차수(boardDepth)는 단일 죄호의 결과로 얻은 parentDto의 차수에 1을 더한 값
boardDto.setBoardDepth(parentDto.getBoardDepth() + 1);
// 상위글(boardParent)은 하이퍼링크를 통해 전달받으며 그 값(value)는 boardNo가 된다
}
// setter 메소드로 게시글 번호(boardNo)가 다음 시퀀스 번호가 되도록 설정
boardDto.setBoardNo(boardNo);
// 설정이 끝난 boardDto로 게시글 작성 실행
boardDao.write(boardDto);
// 게시글 작성 후 해당 게시글의 상세 Mapping으로 강제 이동(redirect)
attr.addAttribute("boardNo", boardNo);
return "redirect:detail";
}
}
BoardDto
- 상위글 번호(boardParent)가 0일 경우 DB에 등록할 때 null을 입력하도록 설정
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BoardDto {
// 필드
private int boardNo;
private String boardWriter;
private String boardTitle;
private String boardContent;
private Date boardWritetime;
private Date boardUpdatetime;
private int boardRead;
private int boardLike;
private String boardHead;
// 계층형 게시판을 위한 필드 추가
private int boardGroup;
private int boardParent;
private int boardDepth;
// 상위글 번호(boardParent)가 0일 경우 DB에 등록할 때 null을 입력
public Integer getBoardParentInteger() {
if(boardParent == 0) return null;
else return boardParent;
}
}