Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.back.domain.board.controller;

import com.back.domain.board.dto.CommentListResponse;
import com.back.domain.board.dto.CommentRequest;
import com.back.domain.board.dto.CommentResponse;
import com.back.domain.board.dto.PageResponse;
import com.back.domain.board.service.CommentService;
import com.back.global.common.dto.RsData;
import com.back.global.security.user.CustomUserDetails;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand Down Expand Up @@ -34,6 +39,21 @@ public ResponseEntity<RsData<CommentResponse>> createComment(
));
}

// 댓글 다건 조회
@GetMapping
public ResponseEntity<RsData<PageResponse<CommentListResponse>>> getComments(
@PathVariable Long postId,
@PageableDefault(sort = "createdAt", direction = Sort.Direction.ASC) Pageable pageable
) {
PageResponse<CommentListResponse> response = commentService.getComments(postId, pageable);
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
"댓글 목록이 조회되었습니다.",
response
));
}

// 댓글 수정
@PutMapping("/{commentId}")
public ResponseEntity<RsData<CommentResponse>> updateComment(
Expand Down Expand Up @@ -62,8 +82,8 @@ public ResponseEntity<RsData<Void>> deleteComment(
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
"댓글이 삭제되었습니다.",
null
"댓글이 삭제되었습니다.",
null
));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.back.domain.board.controller;

import com.back.domain.board.dto.CommentListResponse;
import com.back.domain.board.dto.CommentRequest;
import com.back.domain.board.dto.CommentResponse;
import com.back.domain.board.dto.PageResponse;
import com.back.global.common.dto.RsData;
import com.back.global.security.user.CustomUserDetails;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -10,6 +12,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -142,6 +145,115 @@ ResponseEntity<RsData<CommentResponse>> createComment(
@AuthenticationPrincipal CustomUserDetails user
);

@Operation(
summary = "댓글 목록 조회",
description = "특정 게시글에 달린 댓글 목록을 조회합니다. " +
"부모 댓글 기준으로 페이징되며, 각 댓글의 대댓글(children) 목록이 함께 포함됩니다."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "댓글 목록 조회 성공",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": true,
"code": "SUCCESS_200",
"message": "댓글 목록이 조회되었습니다.",
"data": {
"content": [
{
"commentId": 1,
"postId": 101,
"parentId": null,
"author": {
"id": 5,
"nickname": "홍길동"
},
"content": "부모 댓글",
"likeCount": 2,
"createdAt": "2025-09-22T11:30:00",
"updatedAt": "2025-09-22T11:30:00",
"children": [
{
"commentId": 2,
"postId": 101,
"parentId": 1,
"author": {
"id": 5,
"nickname": "홍길동"
},
"content": "자식 댓글",
"likeCount": 0,
"createdAt": "2025-09-22T11:35:00",
"updatedAt": "2025-09-22T11:35:00",
"children": []
}
]
}
],
"pageNumber": 0,
"pageSize": 10,
"totalElements": 1,
"totalPages": 1,
"last": true
}
}
""")
)
),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청 (파라미터 오류)",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "COMMON_400",
"message": "잘못된 요청입니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "POST_001",
"message": "존재하지 않는 게시글입니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "500",
description = "서버 내부 오류",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "COMMON_500",
"message": "서버 오류가 발생했습니다.",
"data": null
}
""")
)
)
})
ResponseEntity<RsData<PageResponse<CommentListResponse>>> getComments(
@PathVariable Long postId,
Pageable pageable
);

@Operation(
summary = "댓글 수정",
description = "로그인한 사용자가 자신이 작성한 댓글을 수정합니다."
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/back/domain/board/dto/AuthorResponse.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.back.domain.board.dto;

import com.back.domain.user.entity.User;
import com.querydsl.core.annotations.QueryProjection;

/**
* 작성자 응답 DTO
Expand All @@ -12,6 +13,9 @@ public record AuthorResponse(
Long id,
String nickname
) {
@QueryProjection
public AuthorResponse {}

public static AuthorResponse from(User user) {
return new AuthorResponse(
user.getId(),
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/back/domain/board/dto/CommentListResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.back.domain.board.dto;

import com.querydsl.core.annotations.QueryProjection;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.List;

/**
* 댓글 목록 응답 DTO
*/
@Getter
public class CommentListResponse {
private final Long commentId;
private final Long postId;
private final Long parentId;
private final AuthorResponse author;
private final String content;

@Setter
private long likeCount;

private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;

@Setter
private List<CommentListResponse> children;

@QueryProjection
public CommentListResponse(Long commentId,
Long postId,
Long parentId,
AuthorResponse author,
String content,
long likeCount,
LocalDateTime createdAt,
LocalDateTime updatedAt,
List<CommentListResponse> children) {
this.commentId = commentId;
this.postId = postId;
this.parentId = parentId;
this.author = author;
this.content = content;
this.likeCount = likeCount;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.children = children;
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/back/domain/board/entity/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public Comment(Post post, User user, String content) {
this.user = user;
this.content = content;
}

public Comment(Post post, User user, String content, Comment parent) {
this.post = post;
this.user = user;
this.content = content;
this.parent = parent;
}

// -------------------- 비즈니스 메서드 --------------------
// 댓글 업데이트
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import org.springframework.stereotype.Repository;

@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
public interface CommentRepository extends JpaRepository<Comment, Long>, CommentRepositoryCustom {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.back.domain.board.repository;

import com.back.domain.board.dto.CommentListResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface CommentRepositoryCustom {
Page<CommentListResponse> getCommentsByPostId(Long postId, Pageable pageable);
}
Loading