728x90
반응형
들어가기 전에
하기 포스팅은 "스프링부트 시작하기(김인우 저)"를 공부하며 만든 springboot 프로젝트 내에 추가로 구현한 부분에 대해 적은 글입니다. 잘못된 부분이 있을 수 있으니 참고만 해주시면 좋을 것 같습니다.
"스프링부트 시작하기" 책에는 파일 업로드 및 다운로드만 존재하여, 기존에 업로드한 파일을 삭제하는 것에 대해 알아볼 것입니다.
뷰 변경하기
boardDetail.html 코드 속 업로드된 파일 리스트 옆에 삭제 버튼을 추가하였습니다.
<!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>
<form id="frm" method="post">
<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>
<input type="hidden" name="boardIdx" th:value="${board.boardIdx}">
</form>
<!-- modify start -->
<div class="file_list" th:each="list : ${board.fileList}">
<a style="float:left" th:href="@{/board/downloadBoardFile.do(idx=${list.idx}, boardIdx=${list.boardIdx})}" th:text="|${list.originalFileName}(${list.fileSize} kb)|"></a>
<input type="button" id="delete_file" th:value="파일삭제" th:onclick="|location.href='@{/board/deleteBoardFile.do(idx=${list.idx}, boardIdx=${list.boardIdx})}'|">
</div>
<!-- modify end -->
<input type="button" id="list" value="목록으로">
<input type="button" id="edit" value="수정하기">
<input type="button" id="delete" value="삭제하기">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#list").on("click", function(){
location.href = "openBoardList.do";
});
$("#edit").on("click", function(){
var frm = $("#frm")[0];
frm.action = "updateBoard.do";
frm.submit();
});
$("#delete").on("click", function(){
var frm = $("#frm")[0];
frm.action = "deleteBoard.do";
frm.submit();
});
})
</script>
</div>
</body>
</html>
- <div class="file_list" th:each="list : ${board.fileList}">: 기존에는 a 태그에서 th:each를 설정하였으나, 파일 삭제 버튼에서도 해당 속성을 사용할 수 있도록 div에 설정하는 것으로 변경했습니다.
- <a style="float:left"...: a 태그에 float 값을 설정하였습니다. a 태그에 float:left를 설정함에 따라 파일 삭제 버튼을 각 파일 옆에 생성할 수 있습니다.
- <input type="button" id="delete_file" th:value="파일삭제" th:onclick="'location.href=\''+@{/board/deleteBoardFile.do(idx=${list.idx}, boardIdx=${list.boardIdx})}+'\''">
- th:onclick에서 location.href=를 '(single quote)로 묶고 +로 concate한 이유는 location.href 구문을 파싱해서 원하는 주소로 변환을 하기 위함입니다.
- '(single quote)로 묶는 대신, th:onclick 값 전체를 |(pipe)로 묶는 것 역시 가능합니다.
- th:onclick="|location.href='@{/board/deleteBoardFile.do(idx=${list.idx}, boardIdx=${list.boardIdx})}'|"
- |(pipe)로 구문을 감싼 이유는 문자열 연산을 하기 위해서입니다. 문자열 연산은 |(pipe)로도 가능하지만, +와 '(single quote)를 이용해서도 가능합니다.
- 예를 들어, 위 구문은 아래와 같이 나타낼 수 있습니다.
- th:onclick="'location.href=\''+@{/board/deleteBoardFile.do(idx=${list.idx}, boardIdx=${list.boardIdx})}+'\''
컨트롤러 영역
...
@RequestMapping("board/deleteBoardFile.do")
public String deleteBoardFile(@RequestParam int idx, @RequestParam int boardIdx) throws Exception {
boardService.deleteBoardFile(idx, boardIdx);
return "redirect:/board/openBoardDetail.do?boardIdx="+boardIdx;
}
...
- 게시글 삭제했을 때와 다르게 파일 삭제시 해당 게시글을 다시 한 번 호출하도록 했습니다.
- 파일 다운로드할 때와 같이 @RequestParam 값을 사용하여 데이터베이스 속 해당 파일에 접근하도록 했습니다.
서비스 및 매퍼 영역
// BoardService.java
...
void deleteBoardFile(int idx, int boardIdx) throws Exception;
...
// BoardServiceImpl.java
...
@Override
public void deleteBoardFile(int idx, int boardIdx) throws Exception {
boardMapper.deleteBoardFile(idx, boardIdx);
}
...
- selectFileInformation 메소드와 비슷하게 작성해주었습니다. SQL 실행 시 필요할 게시글 번호와 파일 번호를 넘겨 쿼리 돌리는 데 사용할 수 있도록 했습니다.
// BoardMapper.java
...
void deleteBoardFile(@Param("idx") int idx, @Param("boardIdx") int boardIdx);
...
- selectFileInformation 메소드와 비슷하게 작성하였습니다. 이 메소드 역시 뷰에서 넘겨준 parameter를 사용할 수 있도록 @Param 어노테이션을 이용했습니다.
SQL 작성하기
...
<update id="deleteBoardFile" parameterType="map">
<![CDATA[
UPDATE
t_file
SET
deleted_yn = 'Y',
updated_datetime = NOW(),
updater_id = 'admin'
WHERE
idx = #{idx}
AND board_idx = #{boardIdx}
]]>
</update>
...
- deleteBoardFile SQL은 게시글 수정 쿼리와 유사합니다. 다만, parameterType에서 필요한 값이 2가지 뿐이라 따로 DTO를 생성하지 않고 map을 사용했습니다(selectBoardFileInformation 역시 map을 사용했습니다).
파일 삭제 결과 확인하기
마치며
다음 응용편에서는 게시글 내용 뿐 아니라 파일 역시 수정할 수 있도록 코드를 변경해볼 예정입니다. 현재까지는 게시글 생성할 때만 파일을 추가할 수 있지만, 해당 과정을 진행하면 /board/openBoardDetail.do에서 신규 파일 추가 역시 가능할 것입니다.
728x90
반응형
'FRAMEWORK > Spring' 카테고리의 다른 글
[SpringBoot] 게시판 구현하기 15 (RESTful 게시판 만들기) (0) | 2021.01.01 |
---|---|
[SpringBoot] 게시판 구현하기 14 (존재하는 게시글에 파일 추가하기) - 책 응용편 (0) | 2021.01.01 |
[SpringBoot] 게시판 구현하기 12 (파일 업로드와 다운로드) (4) | 2020.12.27 |
[SpringBoot] 게시판 구현하기 11 (스프링의 다양한 기능 살펴보기 - 예외처리) (0) | 2020.12.27 |
[SpringBoot] 게시판 구현하기 10 (스프링의 다양한 기능 살펴보기 - 트랜잭션) (0) | 2020.12.26 |