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..0f89dac --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/BoardController.kt @@ -0,0 +1,111 @@ +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 +import org.springframework.beans.factory.annotation.Autowired + +/** + * @author DevCHW + * @since 2025-06-05 + */ +@RestController +@RequestMapping("/api/board") +class BoardController( + @Autowired private val commentController: CommentController +) { + + 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() + post.id = id + posts[id] = post + 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] + return if (post != null) { + ResponseEntity.ok(post) + } else { + ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + } + + /** + * 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)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + updatedPost.id = id + posts[id] = updatedPost + 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)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build() + } + + // 게시물과 관련된 모든 댓글 삭제 + commentController.deleteAllCommentsForPost(id) + + 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/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 new file mode 100644 index 0000000..01a9d14 --- /dev/null +++ b/src/main/kotlin/io/devchw/coderabbit/Post.kt @@ -0,0 +1,12 @@ +package io.devchw.coderabbit + +/** + * @author DevCHW + * @since 2025-06-05 + */ +data class Post( + var id: Long? = null, + var title: String, + var content: String, + var comments: MutableList = mutableListOf() +)