Luver Duck 2022. 9. 22. 09:42

계층형 게시판

- 게시글과 답글이 계층 관계(종속 관계)를 갖는 게시판

- 계층형 게시판의 모든 글은 게시글 또는 답글 중 하나이다

- 계층형 게시판의 모든 글은 기본적인 게시글의 필드 외에 그룹(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">
				&nbsp;&nbsp;
			</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()}">&laquo;</a> <%-- 첫 번째 페이지로 이동 --%>
	</c:when>
	<c:otherwise> <%-- 그렇지 않다면 --%>
		<a href = "">&laquo;</a> <%-- 아무런 페이지 변화가 없도록 --%>
	</c:otherwise>
</c:choose>

<%-- 이전 구간의 마지막 페이지로 이동 --%>
<c:choose>
	<c:when test = "${vo.hasPrev()}"> <%-- 이전 페이지가 있다면 --%>
		<a href = "list?p=${vo.prevBlock()}">&lt;</a> <%-- 이전 구간의 마지막 페이지로 이동 --%>
	</c:when>
	<c:otherwise> <%-- 그렇지 않다면 --%>
		<a href = "">&lt;</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()}">&gt;</a> <%-- 다음 구간의 첫 번째 페이지로 이동 --%>
	</c:when>
	<c:otherwise> <%-- 그렇지 않다면 --%>
		<a href = "">&gt;</a> <%-- 아무런 페이지 변화가 없도록 --%>
	</c:otherwise>
</c:choose>

<%-- 맨 마지막 페이지로 이동 --%>
<c:choose>
	<c:when test = "${not vo.isLast()}"> <%-- 맨 마지막 페이지가 아니라면 --%>
		<a href = "list?p=${vo.lastBlock()}">&raquo;</a> <%-- 맨 마지막 페이지로 이동 --%>
	</c:when>
	<c:otherwise>
		<a href = "">&raquo;</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">
    &nbsp;&nbsp;
</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;
	}
}