Web Project/booking
댓글 작성/조회 (Backend / Front)
hmmmmmmmmmmmm
2024. 12. 29. 19:39
Comment.java
package com.booking.booking.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(columnDefinition = "TEXT")
private String content;
private LocalDateTime createdAt;
// 댓글이 속한 게시글
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id")
private Board board;
// 댓글 작성자
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
CommentRequest.java
package com.booking.booking.request;
import com.booking.booking.model.Board;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class CommentRequest {
@NotBlank
private String content;
@NotNull
private Board board;
}
CommentResponse.java
package com.booking.booking.response;
import com.booking.booking.model.Comment;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommentResponse {
private Long id;
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
private Long boardId;
// 작성자 정보 (Comment 응답 반환 시 해당 게시글의 작성자 정보도 함께 반환)
private UserResponse user;
// 엔티티 -> DTO 변환 생성자
public CommentResponse(Comment comment) {
this.id = comment.getId();
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.boardId = comment.getBoard().getId();
// User 엔티티도 DTO로 변환
this.user = new UserResponse(
comment.getUser().getId(),
comment.getUser().getEmail(),
comment.getUser().getFirstName(),
comment.getUser().getLastName()
);
}
}
CommentController.java
// 댓글 작성
@PostMapping("/add/new-comment")
@Transactional
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<CommentResponse> createComment(@Valid @RequestBody CommentRequest request) {
// 1. 현재 Security Context에서 인증 정보(로그인된 사용자의 인증 객체)를 가져옴
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof HotelUserDetails) {
// 2. 인증 객체에서 principal(사용자 정보)를 가져와 HotelUserDetails로 형변환
HotelUserDetails userDetails = (HotelUserDetails) authentication.getPrincipal();
// 3. HotelUserDetails에서 User 정보 (작성자 정보) 가져옴
User user = userDetails.getUser();
Comment comment = new Comment();
comment.setContent(request.getContent());
comment.setUser(user);
comment.setBoard(request.getBoard());
comment.setCreatedAt(LocalDateTime.now());
Comment savedComment = commentService.createComment(comment);
return ResponseEntity.ok(new CommentResponse(savedComment));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 댓글 조회 (게시글별)
@GetMapping("/board/{boardId}")
public ResponseEntity<List<CommentResponse>> getCommentsByBoardId(@PathVariable Long boardId) {
List<Comment> comments = commentService.getCommentsByBoardId(boardId);
List<CommentResponse> commentResponses = comments.stream()
.map(CommentResponse::new)
.collect(Collectors.toList());
return ResponseEntity.ok(commentResponses);
}
CommentServiceImpl.java
package com.booking.booking.service;
import com.booking.booking.model.Comment;
import com.booking.booking.repository.CommentRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional
public class CommentServiceImpl implements CommentService {
private final CommentRepository commentRepository;
// 댓글 생성
@Override
public Comment createComment(Comment comment) {
return commentRepository.save(comment);
}
// 댓글 조회 (게시글별)
@Override
public List<Comment> getCommentsByBoardId(Long boardId) {
return commentRepository.findByBoardIdOrderByCreatedAtDesc(boardId);
}
}
CommentRepository.java
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByBoardIdOrderByCreatedAtDesc(Long boardId);
}
프론트
어드민 외 댓글 작성 불가
어드민 댓글 작성
ApiFunctions.js
// 댓글 작성
export async function CreateComment(commentData) {
try {
const response = await api.post("/comments/add/new-comment", commentData, {
headers: getHeader()
});
return response.data;
}
catch(error) {
if(error.response && error.response.data) {
throw new Error(error.response.data)
}
else {
throw new Error(`Error creating a comment: ${error.message}`)
}
}
}
// 댓글 조회 (게시글별)
export async function GetCommentsByBoardId(boardId) {
try {
const result = await api.get(`/comments/board/${boardId}`, {
headers: getHeader()
})
return result.data
}
catch(error) {
if(error.response && error.response.data) {
throw new Error(error.response.data)
}
else {
throw new Error(`Error fetching all comments: ${error.message}`)
}
}
}
CommentSection.jsx
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { CreateComment, GetCommentsByBoardId } from '../utils/ApiFunctions';
export default function CommentSection() {
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState('');
const { boardId } = useParams();
const userRole = localStorage.getItem("userRole")
// 댓글 조회
useEffect(() => {
const fetchComments = async () => {
try {
const commentList = await GetCommentsByBoardId(boardId)
setComments(commentList.map((comment) => ({
id: comment.id,
content: comment.content,
author: comment.user.firstName + ' ' + comment.user.lastName,
createdAt: comment.createdAt,
boardId: comment.boardId,
userEmail: comment.user.email,
})));
}
catch (error) {
console.error('댓글 조회 중 오류 발생:', error);
alert(error.message);
}
};
if (boardId) {
fetchComments();
}
}, [boardId]);
const canCreateComment = () => {
return userRole === 'ROLE_ADMIN';
}
const handleSubmit = async (e) => {
e.preventDefault();
if (!newComment.trim()) {
alert('댓글 내용을 입력해주세요.')
return;
}
try {
const commentData = {
content: newComment,
board: {
id: boardId,
}
}
// 댓글 생성 API
await CreateComment(commentData);
// 댓글 생성 후 댓글 목록 다시 조회
const updatedComments = await GetCommentsByBoardId(boardId);
setComments(updatedComments.map((comment) => ({
id: comment.id,
content: comment.content,
author: comment.user.firstName + ' ' + comment.user.lastName,
createdAt: comment.createdAt,
boardId: comment.boardId,
})));
setNewComment('');
}
catch (error) {
console.error('댓글 작성 중 오류 발생:', error);
alert(error.message);
}
};
return (
<div className="mt-12">
<h2 className="text-xl font-bold mb-4">댓글</h2>
{/* 댓글 입력 폼 */}
<form onSubmit={handleSubmit} className="mb-8">
<div className="flex flex-col space-y-2">
<textarea
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
rows="3"
placeholder={canCreateComment()
? "댓글을 입력하세요..."
: "어드민 권한만 작성 가능합니다."
}
className={`w-full p-3 border rounded-lg resize-none focus:outline-none focus:ring-2
${canCreateComment()
? 'focus:ring-blue-500'
: 'bg-gray-100 cursor-not-allowed'
}`}
disabled={!canCreateComment()}
/>
{canCreateComment() && (
<div className="flex justify-end">
<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
>
댓글 작성
</button>
</div>
)}
</div>
</form>
{/* 댓글 목록 */}
{comments.length === 0 ? (
<p className="text-center text-gray-500">아직 작성된 댓글이 없습니다.</p>
) : (
<div className="space-y-4">
{comments.map((comment) => (
<div key={comment.id} className="p-4 bg-gray-50 rounded-lg">
<div className="flex justify-between items-center mb-2">
<span className="font-semibold">{comment.author}</span>
<span className="text-sm text-gray-500">
{new Date(comment.createdAt).toLocaleString()}
</span>
</div>
<p className="text-gray-700 text-left">{comment.content}</p>
</div>
))}
</div>
)}
</div>
);
};