FRAMEWORK/Spring

[SpringBoot] 게시판 구현하기 5 (게시글 상세 화면 만들기)

EARTH_ROOPRETELCHAM 2020. 12. 21. 23:03
728x90
반응형

들어가기 전에

하기 포스팅은 "스프링부트 시작하기(김인우 저)" 책을 공부하며 적은 포스팅입니다. 이번 시간에는 앞서 등록한 게시글을 확인할 수 있는 상세 화면을 만들어 보겠습니다. 

게시글 상세 화면 만들기

게시글 상세 화면 만들기 전 조회 로직의 흐름에 대해 먼저 살펴보겠습니다.

  1. 목록 화면에서 상세 내용을 확인하고 싶은 글을 선택합니다. 즉, 목록 화면에서 선택된 글의 상세 화면을 호출하는 기능이 필요합니다.
  2. 선택된 글의 상세 내용을 조회하고 조회수를 증가시킵니다. 즉, 상세 내용을 조회할 때 비즈니스 로직은 목록 조회와 다르게 두 가지가 됩니다. 비즈니스 로직을 처리하는 서비스 영역에 두 가지 작업(게시글 조회 & 조회수 증가)을 진행해야 합니다.
  3. 조회된 글 내용을 화면에 표시합니다.

목록 화면 수정하기

먼저 게시글을 선택하고, 선택된 게시글의 상세내용을 조회하는 주소를 호출할 수 있도록 게시글 목록 화면을 수정해야 합니다.

boardList.html 파일의 <table> 내 목록을 보여주는 부분을 아래와 같이 수정합니다.

// boardList.html
// [ AS - IS ]
<td class="title" th:text="${list.title}"></td>

// [ TO - BE ]
<td class="title">
	<a href="/board/openBoardDetail.do?boardIdx=" 
		th:attrappend="href=${list.boardIdx}"
		th:text="${list.title}"></a></td>
  • <a href="/board/openBoardDetail.do?board_idx=" th:attrappend="href=${list.board_idx}" th:text="${list.title}"></a>: <a> 태그를 이용해서 게시글 상세 화면으로 링크를 연결합니다. 상세 화면의 주소를 호출할 때에는 선택된 게시글 번호를 파라미터로 추가합니다.

컨트롤러 영역

BoardController 클래스에 글 상세 내용을 조회하는 로직을 추가합니다.

...
import org.springframework.web.bind.annotation.RequestParam;
...

...
	@RequestMapping("/board/openBoardDetail.do")
	public ModelAndView openBoardDetail(@RequestParam int boardIdx) throws Exception {
		ModelAndView mv = new ModelAndView("/board/boardDetail");
		
		BoardDto board = boardService.selectBoardDetail(boardIdx);
		mv.addObject("board", board);
		
		return mv;
	}
...

서비스 및 매퍼 영역

서비스 영역에서는 글 조회 및 조회수 증가 두 가지 로직이 필요합니다. 따라서, 서비스 영역에서는 두 가지 로직을 처리하게 됩니다.

// BoardService.java
...
	BoardDto selectBoardDetail(int boardIdx) throws Exception;
...
// BoardServiceImpl.java
...
	@Override
	public BoardDto selectBoardDetail(int boardIdx) throws Exception {
		boardMapper.updateHitCount(boardIdx);
		
		BoardDto board = boardMapper.selectBoardDetail(boardIdx);
		
		return board;
	}
...
  • boardMapper.updateHitCount(boardIdx): 선택된 게시글의 조회수를 증가시킵니다.
  • BoardDto board = boardMapper.selectBoardDetail(boardIdx): 선택된 게시글의 내용을 조회합니다.

BoardServiceImpl 클래스를 보면, 두 가지 로직을 처리하기 위해 매퍼를 각각 호출하는 것을 볼 수 있습니다.

해당 서비스 로직 관련 매퍼 코드는 아래와 같이 추가하면 됩니다.

// BoardMapper.java
...
	void updateHitCount(int boardIdx) throws Exception;
	
	BoardDto selectBoardDetail(int boardIdx) throws Exception;
...

SQL 작성하기

sql-board.xml에 조회수 증가와 게시글 상세 내용을 조회하는 쿼리를 작성하도록 하겠습니다. 하기 쿼리문 역시 기존에 만든 <mapper> 태그 내에 넣어야 합니다.

