공지사항 만들기
공지사항 게시판 notice
- NoticeController
- NoticeService
- NoticeServiceImpl
- NoticeDAO
- NoticeDTO
- notice-mapper.xml
만드는 순서
DTO에 들어갈 내용
int nno, ndel, nread, nlike
String ntitle, ncontent, ndate
NoticeDTO.java
import lombok.Data;
@Data
public class NoticeDTO {
private int nno, ndel, nread, nlike;
private String ntitle, ncontent, ndate;
}
DB에 들어갈 내용
테이블명 | notice |
column | nno(int, auto, PK), ndel(int 1), nread(int, 기본값 1), nlike(int, 기본값 1), ntitle(varchar, 50), ncontent(varchar, 500), ndate(datetype, current timestamp) |
CREATE TABLE notice (
nno INT AUTO_INCREMENT PRIMARY KEY,
ndel INT,
nread INT,
nlike INT,
ntitle VARCHAR(255),
ncontent VARCHAR(500),
ndate TIMESTAMP
);
NoticeService에 들어갈 추상 메소드 (인터페이스)
- public List<NoticeDTO> noticeList();
- public NoticeDTO detail(int no);
- public int noticeWrite(NoticeDTO);
- public int noticeDel(int no);
- public int noticeUpdate(NoticeDTO);
NoticeService.java
public interface NoticeService {
public List<NoticeDTO> noticeList();
public NoticeDTO detail(int no);
public int noticeWrite(NoticeDTO noticeDTO);
public int noticeDel(int no);
public int noticeUpdate(NoticeDTO noticeDTO);
}
NoticeServiceImpl.java
- 클래스 생성 시, 인터페이스 추가하기
- + AbstractService 상속받기
- @Service("noticeService") 어노테이션 추가
@Service("noticeService")
public class NoticeServiceImpl extends AbstractService implements NoticeService {
@Override
public List<NoticeDTO> noticeList() {
return null;
}
@Override
public NoticeDTO detail(int no) {
return null;
}
@Override
public int noticeWrite(NoticeDTO dto) {
return 0;
}
@Override
public int noticeDel(int no) {
return 0;
}
@Override
public int noticeUpdate(NoticeDTO dto) {
return 0;
}
}
NoticeDAO.java
superclass 선택 후 생성하기 : AbstractDAO
@Repository
public class NoticeDAO extends AbstractDAO {
}
-> DAO를 NoticeServiceImpl 서비스와 연결시키기 (@Autowired로)
NoticeServiceImpl.java
@Autowired
private NoticeDAO noticeDAO;
NoticeController.java
Resource 어노테이션의 name은 NoticeServiceImpl에 적었던 "noticeService"
@Controller
public class NoticeController {
@Resource(name="noticeService") // 서비스와 name이 일치해야함.
private NoticeService noticeService;
// 2024-02-27 공지 리스트
@GetMapping("/notice")
public String notice() {
return "notice"; // ......./views/notice.jsp
}
notice.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<body> 안에 입력할 내용
<div class="text-center">
<h2 class="section-heading text-uppercase">공지사항</h2>
</div>
<div class="row text-center">
<%
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
request.setAttribute("list", list);
%>
<c:choose>
<c:when test="${fn:length(list) gt 0}">테이블 출력</c:when>
<c:otherwise>출력할 값이 없습니다.</c:otherwise>
</c:choose>
import 부분 삭제
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
NoticeController.java
list를 컨트롤러에서 model로 받아오기
// 2024-02-27 공지 리스트, 요구사항 확인
@GetMapping("/notice")
public String notice(Model model) {
List<NoticeDTO> list = noticeService.noticeList();
model.addAttribute("list", list);
return "notice"; // ......./views/notice.jsp
}
NoticeServiceImpl.java
서비스 안에 기존에 있던 리스트의 return 부분
@Override
public List<NoticeDTO> noticeList() {
return noticeDAO.noticeList();
}
-> noticeList 생성 (DAO)
NoticeDAO.java
@Repository
public class NoticeDAO extends AbstractDAO {
public List<NoticeDTO> noticeList() {
return sqlSession.selectList("notice.noticeList"); // 지금은 파라미터가 비어있어서 1개만 적은거임.
}
notice-mapper.xml
<mapper namespace="notice">
<select id="noticeList" resultType="org.green.dto.NoticeDTO">
SELECT nno, ndel, nread, nlike, ntitle, ndate
FROM notice
</select>
</mapper>
notice.jsp
<!-- 게시판 -->
<br>
<br>
<section class="page-section" id="services">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">공지사항</h2>
</div>
<div class="row text-center">
<%-- <%
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
request.setAttribute("list", list);
%> --%>
<c:choose>
<c:when test="${fn:length(list) gt 0}">
<table>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일</th>
<th>조회수</th>
</tr>
</thead>
<c:forEach items="${list }" var="row">
<tr>
<td onclick="detail(${row.nno })">${row.nno }</td>
<td class="title"><a href="./detail?no=${row.nno }">
${row.ntitle } </a></td>
<td>${row.ndate }</td>
<td>${row.nlike }</td>
</tr>
</c:forEach>
</table>
</c:when>
<c:otherwise>
<h1>공지사항이 없습니다.</h1>
</c:otherwise>
</c:choose>
<!-- 글쓰기 버튼 -->
<div class="d-flex justify-content-end">
<button type="button" class="btn btn-primary btn-lg mt-3" data-bs-toggle="modal" data-bs-target="#write">글쓰기</button>
</div>
</div>
</div>
</section>
mybatis-config.xml
* alias : mapper의 resultType명
<typeAlias type="org.green.dto.NoticeDTO" alias="notice"/>
NoticeController.java
// 공지 글 작성하기 -> admin 관리자 화면에서
@GetMapping("/admin/noticeWrite")
public String noticeWrite() {
return "admin/noticeWrite"; // ...../views/admin/noticeWrite.jsp => admin 폴더 만들어야함.
}
-> admin 폴더 만들기 : ...../views/admin/noticeWrite.jsp 경로
-> admin 폴더 안에 noticeWrite 생성
noticeWrite.jsp
<title>admin Write</title>
</head>
<body>
<h1>공자사항 글쓰기</h1>
<form action="./admin/noticeWrite" method="post">
<input name="ntitle">
<textarea name="ncontent"></textarea>
<button type="submit">공지 작성</button>
</form>
</body>
</html>
NoticeController.java
@PostMapping("/admin/noticeWrite")
public String noticeWrite(NoticeDTO dto) {
// System.out.println(dto.getNtitle());
// System.out.println(dto.getNcontent());
int result = noticeService.noticeWrite(dto);
return "redirect:/notice";
}
-> noticeWrite (ServiceImpl, DAO에 생성)
NoticeServiceImpl.java
@Override
public int noticeWrite(NoticeDTO dto) {
return noticeDAO.noticeWrite(dto);
}
NoticeDAO.java
public int noticeWrite(NoticeDTO dto) {
return sqlSession.insert("notice.noticeWrite", dto);
}
-> mapper에 sql문 <insert> 추가
<insert id="noticeWrite" parameterType="notice">
INSERT INTO notice (ntitle, ncontent)
VALUES (#{ntitle}, #{ncontent})
</insert>
NoticeController.java
@GetMapping("/noticeDetail")
public String noticeDetail(@RequestParam(value="no", defaultValue = "0", required = true) int no, Model model) {
if(no == 0) {
return "redirect:/error";
} else {
NoticeDTO detail = noticeService.detail(no);
if(detail.getNno() == 0) {
return "reidrect:/error";
} else {
model.addAttribute("detail", detail);
return "noticeDetail";
}
}
}
}
공지글 상세보기
notice-mapper.xml
<select id="noticeDetail" resultType="notice" parameterType="Integer">
SELECT nno, ndel, nread, ntitle, ndate, ncontent
FROM notice
WHERE nno=#{nno} AND ndel=1
</select>
NoticeServiceImpl.java
@Override
public NoticeDTO detail(int no) {
return noticeDAO.detail(no);
}
-> detail 생성 (DAO에)
NoticeDAO.java
public NoticeDTO detail(int no) {
return sqlSession.selectOne("notice.noticeDetail", no);
}
notice.jsp
<!-- 게시판 -->
<br>
<br>
<section class="page-section" id="services">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">공지사항</h2>
</div>
<div class="row text-center">
<c:choose>
<c:when test="${fn:length(list) gt 0}">
<table>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일</th>
<th>조회수</th>
</tr>
</thead>
<c:forEach items="${list }" var="row">
<tr>
<td onclick="noticeDetail(${row.nno })">${row.nno }</td>
<td class="title"><a href="./noticeDetail?no=${row.nno }">
${row.ntitle } </a></td>
<td>${row.ndate }</td>
<td>${row.nlike }</td>
</tr>
</c:forEach>
</table>
</c:when>
<c:otherwise>
<h1>공지사항이 없습니다.</h1>
</c:otherwise>
</c:choose>
noticeDetail.jsp
<!-- 게시판 -->
<section class="page-section" id="detail">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">공지사항 디테일</h2>
</div>
<c:if test="${not empty detail}">
<div class="card mb-4" style="min-height: 500px;">
<div class="card-body">
<div class="h2">${detail.ntitle}</div>
<div class="row p-2 bg-secondary">
<div class="col align-middle text-start">
관리자
<a class="btn btn-primary" href="./noticeDel${detail.nno }">[삭제]</a>
<button class="btn btn-primary" onclick="location.href='./noticeUpdate?no=${detail.nno}'">수정</button>
</div>
<div class="col align-middle text-end">${detail.ndate}</div>
</div>
<div class="mt-4 h-auto">${detail.ncontent}</div>
</div>
</div>
<button class="btn btn-warning" onclick="history.back()">공지사항</button>
<hr>
</c:if>
<c:if test="${empty detail}">
<!-- detail이 null인 경우 처리 -->
<p>공지사항이 존재하지 않습니다.</p>
</c:if>
</div>
</section>
공지 글 삭제하기
NoticeController.java
// noticeDel
@GetMapping("/noticeDel{no}")
public String noticeDel(@PathVariable("no") int no) {
System.out.println("@PathVariable : " + no);
noticeService.noticeDel(no);
return "redirect:/notice";
}
NoticeServiceImpl.java
@Override
public int noticeDel(int no) {
return noticeDAO.noticeDel(no);
}
NoticeDAO.java
public int noticeDel(int no) {
return sqlSession.update("notice.noticeDel",no);
}
notice-mapper.xml
<update id="noticeDel" parameterType="Integer">
UPDATE notice SET ndel='0'
WHERE nno=#{nno} AND ndel=1
</update>
삭제된 글은 화면에 보이지 않도록 하려면 mapper의 noticeList를 수정해야 함.
* ndel이 1인 글만 화면에 노출 : WHERE ndel=1
<select id="noticeList" resultType="notice">
SELECT nno, ndel, nread, nlike, ntitle, ncontent, ndate
FROM notice
WHERE ndel=1
ORDER BY nno DESC
</select>
페이징 만들기
Board에서 페이징 만들었던 내용 참고
NoticeController.java
@Controller
public class NoticeController {
@Resource(name="noticeService") // 서비스와 name이 일치해야함.
private NoticeService noticeService;
@Autowired
private Util util;
// 2024-02-27 공지 리스트, 요구사항 확인
// 페이징
@GetMapping("/notice")
public String notice(@RequestParam(value = "pageNo", required = false) String no ,Model model) {
int currentPageNo=1;
if(util.str2Int(no) > 0 ) { // 정수=1, 정수가 아니면 currentPageNo
currentPageNo = Integer.parseInt(no);
}
// 전체 글 수 totalRecordCount (0, 1인 것들만 가져오기)
int totalRecordCount = noticeService.totalRecordCount();
// pagination
PaginationInfo paginationInfo = new PaginationInfo();
paginationInfo.setCurrentPageNo(currentPageNo); // 현재 페이지 번호
paginationInfo.setRecordCountPerPage(10); // 한 페이지에 게시되는 게시물 수
paginationInfo.setPageSize(10); // 페이징 리스트의 사이즈 (페이징 숫자 수)
paginationInfo.setTotalRecordCount(totalRecordCount); // 전체 게시물 건 수
List<NoticeDTO> list = noticeService.noticeList(paginationInfo.getFirstRecordIndex());
model.addAttribute("list", list);
//페이징 관련 정보가 있는 PaginationInfo 객체를 모델에 반드시 넣어준다.
model.addAttribute("paginationInfo", paginationInfo);
model.addAttribute("pageNo", currentPageNo);
return "notice"; // ......./views/notice.jsp
}
-> totalRecordCount를 NoticeService에 추가
NoticeService.java
추가 내용 : int totalRecordCount();
public interface NoticeService {
public List<NoticeDTO> noticeList(int pageNo);
public NoticeDTO detail(int no);
public int noticeWrite(NoticeDTO dto);
public int noticeDel(int no);
public int noticeUpdate(NoticeDTO dto);
public int totalRecordCount();
}
NoticeServiceImpl.java
* noticeList 매개변수에 int pageNo
* totalRecordCount() 생성
@Override
public List<NoticeDTO> noticeList(int pageNo) {
return noticeDAO.noticeList(pageNo);
}
@Override
public int totalRecordCount() {
return noticeDAO.totalRecordCount();
}
notice.jsp
<script type="text/javascript">
// 전자정부 페이징 이동 스크립트
function linkPage(pageNo){
location.href="./notice?pageNo="+pageNo;
}
</script>
페이징 코드 : table이 끝나는 밑에 넣어주기
<!-- 페이징 -->
<div class="m-2 bg-secondary">
<ui:pagination paginationInfo="${paginationInfo}" type="text" jsFunction="linkPage" />
</div>
notice-mapper.xml
<select id="totalRecordCount" resultType="Integer">
SELECT COUNT(*) FROM notice WHERE ndel=1
</select>
<select id="noticeList" parameterType="Integer" resultType="notice">
SELECT nno, ntitle, ndate
FROM notice
WHERE ndel=1
ORDER BY nno DESC
LIMIT #{pageNo}, 10
</select>
notice.jsp 전체코드
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://egovframework.gov/ctl/ui" prefix="ui"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>공지사항</title>
<!-- Font Awesome icons (free version)-->
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js"
crossorigin="anonymous"></script>
<!-- Google fonts-->
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700"
rel="stylesheet" type="text/css" />
<link
href="https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700"
rel="stylesheet" type="text/css" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="css/styles.css" rel="stylesheet" />
<link href="css/board.css" rel="stylesheet" />
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- summer note -->
<script src="/js/summernote-lite.js"></script>
<script src="/js/summernote/summernote-ko-KR.js"></script>
<link rel="stylesheet" href="/css/summernote/summernote-lite.css">
<link
href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.css"
rel="stylesheet">
<script
src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.js"></script>
<!-- summer note -->
<script type="text/javascript">
// 전자정부 페이징 이동 스크립트
function linkPage(pageNo){
location.href="./notice?pageNo="+pageNo;
}
</script>
<style>
thead {
background-color: #007bff; /* 원하는 배경색으로 수정 가능 */
color: #fff; /* 텍스트 색상 설정 */
}
</style>
</head>
<body id="page-top">
<!-- Navigation 코드 이동 : menu.jsp -->
<%@ include file="menu.jsp"%>
<!-- 게시판 -->
<br>
<br>
<section class="page-section" id="services">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">공지사항</h2>
</div>
<div class="row text-center">
<c:choose>
<c:when test="${fn:length(list) gt 0}">
<table>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일</th>
<th>조회수</th>
</tr>
</thead>
<c:forEach items="${list}" var="row">
<tr>
<td onclick="noticeDetail(${row.nno})">${row.nno}</td>
<td class="title"><a href="./noticeDetail?no=${row.nno}">
${row.ntitle}
</a></td>
<td>${row.ndate}</td>
<td>${row.nlike}</td>
</tr>
</c:forEach>
</table>
<!-- 페이징 -->
<div class="m-2 bg-secondary">
<ui:pagination paginationInfo="${paginationInfo}" type="text"
jsFunction="linkPage" />
</div>
</c:when>
<c:otherwise>
<p>공지사항이 없습니다.</p>
</c:otherwise>
</c:choose>
<!-- 글쓰기 버튼 -->
<div class="d-flex justify-content-end">
<button type="button" class="btn btn-primary btn-lg mt-3"
data-bs-toggle="modal" data-bs-target="#write">글쓰기</button>
</div>
</div>
</div>
</section>
<!-- Bootstrap core JS-->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script src="js/scripts.js"></script>
<script src="https://cdn.startbootstrap.com/sb-forms-latest.js"></script>
<script>
$(document).ready(function() {
//여기 아래 부분
$('#summernote').summernote({
lang: "ko-KR", // default: 'en-US'
height: 400,
fontNames : ['D2Coding', 'Arial Black', 'Comic Sans MS', 'Courier New'],
placeholder: '간단하게 작성해주세요🥰',
toolbar: [
// [groupName, [list of button]]
['style', ['bold', 'italic', 'underline', 'clear']],
/* ['font', ['strikethrough', 'superscript', 'subscript']], */
['fontname', ['fontname','fontsize', 'color']],
['para', ['ul', 'ol', 'paragraph']],
/* ['height', ['height']] */
['table', ['table','link', 'picture', 'video', 'fullscreen', 'codeview', 'help']]
]
});
});
</script>
</body>
</html>
'개발 공부 Today I Learned' 카테고리의 다른 글
[국비 67일차 TIL] 글 제목 앞에 new 붙이기, 비밀번호 암호화 (0) | 2024.02.28 |
---|---|
[spring] 회원가입 비밀번호 일치 확인 (0) | 2024.02.28 |
[국비 65일차 TIL] 갤러리 글 상세보기 (0) | 2024.02.26 |
[국비 64일차 TIL] 이메일, 파일 업로드, 이미지 갤러리 (0) | 2024.02.23 |
[국비 63일차 TIL] spring 조회수, 좋아요 버튼, myinfo, 이메일 인증 (0) | 2024.02.22 |
댓글