Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8956b40
Post Entity 생성
sohyun119 Apr 1, 2025
b34c179
게시물 단일 조회 Controller 구현
sohyun119 Apr 1, 2025
8bf46d4
게시물 전체 조회 Controller 구현
sohyun119 Apr 1, 2025
8b78bbc
게시물 생성 Controller 구현
sohyun119 Apr 1, 2025
7959211
게시물 수정 Controller 구현
sohyun119 Apr 1, 2025
b46fe6a
게시물 삭제 Controller 구현
sohyun119 Apr 1, 2025
dd318d6
게시물 삭제시 응답 DTO 구현
sohyun119 Apr 1, 2025
1123d63
게시물 단일 조회 (상세 조회)시 응답 DTO 생성
sohyun119 Apr 1, 2025
244e893
게시물 Request DTO 생성
sohyun119 Apr 1, 2025
be5d471
게시물 Response DTO 생성
sohyun119 Apr 1, 2025
ce2257e
공통 페이지 처리 DTO 생성
sohyun119 Apr 1, 2025
96a7913
게시물 수정시 응답 DTO 생성
sohyun119 Apr 1, 2025
6aa727e
게시물 Repository 구현
sohyun119 Apr 1, 2025
e5a8db2
게시물 단일 조회 Service 구현
sohyun119 Apr 1, 2025
8477f19
게시물 전체 조회 Service 구현
sohyun119 Apr 1, 2025
99cf968
게시물 생성 Service 구현
sohyun119 Apr 1, 2025
c18a4b4
게시물 수정 Service 구현
sohyun119 Apr 1, 2025
25089b8
게시물 삭제 Service 구현
sohyun119 Apr 1, 2025
1066598
CreateResponseDto 분리
sohyun119 Apr 1, 2025
a65cb17
Comment Entity 생성
sohyun119 Apr 1, 2025
65e9446
Comment Entity Builder 추가
sohyun119 Apr 1, 2025
b5c0100
댓글 등록 api 구현
sohyun119 Apr 1, 2025
0ff1e44
댓글 등록 Response DTO 생성
sohyun119 Apr 1, 2025
9eb753e
댓글 등록 Request DTO 생성
sohyun119 Apr 1, 2025
a4e6f58
댓글 수정 api 구현
sohyun119 Apr 1, 2025
cec3e2a
댓글 수정 메서드 구현
sohyun119 Apr 1, 2025
a2f2424
댓글 response dto 생성
sohyun119 Apr 1, 2025
dfe3a61
CommentCreateResponseDto에 어노테이션 추가
sohyun119 Apr 1, 2025
4ed2ce7
댓글 삭제 기능 api 구현
sohyun119 Apr 1, 2025
605896d
공통 컬럼 BaseEntity 에서 공통으로 관리 하는 기능 구현
sohyun119 Apr 1, 2025
2820015
@Getter 어노테이션 추가
sohyun119 Apr 1, 2025
ead5db9
postgresql 의존성 추가
sohyun119 Apr 1, 2025
0d4a32e
@EnableJpaAuditing 어노테이션 추가
sohyun119 Apr 1, 2025
f897028
DB설정 변경
sohyun119 Apr 1, 2025
0621c6e
end point 수정
sohyun119 Apr 1, 2025
284684f
JPA 문법 수정
sohyun119 Apr 1, 2025
dbf0a93
README 생성
sohyun119 Apr 1, 2025
8bdd9df
다른 도메인 repository 에 대한 의존성 제거
sohyun119 Apr 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## TODO List
### 게시글, Post
<b>게시글 작성</b>
- [X] 게시글 제목, 내용은 필수 항목
- [X] 게시글 작성 일자 자동 기록

<b>게시글 수정</b>
- [X] 제목과 내용만 수정 가능
- [X] 게시글 수정 일자 자동 기록

<b>게시글 삭제</b>
- [X] 삭제 시 게시글과 함께 해당 게시글의 모든 댓글도 삭제 처리
- [X] 삭제 후 복구 불가능
- [X] 게시글 삭제 시 삭제 상태에 대한 변경만 처리(soft-delete)

