반응형
권한을 안넣어서 약간 이빨빠진채로 만들어둔 유저와 게시판카테고리를 통합시켜 보자..
1. 페이지 작성
role_management.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>권한 관리화면</title>
<sec:csrfMetaTags />
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<header>
<nav>
<div class="menu">
<div class="menu-item"><a href="${pageContext.request.contextPath}/">홈화면으로</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/userList">사용자관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/roleList">권한관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/boardList">게시판화면</a></div>
</div>
</nav>
</header>
<main>
<nav>
<ul>
<li><a href="${pageContext.request.contextPath}/admin/roleList" id="roleList">권한 목록</a></li>
<li><a href="${pageContext.request.contextPath}/admin/addRoleForm" id="addRole">권한 등록</a></li>
</ul>
</nav>
<section id="content">
<h2>권한 목록</h2>
<sec:csrfInput />
<!-- 권한 목록을 테이블로 표시합니다. -->
<table border="1">
<thead>
<tr>
<th>권한 ID</th>
<th>이름</th>
<th>관리</th>
</tr>
</thead>
<tbody>
<c:forEach var="role" items="${roles}">
<tr>
<td>${role.roleId}</td>
<td>${role.roleName}</td>
<td><a href="/admin/modifyRoleForm?roleId=${role.roleId}">수정</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</section>
</main>
<script>
</script>
</body>
</html>
role_modify_form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>권한 관리화면</title>
<sec:csrfMetaTags />
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<header>
<nav>
<div class="menu">
<div class="menu-item"><a href="${pageContext.request.contextPath}/">홈화면으로</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/userList">사용자관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/roleList">권한관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/boardList">게시판화면</a></div>
</div>
</nav>
</header>
<main>
<nav>
<ul>
<li><a href="${pageContext.request.contextPath}/admin/roleList" id="roleList">권한 목록</a></li>
<li><a href="${pageContext.request.contextPath}/admin/addRoleForm" id="addRole">권한 등록</a></li>
</ul>
</nav>
<section id="content">
<form action="/admin/modifyRole" method="post">
<sec:csrfInput />
<div>
<input type="hidden" id="roleId" name="roleId" value="${role.roleId}">
<label for="roleName">이름:</label>
<input type="text" id="roleName" name="roleName" value="${role.roleName}" required> ROLE_XXXX 형태로 생성해야함
</div>
<div>
<label for="description">설명:</label>
<input type="text" id="description" name="description" value="${role.description}" required>
</div>
<br>
<hr>
<h3> 유저 권한 추가 </h3>
<div class="container" id="containerUser">
<div id="contentLeftUser" class="content-div">
<c:forEach var="user" items="${userList}">
<c:if test="${user.roleId != role.roleId}">
<div><label for="${user.username}"><input type="checkbox" id="${user.username}" name="contentLeftUser" value="${user.username}">${user.name}</label></div>
</c:if>
</c:forEach>
</div>
<div class="arrow-buttons">
<button id="leftArrowUser">◁</button>
<button id="rightArrowUser">▷</button>
<button id="upArrowUser">△</button>
<button id="downArrowUser">▽</button>
</div>
<div id="contentRightUser" class="content-div">
<c:forEach var="user" items="${userList}">
<c:if test="${user.roleId == role.roleId}">
<div><label for="${user.username}"><input type="checkbox" id="${user.username}" name="contentRightUser" value="${user.username}">${user.name}</label></div>
</c:if>
</c:forEach>
</div>
</div>
<hr>
<h3> 게시판 권한 </h3>
<div class="container" id="containerCategory">
<div id="contentLeftCategory" class="content-div">
<c:forEach var="category" items="${categoryList}">
<c:if test="${category.roleId != role.roleId}">
<div><label for="${category.categoryId}"><input type="checkbox" id="${category.categoryId}" name="contentLeftCategory" value="${category.categoryId}">${category.categoryName}</label></div>
</c:if>
</c:forEach>
</div>
<div class="arrow-buttons">
<button id="leftArrowCategory">◁</button>
<button id="rightArrowCategory">▷</button>
<button id="upArrowCategory">△</button>
<button id="downArrowCategory">▽</button>
</div>
<div id="contentRightCategory" class="content-div">
<c:forEach var="category" items="${categoryList}">
<c:if test="${category.roleId == role.roleId}">
<div><label for="${category.categoryId}"><input type="checkbox" id="${category.categoryId}" name="contentRightCategory" value="${category.categoryId}">${category.categoryName}</label></div>
</c:if>
</c:forEach>
</div>
</div>
<div>
<button type="submit">등록</button>
</div>
</form>
<button id="deleteRole" value="${role.roleId}">권한 삭제</button>
</section>
</main>
<script>
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': '${_csrf.token}'
}
});
const textInputUser = document.getElementById("textInputUser");
const searchButtonUser = document.getElementById("searchButtonUser");
const leftArrowUser = document.getElementById("leftArrowUser");
const rightArrowUser = document.getElementById("rightArrowUser");
const upArrowUser = document.getElementById("upArrowUser");
const downArrowUser = document.getElementById("downArrowUser");
const textInputCategory = document.getElementById("textInputCategory");
const searchButtonCategory = document.getElementById("searchButtonCategory");
const leftArrowCategory = document.getElementById("leftArrowCategory");
const rightArrowCategory = document.getElementById("rightArrowCategory");
const upArroCategory = document.getElementById("upArrowCategory");
const downArrowCategory = document.getElementById("downArrowCategory");
$(document).ready(function(){
$('#addRole').click(function() {
$.ajax({
url: '/admin/addRoleForm',
method: 'GET',
success: function(data) {
$('#content').html(data);
}
});
});
$('#deleteRole').click(function(){
let roleId = $(this).val();
if(confirm('삭제 하시겠습니까?')){
$.ajax({
url: '/admin/deleteRole',
method: 'POST',
data: { roleId, roleId },
success: function(data){
window.location.href = '/admin/roleList'
}, error: function(err){
console.log(err);
}
});
} else {
console.log("취소");
}
});
leftArrowUser.addEventListener("click", function() {
event.preventDefault();
let checkboxes = document.querySelectorAll('#containerUser input[type="checkbox"]:checked');
let targetContentDiv = document.getElementById("contentLeftUser");
checkboxes.forEach(checkbox => {
let targetDiv = checkbox.parentElement.parentElement;
targetContentDiv.appendChild(targetDiv);
checkbox.name = "contentLeftUser";
});
});
//check된 항목들을 찾아서 targetDiv로 이동시킴.
rightArrowUser.addEventListener("click", function() {
event.preventDefault();
let checkboxes = document.querySelectorAll('#containerUser input[type="checkbox"]:checked');
let targetContentDiv = document.getElementById("contentRightUser");
checkboxes.forEach(checkbox => {
let targetDiv = checkbox.parentElement.parentElement;
targetContentDiv.appendChild(targetDiv);
checkbox.name = "contentRightUser";
});
});
upArrowUser.addEventListener("click", function() {
event.preventDefault();
let container_l = document.getElementById('contentLeftUser');
let checkedDivs_l = [];
let leftflag = false;
Array.from(container_l.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_l.push(div);
leftflag = true;
}
});
if(leftflag){
checkedDivs_l.forEach(div =>{
let curIndex = Array.from(container_l.children).indexOf(div);
if(curIndex > 0){
container_l.insertBefore(div, container_l.children[curIndex - 1]);
}
})
}
//우측 div 처리
let container_r = document.getElementById('contentRightUser');
let checkedDivs_r = [];
let rightflag = false;
Array.from(container_r.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_r.push(div);
rightflag = true;
}
});
if(rightflag){
checkedDivs_r.forEach(div =>{
let curIndex = Array.from(container_r.children).indexOf(div);
if(curIndex > 0){
container_r.insertBefore(div, container_r.children[curIndex - 1]);
}
})
}
});
downArrowUser.addEventListener("click", function() {
event.preventDefault();
let container_l = document.getElementById('contentLeftUser');
let checkedDivs_l = [];
let leftflag = false;
Array.from(container_l.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_l.push(div);
leftflag = true;
}
});
if(leftflag){
//index 로 계산해서 순서 변경하기위해 insertBefore 를 사용하였는데, reverse 를 하지않으면 위에서부터 계산해서 내려옴.
//위 객체부터 index +2 를 하다보면, 그 아래객체에 index+2 를 하여도 기존 index 를 그대로 유지하기에 아래부터 계산해야 순차적으로 내려갈 수있다.
checkedDivs_l.reverse();
checkedDivs_l.forEach(div =>{
let curIndex = Array.from(container_l.children).indexOf(div);
if(curIndex < container_l.children.length - 1){
container_l.insertBefore(div, container_l.children[curIndex + 2]);
}
})
}
let container_r = document.getElementById('contentRightUser');
let checkedDivs_r = [];
let rightflag = false;
Array.from(container_r.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_r.push(div);
rightflag = true;
}
});
if(rightflag){
checkedDivs_r.reverse();
checkedDivs_r.forEach(div =>{
let curIndex = Array.from(container_r.children).indexOf(div);
if(curIndex < container_r.children.length - 1){
container_r.insertBefore(div, container_r.children[curIndex + 2]);
}
})
}
});
/*
---------------------------------------------- 게시판 ----------------------------------------------
*/
leftArrowCategory.addEventListener("click", function() {
event.preventDefault();
let checkboxes = document.querySelectorAll('#containerCategory input[type="checkbox"]:checked');
let targetContentDiv = document.getElementById("contentLeftCategory");
checkboxes.forEach(checkbox => {
let targetDiv = checkbox.parentElement.parentElement;
targetContentDiv.appendChild(targetDiv);
checkbox.name = "contentLeftCategory";
});
});
//check된 항목들을 찾아서 targetDiv로 이동시킴.
rightArrowCategory.addEventListener("click", function() {
event.preventDefault();
let checkboxes = document.querySelectorAll('#containerCategory input[type="checkbox"]:checked');
let targetContentDiv = document.getElementById("contentRightCategory");
checkboxes.forEach(checkbox => {
let targetDiv = checkbox.parentElement.parentElement;
targetContentDiv.appendChild(targetDiv);
checkbox.name = "contentRightCategory";
});
});
upArrowCategory.addEventListener("click", function() {
event.preventDefault();
let container_l = document.getElementById('contentLeftCategory');
let checkedDivs_l = [];
let leftflag = false;
Array.from(container_l.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_l.push(div);
leftflag = true;
}
});
if(leftflag){
checkedDivs_l.forEach(div =>{
let curIndex = Array.from(container_l.children).indexOf(div);
if(curIndex > 0){
container_l.insertBefore(div, container_l.children[curIndex - 1]);
}
})
}
//우측 div 처리
let container_r = document.getElementById('contentRightCategory');
let checkedDivs_r = [];
let rightflag = false;
Array.from(container_r.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_r.push(div);
rightflag = true;
}
});
if(rightflag){
checkedDivs_r.forEach(div =>{
let curIndex = Array.from(container_r.children).indexOf(div);
if(curIndex > 0){
container_r.insertBefore(div, container_r.children[curIndex - 1]);
}
})
}
});
downArrowCategory.addEventListener("click", function() {
event.preventDefault();
let container_l = document.getElementById('contentLeftCategory');
let checkedDivs_l = [];
let leftflag = false;
Array.from(container_l.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_l.push(div);
leftflag = true;
}
});
if(leftflag){
//index 로 계산해서 순서 변경하기위해 insertBefore 를 사용하였는데, reverse 를 하지않으면 위에서부터 계산해서 내려옴.
//위 객체부터 index +2 를 하다보면, 그 아래객체에 index+2 를 하여도 기존 index 를 그대로 유지하기에 아래부터 계산해야 순차적으로 내려갈 수있다.
checkedDivs_l.reverse();
checkedDivs_l.forEach(div =>{
let curIndex = Array.from(container_l.children).indexOf(div);
if(curIndex < container_l.children.length - 1){
container_l.insertBefore(div, container_l.children[curIndex + 2]);
}
})
}
let container_r = document.getElementById('contentRightCategory');
let checkedDivs_r = [];
let rightflag = false;
Array.from(container_r.children).forEach(div =>{
let checkbox = div.querySelector('input[type="checkbox"]');
if(checkbox && checkbox.checked){
checkedDivs_r.push(div);
rightflag = true;
}
});
if(rightflag){
checkedDivs_r.reverse();
checkedDivs_r.forEach(div =>{
let curIndex = Array.from(container_r.children).indexOf(div);
if(curIndex < container_r.children.length - 1){
container_r.insertBefore(div, container_r.children[curIndex + 2]);
}
})
}
});
});
document.addEventListener("DOMContentLoaded", function() {
const form = document.querySelector("form");
const rightCategoryDiv = document.getElementById("contentRightCategory");
const rightUserDiv = document.getElementById("contentRightUser");
form.addEventListener("submit", function(event) {
// contentRightCategory 체크박스 값 수집
const categoryCheckboxes = Array.from(rightCategoryDiv.querySelectorAll("input[type='checkbox']"));
categoryCheckboxes.forEach(checkbox => {
if (!checkbox.checked) {
const hiddenInput = document.createElement("input");
hiddenInput.type = "hidden";
hiddenInput.name = checkbox.name;
hiddenInput.value = checkbox.value;
form.appendChild(hiddenInput);
}
});
// contentRightUser 체크박스 값 수집
const userCheckboxes = Array.from(rightUserDiv.querySelectorAll("input[type='checkbox']"));
userCheckboxes.forEach(checkbox => {
if (!checkbox.checked) {
const hiddenInput = document.createElement("input");
hiddenInput.type = "hidden";
hiddenInput.name = checkbox.name;
hiddenInput.value = checkbox.value;
form.appendChild(hiddenInput);
}
});
});
});
</script>
</body>
</html>
이번 코드는 좀 긴데, 지난번 JS 에서 만든 타겟 선택해서 박스 이동하는 자바스크립트를 가져다가 썼다.
체크박스를 form 으로 제출시 checked 된것만 제출되기떄문에 마지막 제출 전에 chekcbox 의 체크여부가아닌 위치여부에따라 전달하도록 script 로 한번 더 확인해주었다.
rold_add_form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>권한 관리화면</title>
<sec:csrfMetaTags />
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<header>
<nav>
<div class="menu">
<div class="menu-item"><a href="${pageContext.request.contextPath}/">홈화면으로</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/userList">사용자관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/roleList">권한관리화면</a></div>
<div class="menu-item"><a href="${pageContext.request.contextPath}/admin/boardList">게시판화면</a></div>
</div>
</nav>
</header>
<main>
<nav>
<ul>
<li><a href="${pageContext.request.contextPath}/admin/roleList" id="roleList">권한 목록</a></li>
<li><a href="${pageContext.request.contextPath}/admin/addRoleForm" id="addRole">권한 등록</a></li>
</ul>
</nav>
<section id="content">
<form action="/admin/addRole" method="post">
<sec:csrfInput />
<div>
<label for="roleName">이름:</label>
<input type="text" id="roleName" name="roleName" required> ROLE_XXXX 형태로 생성해야함
</div>
<div>
<label for="description">설명:</label>
<input type="text" id="description" name="description" required>
</div>
<div>
<button type="submit">등록</button>
</div>
</form>
</section>
</main>
<script>
</script>
</body>
</html>
2. 컨트롤러 추가하기
role 추가한 adminController
@GetMapping("/roleList")
public ModelAndView roleListPage() {
mav.addObject("roles", userService.findAllRole());
mav.setViewName("admin/role_management");
return mav;
}
@PostMapping("/addRole")
public String registerRole(@ModelAttribute Role role) {
userService.addRole(role);
return "redirect:roleList";
}
@GetMapping("/addRoleForm")
public String addRoleForm() {
return "admin/role_add_form";
}
@GetMapping("/modifyRoleForm")
public ModelAndView modifyRoleForm(HttpServletRequest request) {
String roleId = request.getParameter("roleId");
mav.addObject("role", userService.getRoleInfo(roleId));
mav.addObject("userList", userService.findUserByRoleId(roleId));
mav.addObject("categoryList", postService.findCategoryByRoleId(roleId));
mav.setViewName("admin/role_modify_form");
return mav;
}
@PostMapping("/modifyRole")
public String postMethodName(@ModelAttribute Role role,
@RequestParam(value = "contentRightUser", required = false) List<String> contentRightUser,
@RequestParam(value = "contentRightCategory", required = false) List<String> contentRightCategory ) {
List<String> role_users = contentRightUser;
List<String> role_categories = contentRightCategory;
if(role_categories != null){
postService.modifyRoleCategory(role_categories, role.getRoleId());
}else{
postService.deleteCategoryByRoleId(role.getRoleId());
}
if(role_users != null){
userService.modifyRoleUser(role_users, role.getRoleId());
}else{
userService.deleteUserByRoleId(role.getRoleId());
}
return "redirect:/admin/roleList";
}
@PostMapping("/deleteRole")
public ResponseEntity<String> deleteRole(HttpServletRequest request){
String roleId = request.getParameter("roleId");
userService.deleteRole(roleId);
return ResponseEntity.ok("");
}
3. 서비스 추가하기
Role 추가에따라 post와 user 서비스 수정이 있었음.
package com.example.post.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.post.dao.UserDao;
import com.example.post.model.Role;
import com.example.post.model.User;
@Service
public class UserService {
@Autowired
UserDao userDao;
@Autowired
PasswordEncoder passwordEncoder;
public void addUser(User user) {
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
userDao.insertUser(user);
}
public List<User> findAllUser() {
return userDao.selectAllUser();
}
public List<Role> findAllRole() {
return userDao.selectAllRole();
}
public void addRole(Role role) {
userDao.insertRole(role);
}
public Role getRoleInfo(String roleId) {
return userDao.selectRoleInfo(roleId);
}
@Transactional
public void modifyRoleUser(List<String> users, String roleId) {
System.out.println("!#!#$!#$ " + users + roleId);
userDao.deleteUserByRoleId(roleId);
for (String user : users) {
System.out.println("user" + user + "\n roleId" + roleId);
userDao.insertRoleByUsername(user, roleId);
}
}
public List<User> findUserByRoleId(String roleId) {
return userDao.selectUserByRoleId(roleId);
}
public void deleteUserByRoleId(String roleId){
userDao.deleteUserByRoleId(roleId);
}
public User getUserInfo(String username) {
return userDao.selectUserInfo(username);
}
@Transactional
public void deleteRole(String roleId) {
userDao.deleteRoleUser(roleId);
userDao.deleteRoleCategory(roleId);
userDao.delteRole(roleId);
}
public void modifyUserInfo(User user) {
String password = user.getPassword();
user.setPassword(passwordEncoder.encode(password));
userDao.modifyUserInfo(user);
}
@Transactional
public void deleteUser(String username) {
userDao.deleteRoleUserByUsername(username);
userDao.deleteUser(username);
}
}
package com.example.post.service;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.post.dao.CommentDao;
import com.example.post.dao.FileDao;
import com.example.post.dao.PostDao;
import com.example.post.model.Category;
import com.example.post.model.FileInfo;
import com.example.post.model.Post;
@Service
public class PostService{
@Autowired PostDao postDao;
@Autowired FileDao fileDao;
@Autowired CommentDao commentDao;
@Autowired FileService fileService;
public List<?> postList(Map params){
return postDao.findAll(params);
}
public Post getPost(String num) {
return postDao.selectPost(num);
}
public void modifyPost(Post post) {
postDao.modifyPost(post);
}
@Transactional
public void modifyPost(Post post, FileInfo fileInfo) throws IOException {
postDao.modifyPost(post);
String fileId = fileDao.selectFileByPostId(post.getNum()).get(0).getFileId();
fileInfo.setFileId(fileId);
fileService.deleteFile(fileInfo);
fileDao.insertFile(fileInfo);
}
public void inserPost(Post post) {
postDao.insertPost(post);
}
@Transactional
public void inserPost(Post post, FileInfo fileInfo) {
postDao.insertPost(post);
fileInfo.setPostId(post.getNum());
fileDao.insertFile(fileInfo);
}
@Transactional
public void deletePost(String num) throws IOException {
commentDao.deleteCommentByPostId(num);
List<FileInfo> fileInfoList = fileDao.selectFileByPostId(num);
if(!fileInfoList.isEmpty()){
for(FileInfo fileInfo : fileInfoList){
fileService.deleteFile(fileInfo);
}
}
//외래키때문에 맨마지막에 삭제해야함.
postDao.deletePost(num);
}
public List<Category> findCategory(String username) {
return postDao.selectCategory(username);
}
public List<Category> findAllCategory() {
return postDao.selectAllCategory();
}
public void addCategory(Category category) {
postDao.insertCategory(category);
}
public String getCategoryName(String categoryId) {
return postDao.selectCategoryName(categoryId);
}
// public Object getCateogryInfo(String categoryId) {
// return postDao.selectCategoryInfo(categoryId);
// }
@Transactional
public void modifyRoleCategory(List<String> role_categories, String roleId) {
postDao.deleteCategoryByRoleId(roleId);
for (String category : role_categories) {
postDao.insertRoleByCategory(category, roleId);
}
}
public void deleteCategoryByRoleId(String RoleId){
postDao.deleteCategoryByRoleId(RoleId);
}
public Category getCateogryInfo(String categoryId) {
return postDao.selectCategoryInfo(categoryId);
}
public List<Category> findCategoryByRoleId(String roleId) {
return postDao.findCategoryByRoleId(roleId);
}
public void modifyBoardInfo(Category category) {
postDao.modifyBoardInfo(category);
}
}
분량상상 이어서...
728x90
반응형
'IT 공부 > 프로젝트' 카테고리의 다른 글
[Spring] 게시판 만들기 15 - 댓글 알람 추가하기 (WebSocket, AOP) (0) | 2024.07.29 |
---|---|
[Spring] 게시판 만들기 14 - 권한 관리 2 (5) | 2024.07.24 |
[Spring] 게시판 만들기 12 - 게시판 카테고리 관리기 (1) | 2024.07.23 |
[Spring] 게시판 만들기 11 - 계정 생성하기 (Spring Security 3) (1) | 2024.07.22 |
[Spring] 게시판 만들기 10 - Spring Security 2 (1) | 2024.07.20 |