
구현은 완료된 상태. 교육 수업 시간에 교수님과 함께 만든 것이기 때문에 수업들었던 흐름을 생각하며 다시 되짚어 보려고 한다.
1. 첫 요청 MainController
인증 여부에 따라 각 메뉴별 출력을 다르게 함 (우선은 칸만 보이게 한다.)
package net.kdigital.board.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping({"/", ""})
public String index() {
return "index";
}
}
return "index"; 이므로 index.html 파일 필요
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시판</title>
<!-- CSS 설정 -->
<link rel="stylesheet" th:href="@{/css/main.css}">
</head>
<body>
<div class="container">
<div class="logo">
<img th:src="@{/images/logo.png}" alt="logo">
<h2>회원 전용 게시판</h2>
</div>
<div class="gnb">
<ul>
<!-- 인증이 되지 않은 사람 -->
<li><a th:href="@{/}">회원가입</a></li>
<li><a th:href="@{/}">로그인</a></li>
<!-- 인증이 된 사람 -->
<li><a th:href="@{/}">로그아웃</a></li>
<!-- 인증과 관계없음 -->
<li><a th:href="@{/board/boardList}">게시판</a></li>
</ul>
</div>
</div> <!-- div.container 끝 -->
</body>
</html>
이 부분은 미리 css 적용까지 마무리 되어있기 때문에 해당 사진 처럼 나오는 것 (원래는 왼쪽 사이드에 검은 글씨로만 쓰여져 있음)