<b>게시글 단건 조회</b>
- [X] 게시글에 포함된 모든 댓글 목록을 조회
- [X] 삭제된 데이터는 조회 불가능

### 댓글, Comment
<b>댓글 등록</b>
- [X] 댓글 내용은 필수 항목
- [X] 댓글 등록 일자 자동 기록

<b>댓글 수정</b>
- [X] 댓글 내용만 수정 가능
- [X] 댓글 수정 일자 자동 기록

<b>댓글 삭제</b>
- [X] 삭제 후 복구 불가능
- [X] 댓글 삭제 시 삭제 상태에 대한 변경만 처리(soft-delete)

### 도전과제
- [X] Pagination 시 요청 Page Size가 10/30/50이 아닌 경우 10으로 고정.

## 구현 시 어렵거나 이해가 되지 않는 부분
- 구현은 어렵지 않게 다 했으나, 이론적인 부분을 더 깊이 공부해야겠다고 생각했습니다.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'

annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'org.postgresql:postgresql'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/sparta/board/BoardApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class BoardApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.sparta.board.comment.controller;

import io.sparta.board.comment.dto.requestDto.CommentCreateRequestDto;
import io.sparta.board.comment.dto.requestDto.CommentUpdateRequestDto;
import io.sparta.board.comment.dto.responseDto.CommentCreateResponseDto;
import io.sparta.board.comment.dto.responseDto.CommentDeleteResponseDto;
import io.sparta.board.comment.dto.responseDto.CommentUpdateResponseDto;
import io.sparta.board.comment.service.CommentService;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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;