...
	<update id="updateHitCount" parameterType="int">
		<![CDATA[
			UPDATE 
				t_board
			SET
				hit_cnt = hit_cnt + 1
			WHERE
				board_idx = #{boardIdx}
		]]>
	</update>
	<select id="selectBoardDetail" parameterType="int" resultType="board.board.dto.BoardDto">
		<![CDATA[
			SELECT 
				board_idx, title, contents, hit_cnt,
				created_datetime,
				creator_id
			FROM
				t_board
			WHERE
				board_idx = ${boardIdx} AND deleted_yn = 'N'
		]]>
	</select>
...

뷰 작성하기

마지막으로 게시글 상세 내용을 사용자에게 보여주기 위한 화면을 만들어야 합니다. boardDetail.html 파일을 templates/board 내에 생성하여 하기 내용을 넣어줍니다.

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>board</title>
	<link rel="stylesheet" th:href="@{/css/style.css}"/>
</head>
<body>
	<div class="container">
		<h2>게시글 상세 화면</h2>
	    	<table class="board_detail">
				<colgroup>
					<col width="15%"/>
					<col width="35%"/>
					<col width="15%"/>
					<col width="35%"/>
				</colgroup>
				<caption>게시글 상세내용</caption>
				<tbody>
					<tr>
						<th scope="row">글 번호</th>
						<td th:text="${board.boardIdx}"></td>
						<th scope="row">조회수</th>
						<td th:text="${board.hitCnt}"></td>
					</tr>
					<tr>
						<th scope="row">작성자</th>
						<td th:text="${board.creatorId}"></td>
						<th scope="row">작성일</th>
						<td th:text="${#temporals.format(board.createdDatetime, 'yyyy-MM-dd HH:mm:ss')}"></td>
					</tr>
					<tr>
						<th scope="row">제목</th>
						<td colspan="3">
							<input type="text" id="title" name="title" 
								th:value="${board.title }"/>
						</td>
					</tr>
					<tr>
						<td colspan="4" class="view_text">
							<textarea title="내용" id="contents" name="contents" 
								th:text="${board.contents }"></textarea>
						</td>
					</tr>
				</tbody>
			</table>
	</div>
</body>
</html>
  • 게시글 상세 화면을 호출하면, 게시글의 상세 내용을 조회하고 그 결과를 board라는 키로 뷰에 넘겨주었습니다. 이를 통해 전달받은 데이터 중 사용자가 변경하면 안되는 정보(조회용 정보 - 글 번호, 조회수, 작성자, 작성일)는 화면에 보여주기만 합니다(input 태그 없음).
  • 사용자가 변경할 수 있는 제목 및 내용은 <input> 태그와 <textarea>를 이용하여 수정할 수 있도록 합니다.
  • 수정 및 삭제 기능을 추후 구성하기 위해 게시글 상세화면에서 수정 및 삭제를 할 수 있도록 버튼을 미리 생성해두었습니다.

수정 및 삭제는 로그인 여부나 게시글 비밀번호를 이용하여 처리하게 할 수도 있겠지만, 여기서는 기본 기능을 먼저 알아보기 위해 추가 기능을 고려하지 않고 기본 기능만 체크해 본다고 생각하면 됩니다.

실행 결과 확인하기

실행 결과를 확인했을 때, localhost:8080/board/openBoardList.do까지는 문제가 없었지만, 막상 게시글을 클릭하면 하기와 같은 에러가 발생했다.

게시글을 선택한 경우 발생한 에러 페이지

해당 에러는 컨트롤러에서 뷰를 연결하는 과정에서, 아래와 같이 /board/openBoardDetail이라고.. 경로를 잘못 지정해서였다. 혹시, 위 에러가 발생한다면, 필자와 동일한 문제는 아닌지 살펴보길 바란다😅

	@RequestMapping("/board/openBoardDetail.do")
	public ModelAndView openBoardDetail(@RequestParam int boardIdx) throws Exception {
		ModelAndView mv = new ModelAndView("/board/openBoardDetail");
		
		BoardDto board = boardService.selectBoardDetail(boardIdx);
		mv.addObject("board", board);
		
		return mv;
	}

위 에러를 수정하고 난 뒤, 아래와 같이 게시글 상세 페이지가 호출됨을 확인할 수 있었다.

게시글 상세 화면 실행 결과

  • 게시글 목록에서 게시글을 클릭하기 전 조회수와, 게시글 상세 화면에서의 조회수에 차이가 있는 것을 확인할 수 있습니다. 제대로 + 1이 되었음을 알 수 있습니다.
  • 게시글 상세 화면에서는 제목과 내용은 수정할 수 있도록 <input>, <textarea> 태그로 들어가있음을 확인할 수 있습니다.
728x90
반응형