From 0953e85d8904b6c68bdd11d76156628671b33c63 Mon Sep 17 00:00:00 2001 From: Seogyeong Yun Date: Sun, 2 Nov 2025 18:47:51 +0900 Subject: [PATCH 1/4] feat: add Post and Comment entities --- build.gradle | 9 ++++ .../com/example/devSns/entity/Comment.java | 43 ++++++++++++++++ .../java/com/example/devSns/entity/Post.java | 49 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/main/java/com/example/devSns/entity/Comment.java create mode 100644 src/main/java/com/example/devSns/entity/Post.java diff --git a/build.gradle b/build.gradle index 610d6a6..033c4ee 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,16 @@ repositories { } dependencies { + // 기본 웹 라이브러리 implementation 'org.springframework.boot:spring-boot-starter-web' + // 데이터베이스 연동(JPA) 라이브러리 + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // 테스트용 H2 인메모리 데이터베이스 + runtimeOnly 'com.h2database:h2' + // Lombok 라이브러리 (코드 자동 생성) + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + // 기본 테스트 라이브러리 testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } diff --git a/src/main/java/com/example/devSns/entity/Comment.java b/src/main/java/com/example/devSns/entity/Comment.java new file mode 100644 index 0000000..f07b3be --- /dev/null +++ b/src/main/java/com/example/devSns/entity/Comment.java @@ -0,0 +1,43 @@ +package com.example.devSns.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "comments") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) // auto-incremented + private Long id; + + @Column(nullable = false) + private String content; + + @Column(nullable = false) + private String writer; + + @ManyToOne(fetch = FetchType.LAZY) // 댓글 조회 시 Post는 필요할 때만 불러옴 + @JoinColumn(name = "post_id", nullable = false) + private Post post; + + @Column(nullable = false, updatable = false) + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + @PrePersist + public void prePersist() { + createdAt = LocalDateTime.now(); + } + + @PreUpdate + public void preUpdate() { + updatedAt = LocalDateTime.now(); + } +} diff --git a/src/main/java/com/example/devSns/entity/Post.java b/src/main/java/com/example/devSns/entity/Post.java new file mode 100644 index 0000000..bd058ca --- /dev/null +++ b/src/main/java/com/example/devSns/entity/Post.java @@ -0,0 +1,49 @@ +package com.example.devSns.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "posts") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Post { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String title; + + @Column(nullable = false, length = 1000) + private String content; + + @Column(nullable = false) + private String writer; + + private int likeCount = 0; + + @Column(nullable = false, updatable = false) + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments = new ArrayList<>(); + + @PrePersist + public void prePersist() { + createdAt = LocalDateTime.now(); + } + + @PreUpdate + public void preUpdate() { + updatedAt = LocalDateTime.now(); + } +} From 825b9e0d8dcae08da57768e6fcb45d7013adbb61 Mon Sep 17 00:00:00 2001 From: Seogyeong Yun Date: Tue, 4 Nov 2025 22:35:13 +0900 Subject: [PATCH 2/4] feat: add PostRepository and CommentRepository interfaces --- .../example/devSns/repository/CommentRepository.java | 12 ++++++++++++ .../example/devSns/repository/PostRepository.java | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/com/example/devSns/repository/CommentRepository.java create mode 100644 src/main/java/com/example/devSns/repository/PostRepository.java diff --git a/src/main/java/com/example/devSns/repository/CommentRepository.java b/src/main/java/com/example/devSns/repository/CommentRepository.java new file mode 100644 index 0000000..22271d9 --- /dev/null +++ b/src/main/java/com/example/devSns/repository/CommentRepository.java @@ -0,0 +1,12 @@ +package com.example.devSns.repository; + +import com.example.devSns.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository +public interface CommentRepository extends JpaRepository { + // Comment에서 연관된 Post 객체의 id 기준 + List findByPost_Id(Long postId); +} diff --git a/src/main/java/com/example/devSns/repository/PostRepository.java b/src/main/java/com/example/devSns/repository/PostRepository.java new file mode 100644 index 0000000..a648eaf --- /dev/null +++ b/src/main/java/com/example/devSns/repository/PostRepository.java @@ -0,0 +1,10 @@ +package com.example.devSns.repository; + +import com.example.devSns.entity.Post; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PostRepository extends JpaRepository { + +} From 3d0fa47875975c678d35727a3bc6b7ba77f5c214 Mon Sep 17 00:00:00 2001 From: Seogyeong Yun Date: Thu, 6 Nov 2025 22:43:02 +0900 Subject: [PATCH 3/4] feat: implement basic CRUD for posts and comments --- data/devsns_db.mv.db | Bin 0 -> 32768 bytes .../devSns/controller/CommentController.java | 39 +++++++++++++ .../devSns/controller/PostController.java | 51 ++++++++++++++++ .../devSns/service/CommentService.java | 55 ++++++++++++++++++ .../example/devSns/service/PostService.java | 52 +++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 data/devsns_db.mv.db create mode 100644 src/main/java/com/example/devSns/controller/CommentController.java create mode 100644 src/main/java/com/example/devSns/controller/PostController.java create mode 100644 src/main/java/com/example/devSns/service/CommentService.java create mode 100644 src/main/java/com/example/devSns/service/PostService.java diff --git a/data/devsns_db.mv.db b/data/devsns_db.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..9220cb8f05a1b8871ad132da50dbbb792f135851 GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/src/main/java/com/example/devSns/controller/CommentController.java b/src/main/java/com/example/devSns/controller/CommentController.java new file mode 100644 index 0000000..06f2062 --- /dev/null +++ b/src/main/java/com/example/devSns/controller/CommentController.java @@ -0,0 +1,39 @@ +package com.example.devSns.controller; + +import com.example.devSns.entity.Comment; +import com.example.devSns.service.CommentService; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/posts/{postId}/comments") +public class CommentController { + + private final CommentService commentService; + + public CommentController(CommentService commentService) { + this.commentService = commentService; + } + + @PostMapping + public Comment createComment(@PathVariable Long postId, @RequestBody Comment comment) { + return commentService.createComment(postId, comment); + } + + @GetMapping + public List getCommentsByPostId(@PathVariable Long postId) { + return commentService.getCommentsByPostId(postId); + } + + @PutMapping("/{id}") + public Comment updateComment(@PathVariable Long id, @RequestBody Comment comment) { + return commentService.updateComment(id, comment); + } + + @DeleteMapping("/{id}") + public void deleteComment(@PathVariable Long id) { + commentService.deleteComment(id); + } +} + diff --git a/src/main/java/com/example/devSns/controller/PostController.java b/src/main/java/com/example/devSns/controller/PostController.java new file mode 100644 index 0000000..b9d8030 --- /dev/null +++ b/src/main/java/com/example/devSns/controller/PostController.java @@ -0,0 +1,51 @@ +package com.example.devSns.controller; + +import com.example.devSns.entity.Post; +import com.example.devSns.service.PostService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/posts") +public class PostController { + private final PostService postService; + + public PostController(PostService postService) { + this.postService = postService; + } + + @PostMapping + public ResponseEntity createPost(@RequestBody Post post) { + Post createdPost = postService.createPost(post); + return ResponseEntity.status(HttpStatus.CREATED).body(createdPost); + } + + @GetMapping + public ResponseEntity> getAllPosts() { + List posts = postService.getAllPosts(); + return ResponseEntity.ok(posts); + } + + @GetMapping("/{id}") + public ResponseEntity getPostById(@PathVariable Long id) { + Post post = postService.getPostById(id); + return ResponseEntity.ok(post); + } + + @PutMapping("/{id}") + public ResponseEntity updatePost( + @PathVariable Long id, + @RequestBody Post updatedPost) { + Post post = postService.updatePost(id, updatedPost); + return ResponseEntity.ok(post); + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePost(@PathVariable Long id) { + postService.deletePost(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/example/devSns/service/CommentService.java b/src/main/java/com/example/devSns/service/CommentService.java new file mode 100644 index 0000000..318ad69 --- /dev/null +++ b/src/main/java/com/example/devSns/service/CommentService.java @@ -0,0 +1,55 @@ +package com.example.devSns.service; + +import com.example.devSns.entity.Comment; +import com.example.devSns.entity.Post; +import com.example.devSns.repository.CommentRepository; +import com.example.devSns.repository.PostRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class CommentService { + + private final CommentRepository commentRepository; + private final PostRepository postRepository; + + public CommentService(CommentRepository commentRepository, + PostRepository postRepository) { + this.commentRepository = commentRepository; + this.postRepository = postRepository; + } + + public Comment createComment(Long postId, Comment comment) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new RuntimeException("Post not found with id: " + postId)); + comment.setPost(post); + return commentRepository.save(comment); + } + + @Transactional(readOnly = true) + public List getCommentsByPostId(Long postId) { + return commentRepository.findByPostId(postId); + } + + public Comment updateComment(Long id, Comment updatedComment) { + Comment foundComment = commentRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Comment not found with id: " + id)); + + foundComment.setContent(updatedComment.getContent()); + foundComment.setWriter(updatedComment.getWriter()); + + return commentRepository.save(foundComment); + } + + public void deleteComment(Long id) { + if (!commentRepository.existsById(id)) { + throw new RuntimeException("Comment not found with id: " + id); + } + commentRepository.deleteById(id); + } +} + + diff --git a/src/main/java/com/example/devSns/service/PostService.java b/src/main/java/com/example/devSns/service/PostService.java new file mode 100644 index 0000000..0d1297e --- /dev/null +++ b/src/main/java/com/example/devSns/service/PostService.java @@ -0,0 +1,52 @@ +package com.example.devSns.service; + +import com.example.devSns.entity.Post; +import com.example.devSns.repository.PostRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class PostService { + private final PostRepository postRepository; + + public PostService(PostRepository postRepository) { + this.postRepository = postRepository; + } + + public Post createPost(Post post) { + return postRepository.save(post); + } + + @Transactional(readOnly = true) + public List getAllPosts() { + return postRepository.findAll(); + } + + @Transactional(readOnly = true) + public Post getPostById(Long id) { + return postRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Post not found with id: " + id)); + } + + public Post updatePost(Long id, Post updatedPost) { + Post foundPost = postRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Post not found with id: " + id)); + + foundPost.setTitle(updatedPost.getTitle()); + foundPost.setContent(updatedPost.getContent()); + foundPost.setWriter(updatedPost.getWriter()); + + return postRepository.save(foundPost); + } + + public void deletePost(Long id) { + if (!postRepository.existsById(id)) { + throw new RuntimeException("Post not found with id: " + id); + } + postRepository.deleteById(id); + } +} + From 27419c13da16eadd2341a8e6babec9aa242b0869 Mon Sep 17 00:00:00 2001 From: Seogyeong Yun Date: Thu, 6 Nov 2025 23:00:24 +0900 Subject: [PATCH 4/4] fix: update CommentService to use findByPost_Id --- src/main/java/com/example/devSns/service/CommentService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/devSns/service/CommentService.java b/src/main/java/com/example/devSns/service/CommentService.java index 318ad69..d5be05d 100644 --- a/src/main/java/com/example/devSns/service/CommentService.java +++ b/src/main/java/com/example/devSns/service/CommentService.java @@ -31,7 +31,7 @@ public Comment createComment(Long postId, Comment comment) { @Transactional(readOnly = true) public List getCommentsByPostId(Long postId) { - return commentRepository.findByPostId(postId); + return commentRepository.findByPost_Id(postId); } public Comment updateComment(Long id, Comment updatedComment) {