마지막 댓글 부분이다.
댓글은 ajax를 이용해서 한다.
ajax를 사용하면 비동기적으로 통신하기 때문에 페이지를 새로 고침하지 않아도
데이터를 서버로부터 받아오거나 서버에 데이터를 보낼 수 있다.
또 전체 페이지를 다시 로드하지 않고도 특정 부분만 업데이트가 가능하다.
detail.html 에 댓글 관련 코드를 추가한다.
board.id를 받아오는 것이 중요하다.
[ detail.html ]
<!-- 댓글 작성 부분 -->
<div id="comment-write">
<input type="text" id="commentWriter" placeholder="작성자">
<input type="text" id="commentContents" placeholder="내용">
<button id="comment-write-btn" onclick="commentWrite()">댓글작성</button>
</div>
<!-- 댓글 출력 부분 -->
<div id="comment-list">
<table>
<tr>
<th>댓글번호</th>
<th>작성자</th>
<th>내용</th>
<th>작성시간</th>
</tr>
<tr th:each="comment: ${commentList}">
<td th:text="${comment.id}"></td>
<td th:text="${comment.commentWriter}"></td>
<td th:text="${comment.commentContents}"></td>
<td th:text="${comment.commentCreatedTime}"></td>
</tr>
</table>
</div>
</body>
<script th:inline="javascript">
const commentWrite = () => {
const writer = document.getElementById("commentWriter").value;
const contents = document.getElementById("commentContents").value;
console.log("작성자: ", writer);
console.log("내용: ", contents);
const id = [[${board.id}]];
$.ajax({
// 요청방식: post, 요청주소: /comment/save, 요청데이터: 작성자, 작성내용, 게시글번호
type: "post",
url: "/comment/save",
data: {
"commentWriter": writer,
"commentContents": contents,
"boardId": id
},
success: function (res) {
console.log("요청성공", res);
let output = "<table>";
output += "<tr><th>댓글번호</th>";
output += "<th>작성자</th>";
output += "<th>내용</th>";
output += "<th>작성시간</th></tr>";
for (let i in res) {
output += "<tr>";
output += "<td>" + res[i].id + "</td>";
output += "<td>" + res[i].commentWriter + "</td>";
output += "<td>" + res[i].commentContents + "</td>";
output += "<td>" + res[i].commentCreatedTime + "</td>";
output += "</tr>";
}
output += "</table>";
document.getElementById('comment-list').innerHTML = output;
document.getElementById('commentWriter').value = '';
document.getElementById('commentContents').value = '';
},
error: function (err) {
console.log("요청실패", err);
}
});
}
CommentController 클래스를 생성한다.
[ CommentController ]
package test.SpringBootBoard.board.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import test.SpringBootBoard.board.dto.CommentDTO;
import test.SpringBootBoard.board.service.CommentService;
import java.util.List;
@Controller
@RequiredArgsConstructor
@RequestMapping("/comment")
public class CommentController {
private final CommentService commentService;
@PostMapping("/save")
public ResponseEntity save(@ModelAttribute CommentDTO commentDTO) {
System.out.println("commentDTO = " + commentDTO);
Long saveResult = commentService.save(commentDTO);
if (saveResult != null) {
List<CommentDTO> commentDTOList = commentService.findAll(commentDTO.getBoardId());
return new ResponseEntity<>(commentDTOList, HttpStatus.OK);
} else {
return new ResponseEntity<>("해당 게시글이 존재하지 않습니다.", HttpStatus.NOT_FOUND);
}
}
}
public ResponseEntity save(@ModelAttribute CommentDTO commentDTO)
클라이언트로부터 받은 데이터는 CommentDTO 객체로 매핑된다.
Long saveResult = commentService.save(commentDTO);
CommentService를 사용하여 댓글을 저장하고, 결과로 저장된 댓글의 ID 값을 받아온다.
if (saveResult != null)
저장 결과가 null이 아닌 경우(즉, 댓글이 성공적으로 저장된 경우) 해당 블록을 실행한다.
List<CommentDTO> commentDTOList = commentService.findAll(commentDTO.getBoardId());
댓글이 성공적으로 저장되면 해당 게시글에 대한 모든 댓글을 가져온다.
return new ResponseEntity<>(commentDTOList, HttpStatus.OK);
댓글 목록을 HttpStatus.OK(200) 상태 코드와 함께 응답한다.
return new ResponseEntity<>("해당 게시글이 존재하지 않습니다.", HttpStatus.NOT_FOUND);
저장 결과가 null 인 경우 (해당 게시글이 존재하지 않는 경우) HttpStatus.NOT_FOUND(404) 상태 코드와 함께 응답한다.
CommentService 클래스도 생성한다. save 메서드와 findAll 메서드를 추가한다.
[ CommentService ]
package test.SpringBootBoard.board.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import test.SpringBootBoard.board.dto.CommentDTO;
import test.SpringBootBoard.board.entity.BoardEntity;
import test.SpringBootBoard.board.entity.CommentEntity;
import test.SpringBootBoard.board.repository.BoardRepository;
import test.SpringBootBoard.board.repository.CommentRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class CommentService {
private final CommentRepository commentRepository;
private final BoardRepository boardRepository;
public Long save(CommentDTO commentDTO) {
/* 부모엔티티(BoardEntity) 조회 */
Optional<BoardEntity> optionalBoardEntity = boardRepository.findById(commentDTO.getBoardId());
if (optionalBoardEntity.isPresent()) {
BoardEntity boardEntity = optionalBoardEntity.get();
CommentEntity commentEntity = CommentEntity.toSaveEntity(commentDTO, boardEntity);
return commentRepository.save(commentEntity).getId();
} else {
return null;
}
}
public List<CommentDTO> findAll(Long boardId) {
BoardEntity boardEntity = boardRepository.findById(boardId).get();
List<CommentEntity> commentEntityList = commentRepository.findAllByBoardEntityOrderByIdDesc(boardEntity);
/* EntityList -> DTOList */
List<CommentDTO> commentDTOList = new ArrayList<>();
for (CommentEntity commentEntity: commentEntityList) {
CommentDTO commentDTO = CommentDTO.toCommentDTO(commentEntity, boardId);
commentDTOList.add(commentDTO);
}
return commentDTOList;
}
}
<save 메서드>
Optional<BoardEntity> optionalBoardEntity = boardRepository.findById(commentDTO.getBoardId());
주어진 boardId로 BoardEntity를 조회한다.
if (optionalBoardEntity.isPresent())
조회된 BoardEntity가 존재하는 경우 해당 블록을 실행한다.
BoardEntity boardEntity = optionalBoardEntity.get();
조회된 BoardEntity를 가져온다.
CommentEntity commentEntity = CommentEntity.toSaveEntity(commentDTO, boardEntity);
CommentEntity 객체를 생성하고, CommentDTO 및 BoardEntity의 정보를 사용하여 초기화한다.
return commentRepository.save(commentEntity).getId();
생성된 CommentEntity를 저장하고, 저장된 댓글의 ID 값을 반환한다.
<findAll 메서드> 특정 게시글에 대한 모든 댓글을 조회하는 메서드이다.
BoardEntity boardEntity = boardRepository.findById(boardId).get();
주어진 boardId로 BoardEntity를 조회한다.
List<CommentEntity> commentEntityList = commentRepository.findAllByBoardEntityOrderByIdDesc(boardEntity);
해당 게시글에 대한 댓글 목록을 생성일자(ID 필드) 역순으로 조회한다.
List<CommentDTO> commentDTOList = new ArrayList<>();
댓글 목록을 저장할 CommentDTO 객체의 리스트를 생성한다.
for (CommentEntity commentEntity: commentEntityList) {
각 댓글에 대해 반복하면서 DTO로 변환하고 리스트에 추가한다.
CommentDTO 도 생성해준다.
[ CommentDTO ]
package test.SpringBootBoard.board.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import test.SpringBootBoard.board.entity.CommentEntity;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
public class CommentDTO {
private Long id;
private String commentWriter;
private String commentContents;
private Long boardId;
private LocalDateTime commentCreatedTime;
public static CommentDTO toCommentDTO(CommentEntity commentEntity, Long boardId) {
CommentDTO commentDTO = new CommentDTO();
commentDTO.setId(commentEntity.getId());
commentDTO.setCommentWriter(commentEntity.getCommentWriter());
commentDTO.setCommentContents(commentEntity.getCommentContents());
commentDTO.setCommentCreatedTime(commentEntity.getCreatedTime());
commentDTO.setBoardId(boardId);
return commentDTO;
}
}
CommentRepostiory 도 생성해준다.
[ CommentRepostiory ]
package test.SpringBootBoard.board.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import test.SpringBootBoard.board.entity.BoardEntity;
import test.SpringBootBoard.board.entity.CommentEntity;
import java.util.List;
public interface CommentRepository extends JpaRepository<CommentEntity, Long> {
// select * from comment_table where board_id=? order by id desc;
List<CommentEntity> findAllByBoardEntityOrderByIdDesc(BoardEntity boardEntity);
}
List<CommentEntity> findAllByBoardEntityOrderByIdDesc(BoardEntity boardEntity);
BoardEntity에 속한 모든 댓글들을 가져오는데 사용된다.
findAllByBoardEntity: BoardEntity에 속한 모든 엔티티를 찾는다.
OrderByIdDesc: id 필드를 기준으로 내림차순으로 정렬한다.
CommentEntity 도 생성한다.
[ CommentEntity ]
@Entity
@Getter
@Setter
@Table(name = "comment_table")
public class CommentEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 20, nullable = false)
private String commentWriter;
@Column
private String commentContents;
/* Board:Comment = 1:N */
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id")
private BoardEntity boardEntity;
public static CommentEntity toSaveEntity(CommentDTO commentDTO, BoardEntity boardEntity) {
CommentEntity commentEntity = new CommentEntity();
commentEntity.setCommentWriter(commentDTO.getCommentWriter());
commentEntity.setCommentContents(commentDTO.getCommentContents());
commentEntity.setBoardEntity(boardEntity);
return commentEntity;
}
}
저번에 File 테이블과 비슷하다.
BoardEntity에도 댓글 관련 코드를 추가한다.
[ BoardEntity ]
@OneToMany(mappedBy = "boardEntity", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
private List<CommentEntity> commentEntityList = new ArrayList<>();
BoardController 도 수정해준다.
[ BoardController ]
private final CommentService commentService;
@GetMapping("/{id}")
public String findById(@PathVariable Long id, Model model,
@PageableDefault(page=1) Pageable pageable){
/*
해당 게시글의 조회수를 하나 올리고
게시글 데이터를 가져와서 detail.html에 출력
*/
boardService.updateHits(id);
BoardDTO boardDTO = boardService.findById(id);
/* 댓글 목록 가져오기 */
List<CommentDTO> commentDTOList = commentService.findAll(id);
model.addAttribute("commentList", commentDTOList);
model.addAttribute("board", boardDTO);
model.addAttribute("page", pageable.getPageNumber());
return "detail";
}
실행해보좌
댓글 폼에 댓글을 입력한다.
짠 생성 완
비동기이기 때문에 페이지가 로드되지 않아도 바로 생성되는 것을 확인할 수 있다.
콘솔을 보면 잘 찍힌걸 알 수 있다.
두번째 댓글도 작성
2개가 됐다.
디비버에서도 잘 들어온걸 확인할 수 있다.
'Spring Boot' 카테고리의 다른 글
[게시판 만들기 (10)] 파일 첨부_다중파일 첨부 (0) | 2024.01.15 |
---|---|
[게시판 만들기 (9)] 게시글 페이징_페이징 후 상세조회 (0) | 2024.01.14 |
[게시판 만들기 (8)] 게시글 페이징_페이징 화면 처리 (0) | 2024.01.14 |
[게시판 만들기 (7)] 게시글 삭제 (0) | 2024.01.14 |
[게시판 만들기 (6)] 게시글 수정 (0) | 2024.01.14 |