오늘은 : admin 페이지 계정 승인 및 삭제 , 게시물 삭제 를 구현해 볼생각이다
1. admin ROLE_ADMIN 인것을 확인한다 그리고 admin 일경우 전체 유저 및 게시물 정보를 보여준다
2. 승인된 유저 승인되지 않은 유저를 나눠 준다
3. 게시물을 따로 보여준다
@GetMapping("/mypage")
public String mypage(Principal principal, Model model) {
// 헌재 로그인한 유저의 인증정보를 가져온다
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = user.getAuthorities(); // roles()말고 authorities()
// 로그인 유저의 role 상태를 체크해서 ADMIN 이면 true 를 반납 anyMatch -> 있는경우 true
boolean isAdmin = authorities.stream()
.anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN")); // "ROLE_ADMIN"으로 체크
Map<Long, Member> boardMembersMap = new HashMap<>(); // 게시물 작성자 정보를 가져오기
Map<Long, List<String>> boardImagesMap = new HashMap<>(); // 게시물 이미지 정보 "," 로 가져오기
// 로그인 한 유저가 admin 인지 체크한다
if (isAdmin) {
String username = principal.getName();
Member member = memberRepository.findByUserId(username)
.orElseThrow(() -> new RuntimeException("해당 유저를 찾을 수 없습니다"));
// 모든 유저들의 정보를 가져오기
List<Member> memberList = memberRepository.findAllByOrderByUserAtDesc();
// 게시물에 대한 댓글 가져오기
Map<Long,List<Comment>> CommentMap = new HashMap<>();
// 댓글 에 대한 유저 정보 가져오기
Map<Long,Member> commentMemberMap = new HashMap<>();
// 댓글에 대한 이미지 정보 가져오기
Map<Long,List<String>> commentImagesMap = new HashMap<>();
// 현재 로그인 중인 유저들의 숫자만 가져오기
Long connectUserCount = memberList.stream()
.filter(memberC -> "connect".equals(memberC.getUserStatus()))
.count();
// 현재 승인 되지 않은 유저들의 수
Long InProgress = memberList.stream()
.filter(memberA -> "In-progress".equals(memberA.getUserAction()))
.count();
// 현재 승인 된 유저들의 수
Long InCompleted = memberList.stream()
.filter(memberA -> "In-completed".equals(memberA.getUserAction()))
.count();
// 모든 유저들의 게시물 가져오기
List<Board> boardList = boardRepository.findAllByOrderByBoardIdxDesc();
for (Board board : boardList) {
Member boardMemberList = memberRepository.findByIdx(board.getBoardUseridx()).orElseThrow(() -> new RuntimeException("해당유저를 찾을수없습니다"));
boardMembersMap.put(board.getBoardIdx(), boardMemberList);
List<String> boardImageList = new ArrayList<>();
if (board.getBoardImages() != null && !board.getBoardImages().isEmpty()) {
boardImageList = Arrays.asList(board.getBoardImages().split(","));
}
boardImagesMap.put(board.getBoardIdx(), boardImageList);
// 게시판에 달린 댓글
List<Comment> commentList = commentRepository.findByCommentBoardidx(board.getBoardIdx());
CommentMap.put(board.getBoardIdx(), commentList);
for (Comment comment : commentList) {
// 댓글에 달린유저 정보
Member commentMember = memberRepository.findByIdx(comment.getCommentUseridx()).orElseThrow(() -> new RuntimeException("해당유저를 찾을수없습니다"));
commentMemberMap.put(comment.getCommentIdx(), commentMember);
// 댓글에 달린 이미지
List<String> commentImageList = new ArrayList<>();
if (comment.getCommentImages() != null && !comment.getCommentImages().isEmpty()) {
commentImageList = Arrays.asList(comment.getCommentImages().split(","));
}
commentImagesMap.put(comment.getCommentIdx(), commentImageList);
}
}
model.addAttribute("memberList", memberList); // 모든 유저의 정보
model.addAttribute("boardList", boardList); // 게시물 정보
model.addAttribute("writers", boardMembersMap); // 게시물 작성자 정보
model.addAttribute("boardImages", boardImagesMap); // 게시물 이미지 정보
model.addAttribute("connectMember", connectUserCount); // 현재 로그인 중인 유저 수
model.addAttribute("InProgress", InProgress); // 현재 유저의 수를 가져온다
model.addAttribute("InCompleted", InCompleted); // 현재 유저의 수를 가져온다
model.addAttribute("commentList",CommentMap); // 댓글 정보
model.addAttribute("commentwirtes",commentMemberMap); // 댓글을 작성한 유저의 정보
model.addAttribute("commentImages",commentImagesMap); // 댓글에 작성된 이미지정보
}
return "mypage";
}
document.querySelectorAll(".approve-btn").forEach(btn => {
btn.addEventListener("click", function () {
const setIdx = this.dataset.idx;
const currentBtn = this; // 버튼 참조 저장
const isConfirmed = confirm("정말 이 유저를 승인하시겠니까?");
if (!isConfirmed) {
return;
}
fetch(`/admin/approveUser`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({userIdx: setIdx}) // userIdx 값을 Controller 보내줌
})
.then(response => response.json()) // 서버 응답을 JSON 으로 받기
.then(data => {
console.log("서버 응답 :", data);
// 버튼 텍스트를 "승인 완료"로 변경
currentBtn.textContent = "승인 완료";
currentBtn.classList.add("un-approve-btn");
// 버튼 비활성화 (원하는 경우)
currentBtn.disabled = true;
// 실시간 승인 유저 업데이트
if (data.InProgressCount !== undefined) {
document.getElementById('InProgress').textContent = data.InProgressCount;
}
if (data.InCompletedCount !== undefined) {
document.getElementById('InCompleted').textContent = data.InCompletedCount;
}
// 기존 In-progress에서 삭제
const memberElement = btn.closest('.In-progress');
if (memberElement) {
memberElement.remove();
}
// In-completed 영역에 새로 추가
const completedContainer = document.querySelector('.In-completed');
if (completedContainer) {
const newMemberDiv = document.createElement('div');
newMemberDiv.className = 'In-completed'; // 스타일 클래스 이름
newMemberDiv.innerHTML = `
<div class="In-completed" th:each="member : ${memberList}"
th:if="${member.userAction == 'In-completed'}">
<a th:href="@{${'/profile/' + member.idx}}">
<div class="user-image">
<img th:src="@{${'/profile-images/' + member.userImage}}" alt="" width="50px"
style="border-radius: 50%">
</div>
</a>
<div class="user-info">
<div class="user-info-inner">
<span class="user-name" th:text="${member.userName}"></span>
<span class="user-id" th:text="${'@'+member.userId}"></span>
<a th:href="@{${'/message/' + member.idx}}">
<img src="/local-images/conversation.png" alt="" width="20px">
</a>
</div>
<div class="user-action">
<button class="un-approve-btn" disabled>승인완료</button>
<button class="delete-btn" th:data-idx="${member.idx}">계정삭제 하기</button>
</div>
</div>
</div>
`;
completedContainer.appendChild(newMemberDiv);
}
});
});
});
-@RequestBody Map<String, Object> requestData -> 클라이언트에서 보는 JSON 데이터를 Map 형태로 받겠다는 의미이다 그래서 받으면 Map 객체로 변환 된다
@PostMapping("/admin/approveUser")
public ResponseEntity<Map<String, Object>> approveUser(@RequestBody Map<String, Object> requestData) {
Map<String, Object> map = new HashMap<>();
Long useridx = Long.valueOf(requestData.get("userIdx").toString());
Member member = memberRepository.findByIdx(useridx).
orElseThrow(() -> new RuntimeException("해당 유저를 찾을수없습니다"));
member.setUserAction("In-completed");
memberRepository.save(member);
// 현재 승인 되지 않은 유저
Long InProgress = memberRepository.countByUserAction("In-progress");
map.put("InProgressCount", InProgress);
// 현재 승인 된 유저 수
Long InCompleted = memberRepository.countByUserAction("In-completed");
map.put("InCompletedCount", InCompleted);
return ResponseEntity.ok(map);
}
전체적인 부부은 앞에서 작성한 main 이랑 같다 아니 거기서 대부분 글을 들고왔다
이미지 슬라이드 부터 댓글 팝업 댓글 이미지 슬라이드 , 해당 유저 클릭시 및 이미지 클릭시 해당 페이지로 이동
<div class="admin-container">
<!-- 상단 정보 -->
<div class="admin-info">
<div>전체 회원 수: <span id="total-users" th:text="${memberList.size()}"></span></div>
<div>전체 게시물 수: <span id="total-posts" th:text="${boardList.size()}"></span></div>
<div>현재 로그인 중인 회원 수: <span id="online-users" th:text="${connectMember}"></span></div>
</div>
<!-- 왼쪽과 오른쪽 div로 나누기 -->
<div class="admin-content">
<!-- 왼쪽: 유저 승인 정보 -->
<div class="user-info" id="user-info">
<div class="unapproved-users">
<h3>
<span>승인되지 않은 유저 :</span>
<span th:text="${InProgress}" class="action-count" id="InProgress"></span>
</h3>
<div class="In-progress" th:each="member : ${memberList}"
th:if="${member.userAction == 'In-progress'}">
<div class="user-image">
<img th:src="@{${'/profile-images/' + member.userImage}}" alt="" width="50px"
style="border-radius: 50%">
</div>
<div class="user-info">
<div class="user-info-inner">
<span class="user-name" th:text="${member.userName}"></span>
<span class="user-id" th:text="${'@'+member.userId}"></span>
</div>
<div class="user-action">
<button class="approve-btn" th:data-idx="${member.idx}">승인하기</button>
<button class="delete-btn" th:data-idx="${member.idx}">계정삭제 하기</button>
</div>
</div>
</div>
</div>
<div class="approved-users">
<h3>
<span>승인된 유저 :</span>
<span th:text="${InCompleted}" id="InCompleted" class="action-count"></span>
</h3>
<!-- 승인된 유저 목록 -->
<div class="In-completed" th:each="member : ${memberList}"
th:if="${member.userAction == 'In-completed'}">
<a th:href="@{${'/profile/' + member.idx}}">
<div class="user-image">
<img th:src="@{${'/profile-images/' + member.userImage}}" alt="" width="50px"
style="border-radius: 50%">
</div>
</a>
<div class="user-info">
<div class="user-info-inner">
<span class="user-name" th:text="${member.userName}"></span>
<span class="user-id" th:text="${'@'+member.userId}"></span>
<a th:href="@{${'/message/' + member.idx}}">
<img src="/local-images/conversation.png" alt="" width="20px">
</a>
</div>
<div class="user-action">
<button class="un-approve-btn" disabled>승인완료</button>
<button class="delete-btn" th:data-idx="${member.idx}">계정삭제 하기</button>
</div>
</div>
</div>
</div>
</div>
<!-- 오른쪽: 게시물 정보 -->
<div class="post-info" id="post-info">
<h3>게시물 정보</h3>
<!-- 게시물 목록 (스크롤 가능) -->
<div class="post-list">
<div class="post" th:each="boards : ${boardList}" style="position: relative">
<!-- 삭제 버튼 -->
<div class="delete-section">
<div class="delete-section-inner" onclick="toggleDeleteForm(this)">
<span class="delete-span">...</span>
</div>
</div>
<!-- 삭제 폼 (처음엔 숨김) -->
<div class="delete-form" th:data-idx="${boards.boardIdx}">
<span>해당 게시물 삭제</span>
</div>
<div class="post-header">
<a th:href="@{'/profile/' + ${writers[boards.boardIdx].idx}}">
<img class="profile-img"
th:src="@{'/profile-images/' + ${writers[boards.boardIdx].userImage}}"
alt="프로필">
</a>
<div class="board-user-info">
<span class="user-name" th:text="${writers[boards.boardIdx].userName}"></span>
<span class="user-id" th:text="${'@' +writers[boards.boardIdx].userId}"></span>
</div>
<span class="time" th:data-time="${boards.boardAt}"></span>
</div>
<a class="boardDetail" th:href="@{${'/board/' + boards.boardIdx}}">
<div class="post-content" th:text="${boards.boardContent}">
</div>
<div class="board-img-list">
<div class="board-img-list-inner">
<button class="board-slide-left">❮</button>
<div class="img-list-inner board-list"
th:each="imgUrl : ${boardImages[boards.boardIdx]}">
<img th:src="@{${'/board-images/' + imgUrl}}" alt="게시물 이미지">
</div>
<button class="board-slide-right">❯</button>
</div>
</div>
</a>
<div class="post-actions">
<span class="toggle-comments">💬 <span th:text="${boards.boardComent}"></span></span>
<span>
<span class="boardLike-btn" th:data-board-idx="${boards.boardIdx}">❤️</span>
<span class="board-like-count" th:text="${boards.boardLike}"></span>
</span>
<span>👀 <span th:text="${boards.boardView}"></span></span>
</div>
<!-- 댓글창 팝업 -->
<div class="comment-section" th:attr="data-board-id=${boards.boardIdx}"
style="border-top: none;">
<!-- 댓글 목록 가져오기 -->
<div th:each="comment : ${commentList[boards.boardIdx]}" class="comment-bar">
<div>
<div class="post-header">
<a th:href="@{${'/profile/' + commentwirtes[comment.commentIdx].idx}}">
<img class="profile-img"
th:src="@{${'/profile-images/' + commentwirtes[comment.commentIdx].userImage}}"
alt="프로필">
</a>
<div class="user-info">
<span class="user-name"
th:text="${commentwirtes[comment.commentIdx].userName}"></span>
<span class="user-id"
th:text="${'@' +commentwirtes[comment.commentIdx].userId}"></span>
</div>
</div>
<div class="comment-main">
<div class="post-content comment-content" th:text="${comment.commentContent}">
</div>
<div class="board-img-list comment-img-list">
<div class="board-img-list-inner comment-img-list-inner">
<button class="board-slide-left">❮</button>
<div class="comment-img-list-inner-container">
<div class="img-list-inner"
th:each="imgUrl : ${commentImages.get(comment.commentIdx)}">
<img th:src="@{${'/comment-images/' + imgUrl}}" alt="게시물 이미지">
</div>
</div>
<button class="board-slide-right">❯</button>
</div>
</div>
<div class="post-actions">
<span class="toggle-comments">💬 <span
th:text="${comment.commentRelay}"></span></span>
<span>
<span class="comment-like-btn" th:data-comment-idx="${comment.commentIdx}">❤️</span>
<span class="comment-like-count" th:text="${comment.commentLike}"></span>
</span>
<span>👀 <span th:text="${comment.commentView}"></span></span>
</div>
</div>
</div>
</div>
<div class="board-detail-src">
<a th:href="@{${'/board/' + boards.boardIdx}}">댓글 더보기</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
!따로 게시물 삭제 및 유저 삭제는 코드로 보여주지 않았다 유저 승인이랑 너무 유사해서..
전체적인 이미지 -