@RestController
@RequiredArgsConstructor
@RequestMapping("/comment")
public class CommentController {
private final CommentService commentService;

// 댓글 등록
@PostMapping("/{post_id}")
public ResponseEntity<CommentCreateResponseDto> createComment(
@PathVariable("post_id") UUID postId,
@RequestBody CommentCreateRequestDto requestDto) {
CommentCreateResponseDto responseDto = commentService.createComment(postId, requestDto);
return ResponseEntity.ok(responseDto);
}

// 댓글 수정
@PutMapping("/{comment_id}")
public ResponseEntity<CommentUpdateResponseDto> updateComment(
@PathVariable("comment_id") UUID commentId,
@RequestBody CommentUpdateRequestDto requestDto) {

CommentUpdateResponseDto responseDto = commentService.updateComment(commentId, requestDto);
return ResponseEntity.ok(responseDto);
}

// 댓글 삭제
@DeleteMapping("/{comment_id}")
public ResponseEntity<CommentDeleteResponseDto> deleteComment(@PathVariable("comment_id") UUID commentId){

CommentDeleteResponseDto responseDto = commentService.deleteComment(commentId);
return ResponseEntity.ok(responseDto);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.sparta.board.comment.dto.requestDto;


import java.util.UUID;
import lombok.Getter;

@Getter
public class CommentCreateRequestDto {

private String content;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.sparta.board.comment.dto.requestDto;

import java.util.UUID;
import lombok.Getter;

@Getter
public class CommentUpdateRequestDto {

private String content;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.sparta.board.comment.dto.responseDto;

import io.sparta.board.comment.model.Comment;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CommentCreateResponseDto {

private UUID id;
private UUID postId;
private String comment;
private LocalDateTime createdAt;

public CommentCreateResponseDto(Comment comment) {
this.id = comment.getId();
this.postId = comment.getPost().getId();
this.comment = comment.getContent();
this.createdAt = comment.getCreatedAt();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.sparta.board.comment.dto.responseDto;

import java.util.UUID;
import lombok.Getter;

@Getter
public class CommentDeleteResponseDto {

private final UUID commnetId;
private String message;

public CommentDeleteResponseDto(UUID commentId, String message) {
this.commnetId = commentId;
this.message = message;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.sparta.board.comment.dto.responseDto;

import io.sparta.board.comment.model.Comment;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CommentResponseDto {

private UUID id;
private UUID postId;
private String content;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

public CommentResponseDto(Comment comment) {
this.id = comment.getId();
this.postId = comment.getPost().getId();
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.updatedAt = comment.getUpdatedAt();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.sparta.board.comment.dto.responseDto;

import io.sparta.board.comment.model.Comment;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CommentUpdateResponseDto {

private UUID commentId;
private UUID postId;
private String content;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

public CommentUpdateResponseDto(Comment comment) {
this.commentId = comment.getId();
this.postId = comment.getPost().getId();
this.content = comment.getContent();
this.createdAt = comment.getCreatedAt();
this.updatedAt = comment.getUpdatedAt();
}
}
48 changes: 48 additions & 0 deletions src/main/java/io/sparta/board/comment/model/Comment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.sparta.board.comment.model;

import io.sparta.board.common.BaseEntity;
import io.sparta.board.post.model.Post;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "p_comment")
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Comment extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private Post post;

@Column(length = 512, nullable = false)
private String content;


@Builder
public Comment(Post post, String content) {
this.post = post;
this.content = content;
}

public void updateComment(String content) {
this.content = content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.sparta.board.comment.repository;

import io.sparta.board.comment.model.Comment;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CommentRepository extends JpaRepository<Comment, UUID> {

List<Comment> findAllByPostId(UUID postId);

Page<Comment> findAllByPostIdAndIsDeletedFalse(UUID postId, Pageable pageable);

Optional<Comment> findByIdAndIsDeletedFalse(UUID commentId);
}
79 changes: 79 additions & 0 deletions src/main/java/io/sparta/board/comment/service/CommentService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.sparta.board.comment.service;

import io.sparta.board.comment.dto.requestDto.CommentCreateRequestDto;
import io.sparta.board.comment.dto.requestDto.CommentUpdateRequestDto;
import io.sparta.board.comment.dto.responseDto.CommentCreateResponseDto;
import io.sparta.board.comment.dto.responseDto.CommentDeleteResponseDto;
import io.sparta.board.comment.dto.responseDto.CommentResponseDto;
import io.sparta.board.comment.dto.responseDto.CommentUpdateResponseDto;
import io.sparta.board.comment.model.Comment;
import io.sparta.board.comment.repository.CommentRepository;
import io.sparta.board.common.PageRequestDto;
import io.sparta.board.post.service.PostService;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j(topic = "Comment Service")
public class CommentService {

private final CommentRepository commentRepository;
private final PostService postService;

// 댓글 등록
@Transactional
public CommentCreateResponseDto createComment(UUID postId, CommentCreateRequestDto requestDto) {

Comment comment = Comment.builder()
.content(requestDto.getContent())
.post(postService.existsPost(postId))
.build();

commentRepository.save(comment);
return new CommentCreateResponseDto(comment);
}

// 댓글 수정
@Transactional
public CommentUpdateResponseDto updateComment(UUID commentId, CommentUpdateRequestDto requestDto) {

Comment comment = commentRepository.findByIdAndIsDeletedFalse(commentId)
.orElseThrow(EntityNotFoundException::new);

comment.updateComment(requestDto.getContent());

return new CommentUpdateResponseDto(comment);
}

// 댓글 삭제 (soft delete)
@Transactional
public CommentDeleteResponseDto deleteComment(UUID commentId) {
Comment comment = commentRepository.findByIdAndIsDeletedFalse(commentId)
.orElseThrow(EntityNotFoundException::new);

comment.delete();
return new CommentDeleteResponseDto(commentId, "Successfully deleted comment");
}

// 게시물 내 -> 댓글 전체 조회
public Page<CommentResponseDto> getComments(UUID postId, Integer page, Integer size) {

Page<Comment> comment = commentRepository.findAllByPostIdAndIsDeletedFalse(
postId, new PageRequestDto(page,size).getPageable());

return comment.map(CommentResponseDto::new);
}

// 게시물 삭제시 -> 댓글 함께 삭제
public void deleteAllComments(UUID postId) {
List<Comment> comments = commentRepository.findAllByPostId(postId);
comments.forEach(Comment::delete);
}
}
Loading