From 7b44554b309b6c4f13b61e41f13d618e631af148 Mon Sep 17 00:00:00 2001 From: DevCHW Date: Thu, 5 Jun 2025 20:04:29 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=ED=8C=90=20CRUD=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/devchw/coderabbit/BoardController.kt | 72 +++++++++++++++++++ src/main/kotlin/io/devchw/coderabbit/Post.kt | 11 +++ 2 files changed, 83 insertions(+) create mode 100644 src/main/kotlin/io/devchw/coderabbit/BoardController.kt create mode 100644 src/main/kotlin/io/devchw/coderabbit/Post.kt diff --git a/src/main/kotlin/io/devchw/coderabbit/BoardController.kt b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt new file mode 100644 index 0000000..fe5c88b --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt @@ -0,0 +1,72 @@ +package io.devchw.coderabbit + +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.util.concurrent.atomic.AtomicLong + +/** + * @author DevCHW + * @since 2025-06-05 + */ +@RestController +@RequestMapping("/api/board") +class BoardController { + + private val posts = mutableMapOf() + private val idGenerator = AtomicLong(1) + + // 게시물 생성 + @PostMapping + fun createPost(@RequestBody post: Post): ResponseEntity { + val id = idGenerator.getAndIncrement() + post.id = id + posts[id] = post + return ResponseEntity.status(HttpStatus.CREATED).body(post) + } + + // 게시물 목록 조회 + @GetMapping + fun getPosts(): ResponseEntity> { + return ResponseEntity.ok(posts.values.toList()) + } + + // 게시물 상세 조회 + @GetMapping("/{id}") + fun getPost(@PathVariable id: Long): ResponseEntity { + val post = posts[id] + return if (post != null) { + ResponseEntity.ok(post) + } else { + ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + } + + // 게시물 수정 + @PutMapping("/{id}") + fun updatePost(@PathVariable id: Long, @RequestBody updatedPost: Post): ResponseEntity { + if (!posts.containsKey(id)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + updatedPost.id = id + posts[id] = updatedPost + return ResponseEntity.ok(updatedPost) + } + + // 게시물 삭제 + @DeleteMapping("/{id}") + fun deletePost(@PathVariable id: Long): ResponseEntity { + if (!posts.containsKey(id)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + posts.remove(id) + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/devchw/coderabbit/Post.kt b/src/main/kotlin/io/devchw/coderabbit/Post.kt new file mode 100644 index 0000000..c3bd945 --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/Post.kt @@ -0,0 +1,11 @@ +package io.devchw.coderabbit + +/** + * @author DevCHW + * @since 2025-06-05 + */ +data class Post( + var id: Long? = null, + var title: String, + var content: String +) From 9ae52dba4a0ac0c5ff65c581689c248774d806c4 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 11:11:42 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`fea?= =?UTF-8?q?ture-board`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @DevCHW. * https://github.com/DevCHW/coderabbit-demo/pull/2#issuecomment-2943759502 The following files were modified: * `src/main/kotlin/io/devchw/coderabbit/BoardController.kt` --- .../io/devchw/coderabbit/BoardController.kt | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/io/devchw/coderabbit/BoardController.kt b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt index fe5c88b..6c0aacf 100644 --- a/src/main/kotlin/io/devchw/coderabbit/BoardController.kt +++ b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt @@ -23,7 +23,12 @@ class BoardController { private val posts = mutableMapOf() private val idGenerator = AtomicLong(1) - // 게시물 생성 + /** + * Creates a new post, assigns it a unique ID, and stores it in memory. + * + * @param post The post data to create. + * @return The created post with its assigned ID and HTTP status 201 (Created). + */ @PostMapping fun createPost(@RequestBody post: Post): ResponseEntity { val id = idGenerator.getAndIncrement() @@ -32,13 +37,24 @@ class BoardController { return ResponseEntity.status(HttpStatus.CREATED).body(post) } - // 게시물 목록 조회 + /** + * Retrieves a list of all posts. + * + * @return HTTP 200 response containing all stored posts. + */ @GetMapping fun getPosts(): ResponseEntity> { return ResponseEntity.ok(posts.values.toList()) } - // 게시물 상세 조회 + /** + * Retrieves a post by its unique ID. + * + * Returns the post with HTTP 200 if found, or HTTP 404 if no post exists with the given ID. + * + * @param id The unique identifier of the post to retrieve. + * @return A ResponseEntity containing the post if found, or a 404 status if not. + */ @GetMapping("/{id}") fun getPost(@PathVariable id: Long): ResponseEntity { val post = posts[id] @@ -49,7 +65,16 @@ class BoardController { } } - // 게시물 수정 + /** + * Updates an existing post with new data by its ID. + * + * If the post with the specified ID does not exist, returns HTTP 404 (Not Found). + * Otherwise, replaces the post's content and returns the updated post with HTTP 200 (OK). + * + * @param id The ID of the post to update. + * @param updatedPost The new post data to replace the existing post. + * @return The updated post with HTTP 200, or HTTP 404 if the post does not exist. + */ @PutMapping("/{id}") fun updatePost(@PathVariable id: Long, @RequestBody updatedPost: Post): ResponseEntity { if (!posts.containsKey(id)) { @@ -60,7 +85,14 @@ class BoardController { return ResponseEntity.ok(updatedPost) } - // 게시물 삭제 + /** + * Deletes a post by its ID. + * + * Removes the post with the specified ID from the in-memory store. Returns HTTP 204 (No Content) if the post was deleted, or HTTP 404 (Not Found) if the post does not exist. + * + * @param id The unique identifier of the post to delete. + * @return A ResponseEntity with HTTP status 204 if successful, or 404 if the post is not found. + */ @DeleteMapping("/{id}") fun deletePost(@PathVariable id: Long): ResponseEntity { if (!posts.containsKey(id)) { From 07b9e4a2aaaa7e18eccbf6ac088a673d8b9698b1 Mon Sep 17 00:00:00 2001 From: DevCHW Date: Thu, 5 Jun 2025 20:18:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/devchw/coderabbit/BoardController.kt | 9 +- .../kotlin/io/devchw/coderabbit/Comment.kt | 11 +++ .../io/devchw/coderabbit/CommentController.kt | 86 +++++++++++++++++++ src/main/kotlin/io/devchw/coderabbit/Post.kt | 3 +- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/io/devchw/coderabbit/Comment.kt create mode 100644 src/main/kotlin/io/devchw/coderabbit/CommentController.kt diff --git a/src/main/kotlin/io/devchw/coderabbit/BoardController.kt b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt index 6c0aacf..0f89dac 100644 --- a/src/main/kotlin/io/devchw/coderabbit/BoardController.kt +++ b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import java.util.concurrent.atomic.AtomicLong +import org.springframework.beans.factory.annotation.Autowired /** * @author DevCHW @@ -18,7 +19,9 @@ import java.util.concurrent.atomic.AtomicLong */ @RestController @RequestMapping("/api/board") -class BoardController { +class BoardController( + @Autowired private val commentController: CommentController +) { private val posts = mutableMapOf() private val idGenerator = AtomicLong(1) @@ -98,6 +101,10 @@ class BoardController { if (!posts.containsKey(id)) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build() } + + // 게시물과 관련된 모든 댓글 삭제 + commentController.deleteAllCommentsForPost(id) + posts.remove(id) return ResponseEntity.status(HttpStatus.NO_CONTENT).build() } diff --git a/src/main/kotlin/io/devchw/coderabbit/Comment.kt b/src/main/kotlin/io/devchw/coderabbit/Comment.kt new file mode 100644 index 0000000..b05db28 --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/Comment.kt @@ -0,0 +1,11 @@ +package io.devchw.coderabbit + +/** + * @author DevCHW + * @since 2025-06-05 + */ +data class Comment( + var id: Long? = null, + var postId: Long, + var content: String +) diff --git a/src/main/kotlin/io/devchw/coderabbit/CommentController.kt b/src/main/kotlin/io/devchw/coderabbit/CommentController.kt new file mode 100644 index 0000000..5801046 --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/CommentController.kt @@ -0,0 +1,86 @@ +package io.devchw.coderabbit + +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import java.util.concurrent.atomic.AtomicLong + +/** + * @author DevCHW + * @since 2025-06-05 + */ +@RestController +@RequestMapping("/api/board") +class CommentController { + + private val comments = mutableMapOf() + private val idGenerator = AtomicLong(1) + + // 댓글 생성 + @PostMapping("/{postId}/comments") + fun createComment( + @PathVariable postId: Long, + @RequestBody comment: Comment + ): ResponseEntity { + val id = idGenerator.getAndIncrement() + comment.id = id + comment.postId = postId + comments[id] = comment + return ResponseEntity.status(HttpStatus.CREATED).body(comment) + } + + // 게시물의 모든 댓글 조회 + @GetMapping("/{postId}/comments") + fun getCommentsByPost(@PathVariable postId: Long): ResponseEntity> { + val postComments = comments.values.filter { it.postId == postId } + return ResponseEntity.ok(postComments) + } + + // 댓글 상세 조회 + @GetMapping("/comments/{commentId}") + fun getComment(@PathVariable commentId: Long): ResponseEntity { + val comment = comments[commentId] + return if (comment != null) { + ResponseEntity.ok(comment) + } else { + ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + } + + // 댓글 수정 + @PutMapping("/comments/{commentId}") + fun updateComment( + @PathVariable commentId: Long, + @RequestBody updatedComment: Comment + ): ResponseEntity { + val existingComment = comments[commentId] + if (existingComment == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + + // 기존 postId 유지 + updatedComment.id = commentId + updatedComment.postId = existingComment.postId + comments[commentId] = updatedComment + return ResponseEntity.ok(updatedComment) + } + + // 댓글 삭제 + @DeleteMapping("/comments/{commentId}") + fun deleteComment(@PathVariable commentId: Long): ResponseEntity { + if (!comments.containsKey(commentId)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + comments.remove(commentId) + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } + + // 게시물의 모든 댓글 삭제 (게시물이 삭제될 때 호출) + fun deleteAllCommentsForPost(postId: Long) { + val commentIdsToRemove = comments.values + .filter { it.postId == postId } + .mapNotNull { it.id } + + commentIdsToRemove.forEach { comments.remove(it) } + } +} diff --git a/src/main/kotlin/io/devchw/coderabbit/Post.kt b/src/main/kotlin/io/devchw/coderabbit/Post.kt index c3bd945..01a9d14 100644 --- a/src/main/kotlin/io/devchw/coderabbit/Post.kt +++ b/src/main/kotlin/io/devchw/coderabbit/Post.kt @@ -7,5 +7,6 @@ package io.devchw.coderabbit data class Post( var id: Long? = null, var title: String, - var content: String + var content: String, + var comments: MutableList = mutableListOf() )