-
게시판 만들기 📋 목록, 조회, 등록, 상세 페이지 구현하기Spring Boot/게시판 만들기 2022. 6. 20. 16:09
이전 글) 데이터베이스(MariaDB) 연동 및 JPA 입력, 조회, 수정, 삭제 구현
[개발환경] - 윈도우 MariaDB 설치 및 접속하기
🌈 목록, 조회, 등록, 상세 페이지 구현하기
📌 개발환경
IntelliJ Community, SpringBoot, Java 1.8, Gradle, Jar, Thymeleaf, JPA, MariaDB
화면에 구성에 대한 폼이나 컴포넌트는 부트스트랩을 이용하고 템플릿 엔진은 Thymeleaf를 사용해서 진행합니다.
Bootstrap Download
jQuery Download
Bootstrap과 같이 사용될 jQuery도 다운로드 합니다.
정적 리소스 추가
프로젝트에 아래 3개의 파일을 경로에 맞게 추가합니다.
resources/static/css
↪ bootstrap.min.css
resources/static/js
↪ bootstrap.min.js
↪ jquery-3.6.0.min.js컨트롤러 구현
BoardController.java
지난 글에서 만들었던 서비스를 호출하는 컨트롤러를 생성합니다.
URL 매핑을 호출 방식에 맞게 작성합니다.@RequiredArgsConstructor @Controller public class BoardController { private final BoardService boardService; @GetMapping("/board") public String getBoardIndexPage(Model model) { model.addAttribute("result", boardService.findAll()); return "/board/index"; } @GetMapping("/board/write") public String getBoardWritePage(Model model, Board.RequestDto requestDto) { if (requestDto.getId() != null) { model.addAttribute("info", boardService.findById(requestDto.getId())); } return "/board/write"; } @PostMapping("/board/save") public String save(Model model, Board.RequestDto requestDto) { String url = "/error/blank"; if (boardService.save(requestDto) > 0) { url = "redirect:/board"; } return url; } @PostMapping("/board/update") public String update(Model model, Board.RequestDto requestDto) { String url = "/error/blank"; if (boardService.updateBoard(requestDto) > 0) { url = "redirect:/board"; } return url; } @PostMapping("/board/delete") public String delete(Model model, Board.RequestDto requestDto) { boardService.deleteBoard(requestDto); return "redirect:/board"; } }
게시판 목록 화면 구현
HTML - /board/index.html
목록 페이지를 담당하는 HTML을 생성합니다.
css와 js를 호출하고 thymeleaf로 데이터를 표현합니다.
th:each
↪ loop
th:href
↪ href에 값을 삽입
th:text
↪ text에 값을 삽입<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Title</title> <link rel="stylesheet" href="/css/bootstrap.min.css"> <script src="/js/jquery-3.6.0.min.js"></script> <script src="/js/bootstrap.min.js"></script> </head> <body> <div class="container mt-5"> <h2>Toy - Board</h2> <div class="mt-3"> <a href="/board/write" class="btn btn-primary">Register</a> </div> <table class="table table-striped"> <thead> <tr> <th scope="col">#</th> <th scope="col">Title.</th> <th scope="col">Register ID.</th> <th scope="col">Register Time.</th> </tr> </thead> <tbody> <tr th:each="list, status : ${result}"> <th scope="row">1</th> <td> <a th:href="@{/board/write(id=${list.id})}"> <span th:text="${list.title}"></span> </a> </td> <td th:text="${list.registerId}">Otto</td> <td th:text="${list.registerTime}">@mdo</td> </tr> </tbody> </table> </div> </body> </html>
게시판 등록, 상세페이지 구현
HTML - /board/write.html
타임리프 th:if, th:unless를 사용하여 페이지 하나로 등록, 상세 페이지 분기 처리를 합니다.
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Title</title> <link rel="stylesheet" href="/css/bootstrap.min.css"> <script src="/js/jquery-3.6.0.min.js"></script> <script src="/js/bootstrap.min.js"></script> </head> <body> <div class="container mt-5"> <h2>Board - Write</h2> <form id="frmWrite" action="/board/save" method="post"> <div th:if="${info == null}"> <input type="hidden" name="registerId" value="999"> <input type="hidden" name="modifyId" value="999"> <div class="mb-3"> <label class="form-label font-weight-bold">Title.</label> <input type="text" class="form-control" name="title" maxlength="25" required> </div> <div class="mb-3"> <label class="form-label">Contents.</label> <textarea class="form-control" name="contents" rows="5" maxlength="1000" required></textarea> </div> <div class="mb-3"> <label class="form-label">Use.</label> <div class="form-check"> <input class="form-check-input" type="radio" name="useYn" id="flexRadioDefault1" value="Y" checked> <label class="form-check-label" for="flexRadioDefault1">Y</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="useYn" id="flexRadioDefault2" value="N"> <label class="form-check-label" for="flexRadioDefault2">N</label> </div> </div> <div class="float-start"> <button type="button" class="btn btn-success" onclick="javascript:location.href='/board'">Previous</button> </div> <div class="float-end"> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> <div th:unless="${info == null}"> <input type="hidden" name="id" th:value="${info.id}"> <input type="hidden" name="registerId" th:value="${info.registerId}"> <input type="hidden" name="modifyId" th:value="${info.modifyId}"> <div class="mb-3"> <label class="form-label fw-bold">Title.</label> <input type="text" class="form-control" name="title" maxlength="25" th:value="${info.title}" required> </div> <div class="mb-3"> <label class="form-label fw-bold">Contents.</label> <textarea class="form-control" name="contents" rows="5" maxlength="1000" th:text="${info.contents}" required></textarea> </div> <div class="mb-3"> <label class="form-label fw-bold">Use.</label> <div class="form-check"> <input class="form-check-input" type="radio" name="useYn" id="flexRadioDefault1" value="Y" th:checked="${info.useYn == 'Y'}"> <label class="form-check-label" for="flexRadioDefault1">Y</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="useYn" id="flexRadioDefault2" value="N" th:checked="${info.useYn == 'N'}"> <label class="form-check-label" for="flexRadioDefault2">N</label> </div> </div> <div class="mb-3 row"> <div class="col"> <label class="fw-bold">Register ID.</label> <span th:text="${info.registerId}"> </div> <div class="col"> <label class="fw-bold">Register Time.</label> <span th:text="${info.registerTime}"> </div> </div> <div class="mb-3 row"> <div class="col"> <label class="fw-bold">Modify ID.</label> <span th:text="${info.modifyId}"> </div> <div class="col"> <label class="fw-bold">Modify Time.</label> <span th:text="${info.modifyTime}"> </div> </div> <div class="float-start"> <button type="button" class="btn btn-success" onclick="javascript:location.href='/board'">Previous</button> </div> <div class="float-end"> <button type="button" class="btn btn-danger" onclick="javascript:deleteBoard();">Delete</button> <button type="submit" class="btn btn-primary">Modify</button> </div> </div> </form> </div> </body> <script th:inline="javascript"> let frm = $("#frmWrite"); let deleteFlag = false; let deleteBoard = function() { if (confirm("Do you want to delete it")) { deleteFlag = true; frm.attr("action","/board/delete"); frm.submit(); } } frm.submit(function() { let info = [[${info}]]; let flag = false; let msg = "Are you sure you want to register?"; if (deleteFlag) { return true; } if (info != null && info != '') { frm.attr("action", "/board/update"); msg = "Are you sure you want to modify?"; } if (confirm(msg)) { flag = true; } return flag; }); </script> </html>
blank 페이지 구현
HTML - /error/blank.html
에러 화면 처리에서 사용될 페이지 입니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/css/bootstrap.min.css"> </head> <style> .card { position: relative; display: flex; flex-direction: column; min-width: 0; word-wrap: break-word; background-color: #fff; background-clip: border-box; border: 1px solid rgba(0, 0, 0, 0.04); border-radius: .25rem; } .card .card-header { background-color: #fff; border-bottom: none; } </style> <body> <div class="row justify-content-center"> <div class="col-md-12 col-sm-12"> <div class="card shadow-lg border-0 rounded-lg mt-5 mx-auto" style="width: 30rem;"> <h3 class="card-header display-1 text-muted text-center"> Error </h3> <span class="card-subtitle mb-2 text-muted text-center"> An error has occurred. </span> <div class="card-body mx-auto"> <a type="button" href="#" class="btn btn-sm btn-info text-white"> Back To Home </a> </div> </div> </div> </div> </body> </html>
날짜 포맷 변경 메서드 추가
BaseTimeEntity.java
날짜 포맷을 변경할 메서드를 생성합니다.
public static String toStringDateTime(LocalDateTime localDateTime) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return Optional.ofNullable(localDateTime) .map(formatter::format) .orElse(""); }
Board.java
RsponseDto 클래스에서 타입이 LocalDateTime으로 되어있던 등록, 수정시간 변수를 String으로 변경합니다.
ResponseDto 생성자에서도 생성한 날짜 포맷 메서드를 사용하여 변경합니다.@Getter public static class ResponseDto { private Long id; private String title; private String contents; private String useYn; private Long registerId; private String registerTime; private Long modifyId; private String modifyTime; public ResponseDto(Board board) { this.id = board.id; this.title = board.title; this.contents = board.contents; this.useYn = board.useYn; this.registerId = board.getRegisterId(); this.registerTime = board.toStringDateTime(board.getRegisterTime()); this.modifyId = board.getModifyId(); this.modifyTime = board.toStringDateTime(board.getModifyTime()); } }
테스트
목록 페이지
지난 글에서 테스트로 작성된 3개의 게시글이 노출됩니다.
등록 페이지
required 속성이 들어가면 폼 전송 시 아래와 같이 필수 입력을 요구합니다.
상세페이지
등록 페이지와 같은 HTML 하나로 분기를 통해 상세페이지를 구현했습니다.
등록 페이지와 다르게 등록, 수정에 대한 정보가 노출됩니다.
Next Step
📁 Project Download
Reference
'Spring Boot > 게시판 만들기' 카테고리의 다른 글
게시판 만들기 📋 페이징 (PageRequest) 처리 (0) 2022.06.21 게시판 만들기 📋 데이터베이스(MariaDB) 연동 및 JPA 입력, 조회, 수정, 삭제 구현 (0) 2022.06.18 스프링 부트 프로젝트 생성하기 (0) 2022.06.17