Files
sb-backend/src/main/java/com/sb/web/board/controller/BoardController.java
T
ByungCheol a4b6c3fd2d
CI / build (push) Failing after 12m6s
fix: 목록 태그 필터도 게시판별로 — /board/tags?category 로 매핑된 그룹 태그만
- 게시판 목록 화면의 태그 필터가 전체 태그를 보여주던 문제(매핑 무시) 수정
- 글쓰기와 동일한 listWritableGroups 기준으로 일관 적용

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:44:24 +09:00

182 lines
7.3 KiB
Java

package com.sb.web.board.controller;
import com.sb.web.auth.dto.SessionUser;
import com.sb.web.auth.web.AuthInterceptor;
import com.sb.web.board.dto.CommentRequest;
import com.sb.web.board.dto.CommentResponse;
import com.sb.web.board.dto.PageResponse;
import com.sb.web.board.dto.PostDetail;
import com.sb.web.board.dto.PostRequest;
import com.sb.web.board.dto.PostSummary;
import com.sb.web.board.dto.Recommender;
import com.sb.web.board.dto.TagCategoryResponse;
import com.sb.web.board.dto.VoteRequest;
import com.sb.web.board.dto.VoteResponse;
import com.sb.web.board.service.BoardService;
import com.sb.web.board.service.TagService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 자유게시판 API. (전체 로그인 필요 — WebConfig 인터셉터로 /api/board/** 보호)
* GET /api/board/posts 목록(페이징·태그·검색)
* GET /api/board/posts/{id} 상세(조회수 증가)
* POST /api/board/posts 작성
* PUT /api/board/posts/{id} 수정(작성자/관리자)
* DELETE /api/board/posts/{id} 삭제(작성자/관리자)
* GET /api/board/tags 전체 태그
* POST /api/board/posts/{id}/comments 댓글 작성
* DELETE /api/board/comments/{commentId} 댓글 삭제(작성자/관리자)
*/
@RestController
@RequestMapping("/api/board")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
private final TagService tagService;
@GetMapping("/posts")
public PageResponse<PostSummary> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String category,
@RequestParam(required = false) String tag,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String searchType) {
return boardService.list(page, size, category, tag, keyword, searchType);
}
@GetMapping("/posts/{id}")
public PostDetail get(
@PathVariable Long id,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return boardService.get(id, current);
}
@PostMapping("/posts")
public ResponseEntity<PostDetail> create(
@Valid @RequestBody PostRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return ResponseEntity.status(HttpStatus.CREATED).body(boardService.create(req, current));
}
@PutMapping("/posts/{id}")
public PostDetail update(
@PathVariable Long id,
@Valid @RequestBody PostRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return boardService.update(id, req, current);
}
@DeleteMapping("/posts/{id}")
public ResponseEntity<Void> delete(
@PathVariable Long id,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.delete(id, current);
return ResponseEntity.noContent().build();
}
/** 열람 제한 (관리자) */
@PostMapping("/posts/{id}/block")
public ResponseEntity<Void> block(
@PathVariable Long id,
@RequestBody(required = false) Map<String, String> body,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.setBlocked(id, true, body != null ? body.get("reason") : null, current);
return ResponseEntity.noContent().build();
}
/** 열람 제한 해제 (관리자) */
@PostMapping("/posts/{id}/unblock")
public ResponseEntity<Void> unblock(
@PathVariable Long id,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.setBlocked(id, false, null, current);
return ResponseEntity.noContent().build();
}
/** 공지 등록 (관리자) — 목록 최상단 고정 */
@PostMapping("/posts/{id}/notice")
public ResponseEntity<Void> notice(
@PathVariable Long id,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.setNotice(id, true, current);
return ResponseEntity.noContent().build();
}
/** 공지 해제 (관리자) */
@PostMapping("/posts/{id}/unnotice")
public ResponseEntity<Void> unnotice(
@PathVariable Long id,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.setNotice(id, false, current);
return ResponseEntity.noContent().build();
}
/** 목록 태그 필터 — 게시판 지정 시 그 게시판에 매핑된 그룹의 태그만 */
@GetMapping("/tags")
public List<String> tags(@RequestParam(required = false) String category) {
if (category == null || category.isBlank()) {
return boardService.allTags();
}
return tagService.listWritableGroups(category).stream()
.flatMap(g -> g.getTags().stream())
.map(com.sb.web.board.dto.TagResponse::getName)
.distinct()
.toList();
}
/** 글 작성 시 선택 가능한 태그 (해당 게시판에 매핑된 그룹만) */
@GetMapping("/tag-groups")
public List<TagCategoryResponse> tagGroups(@RequestParam(required = false) String category) {
return tagService.listWritableGroups(category);
}
@PostMapping("/posts/{id}/comments")
public ResponseEntity<CommentResponse> addComment(
@PathVariable Long id,
@Valid @RequestBody CommentRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return ResponseEntity.status(HttpStatus.CREATED).body(boardService.addComment(id, req, current));
}
@DeleteMapping("/comments/{commentId}")
public ResponseEntity<Void> deleteComment(
@PathVariable Long commentId,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
boardService.deleteComment(commentId, current);
return ResponseEntity.noContent().build();
}
/** 게시글 추천/비추천 (토글) */
@PostMapping("/posts/{id}/vote")
public VoteResponse votePost(
@PathVariable Long id,
@Valid @RequestBody VoteRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return boardService.votePost(id, req.getType(), current);
}
/** 게시글 추천(👍)한 사람 목록 */
@GetMapping("/posts/{id}/recommenders")
public List<Recommender> recommenders(@PathVariable Long id) {
return boardService.recommenders(id);
}
/** 댓글 추천/비추천 (토글) */
@PostMapping("/comments/{commentId}/vote")
public VoteResponse voteComment(
@PathVariable Long commentId,
@Valid @RequestBody VoteRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return boardService.voteComment(commentId, req.getType(), current);
}
}