https://greed-yb.tistory.com/265
Summernote를 이용한 게시판 구현 연장선이다
1. 게시판 목록
https://greed-yb.tistory.com/252
게시판 목록은 이전글을 참고하길 바란다
게시판 목록페이지 javascript.
function boardInit(data){
$("#board_dataTable").DataTable({
data: data, // 가져온 데이터
"paging": true, // 페이징
"lengthMenu": [[10,30, 50, 100, -1], [10,30, 50, 100, "All"] ], // 목록 개수
"pageLength": 10, // 보여지는 기본 개수
"searching": true, // 검색 기능
"info": true, // 정보 표시
// "scrollX": false, // 가로 스크롤(true , false)
// "scrollY": false, // 새로 스크롤(false , px 단위 : 200 )
"ordering": true, // 정렬 기능
"order": [0 , "DESC"], // 정렬 기준
"language": { // 언어 설정
decimal: "",
emptyTable: "검색된 데이터가 없습니다.",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
infoEmpty: "Showing 0 to 0 of 0 entries",
infoFiltered: "(filtered from _MAX_ total entries)",
infoPostFix: "",
thousands: ",",
lengthMenu: " _MENU_ ",
loadingRecords: "Loading...",
processing: "Processing...",
search: "Search",
zeroRecords: "항목이 존재하지 않습니다",
paginate: {
"first": "처음",
"last": "마지막",
"next": "다음",
"previous": "이전"
},
aria: {
sortAscending: ": activate to sort column ascending",
sortDescending: ": activate to sort column descending"
}
},
"columnDefs": [
],
"columns":[
{
data: "no",
render: function (data, type, row){
return data;
}
},
{
data: "title",
render: function (data, type, row){
return '<a href="/companyBoardRead/'+ row.no +'"' +' style="text-decoration-line: none; color: rgba(0,0,0,0.22)">' + data + '</a>';
}
},
{
data: "writer",
render: function (data, type, row){
return data;
}
},
{
data: "created",
render: function (data, type, row){
return data;
}
},
{
data: "views",
render: function (data, type, row){
return data;
}
},
]
})
}
dataTable 로 그려줄 data column 명을 재정의 했고
글 제목을 눌러서 상세보기 페이지로 넘어갈 거라 <a태그> 를 추가하였다
게시판 목록 html
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">사내 게시판</h1>
</div>
<!-- Content Row -->
<div>
<!-- DataTales Example -->
<div class="card shadow mb-4">
<div class="card-header py-3" style="text-align: right">
<button class="btn btn-secondary" id="writeBtn" type="button" onclick="writePageBtn()">글쓰기</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="board_dataTable" style="width: 98%">
<thead>
<tr>
<th>No</th>
<th>제목</th>
<th>작성자</th>
<th>작성날짜</th>
<th>조회수</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
2. 게시판 글 상세 보기
상세 보기 페이지 - read.html
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">사내 게시판</h1>
</div>
<!-- Content Row -->
<div>
<!-- DataTales Example -->
<div class="card shadow mb-4">
<div class="card-body">
<form class="user" id="form" name="form">
<div class="form-group row">
<div class="col-sm-10 mb-3 mb-sm-0">
<input type="text" class="form-control form-control-user" id="title" name="title" readonly th:value="${vo.title}">
</div>
<div class="col-sm-2">
<input type="text" class="form-control form-control-user" id="writer" name="writer" readonly th:value="${vo.writer}">
<input type="hidden" id="id" name="id" readonly th:value="${vo.id}">
</div>
</div>
<div class="card shadow mb-4" style="min-height:400px; height: 100%; min-width: 400px; width: 100%" th:utext="${vo.content}"></div>
<div class="card shadow mb-4">
<a th:href="@{/upload/downloadFile/{filePath}(filePath = ${vo.filePath})}"><span id="fileName" th:text="${vo.fileName}"></span></a>
</div>
<div class="form-group">
<div style="text-align: center">
<button type="button" class="btn btn-info" onclick="backBtn()">뒤로</button>
<button type="button" class="btn btn-primary" id="updateBtn" th:value="${vo.no}" onclick="updatePage(this)">수정</button>
<button type="button" class="btn btn-danger" id="dBtn" onclick="deleteBtn(this)" th:value="${vo.no}">삭제</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
// 스크립트 경로는 thymeleaf layout 를 사용중이라 본인에 맞게 바꿔야 한다
<script th:src="@{/bootstrap/vendor/datatables/jquery.dataTables.min.js}"></script>
<script th:src="@{/bootstrap/vendor/datatables/dataTables.bootstrap4.min.js}"></script>
<script th:src="@{/bootstrap/js/companyBoardRead.js}"></script>
</script>
상세보기 - read.js
// 유저 정보
let user_info = [];
// 뒤로가기
function backBtn(){
window.history.back();
}
function updatePage(e) {
if(!confirm("수정 하시겠습니까?")){
return false;
}
location.href = "/companyBoardUpdate/"+e.value;
}
// 글 삭제
function deleteBtn(e){
if(!confirm("게시글을 삭제하시겠습니까?")){
return false;
}
let boardNo = e.value;
$.ajax({
url : "/companyBoard/delete",
type : "DELETE",
dataType : "text",
data : {"no" : boardNo},
success : function (data) {
if(data == 'success'){
location.href = "/companyBoard"
}else{
alert("장애가 발생하였습니다.");
}
},
error(e){
console.log("error : "+ e);
}
});
}
// 유저 정보 가져오기
function userInfo(){
$.ajax({
url : "/userInfo",
type : "GET",
success : function (data) {
if(data != null){
user_info = data;
// 권한작업을 아직 하지 않아서 작성자ID와 로그인ID가 맞지 않으면 수정 삭제 버튼을 없애준다
if($("#id").val() != user_info.id){
$("#updateBtn").remove();
$("#deleteBtn").remove();
}
}
},
error(e){
console.log("error : "+ e);
}
});
}
$(document).ready(function (){
// 유저 정보 가져오기
userInfo();
});
UserController - 유저 정보
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/userInfo")
public UserVo userInfo(HttpServletRequest request , HttpServletResponse response) throws Exception{
String test = String.valueOf(request.getUserPrincipal().getName());
UserVo vo = userService.getUserById(test);
return vo;
}
}
페이지 시작 시 유저 정보를 가져온다
WebController - 웹이동
@Controller
public class WebController {
@Autowired
private CompanyBoardService companyBoardService;
// 사내게시판
@GetMapping("/companyBoard")
public String companyBoardPage() throws Exception{
return "companyBoard";
}
// 사내 게시판 글 읽기
@GetMapping("/companyBoardRead/{no}")
public String companyBoardRead(@PathVariable String no , Model model) throws Exception{
CompanyBoardVo vo = companyBoardService.read(no);
model.addAttribute("vo" , vo);
return "companyBoardRead";
}
// 사내 게시판 글 수정
@GetMapping("/companyBoardUpdate/{no}")
public String companyBoardUpdate(@PathVariable String no , Model model) throws Exception{
model.addAttribute("no" , no);
return "companyBoardUpdate";
}
}
페이지가 열리면서 데이터를 넣어주려고 model 을 이용하였다
수정페이지 부분에서는 ajax를 이용하여 데이터를 바인딩했는데
본인이 편한 대로 하면 된다
CompanyBoardController - 게시판
@RestController
@Tag(name="Swagger Company_Boards", description = "Company_Boards")
public class CompanyBoardController {
@Autowired
private CompanyBoardService companyBoardService;
@Operation(summary = "사내게시판 글 삭제", description = "사내게시판 작성된 글을 삭제한다.")
@DeleteMapping("/companyBoard/delete")
public String companyBoardWriteDelete(@RequestParam String no) throws Exception{
CompanyBoardVo vo = companyBoardService.read(no);
try {
companyBoardService.delete(no);
// 파일 업로드 경로
Path FILE_ROOT = Paths.get("./").toAbsolutePath().normalize();
String uploadPath = FILE_ROOT.toString() + "/upload/image/";
Path path = Paths.get(uploadPath, vo.getFilePath());
Files.delete(path);
return "success";
}catch (Exception e){
e.printStackTrace();
return "fail";
}
}
@Operation(summary = "사내게시판 수정 글 정보", description = "사내게시판 수정 글 정보를 가져온다.")
@GetMapping("/companyBoard/updateInfo")
public CompanyBoardVo companyBoardUpdateInfo(@RequestParam String no) throws Exception{
CompanyBoardVo vo = companyBoardService.read(no);
return vo;
}
@Operation(summary = "사내게시판 글 수정", description = "사내게시판 수정 글을 수정한다.")
@PostMapping("/companyBoard/writeUpdate")
public String companyBoardWriteUpdate(@RequestBody CompanyBoardVo vo) throws Exception{
try {
companyBoardService.writeUpdate(vo);
return "success";
}catch (Exception e){
e.printStackTrace();
return "fail";
}
}
}
vo
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Schema(description = "사내게시판 정보")
public class CompanyBoardVo {
@Schema(description = "글 번호" , nullable = false)
private Integer no;
@Schema(description = "제목" , nullable = false)
private String title;
@Schema(description = "작성자" , nullable = false)
private String writer;
@Schema(description = "아이디" , nullable = false)
private String id;
@Schema(description = "내용" , nullable = false)
private String content;
@Schema(description = "작성시간" , nullable = false)
private String created;
@Schema(description = "수정시간" , nullable = false)
private String updated;
@Schema(description = "조회수" , nullable = false)
private Integer views;
@Schema(description = "파일명" , nullable = false)
private String fileName;
@Schema(description = "파일경로" , nullable = false)
private String filePath;
}
lombok 을 사용 안 한다면 getter , setter와 toString을 따로 생성해 주면 된다
service
public interface CompanyBoardService {
/**
* DataTable 사내게시판 리스트
* @return
* @throws Exception
*/
List<CompanyBoardVo> list() throws Exception;
/**
* 사내게시판 게시글 저장
* @param vo
* @throws Exception
*/
void writeSave(CompanyBoardVo vo) throws Exception;
/**
* 사내게시판 게시글 읽기
* @param no
* @return
* @throws Exception
*/
CompanyBoardVo read(String no) throws Exception;
/**
* 사내게시판 게시글 삭제
* @param no
* @throws Exception
*/
void delete(String no) throws Exception;
/**
* 사내게시판 게시글 수정
* @param vo
* @throws Exception
*/
void writeUpdate(CompanyBoardVo vo) throws Exception;
}
serviceImpl
@Service
public class CompanyBoardServiceImpl implements CompanyBoardService {
@Autowired
private CompanyBoardMapper companyBoardMapper;
@Override
public List<CompanyBoardVo> list() throws Exception {
return companyBoardMapper.list();
}
@Override
public void writeSave(CompanyBoardVo vo) throws Exception {
companyBoardMapper.writeSave(vo);
}
@Override
public CompanyBoardVo read(String no) throws Exception {
return companyBoardMapper.read(no);
}
@Override
@Transactional
public void delete(String no) throws Exception {
companyBoardMapper.delete(no);
}
@Override
public void writeUpdate(CompanyBoardVo vo) throws Exception {
companyBoardMapper.writeUpdate(vo);
}
}
mapper
@Mapper
public interface CompanyBoardMapper {
/**
* DataTable 사내게시판 리스트
* @return
* @throws Exception
*/
List<CompanyBoardVo> list() throws Exception;
/**
* 사내게시판 게시글 저장
* @param vo
* @throws Exception
*/
void writeSave(CompanyBoardVo vo) throws Exception;
/**
* 사내게시판 게시글 읽기
* @param no
* @return
* @throws Exception
*/
CompanyBoardVo read(String no) throws Exception;
/**
* 사내게시판 게시글 삭제
* @param no
* @throws Exception
*/
void delete(String no) throws Exception;
/**
* 사내게시판 게시글 수정
* @param vo
* @throws Exception
*/
void writeUpdate(CompanyBoardVo vo) throws Exception;
}
mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.practice.mapper.board.CompanyBoardMapper">
<select id="list" resultType="com.example.practice.vo.board.CompanyBoardVo">
SELECT
NO,
TITLE,
WRITER,
ID,
CONTENT,
CREATED,
UPDATED,
VIEWS
FROM COMPANY_BOARD
ORDER BY NO DESC
</select>
<insert id="writeSave" parameterType="com.example.practice.vo.board.CompanyBoardVo">
INSERT INTO COMPANY_BOARD(
NO,
TITLE,
WRITER,
ID,
CONTENT,
VIEWS,
FILE_NAME,
FILE_PATH
)VALUES(
(SELECT NVL(Max(NO), 0) + 1 AS NO FROM COMPANY_BOARD),
#{title},
#{writer},
#{id},
#{content},
0,
#{fileName},
#{filePath}
)
</insert>
<select id="read" resultType="com.example.practice.vo.board.CompanyBoardVo">
SELECT
NO,
TITLE,
WRITER,
CONTENT,
ID,
CREATED,
UPDATED,
VIEWS,
FILE_NAME,
FILE_PATH
FROM COMPANY_BOARD
WHERE NO = #{no}
</select>
<delete id="delete">
DELETE FROM COMPANY_BOARD WHERE NO = #{no}
</delete>
<update id="writeUpdate" parameterType="com.example.practice.vo.board.CompanyBoardVo">
UPDATE COMPANY_BOARD SET
TITLE = #{title} , CONTENT = #{content} , UPDATED = SYSDATE , FILE_NAME = #{fileName} , FILE_PATH = #{filePath}
WHERE NO = #{no}
</update>
</mapper>
하단에 파일 첨부도 만들었는데 이전글에 추가가 되지 않았다
수정페이지에 있는 코드를 참고하길 바란다
https://greed-yb.tistory.com/267
다음글 - 게시판 글 수정하기
'개발 > Spring' 카테고리의 다른 글
[SpringBoot] Chart.js 를 이용하여 실시간 (반도넛)차트 구현하기 (0) | 2024.07.16 |
---|---|
[SpringBoot] Summernote Editor(게시판 글 수정하기) 적용하기 - 3 (0) | 2024.07.15 |
[SpringBoot] Summernote Editor(이미지,동영상 삽입) 적용하기 -1 (0) | 2024.07.11 |
[SpringBoot] 로그인 시 아이디 기억하기(쿠키 적용) (1) | 2024.07.10 |
[SpringBoot] 카카오(다음) 주소 API 적용하기 (0) | 2024.07.09 |
댓글