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/data/devsns_db.mv.db b/data/devsns_db.mv.db new file mode 100644 index 0000000..9220cb8 Binary files /dev/null and b/data/devsns_db.mv.db differ 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/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(); + } +} 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 { + +} 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..d5be05d --- /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.findByPost_Id(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); + } +} +