댓글 생성 API를 구현하고, 게시글 상세조회 기능을 수정한다.
1. 프로젝트 구조
src/main/java/com/example/withpeace/
│
├── domain/ # 도메인 모델 (엔티티)
│ ├── Post.java # 게시글 엔티티
│ └── Comment.java # 댓글 엔티티
│
├── repository/ # 데이터 접근 계층
│ ├── CommentRepository.java # 댓글 레포지토리
│ └── PostRepository.java # 게시글 레포지토리
│
├── dto/ # 데이터 전송 객체
│ └── response/
│ ├── CommentListResponseDto.java # 댓글 리스트 응답 DTO
│ └── PostDetailResponseDto.java # 게시글 상세조회 응답 DTO
│
├── controller/ # 컨트롤러 계층
│ └── PostController.java # 게시글 관련 API 엔드포인트
│
└── service/ # 비즈니스 로직 계층
└── PostService.java # 게시글 관련 비즈니스 로직
2. 도메인 엔티티 구현
Comment
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DynamicUpdate
@Table(name = "comments")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false, unique = true)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private Post post;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "writer_id", nullable = false)
private User writer;
@Column(name = "content", nullable = false)
private String content;
@Column(name = "create_date", nullable = false)
private LocalDateTime createDate;
@Builder
public Comment(Post post, User writer, String content) {
this.post = post;
this.writer = writer;
this.content = content;
this.createDate = LocalDateTime.now();
}
}
Comment 엔티티는 연관된 게시글, 작성자, 내용, 작성 시간을 포함한다.
- `@ManyToOne`: 다대일 관계를 나타낸다. 여러 댓글이 하나의 게시글에 속할 수 있음을 의미한다.
- `fetch = FetchType.LAZY`: 지연 로딩(필요한 시점에 연관된 데이터를 불러옴)으로 성능을 최적화한다.
- `@JoinColumn`: 외래키를 지정한다. post_id와 writer_id가 각각 Post와 User 엔티티를 참조하는 외래키가 된다.
3. 레포지토리 구현
CommentRepository
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query("SELECT c FROM Comment c WHERE c.post = :post")
List<Comment> findCommentsByPost(Post post);
}
`CommentRepository`는 `JpaRepository`를 상속받아 기본적인 CRUD 기능을 제공한다.
`findCommentsByPost` 는 특정 게시글에 연관된 모든 댓글을 조회한다.
4. DTO 구현
4.1. CommentListResponseDto
@Builder
public record CommentListResponseDto(
Long commentId, // 댓글 ID
Long userId, // 작성자 ID
String nickname, // 작성자 닉네임
String profileImageUrl, // 작성자 프로필 이미지 URL
String content, // 댓글 내용
String createDate // 작성 날짜
) {}
게시글 상세조회 시 반환할 댓글 목록이다.
4.2. PostDetailResponseDto
@Builder
public record PostDetailResponseDto(
Long postId, // 게시글 ID
Long userId, // 작성자 ID
String nickname, // 작성자 닉네임
String profileImageUrl, // 작성자 프로필 이미지 URL
String title, // 게시글 제목
String content, // 게시글 내용
ETopic type, // 게시글 타입
String createDate, // 작성 날짜
List<String> postImageUrls, // 게시글 이미지 URL 목록
List<CommentListResponseDto> comments // 댓글 목록
) {}
게시글 상세조회 응답 DTO로 게시글의 정보와 댓글 목록(`List<CommentListResponseDto>`)을 포함한다.
5. 컨트롤러 구현
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/posts")
public class PostController {
private final PostService postService;
// ... (기존 코드 생략)
// 댓글 생성
@PostMapping("/{postId}/comments/register")
public ResponseDto<?> registerComment(@UserId Long userId, // 사용자 ID
@PathVariable Long postId, // 게시글 ID
@RequestBody String content // 댓글 내용
) {
return ResponseDto.ok(postService.registerComment(userId, postId, content));
}
}
사용자 ID, 게시글 ID, 댓글 내용을 받아 서비스로 전달한다.
- `@PathVariable Long postId`: URL 경로에서 게시글 ID를 추출한다.
- `@RequestBody String content`: 요청 본문에서 댓글 내용을 추출한다.
6. 서비스 구현
6.1. 사용자 및 게시글 조회 메서드
private User getUserById(Long userId) {
// 사용자 ID로 사용자를 조회하고, 없으면 예외 발
return userRepository.findById(userId).orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_USER));
}
private Post getPostById(Long postId) {
// 게시글 ID로 게시글을 조회하고, 없으면 예외 발생
return postRepository.findById(postId).orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_POST));
}
6.2. registerComment
@Transactional
public Boolean registerComment(Long userId, Long postId, String content) {
// 사용자와 게시글 조회
User user = getUserById(userId);
Post post = getPostById(postId);
// 댓글 생성 및 저장
commentRepository.save(Comment.builder()
.post(post)
.writer(user)
.content(content)
.build());
return true;
}
`registerComment` 메서드는 댓글 정보를 받아 댓글을 생성하는 로직을 수행한다.
- 사용자와 게시글을 조회한다. (존재 여부 확인)
- `commentRepository`의 `save` 메서드를 사용하여 주어진 정보로 생성한 댓글을 저장한다.
6.3. getPostDetail 수정
@Transactional
public PostDetailResponseDto getPostDetail(Long userId, Long postId) {
// 사용자와 게시글 조회
User user = getUserById(userId);
Post post = getPostById(postId);
// 게시글 이미지 URL 목록 조회
List<String> postImageUrls = Optional.ofNullable(imageRepository.findUrlsByPost(post))
.orElse(Collections.emptyList());
// 댓글 목록 조회 및 DTO 변환
List<CommentListResponseDto> comments = Optional.ofNullable(commentRepository.findCommentsByPost(post))
.orElse(Collections.emptyList())
.stream()
.map(comment -> CommentListResponseDto.builder()
.commentId(comment.getId())
.userId(comment.getWriter().getId())
.nickname(comment.getWriter().getNickname())
.profileImageUrl(comment.getWriter().getProfileImage())
.content(comment.getContent())
.createDate(TimeFormatter.timeFormat(comment.getCreateDate()))
.build())
.collect(Collectors.toList());
// PostDetailResponseDto 생성 및 반환
PostDetailResponseDto postDetailResponseDto =
PostDetailResponseDto.builder()
.postId(postId)
.userId(post.getWriter().getId())
.nickname(user.getNickname())
.profileImageUrl(user.getProfileImage())
.title(post.getTitle())
.content(post.getContent())
.type(post.getType())
.createDate(TimeFormatter.timeFormat(post.getCreateDate()))
.postImageUrls(postImageUrls)
.comments(comments) // 댓글 목록 추가
.build();
return postDetailResponseDto;
}
기존 `getPostDetail` 메서드를 수정하여 댓글 목록을 포함하도록 했다.
`commentRepository.findCommentsByPost(post)`로 게시글과 연관된 댓글들을 조회하고, 각 댓글을 `CommentListResponseDto`로 변환한 후 `PostDetailResponseDto` 에 포함시킨다.
7. API 응답 예시
요청 URL
[POST] http://cheongha.site/api/v1/posts/437/comments/register
Response Body
{
"data": true,
"error": null
}
반응형
'Projects > 청하-청년을 위한 커뮤니티 서비스' 카테고리의 다른 글
[청하] 17. 게시글 신고 기능 구현 (3) | 2024.10.15 |
---|---|
[청하] 16. 게시글 리스트 조회 기능 - 댓글수 반환 추가 (0) | 2024.10.14 |
[청하] 14. Spring Boot 파일 업로드 용량 문제 해결 (0) | 2024.10.10 |
[청하] 13. Spring Boot 서버 배포 시 타임존 문제 해결 (feat. @PostConstruct) (2) | 2024.10.09 |
[청하] 12. 게시글 수정 기능 구현 (1) | 2024.10.09 |
댓글