게시판
2. BoardController
2-1) 게시글 목록 요청
페이징처리, 검색기능(글쓴이, 제목, 내용)
@Slf4j
@Controller
@RequestMapping("/board")
public class BoardController {
private BoardService boardService;
// 생성자 초기화
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
// 파일의 저장경로
@Value("${spring.servlet.multipart.location}")
String uploadPath;
boardList.html에서 form 태그의 Metod="GET" 일때 글 목록을 요청
/**
* 글 목록 요청
* 1) index에서 넘어올 경우 searchItem과 searchWord가 없다
* 2) 목록에서 검색하여 넘어올 경우 earchItem과 searchWord가 있다
* @return
*/
@GetMapping("/boardList")
public String boardList(
@RequestParam(name="searchItem", defaultValue="boardTitle") String searchItem
, @RequestParam(name="searchWord", defaultValue="") String searchWord
, Model model) {
List<BoardDTO> dtoList = boardService.selectAll(searchItem, searchWord);
model.addAttribute("list", dtoList);
model.addAttribute("searchItem", searchItem);
model.addAttribute("searchWord", searchWord);
return "board/boardList";
}
BoardService에서 selectAll 이라는 메소드를 통해 리스트를 반환한다. 형태는 List로 오기 때문에 dtoList로 받는다.
* 게시글을 클릭하여 다시 돌아와도 검색한 단어와 제목/작성자/내용 중 선택한 값이 남아있도록 설정되어있다.


boardList.html 전체 코드
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시글 목록</title>
<!-- CSS 설정 -->
<link rel="stylesheet" th:href="@{/css/list.css}">
</head>
<body>
<div class="container">
<div class="logo">
<a th:href="@{/}"><img th:src="@{/images/logo.png}" alt="logo"></a>
<h2>게시글 목록</h2>
</div>
</div> <!-- div.container 끝 -->
<!-- 게시글 전체 목록 출력-->
<div class="content">
<p th:if="${#lists.isEmpty(list)}">목록이 없습니다.</p>
<div class="head">
<!-- 전체 글 개수 출력 -->
<div class="count">
<p>게시글 개수 : <span>0</span></p>
</div>
<!-- 검색 창 -->
<div class="search">
<form th:action="@{/board/boardList}">
<select name="searchItem">
<option value="boardTitle" th:selected="${searchItem == 'boardTitle'}">글제목</option>
<option value="boardWriter" th:selected="${searchItem == 'boardWriter'}">작성자</option>
<option value="boardContent" th:selected="${searchItem == 'boardContent'}">글내용</option>
</select>
<input type="text" name="searchWord" th:value="${searchWord}">
<input type="submit" value="검색" class="btn btn-light">
</form>
</div>
</div>
<!-- 게시글 목록 테이블 -->
<div>
<table border="1">
<tr>
<th class="no">글번호</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>작성일</th>
</tr>
<!-- 반복 구간 -->
<tr th:each="board, status : ${list}">
<td th:text="${status.count}"></td>
<td>
<a th:href="@{/board/boardDetail(boardNum=${board.boardNum}, searchItem=${searchItem}, searchWord=${searchWord})}" th:text="${board.boardTitle}"></a>
<span th:if="${board.originalFileName != null}">
<img th:src="@{/images/attachment.png}" alt="첨부파일" width="17px">
</span>
</td>
<td th:text="${board.boardWriter}">홍길동</td>
<td th:text="${board.hitCount}">22</td>
<td th:text="*{#temporals.format(board.createDate, 'yyyy-MM-dd hh:mm:ss')}">2024-03-12</td>
</tr>
</table>
</div> <!-- div.content 끝 -->
<!-- 글쓰기 버튼 -->
<div class="write">
<a th:href="@{/board/boardWrite}" class="btn btn-light">글쓰기</a>
</div>
<!-- Page Navigation -->
<nav class="pagination">
First Prev
<a href="#">1</a>
2 3 4 5 Next Last
</nav>
</div>
</body>
</html>
<!-- 게시글 전체 목록 출력-->
<div class="content">
<p th:if="${#lists.isEmpty(list)}">목록이 없습니다.</p>
list 안에 내용이 없다면 목록이 없습니다. 를 화면에 보이게 했다.
만약 게시글이 있다면 전체 글 개수를 출력하고 글제목과 내용 그리고 작성자가 보이게 해 두었다.
<div class="head">
<!-- 전체 글 개수 출력 -->
<div class="count">
<p>게시글 개수 : <span>0</span></p>
</div>
<!-- 검색 창 -->
<div class="search">
<form th:action="@{/board/boardList}">
<select name="searchItem">
<option value="boardTitle" th:selected="${searchItem == 'boardTitle'}">글제목</option>
<option value="boardWriter" th:selected="${searchItem == 'boardWriter'}">작성자</option>
<option value="boardContent" th:selected="${searchItem == 'boardContent'}">글내용</option>
</select>
<input type="text" name="searchWord" th:value="${searchWord}">
<input type="submit" value="검색" class="btn btn-light">
</form>
</div>
</div>
코드 | 내용 |
<option value="boardTitle" th:selected="${searchItem == 'boardTitle'}">글제목</option> | 찾으려는 단어와 같으면 선택되게 하였다. |
<!-- 글쓰기 버튼 -->
<div class="write">
<a th:href="@{/board/boardWrite}" class="btn btn-light">글쓰기</a>
</div>
글쓰기 버튼을 누르면 boardWrite로 가도록 설정
3. BoardEntity
전체 코드
package net.kdigital.board.entity;
import java.time.LocalDateTime;
import org.hibernate.annotations.CreationTimestamp;
import org.springframework.data.annotation.LastModifiedDate;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import net.kdigital.board.dto.BoardDTO;
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
@Builder
@Entity
@Table(name = "board")
public class BoardEntity {
@SequenceGenerator(
name = "board_seq"
, sequenceName = "board_seq"
, initialValue = 1
, allocationSize = 1
)
@Id
@GeneratedValue(generator = "board_seq")
@Column(name = "board_num")
private Long boardNum;
@Column(name = "board_writer", nullable = false)
private String boardWriter;
@Column(name = "board_title")
private String boardTitle;
@Column(name = "board_content")
private String boardContent;
@Column(name = "hit_count")
private int hitCount;
@Column(name = "favorite_count")
private int favoriteCount;
@Column(name = "create_date")
@CreationTimestamp // 게시글이 처음 생성될 때 자동으로 날짜를 세팅
private LocalDateTime createDate;
@Column(name = "update_date")
@LastModifiedDate // 게시글이 수정된 마지막 날짜/시간을 세팅
private LocalDateTime updateDate;
// 첨부 파일이 있을 때
@Column(name = "original_file_name")
private String originalFileName; // 원본 파일의 파일명
@Column(name = "saved_file_name")
private String savedFileName; // 하드디스크에 저장될 파일명
// DTO --> Entity 반환
public static BoardEntity toEntity(BoardDTO boardDTO) {
return BoardEntity.builder()
.boardNum(boardDTO.getBoardNum())
.boardWriter(boardDTO.getBoardWriter())
.boardTitle(boardDTO.getBoardTitle())
.boardContent(boardDTO.getBoardContent())
.hitCount(boardDTO.getHitCount())
.favoriteCount(boardDTO.getFavoriteCount())
.originalFileName(boardDTO.getOriginalFileName())
.savedFileName(boardDTO.getSavedFileName())
.build();
}
}

데이터 베이스와 직접 연결 된다. (대소문자 주의, 이름 주의)
4. BoardDTO
직접적으로 데이터 베이스와 연결하는 부분이다.
전체 코드
package net.kdigital.board.dto;
import java.time.LocalDateTime;
import org.springframework.web.multipart.MultipartFile;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import net.kdigital.board.entity.BoardEntity;
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
@Builder
public class BoardDTO {
private Long boardNum;
private String boardWriter;
private String boardTitle;
private String boardContent;
private int hitCount;
private int favoriteCount;
private LocalDateTime createDate;
private LocalDateTime updateDate;
// 파일이 첨부되었을 때 추가작업
private MultipartFile uploadFile;
private String originalFileName; // 원본 파일의 파일명
private String savedFileName; // 하드디스크에 저장될 파일명
// Entity --> DTO 반환
public static BoardDTO toDTO(BoardEntity boardEntity) {
return BoardDTO.builder()
.boardNum(boardEntity.getBoardNum())
.boardWriter(boardEntity.getBoardWriter())
.boardTitle(boardEntity.getBoardTitle())
.boardContent(boardEntity.getBoardContent())
.hitCount(boardEntity.getHitCount())
.favoriteCount(boardEntity.getFavoriteCount())
.createDate(boardEntity.getCreateDate())
.updateDate(boardEntity.getUpdateDate())
.originalFileName(boardEntity.getOriginalFileName())
.savedFileName(boardEntity.getSavedFileName())
.build();
}
}
* CSS
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시글 목록</title>
<!-- CSS 설정 -->
<link rel="stylesheet" th:href="@{/css/list.css}">
</head>
main.css 는 전체 페이지를 아우르는 css 로 활용될 것이다.

코드의 깔끔함을 위해 @import 하여 적용시킨다.
'풀스택 개발 학습 과정 > 백엔드(Java, Spring)' 카테고리의 다른 글
[자바] 타입 추론 ,연산자 Operator -1 (0) | 2024.08.02 |
---|---|
[ckeditor 5, spring] 두 개의 테이블이 있을 때 editor 내용 db로 보내기 (0) | 2024.06.23 |
[스프링] 서버 전달 (0) | 2024.03.17 |
[자바] ENUM (0) | 2024.02.13 |