Skip to content

Commit 5290bb7

Browse files
authored
Merge pull request #28 from jeondain/feat/#27
[feat #27] 댓글 작성 API 구현
2 parents 5bf700b + 21ff707 commit 5290bb7

9 files changed

Lines changed: 142 additions & 8 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.fromm.leafmap.domain.comment.controller;
2+
3+
import com.fromm.leafmap.domain.comment.dto.CommentRequestDTO;
4+
import com.fromm.leafmap.domain.comment.dto.CommentResponseDTO;
5+
import com.fromm.leafmap.domain.comment.service.CommentService;
6+
import com.fromm.leafmap.domain.member.entity.Member;
7+
import com.fromm.leafmap.global.annotation.CurrentMember;
8+
import com.fromm.leafmap.global.apiPayload.ApiResponse;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.web.bind.annotation.*;
13+
14+
@Slf4j
15+
@RestController
16+
@RequiredArgsConstructor
17+
@RequestMapping("api/posts/{postId}/comments")
18+
public class CommentController {
19+
20+
private final CommentService commentService;
21+
22+
@PostMapping
23+
@Operation(summary = "댓글 작성", description = "상위 댓글이 있을 경우 parentId로 전달해주세요.")
24+
public ApiResponse<CommentResponseDTO.AddCommentResultDTO> addComment(
25+
@PathVariable Long postId,
26+
@RequestBody CommentRequestDTO.AddCommentDTO addCommentDTO,
27+
@CurrentMember Member member) {
28+
29+
CommentResponseDTO.AddCommentResultDTO addCommentResultDTO = commentService.addComment(postId, addCommentDTO, member);
30+
return ApiResponse.onSuccess(addCommentResultDTO);
31+
}
32+
}
33+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.fromm.leafmap.domain.comment.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
public class CommentRequestDTO {
9+
10+
@Builder
11+
@Getter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public static class AddCommentDTO {
15+
private String content;
16+
private Long parentId;
17+
}
18+
}

src/main/java/com/fromm/leafmap/domain/comment/dto/CommentResponseDTO.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,19 @@ public class CommentResponseDTO {
1414
@NoArgsConstructor
1515
@AllArgsConstructor
1616
public static class CommentDTO {
17-
private Long id;
17+
private Long commentId;
18+
private Long parentId;
1819
private String content;
1920
private String nickname;
2021
private Boolean isWriter;
2122
private LocalDateTime createdAt;
2223
}
24+
25+
@Builder
26+
@Getter
27+
@NoArgsConstructor
28+
@AllArgsConstructor
29+
public static class AddCommentResultDTO {
30+
private Long commentId;
31+
}
2332
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.fromm.leafmap.domain.comment.repository;
2+
3+
import com.fromm.leafmap.domain.comment.entity.Comment;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface CommentRepository extends JpaRepository<Comment, Long> {
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.fromm.leafmap.domain.comment.service;
2+
3+
import com.fromm.leafmap.domain.comment.dto.CommentRequestDTO;
4+
import com.fromm.leafmap.domain.comment.dto.CommentResponseDTO;
5+
import com.fromm.leafmap.domain.member.entity.Member;
6+
7+
public interface CommentService {
8+
public CommentResponseDTO.AddCommentResultDTO addComment(Long postId, CommentRequestDTO.AddCommentDTO request, Member member);
9+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.fromm.leafmap.domain.comment.service;
2+
3+
import com.fromm.leafmap.domain.comment.dto.CommentRequestDTO;
4+
import com.fromm.leafmap.domain.comment.dto.CommentResponseDTO;
5+
import com.fromm.leafmap.domain.comment.entity.Comment;
6+
import com.fromm.leafmap.domain.comment.repository.CommentRepository;
7+
import com.fromm.leafmap.domain.member.entity.Member;
8+
import com.fromm.leafmap.domain.post.entity.Post;
9+
import com.fromm.leafmap.domain.post.repository.PostRepository;
10+
import com.fromm.leafmap.global.apiPayload.code.status.ErrorStatus;
11+
import com.fromm.leafmap.global.apiPayload.exception.handler.ErrorHandler;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
@Service
17+
@RequiredArgsConstructor
18+
public class CommentServiceImpl implements CommentService {
19+
20+
private final PostRepository postRepository;
21+
private final CommentRepository commentRepository;
22+
23+
@Override
24+
@Transactional
25+
public CommentResponseDTO.AddCommentResultDTO addComment(Long postId, CommentRequestDTO.AddCommentDTO request, Member member) {
26+
Post post = postRepository.findById(postId)
27+
.orElseThrow(() -> new ErrorHandler(ErrorStatus.POST_NOT_FOUND));
28+
29+
// 부모 댓글 조회 (대댓글인 경우)
30+
Comment parent = null;
31+
if (request.getParentId() != 0) {
32+
parent = commentRepository.findById(request.getParentId())
33+
.orElseThrow(() -> new ErrorHandler(ErrorStatus.COMMENT_NOT_FOUND));
34+
35+
// 부모 댓글이 해당 게시글에 속하지 않으면 예외
36+
if (!parent.getPost().getId().equals(postId)) {
37+
throw new ErrorHandler(ErrorStatus.INVALID_COMMENT_PARENT);
38+
}
39+
}
40+
41+
Comment comment = Comment.builder()
42+
.content(request.getContent())
43+
.post(post)
44+
.member(member)
45+
.parent(parent)
46+
.build();
47+
48+
commentRepository.save(comment);
49+
return CommentResponseDTO.AddCommentResultDTO.builder()
50+
.commentId(comment.getId())
51+
.build();
52+
}
53+
}

src/main/java/com/fromm/leafmap/domain/post/controller/PostController.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
@Slf4j
2222
@RestController
2323
@RequiredArgsConstructor
24-
@RequestMapping("/api/posts")
24+
@RequestMapping("/api/boards")
2525
public class PostController {
2626

2727
private final PostService postService;
@@ -35,7 +35,7 @@ public ApiResponse<String> createMajortipsPosts() {
3535
return ApiResponse.onSuccess("Majortips 게시글 생성 완료");
3636
}
3737

38-
@PostMapping(value = "/{boardType}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
38+
@PostMapping(value = "/{boardType}/posts", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
3939
@Operation(summary = "게시글 작성", description = "게시글 정보는 JSON 형식으로, 이미지는 Multipart(Form-Data) 형식으로 함께 전달해주세요.\n\n" )
4040
public ApiResponse<PostResponseDTO.AddPostResultDTO> addPost(
4141
@RequestPart(value = "data") PostRequestDTO.AddPostRequestDTO addPostRequestDTO,
@@ -48,7 +48,7 @@ public ApiResponse<PostResponseDTO.AddPostResultDTO> addPost(
4848
return ApiResponse.onSuccess(addPostResultDTO);
4949
}
5050

51-
@GetMapping(value = "/{boardType}")
51+
@GetMapping(value = "/{boardType}/posts")
5252
@Operation(summary = "게시판 목록 조회", description = "첫 페이지 조회 시 cursor 값으로 0을 전달해주세요.\n\n" +
5353
"첫 페이지가 아닌 경우 이전 응답의 hasNext가 true일 때, nextCursor 값을 cursor로 전달해주세요.")
5454
public ApiResponse<PostResponseDTO.PostListResultDTO> getPostList(
@@ -61,7 +61,7 @@ public ApiResponse<PostResponseDTO.PostListResultDTO> getPostList(
6161
return ApiResponse.onSuccess(postListResultDTO);
6262
}
6363

64-
@GetMapping(value = "/{boardType}/{postId}")
64+
@GetMapping(value = "/{boardType}/posts/{postId}")
6565
@Operation(summary = "게시판 상세 조회")
6666
public ApiResponse<PostResponseDTO.PostDetailResultDTO> getPostDetail(
6767
@PathVariable BoardType boardType,
@@ -72,7 +72,7 @@ public ApiResponse<PostResponseDTO.PostDetailResultDTO> getPostDetail(
7272
return ApiResponse.onSuccess(postDetailResultDTO);
7373
}
7474

75-
@PostMapping(value ="/{boardType}/{postId}/like")
75+
@PostMapping(value ="/{boardType}/posts/{postId}/like")
7676
@Operation(summary = "게시글 추천 토글", description = "추천하지 않은 상태라면 추천이 추가되고, 이미 추천한 상태라면 추천이 취소됩니다.")
7777
public ApiResponse<PostLikeResponseDTO.PostLikeResultDTO> toggleLike(
7878
@PathVariable BoardType boardType,

src/main/java/com/fromm/leafmap/domain/post/service/PostServiceImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ public PostResponseDTO.PostDetailResultDTO getPostDetail(BoardType boardType, Lo
119119
&& member.getId().equals(c.getMember().getId());
120120

121121
return CommentResponseDTO.CommentDTO.builder()
122-
.id(c.getId())
122+
.commentId(c.getId())
123+
.parentId(c.getParent() != null ? c.getParent().getId() : null)
123124
.content(c.getContent())
124125
.nickname(c.getMember().getNickname())
125126
.isWriter(isCommentWriter)

src/main/java/com/fromm/leafmap/global/apiPayload/code/status/ErrorStatus.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ public enum ErrorStatus implements BaseErrorCode {
2525
MAJOR_NOT_FOUND(HttpStatus.NOT_FOUND, "MAJOR404", "해당하는 전공이 존재하지 않습니다."),
2626

2727
// Post
28-
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "POST404", "해당하는 게시글이 존재하지 않습니다.")
28+
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "POST404", "해당하는 게시글이 존재하지 않습니다."),
29+
30+
// Comment
31+
COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT404", "해당하는 댓글이 존재하지 않습니다."),
32+
INVALID_COMMENT_PARENT(HttpStatus.BAD_REQUEST, "COMMENT400", "부모 댓글이 해당 게시글에 속하지 않습니다.");
2933
;
3034

3135
private final HttpStatus httpStatus;

0 commit comments

Comments
 (0)