feat: 게시판 공지 기능 (관리자) — 목록 최상단 고정
CI / build (push) Failing after 14m19s

- post.notice 컬럼 추가(멱등 ALTER)
- 목록 정렬 ORDER BY notice DESC, id DESC (공지 최상단)
- 작성 시 공지 설정(관리자만 반영) + /posts/{id}/notice·unnotice 토글
- Post/PostSummary/PostDetail/PostRequest 에 notice 반영

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-27 22:06:48 +09:00
parent b48d8c667c
commit 8d8d26ec3a
9 changed files with 50 additions and 5 deletions
@@ -99,6 +99,24 @@ public class BoardController {
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() {
return boardService.allTags();
@@ -25,6 +25,7 @@ public class Post implements Serializable {
private Integer viewCount;
private Boolean blocked;
private String blockReason;
private Boolean notice; // 공지(상단 고정) — 관리자만 설정
private String category; // 게시판 구분: community/saving/tips
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -22,6 +22,7 @@ public class PostDetail {
private Integer viewCount;
private Boolean blocked;
private String blockReason;
private Boolean notice;
private String category;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -38,6 +39,7 @@ public class PostDetail {
.viewCount(p.getViewCount())
.blocked(Boolean.TRUE.equals(p.getBlocked()))
.blockReason(p.getBlockReason())
.notice(Boolean.TRUE.equals(p.getNotice()))
.category(p.getCategory())
.createdAt(p.getCreatedAt())
.updatedAt(p.getUpdatedAt())
@@ -54,6 +56,7 @@ public class PostDetail {
.authorName(p.getAuthorName())
.viewCount(p.getViewCount())
.blocked(true)
.notice(Boolean.TRUE.equals(p.getNotice()))
.category(p.getCategory())
.createdAt(p.getCreatedAt())
.tags(List.of())
@@ -22,6 +22,9 @@ public class PostRequest {
/** 게시판 구분: community/saving/tips. 없으면 community */
private String category;
/** 공지 등록 여부 (관리자만 반영). 작성 시 선택 */
private Boolean notice;
/** 선택한 태그 ID 목록 (DB에 등록된 태그만, 선택) */
private List<Long> tagIds;
}
@@ -17,6 +17,7 @@ public class PostSummary {
private Integer viewCount;
private Integer commentCount;
private Boolean blocked;
private Boolean notice;
private LocalDateTime createdAt;
private List<String> tags;
}
@@ -35,4 +35,7 @@ public interface PostMapper {
int updateBlocked(@Param("id") Long id,
@Param("blocked") boolean blocked,
@Param("blockReason") String blockReason);
int updateNotice(@Param("id") Long id,
@Param("notice") boolean notice);
}
@@ -91,6 +91,14 @@ public class BoardService {
postMapper.updateBlocked(id, blocked, blocked ? reason : null);
}
/** 게시글 공지 설정/해제 (관리자 전용) — 목록 최상단 고정 */
@Transactional
public void setNotice(Long id, boolean notice, SessionUser user) {
assertAdmin(user);
mustFindPost(id);
postMapper.updateNotice(id, notice);
}
/** Redis 로 (사용자, 게시글) 단위 첫 열람 여부 판단 → 중복 클릭 시 조회수 증가 방지 */
private boolean shouldCountView(Long postId, SessionUser viewer) {
if (viewer == null) {
@@ -108,6 +116,8 @@ public class BoardService {
.content(req.getContent())
.authorId(author.getId())
.authorName(author.getName())
// 공지는 관리자만 설정 가능
.notice(isAdmin(author) && Boolean.TRUE.equals(req.getNotice()))
.category(toCategory(req.getCategory()))
.build();
postMapper.insert(post);