토이 프로젝트/게시판 프로젝트
11. 게시판 프로젝트 - 댓글 작성
코딍코딍
2022. 8. 14. 13:10
게시글에 댓글을 작성할 수 있도록 구현하였다.
따로 댓글을 관리하기 위해 BoardComment를 만들었다.
BoardComment와 Member는 N:1관계이고 BoardComment와 Board도 N:1관계이다.

BoardComment 생성
@Entity
@Getter
public class BoardComment {
@Id
@GeneratedValue
private Long id;
private String comment;
private LocalDateTime createdDate;
private String createdBy;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="member_id")
private Member member;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="board_id")
private Board board;
public BoardComment() {}
@Builder
public BoardComment(String comment, LocalDateTime createdDate, String createdBy, Member member, Board board) {
this.comment = comment;
this.createdDate = createdDate;
this.createdBy = createdBy;
this.member = member;
this.board = board;
}
}
- 식별자, 댓글, 작성일자, 작성자, 연관관계를 가진다.
Board
@OneToMany(mappedBy = "board", fetch=FetchType.LAZY)
List<BoardComment> boardComments = new ArrayList<>();
- 연관관계 설정
Member
@OneToMany(mappedBy = "member", fetch=FetchType.LAZY)
private List<BoardComment> boardComments = new ArrayList<>();
- 연관관계 설정
BoardCommentDto 생성
@Data
public class BoardCommentDto {
private String comment;
private LocalDateTime createdDate;
private String createdBy;
private Member member;
private Board board;
public BoardComment toEntity() {
return BoardComment.builder()
.comment(comment)
.createdDate(createdDate)
.createdBy(createdBy)
.member(member)
.board(board)
.build();
}
}
- 컨트롤러와 뷰에 BoardComment를 보내지 않고 BoardCommentDto를 보내 데이터 교환
BoardRepository 생성
public interface BoardCommentRepository extends JpaRepository<BoardComment, Long> {
List<BoardComment> findByBoardId(Long boardId);
}
- 새로운 조회기능을 추가하였다. - findByBoardId(Long boardId)
- DB에 있는 BoardComment 엔티티의 board_id가 boardId와 같은 엔티티들을 List형태로 반환한다.
BoardCommentService 생성
@Service
@RequiredArgsConstructor
@Transactional
public class BoardCommentService {
private final BoardCommentRepository boardCommentRepository;
public void save(BoardComment boardComment) {
boardCommentRepository.save(boardComment);
}
@Transactional(readOnly = true)
public List<BoardComment> findAll(Long BoardId) {
return boardCommentRepository.findByBoardId(BoardId);
}
}
- 댓글 추가 - save()
- 해당 게시글의 댓글 반환 - findByBoardId(BoardId)
BoardController - content() 수정, addComment() 생성
@GetMapping("/boardContent/{boardId}")
public String content(@PathVariable Long boardId, Model model,
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) MemberDto memberDto) {
Board board = boardService.countVisitIncrease(boardId);
String mine = boardService.myContent(memberDto.getId(), board.getMember().getId());
BoardDto boardDto = new BoardDto(board);
List<BoardComment> boardComments = boardCommentService.findAll(boardId);
model.addAttribute("boardComments", boardComments);
model.addAttribute("boardDto", boardDto);
model.addAttribute("mine", mine);
return "board/boardContent";
}
@PostMapping("/comment/{boardId}")
public String addcomment(@PathVariable Long boardId, @ModelAttribute BoardCommentDto boardCommentDto,
@SessionAttribute(name=SessionConst.LOGIN_MEMBER, required = false) MemberDto memberDto, RedirectAttributes redirectAttributes) {
boardCommentDto.setCreatedDate(LocalDateTime.now());
boardCommentDto.setCreatedBy(memberDto.getLoginId());
boardCommentDto.setBoard(boardService.findOne(boardId));
boardCommentDto.setMember(memberService.findOne(memberDto.getId()));
BoardComment boardComment = boardCommentDto.toEntity();
boardCommentService.save(boardComment);
redirectAttributes.addAttribute("boardId", boardId);
return "redirect:/board/boardContent/{boardId}";
}
- content()
- 해당 게시글의 댓글을 모두 가져오는 코드를 추가했다.
- boardCommentService의 findAll()에 boardId를 인자로 보내 해당 게시글의 댓글을 가져온다.
- addComment()
- 뷰에서 받은 BoardCommentDto로 BoardComment를 만들어 DB에 저장한다.
- 이후 뷰를 갱신하기 위해 게시글 뷰로 redirect한다.
boardContent.html 수정
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
<link href="../../static/css/bootstrap.min.css" rel="stylesheet">
<style>
.fakeimg {
height: 200px;
background: #aaa;
}
</style>
<link href="../../static/css/headers.css" rel="stylesheet">
</head>
<body>
<main>
<header class="d-flex flex-wrap align-items-center justify-content-center justify-content-md-between py-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center col-md-3 mb-2 mb-md-0 text-dark text-decoration-none">
<svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap"><use xlink:href="#bootstrap"></use></svg>
</a>
<ul class="nav col-12 col-md-auto mb-2 justify-content-center mb-md-0">
<li><a href="/" class="nav-link px-2 link-secondary">Home</a></li>
<li><a href="/board/boardList" class="nav-link px-2 link-dark">게시판</a></li>
<li><a href="#" class="nav-link px-2 link-dark">About</a></li>
</ul>
<div class="col-md-3 text-end">
<button type="button" class="btn btn-outline-primary me-2" onclick="location.href='/member/logout'">로그아웃</button>
</div>
</header>
</main>
<div class="container" style="margin-top:30px">
<div class="row">
<div class="col-sm-12">
<div th:if="${mine.equals('true')}">
<a th:href="@{'/board/edit/' + ${boardDto.id}}">
<button>수정</button>
</a>
</div>
<div class="form-group">
<h5 th:text="'제목 : ' + ${boardDto.title}"></h5>
</div>
<div>
<td th:text="'작성자 : ' + ${boardDto.createdBy}"></td>
<br><br>
</div>
<h5> 내용 </h5>
<div style="border:1px solid; padding:10px;">
<dl>
<dd th:text="${boardDto.content}"></dd>
</dl>
</div>
</div>
</div>
</div>
<!--여기부터-->
<form action="boardContent.html" th:action="@{'/board/comment/' + ${boardDto.id}}" method="post">
<div class="comment-form" style="text-align: center;">
<div class="comment-form2" style="width:300px;height: 200px;display: inline-block">
<label for="comment">댓글 달기</label>
<textarea class="form-control" id="comment" name="comment" rows="3"></textarea>
<button type="submit" class="btn btn-primary" onclick="window.location.reload()">작성</button>
</div>
</div>
</form>
<div class="container">
<table class="table table-hover">
<tr>
<th>번호</th>
<th>작성자</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<tr th:each="boardComment : ${boardComments}">
<td th:text="${boardComment.id}"></td>
<td th:text="${boardComment.createdBy}"></td>
<td th:text="${boardComment.comment}"></td>
</tr>
</tbody>
</table>
</div>
<!--여기까지 추가-->
</body>
</html>

