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
Expand Up @@ -33,4 +33,37 @@ public ResponseEntity<RsData<CommentResponse>> createComment(
response
));
}

// 댓글 수정
@PutMapping("/{commentId}")
public ResponseEntity<RsData<CommentResponse>> updateComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@RequestBody @Valid CommentRequest request,
@AuthenticationPrincipal CustomUserDetails user
) {
CommentResponse response = commentService.updateComment(postId, commentId, request, user.getUserId());
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
"댓글이 수정되었습니다.",
response
));
}

// 댓글 삭제
@DeleteMapping("/{commentId}")
public ResponseEntity<RsData<Void>> deleteComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@AuthenticationPrincipal CustomUserDetails user
) {
commentService.deleteComment(postId, commentId, user.getUserId());
return ResponseEntity
.status(HttpStatus.OK)
.body(RsData.success(
"댓글이 삭제되었습니다.",
null
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,260 @@ ResponseEntity<RsData<CommentResponse>> createComment(
@RequestBody CommentRequest request,
@AuthenticationPrincipal CustomUserDetails user
);

@Operation(
summary = "댓글 수정",
description = "로그인한 사용자가 자신이 작성한 댓글을 수정합니다."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "댓글 수정 성공",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": true,
"code": "SUCCESS_200",
"message": "댓글이 수정되었습니다.",
"data": {
"commentId": 25,
"postId": 101,
"author": {
"id": 5,
"nickname": "홍길동"
},
"content": "수정된 댓글 내용입니다.",
"createdAt": "2025-09-22T11:30:00",
"updatedAt": "2025-09-22T13:00:00"
}
}
""")
)
),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청 (필드 누락 등)",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "COMMON_400",
"message": "잘못된 요청입니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "401",
description = "인증 실패 (토큰 없음/잘못됨/만료)",
content = @Content(
mediaType = "application/json",
examples = {
@ExampleObject(name = "토큰 없음", value = """
{
"success": false,
"code": "AUTH_001",
"message": "인증이 필요합니다.",
"data": null
}
"""),
@ExampleObject(name = "잘못된 토큰", value = """
{
"success": false,
"code": "AUTH_002",
"message": "유효하지 않은 액세스 토큰입니다.",
"data": null
}
"""),
@ExampleObject(name = "만료된 토큰", value = """
{
"success": false,
"code": "AUTH_004",
"message": "만료된 액세스 토큰입니다.",
"data": null
}
""")
}
)
),
@ApiResponse(
responseCode = "403",
description = "권한 없음 (작성자 아님)",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "COMMENT_002",
"message": "댓글 작성자만 수정/삭제할 수 있습니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글 또는 댓글",
content = @Content(
mediaType = "application/json",
examples = {
@ExampleObject(name = "존재하지 않는 게시글", value = """
{
"success": false,
"code": "POST_001",
"message": "존재하지 않는 게시글입니다.",
"data": null
}
"""),
@ExampleObject(name = "존재하지 않는 댓글", value = """
{
"success": false,
"code": "COMMENT_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<CommentResponse>> updateComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@RequestBody CommentRequest request,
@AuthenticationPrincipal CustomUserDetails user
);

@Operation(
summary = "댓글 삭제",
description = "로그인한 사용자가 자신이 작성한 댓글을 삭제합니다."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "댓글 삭제 성공",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": true,
"code": "SUCCESS_200",
"message": "댓글이 삭제되었습니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "401",
description = "인증 실패 (토큰 없음/잘못됨/만료)",
content = @Content(
mediaType = "application/json",
examples = {
@ExampleObject(name = "토큰 없음", value = """
{
"success": false,
"code": "AUTH_001",
"message": "인증이 필요합니다.",
"data": null
}
"""),
@ExampleObject(name = "잘못된 토큰", value = """
{
"success": false,
"code": "AUTH_002",
"message": "유효하지 않은 액세스 토큰입니다.",
"data": null
}
"""),
@ExampleObject(name = "만료된 토큰", value = """
{
"success": false,
"code": "AUTH_004",
"message": "만료된 액세스 토큰입니다.",
"data": null
}
""")
}
)
),
@ApiResponse(
responseCode = "403",
description = "권한 없음 (작성자 아님)",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"success": false,
"code": "COMMENT_002",
"message": "댓글 작성자만 수정/삭제할 수 있습니다.",
"data": null
}
""")
)
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글 또는 댓글",
content = @Content(
mediaType = "application/json",
examples = {
@ExampleObject(name = "존재하지 않는 게시글", value = """
{
"success": false,
"code": "POST_001",
"message": "존재하지 않는 게시글입니다.",
"data": null
}
"""),
@ExampleObject(name = "존재하지 않는 댓글", value = """
{
"success": false,
"code": "COMMENT_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<Void>> deleteComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@AuthenticationPrincipal CustomUserDetails user
);
}
6 changes: 6 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,4 +41,10 @@ public Comment(Post post, User user, String content) {
this.user = user;
this.content = content;
}

// -------------------- 비즈니스 메서드 --------------------
// 댓글 업데이트
public void update(String content) {
this.content = content;
}
}
53 changes: 53 additions & 0 deletions src/main/java/com/back/domain/board/service/CommentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,57 @@ public CommentResponse createComment(Long postId, CommentRequest request, Long u
commentRepository.save(comment);
return CommentResponse.from(comment);
}

/**
* 댓글 수정 서비스
* 1. Post 조회
* 2. Comment 조회
* 3. 작성자 검증
* 4. Comment 업데이트 (내용)
* 5. CommentResponse 반환
*/
public CommentResponse updateComment(Long postId, Long commentId, CommentRequest request, Long userId) {
// Post 조회
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));

// Comment 조회
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new CustomException(ErrorCode.COMMENT_NOT_FOUND));

// 작성자 검증
if (!comment.getUser().getId().equals(userId)) {
throw new CustomException(ErrorCode.COMMENT_NO_PERMISSION);
}

// Comment 업데이트
comment.update(request.content());

// 응답 반환
return CommentResponse.from(comment);
}

/**
* 댓글 삭제 서비스
* 1. Post 조회
* 2. Comment 조회
* 3. 작성자 검증
* 4. Comment 삭제
*/
public void deleteComment(Long postId, Long commentId, Long userId) {
// Post 조회
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));

// Comment 조회
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new CustomException(ErrorCode.COMMENT_NOT_FOUND));

// 작성자 검증
if (!comment.getUser().getId().equals(userId)) {
throw new CustomException(ErrorCode.COMMENT_NO_PERMISSION);
}

commentRepository.delete(comment);
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/back/global/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public enum ErrorCode {
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "POST_001", "존재하지 않는 게시글입니다."),
POST_NO_PERMISSION(HttpStatus.FORBIDDEN, "POST_002", "게시글 작성자만 수정/삭제할 수 있습니다."),
CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "POST_003", "존재하지 않는 카테고리입니다."),
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT_001", "존재하지 않는 댓글입니다."),
COMMENT_NO_PERMISSION(HttpStatus.FORBIDDEN, "COMMENT_002", "댓글 작성자만 수정/삭제할 수 있습니다."),

// ======================== 공통 에러 ========================
BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_400", "잘못된 요청입니다."),
Expand Down
Loading