Skip to content

Commit e1fee85

Browse files
committed
1.게시글 작성, 2.게시글 목록 조회(게시판), 3.게시글 조회, 4.게시글에 댓글 작성, 5.댓글에 대댓글 작성, 6.대댓글 조회 기능 초안 구현하여 second commit
1 parent 400d2af commit e1fee85

8 files changed

Lines changed: 437 additions & 35 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.webservice.algorithmchef.controller;
2+
3+
import com.webservice.algorithmchef.dto.board.BoardCommentRequest;
4+
import com.webservice.algorithmchef.dto.board.BoardPostResponse;
5+
import com.webservice.algorithmchef.dto.board.BoardPostListResponse;
6+
import com.webservice.algorithmchef.dto.board.BoardPostRequest;
7+
import com.webservice.algorithmchef.dto.board.CommentReplyListResponse;
8+
import com.webservice.algorithmchef.service.BoardService;
9+
import lombok.RequiredArgsConstructor;
10+
import lombok.extern.slf4j.Slf4j;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
14+
import org.springframework.security.core.userdetails.UserDetails;
15+
import org.springframework.web.bind.annotation.*;
16+
17+
import java.util.Map;
18+
19+
@Slf4j
20+
@RestController
21+
@RequestMapping("/board")
22+
@RequiredArgsConstructor
23+
public class BoardController {
24+
25+
private final BoardService boardService;
26+
27+
/**
28+
* 1. 게시글 목록 조회
29+
* GET /board/posts
30+
*/
31+
@GetMapping("/posts")
32+
public ResponseEntity<BoardPostListResponse> getPostList(
33+
@RequestParam(defaultValue = "0") int page,
34+
@RequestParam(defaultValue = "20") int size,
35+
@RequestParam(defaultValue = "createdAt,desc") String sort,
36+
@RequestParam(required = false) String filter
37+
) {
38+
BoardPostListResponse response = boardService.getPostList(page, size, sort, filter);
39+
return ResponseEntity.ok(response);
40+
}
41+
42+
/**
43+
* 2. 새 게시글 작성
44+
* POST /board/post
45+
*/
46+
@PostMapping("/post")
47+
public ResponseEntity<Map<String, String>> createPost(
48+
@RequestBody BoardPostRequest requestDto,
49+
@AuthenticationPrincipal UserDetails userDetails
50+
) {
51+
if (userDetails == null) {
52+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
53+
}
54+
55+
boardService.createPost(requestDto, userDetails.getUsername());
56+
57+
return ResponseEntity.status(HttpStatus.CREATED)
58+
.body(Map.of("message", "새 게시글 작성완료 되었습니다."));
59+
}
60+
61+
/**
62+
* 3. 게시글 상세 조회
63+
* GET /board/post/{postId}
64+
*/
65+
@GetMapping("/post/{postId}")
66+
public ResponseEntity<BoardPostResponse> getPostDetail(
67+
@PathVariable Long postId,
68+
@RequestParam(defaultValue = "0") int page,
69+
@RequestParam(defaultValue = "20") int size,
70+
@RequestParam(defaultValue = "createdAt,asc") String sort
71+
) {
72+
try {
73+
BoardPostResponse response = boardService.getPostDetail(postId, page, size, sort);
74+
return ResponseEntity.ok(response);
75+
} catch (IllegalArgumentException e) {
76+
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
77+
}
78+
}
79+
80+
/**
81+
* 6. 댓글 작성
82+
* POST /board/post/{postId}/comment
83+
*/
84+
@PostMapping("/post/{postId}/comment")
85+
public ResponseEntity<Map<String, String>> createComment(
86+
@PathVariable Long postId,
87+
@RequestBody BoardCommentRequest requestDto,
88+
@AuthenticationPrincipal UserDetails userDetails
89+
) {
90+
if (userDetails == null) {
91+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
92+
}
93+
94+
try {
95+
boardService.createComment(postId, requestDto, userDetails.getUsername());
96+
return ResponseEntity.status(HttpStatus.CREATED)
97+
.body(Map.of("message", "댓글 작성완료"));
98+
} catch (IllegalArgumentException e) {
99+
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
100+
}
101+
}
102+
103+
/**
104+
* 5. 대댓글 조회
105+
* GET /board/comments/{commentId}/replies
106+
*/
107+
@GetMapping("/comments/{commentId}/replies")
108+
public ResponseEntity<CommentReplyListResponse> getReplies(
109+
@PathVariable Long commentId,
110+
@RequestParam(defaultValue = "0") int page,
111+
@RequestParam(defaultValue = "20") int size,
112+
@RequestParam(defaultValue = "createdAt,asc") String sort
113+
) {
114+
try {
115+
CommentReplyListResponse response = boardService.getReplies(commentId, page, size, sort);
116+
return ResponseEntity.ok(response);
117+
} catch (IllegalArgumentException e) {
118+
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
119+
}
120+
}
121+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.webservice.algorithmchef.dto.board;
2+
3+
import com.webservice.algorithmchef.model.BoardPost;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.time.format.DateTimeFormatter;
10+
import java.util.List;
11+
12+
@Getter
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
@Builder
16+
public class BoardPostListResponse {
17+
// 목록 조회 시 반환할 래퍼 클래스
18+
private List<BoardPostSimple> posts;
19+
private PageInfo pageInfo;
20+
21+
@Getter
22+
@Builder
23+
public static class BoardPostSimple {
24+
private String title;
25+
private String userId;
26+
private String createdAt;
27+
private String category;
28+
private String content; // 목록 미리보기용
29+
30+
public static BoardPostSimple from(BoardPost post) {
31+
return BoardPostSimple.builder()
32+
.title(post.getTitle())
33+
// User 엔티티의 userId (로그인 아이디) 사용
34+
.userId(post.getUser().getUserId())
35+
// 날짜 포맷팅 (yyyy-MM-dd)
36+
.createdAt(post.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
37+
.category(post.getCategory())
38+
.content(post.getContent())
39+
.build();
40+
}
41+
}
42+
}
Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.webservice.algorithmchef.dto.board;
22

3+
import com.webservice.algorithmchef.model.BoardComment;
34
import com.webservice.algorithmchef.model.BoardPost;
45
import lombok.AllArgsConstructor;
56
import lombok.Builder;
@@ -14,31 +15,53 @@
1415
@AllArgsConstructor
1516
@Builder
1617
public class BoardPostResponse {
17-
1818
private Long postId;
19-
private Long userId; // 작성자 식별자
20-
private String writerName; // 작성자 닉네임/이름 (User 테이블 조인 필요 시)
19+
private String userId;
2120
private String category;
2221
private String title;
23-
private String content; // BLOB -> String 변환된 내용
22+
private String content;
2423
private LocalDateTime createdAt;
25-
private LocalDateTime modifiedDate;
24+
private List<CommentSimple> comments;
25+
private PageInfo pageInfo;
26+
27+
@Getter
28+
@Builder
29+
public static class CommentSimple {
30+
private Long commentId;
31+
private String userId;
32+
private String content;
33+
private LocalDateTime createdAt;
34+
private boolean isDeleted;
35+
private int replyCount;
2636

27-
// 해당 게시글에 달린 댓글 목록 (선택사항: 상세 조회 시 같이 내려줄 경우 필요)
28-
private List<BoardCommentResponse> comments;
37+
public static CommentSimple from(BoardComment comment) {
38+
// 삭제된 댓글 처리
39+
String displayContent = comment.isDeleted() ? "삭제된 댓글입니다." : comment.getContent();
40+
// 유저 처리 (삭제된 유저일 경우 로직 추가 가능, 여기선 기본 처리)
41+
String displayUserId = comment.getUser() != null ? comment.getUser().getUserId() : "(삭제된 사용자)";
42+
43+
return CommentSimple.builder()
44+
.commentId(comment.getCommentId())
45+
.userId(displayUserId)
46+
.content(displayContent)
47+
.createdAt(comment.getCreatedAt())
48+
.isDeleted(comment.isDeleted())
49+
// 대댓글 개수 (Children 리스트 사이즈)
50+
.replyCount(comment.getChildren().size())
51+
.build();
52+
}
53+
}
2954

30-
// Entity -> DTO 변환 편의 메서드
31-
public static BoardPostResponse from(BoardPost post, String writerName, List<BoardCommentResponse> comments) {
55+
public static BoardPostResponse of(BoardPost post, List<CommentSimple> comments, PageInfo pageInfo) {
3256
return BoardPostResponse.builder()
3357
.postId(post.getPostId())
34-
.userId(post.getUser().getId()) // User 객체에서 ID 추출 가정
35-
.writerName(writerName)
58+
.userId(post.getUser().getUserId())
3659
.category(post.getCategory())
3760
.title(post.getTitle())
38-
.content(post.getContent()) // post.getContent()가 String을 반환한다고 가정
61+
.content(post.getContent())
3962
.createdAt(post.getCreatedAt())
40-
.modifiedDate(post.getModifiedDate())
4163
.comments(comments)
64+
.pageInfo(pageInfo)
4265
.build();
4366
}
4467
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.webservice.algorithmchef.dto.board;
2+
3+
import com.webservice.algorithmchef.dto.board.BoardPostResponse.CommentSimple;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.util.List;
10+
11+
@Getter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Builder
15+
public class CommentReplyListResponse {
16+
private List<CommentSimple> replies;
17+
private PageInfo pageInfo;
18+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.webservice.algorithmchef.dto.board;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import org.springframework.data.domain.Page;
8+
9+
@Getter
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Builder
13+
public class PageInfo {
14+
private int page;
15+
private int size;
16+
private long totalElements;
17+
private int totalPages;
18+
19+
public static PageInfo from(Page<?> page) {
20+
return PageInfo.builder()
21+
.page(page.getNumber())
22+
.size(page.getSize())
23+
.totalElements(page.getTotalElements())
24+
.totalPages(page.getTotalPages())
25+
.build();
26+
}
27+
}

src/main/java/com/webservice/algorithmchef/repository/BoardCommentRepository.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.webservice.algorithmchef.repository;
22

33
import com.webservice.algorithmchef.model.BoardComment;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
46
import org.springframework.data.jpa.repository.JpaRepository;
57
import org.springframework.data.jpa.repository.Query;
68
import org.springframework.data.repository.query.Param;
@@ -22,15 +24,25 @@ public interface BoardCommentRepository extends JpaRepository<BoardComment, Long
2224
List<BoardComment> findByPost_PostIdOrderByCreatedAtAsc(Long postId);
2325

2426
/**
25-
* (선택 사항) 특정 게시글의 '최상위 댓글(부모가 없는 댓글)'만 조회합니다.
26-
* 대댓글을 지연 로딩(Lazy Loading)으로 가져오거나 페이징이 필요할 때 사용합니다.
27+
* 특정 게시글의 '최상위 댓글(부모가 없는 댓글)'만 페이징 조회합니다.
28+
* 게시글 상세 조회 시 댓글 목록 페이징에 사용됩니다.
2729
*/
28-
List<BoardComment> findByPost_PostIdAndParentCommentIsNullOrderByCreatedAtAsc(Long postId);
30+
Page<BoardComment> findByPost_PostIdAndParentCommentIsNull(Long postId, Pageable pageable);
2931

3032
/**
3133
* (성능 최적화용) 게시글의 댓글을 가져올 때 작성자(User) 정보까지 한 번에 가져옵니다 (Fetch Join).
3234
* N+1 문제를 방지하여 조회 성능을 높입니다.
3335
*/
3436
@Query("SELECT c FROM BoardComment c JOIN FETCH c.user WHERE c.post.postId = :postId ORDER BY c.createdAt ASC")
3537
List<BoardComment> findCommentsByPostIdWithUser(@Param("postId") Long postId);
38+
39+
/**
40+
* 특정 부모 댓글의 자식 댓글(대댓글)을 페이징하여 조회합니다.
41+
* 대댓글 더보기 기능 등에 사용됩니다.
42+
* * @param parentCommentId 부모 댓글 ID (CommentId)
43+
*
44+
* @param pageable 페이징 정보
45+
* @return 대댓글 Page 객체
46+
*/
47+
Page<BoardComment> findByParentComment_CommentId(Long parentCommentId, Pageable pageable);
3648
}
Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
11
package com.webservice.algorithmchef.repository;
22

33
import com.webservice.algorithmchef.model.BoardPost;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
46
import org.springframework.data.jpa.repository.JpaRepository;
57
import org.springframework.stereotype.Repository;
68

7-
import java.util.List;
8-
99
@Repository
1010
public interface BoardPostRepository extends JpaRepository<BoardPost, Long> {
11+
// 카테고리 필터링 + 페이징
12+
Page<BoardPost> findByCategory(String category, Pageable pageable);
1113

12-
/**
13-
* 게시글 전체 목록을 작성일 기준 내림차순(최신순)으로 조회합니다.
14-
* 사용처: 게시판 메인 화면
15-
*/
16-
List<BoardPost> findAllByOrderByCreatedAtDesc();
17-
18-
/**
19-
* 특정 카테고리의 게시글 목록을 작성일 기준 내림차순으로 조회합니다.
20-
* 사용처: 카테고리별 필터링 (나눔, 질문 등)
21-
*/
22-
List<BoardPost> findByCategoryOrderByCreatedAtDesc(String category);
23-
24-
/**
25-
* 특정 사용자가 작성한 게시글 목록을 조회합니다.
26-
* 사용처: 마이페이지 - 내가 쓴 글
27-
*/
28-
List<BoardPost> findByUser_IdOrderByCreatedAtDesc(Long userId);
14+
// 전체 페이징 (필터 없을 때) - findAll(Pageable)은 JpaRepository에 기본 내장됨
2915
}

0 commit comments

Comments
 (0)