728x90
계층간 데이터를 주고 받기 위해 사용되는 객체
Mybatis [ #{} , ${} 차이 ]
<delete id="userLeave" parameterType="String" >
<![CDATA[ DELETE FROM user WHERE user_no in ( ${deleteList} ) ]]>
</delete>
<update id="userInfoModify" parameterType="userConfig">
update user set
user_email = #{user_email} ,
user_tel = #{user_tel}
where user_id = #{user_id}
</update>
#{} >> PrepareStatement > ( ? , ? , ? )
${} >> Statement >> ' " + title + " '
Xml 내의 특수문자 <>&... 는 < > 로 변환 필요
특수문자가 포함된 쿼리를 <![CDATA[ ]]> 로 감싼다.
페이징 처리
BoardDto
package com.test.ch4.domain;
import java.util.*;
public class BoardDto {
private Integer bno;
private String title;
private String content;
private String writer;
private int view_cnt;
private int comment_cnt;
private Date reg_date;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BoardDto boardDto = (BoardDto) o;
return Objects.equals(bno, boardDto.bno) && Objects.equals(title, boardDto.title) && Objects.equals(content, boardDto.content) && Objects.equals(writer, boardDto.writer);
}
@Override
public int hashCode() {
return Objects.hash(bno, title, content, writer);
}
public BoardDto() { this("","",""); }
public BoardDto(String title, String content, String writer){
this.title = title;
this.content = content;
this.writer = writer;
}
public Integer getBno() {
return bno;
}
public void setBno(Integer bno) {
this.bno = bno;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public int getView_cnt() {
return view_cnt;
}
public void setView_cnt(int view_cnt) {
this.view_cnt = view_cnt;
}
public int getComment_cnt() {
return comment_cnt;
}
public void setComment_cnt(int comment_cnt) {
this.comment_cnt = comment_cnt;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
@Override
public String toString() {
return "BoardDto{" +
"bno=" + bno +
", title='" + title + '\'' +
", content='" + content + '\'' +
", writer='" + writer + '\'' +
", view_cnt=" + view_cnt +
", comment_cnt=" + comment_cnt +
", reg_date=" + reg_date +
'}';
}
}
PageHandler
package com.test.ch4.domain;
import org.springframework.web.util.*;
public class PageHandler {
private SearchCondition sc;
// private int pageSize = 10; // 한 페이지당 게시물 갯수
// private int page; // 현재 페이지
// private String option;
// private String keyword;
public final int NAV_SIZE = 10; // page navigation size
private int totalCnt; // 게시물의 총 갯수
private int totalPage; // 전체 페이지의 갯수
private int beginPage; // 화면에 보여줄 첫 페이지
private int endPage; // 화면에 보여줄 마지막 페이지
private boolean showNext = false; // 이후를 보여줄지의 여부. endPage==totalPage이면, showNext는 false
private boolean showPrev = false; // 이전을 보여줄지의 여부. beginPage==1이 아니면 showPrev는 false
public PageHandler(int totalCnt, Integer page) {
this(totalCnt, new SearchCondition(page, 10));
}
public PageHandler(int totalCnt, Integer page, Integer pageSize) {
this(totalCnt, new SearchCondition(page, pageSize));
}
public PageHandler(int totalCnt, SearchCondition sc) {
this.totalCnt = totalCnt;
this.sc = sc;
doPaging(totalCnt, sc);
}
private void doPaging(int totalCnt, SearchCondition sc) {
this.totalPage = totalCnt / sc.getPageSize() + (totalCnt % sc.getPageSize()==0? 0:1);
this.sc.setPage(Math.min(sc.getPage(), totalPage)); // page가 totalPage보다 크지 않게
this.beginPage = (this.sc.getPage() -1) / NAV_SIZE * NAV_SIZE + 1; // 11 -> 11, 10 -> 1, 15->11. 따로 떼어내서 테스트
this.endPage = Math.min(beginPage + NAV_SIZE - 1, totalPage);
this.showPrev = beginPage!=1;
this.showNext = endPage!=totalPage;
}
public String getQueryString() {
return getQueryString(this.sc.getPage());
}
public String getQueryString(Integer page) {
// ?page=10&pageSize=10&option=A&keyword=title
return UriComponentsBuilder.newInstance()
.queryParam("page", page)
.queryParam("pageSize", sc.getPageSize())
.queryParam("option", sc.getOption())
.queryParam("keyword", sc.getKeyword())
.build().toString();
}
void print() {
System.out.println("page="+ sc.getPage());
System.out.print(showPrev? "PREV " : "");
for(int i=beginPage;i<=endPage;i++) {
System.out.print(i+" ");
}
System.out.println(showNext? " NEXT" : "");
}
public SearchCondition getSc() {
return sc;
}
public void setSc(SearchCondition sc) {
this.sc = sc;
}
public int getTotalCnt() {
return totalCnt;
}
public void setTotalCnt(int totalCnt) {
this.totalCnt = totalCnt;
}
public boolean isShowNext() {
return showNext;
}
public void setShowNext(boolean showNext) {
this.showNext = showNext;
}
public int getBeginPage() {
return beginPage;
}
public void setBeginPage(int beginPage) {
this.beginPage = beginPage;
}
public int getNAV_SIZE() {
return NAV_SIZE;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
public boolean isShowPrev() {
return showPrev;
}
public void setShowPrev(boolean showPrev) {
this.showPrev = showPrev;
}
@Override
public String toString() {
return "PageHandler{" +
"sc=" + sc +
", totalCnt=" + totalCnt +
", showNext=" + showNext +
", beginPage=" + beginPage +
", NAV_SIZE=" + NAV_SIZE +
", totalPage=" + totalPage +
", endPage=" + endPage +
", showPrev=" + showPrev +
'}';
}
}
Junit Test
import org.junit.Test;
import static org.junit.Assert.*;
public class PageHandlerTest {
@Test
public void test(){
PageHandler ph = new PageHandler(250,1);
ph.print();
System.out.println("ph = " + ph);
assertTrue(ph.getBeginPage() ==1);
assertTrue(ph.getBeginPage() == 10);
}
@Test
public void test2(){
PageHandler ph = new PageHandler(250,11);
ph.print();
System.out.println("ph = " + ph);
assertTrue(ph.getBeginPage() ==11);
assertTrue(ph.getBeginPage() == 20);
}
@Test
public void test3(){
PageHandler ph = new PageHandler(255,25);
ph.print();
System.out.println("ph = " + ph);
assertTrue(ph.getBeginPage() ==21);
assertTrue(ph.getEndPage() == 26);
}
}
SearchCondition
package com.test.ch4.domain;
import org.springframework.web.util.UriComponentsBuilder;
import static java.lang.Math.*;
import static java.util.Objects.requireNonNullElse;
public class SearchCondition {
private Integer page = 1;
private Integer pageSize = DEFAULT_PAGE_SIZE;
private String option = "";
private String keyword = "";
// private Integer offset;
public static final int MIN_PAGE_SIZE = 5;
public static final int DEFAULT_PAGE_SIZE = 10;
public static final int MAX_PAGE_SIZE = 50;
public SearchCondition(){}
public SearchCondition(Integer page, Integer pageSize) {
this(page, pageSize, "", "");
}
public SearchCondition(Integer page, Integer pageSize, String option, String keyword) {
this.page = page;
this.pageSize = pageSize;
this.option = option;
this.keyword = keyword;
}
public String getQueryString() {
return getQueryString(page);
}
public String getQueryString(Integer page) {
// ?page=10&pageSize=10&option=A&keyword=title
return UriComponentsBuilder.newInstance()
.queryParam("page", page)
.queryParam("pageSize", pageSize)
.queryParam("option", option)
.queryParam("keyword", keyword)
.build().toString();
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = requireNonNullElse(pageSize, DEFAULT_PAGE_SIZE);
// MIN_PAGE_SIZE <= pageSize <= MAX_PAGE_SIZE
this.pageSize = max(MIN_PAGE_SIZE, min(this.pageSize, MAX_PAGE_SIZE));
}
public String getOption() {
return option;
}
public void setOption(String option) {
this.option = option;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public Integer getOffset() {
return (page-1)*pageSize;
}
@Override
public String toString() {
return "SearchCondition{" +
"page=" + page +
", pageSize=" + pageSize +
", option='" + option + '\'' +
", keyword='" + keyword + '\'' +
'}';
}
}
BoardController
package com.test.ch4.controller;
import com.test.ch4.domain.*;
import com.test.ch4.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.ui.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
import javax.servlet.http.*;
import java.time.*;
import java.util.*;
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
BoardService boardService;
@PostMapping("/modify")
public String modify(BoardDto boardDto, SearchCondition sc, RedirectAttributes rattr, Model m, HttpSession session) {
String writer = (String)session.getAttribute("id");
boardDto.setWriter(writer);
try {
if (boardService.modify(boardDto)!= 1)
throw new Exception("Modify failed.");
rattr.addFlashAttribute("msg", "MOD_OK");
return "redirect:/board/list"+sc.getQueryString();
} catch (Exception e) {
e.printStackTrace();
m.addAttribute(boardDto);
m.addAttribute("msg", "MOD_ERR");
return "board";
}
}
@GetMapping("/write")
public String write(Model m) {
m.addAttribute("mode", "new");
return "board";
}
@PostMapping("/write")
public String write(BoardDto boardDto, RedirectAttributes rattr, Model m, HttpSession session) {
String writer = (String)session.getAttribute("id");
boardDto.setWriter(writer);
try {
if (boardService.write(boardDto) != 1)
throw new Exception("Write failed.");
rattr.addFlashAttribute("msg", "WRT_OK");
return "redirect:/board/list";
} catch (Exception e) {
e.printStackTrace();
m.addAttribute(boardDto);
m.addAttribute("mode", "new");
m.addAttribute("msg", "WRT_ERR");
return "board";
}
}
@GetMapping("/read")
public String read(Integer bno, SearchCondition sc, RedirectAttributes rattr, Model m) {
try {
BoardDto boardDto = boardService.read(bno);
m.addAttribute(boardDto);
} catch (Exception e) {
e.printStackTrace();
rattr.addFlashAttribute("msg", "READ_ERR");
return "redirect:/board/list"+sc.getQueryString();
}
return "board";
}
@PostMapping("/remove")
public String remove(Integer bno, SearchCondition sc, RedirectAttributes rattr, HttpSession session) {
String writer = (String)session.getAttribute("id");
String msg = "DEL_OK";
try {
if(boardService.remove(bno, writer)!=1)
throw new Exception("Delete failed.");
} catch (Exception e) {
e.printStackTrace();
msg = "DEL_ERR";
}
rattr.addFlashAttribute("msg", msg);
return "redirect:/board/list"+sc.getQueryString();
}
@GetMapping("/list")
public String list(Model m, SearchCondition sc, HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login?toURL="+request.getRequestURL(); // 로그인을 안했으면 로그인 화면으로 이동
try {
int totalCnt = boardService.getSearchResultCnt(sc);
m.addAttribute("totalCnt", totalCnt);
PageHandler pageHandler = new PageHandler(totalCnt, sc);
List<BoardDto> list = boardService.getSearchResultPage(sc);
m.addAttribute("list", list);
m.addAttribute("ph", pageHandler);
Instant startOfToday = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant();
m.addAttribute("startOfToday", startOfToday.toEpochMilli());
} catch (Exception e) {
e.printStackTrace();
m.addAttribute("msg", "LIST_ERR");
m.addAttribute("totalCnt", 0);
}
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
private boolean loginCheck(HttpServletRequest request) {
// 1. 세션을 얻어서(false는 session이 없어도 새로 생성하지 않는다. 반환값 null)
HttpSession session = request.getSession(false);
// 2. 세션에 id가 있는지 확인, 있으면 true를 반환
return session!=null && session.getAttribute("id")!=null;
}
}
boardMapper.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.test.ch4.dao.BoardMapper">
<select id="count" resultType="int">
SELECT count(*) FROM board
</select>
<delete id="deleteAll">
DELETE FROM board
</delete>
<delete id="delete" parameterType="map">
DELETE FROM board WHERE bno = #{bno} and writer = #{writer}
</delete>
<insert id="insert" parameterType="BoardDto">
INSERT INTO board
(title, content, writer)
VALUES
(#{title}, #{content}, #{writer})
</insert>
<select id="selectAll" resultType="BoardDto">
SELECT *
FROM board
ORDER BY reg_date DESC, bno DESC
</select>
<select id="select" parameterType="int" resultType="BoardDto">
SELECT bno, title, content, writer
, view_cnt, comment_cnt, reg_date
FROM board
WHERE bno = #{bno}
</select>
<select id="selectPage" parameterType="map" resultType="BoardDto">
SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
FROM board
ORDER BY reg_date DESC, bno DESC
LIMIT #{from}, #{size}
</select>
<update id="update" parameterType="BoardDto">
UPDATE board
SET title = #{title}
, content = #{content}
, up_date = now()
WHERE bno = #{bno}
</update>
<update id="increaseViewCnt" parameterType="int">
UPDATE board
SET view_cnt = view_cnt + 1
WHERE bno = #{bno}
</update>
<sql id="searchCondition">
<choose>
<when test='option=="T"'>
AND title LIKE concat('%', #{keyword}, '%')
</when>
<when test='option=="W"'>
AND writer LIKE concat('%', #{keyword}, '%')
</when>
<otherwise>
AND (title LIKE concat('%', #{keyword}, '%')
OR content LIKE concat('%', #{keyword}, '%'))
</otherwise>
</choose>
</sql>
<select id="searchSelectPage" parameterType="SearchCondition" resultType="BoardDto">
SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
FROM board
WHERE bno > 0
<include refid="searchCondition"/>
ORDER BY reg_date DESC, bno DESC
LIMIT #{offset}, #{pageSize}
</select>
<select id="searchResultCnt" parameterType="SearchCondition" resultType="int">
SELECT count(*)
FROM board
WHERE bno > 0
<include refid="searchCondition"/>
</select>
</mapper>
Junit Test
@Test
public void searchSelectPageTest() throws Exception {
boardDao.deleteAll();
for (int i = 1; i <= 20; i++) {
BoardDto boardDto = new BoardDto("title"+i , "www" , "asdf");
boardDao.insert(boardDto);
}
SearchCondition sc = new SearchCondition(1,10,"T","title2"); //title2%
List<BoardDto> list = boardDao.searchSelectPage(sc);
System.out.println("list = " + list);
assertTrue(list.size()==2); // 1~20 , title2 , title20
}
@Test
public void searchResultCntTest() throws Exception {
boardDao.deleteAll();
for (int i = 1; i <= 20; i++) {
BoardDto boardDto = new BoardDto("title"+i , "www" , "asdf");
boardDao.insert(boardDto);
}
SearchCondition sc = new SearchCondition(1,10,"T","title2"); //title2%
int cnt = boardDao.searchResultCnt(sc);
assertTrue(cnt==2); // 1~20 , title2 , title20
}
boardList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt_rt" %>
<%@ page session="true"%>
<c:set var="loginId" value="${sessionScope.id}"/>
<c:set var="loginOutLink" value="${loginId=='' ? '/login/login' : '/login/logout'}"/>
<c:set var="loginOut" value="${loginId=='' ? 'Login' : 'ID='+=loginId}"/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>fastcampus</title>
<link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Noto Sans KR", sans-serif;
}
a {
text-decoration: none;
color: black;
}
button,
input {
border: none;
outline: none;
}
.board-container {
width: 60%;
height: 1200px;
margin: 0 auto;
/* border: 1px solid black; */
}
.search-container {
background-color: rgb(253, 253, 250);
width: 100%;
height: 110px;
border: 1px solid #ddd;
margin-top : 10px;
margin-bottom: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.search-form {
height: 37px;
display: flex;
}
.search-option {
width: 100px;
height: 100%;
outline: none;
margin-right: 5px;
border: 1px solid #ccc;
color: gray;
}
.search-option > option {
text-align: center;
}
.search-input {
color: gray;
background-color: white;
border: 1px solid #ccc;
height: 100%;
width: 300px;
font-size: 15px;
padding: 5px 7px;
}
.search-input::placeholder {
color: gray;
}
.search-button {
/* 메뉴바의 검색 버튼 아이콘 */
width: 20%;
height: 100%;
background-color: rgb(22, 22, 22);
color: rgb(209, 209, 209);
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
}
.search-button:hover {
color: rgb(165, 165, 165);
}
table {
border-collapse: collapse;
width: 100%;
border-top: 2px solid rgb(39, 39, 39);
}
tr:nth-child(even) {
background-color: #f0f0f070;
}
th,
td {
width:300px;
text-align: center;
padding: 10px 12px;
border-bottom: 1px solid #ddd;
}
td {
color: rgb(53, 53, 53);
}
.no { width:150px;}
.title { width:50%; }
td.title { text-align: left; }
td.writer { text-align: left; }
td.viewcnt { text-align: right; }
td.title:hover {
text-decoration: underline;
}
.paging {
color: black;
width: 100%;
align-items: center;
}
.page {
color: black;
padding: 6px;
margin-right: 10px;
}
.paging-active {
background-color: rgb(216, 216, 216);
border-radius: 5px;
color: rgb(24, 24, 24);
}
.paging-container {
width:100%;
height: 70px;
display: flex;
margin-top: 50px;
margin : auto;
}
.btn-write {
background-color: rgb(236, 236, 236); /* Blue background */
border: none; /* Remove borders */
color: black; /* White text */
padding: 6px 12px; /* Some padding */
font-size: 16px; /* Set a font size */
cursor: pointer; /* Mouse pointer on hover */
border-radius: 5px;
margin-left: 30px;
}
.btn-write:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div id="menu">
<ul>
<li id="logo">fastcampus</li>
<li><a href="<c:url value='/'/>">Home</a></li>
<li><a href="<c:url value='/board/list'/>">Board</a></li>
<li><a href="<c:url value='${loginOutLink}'/>">${loginOut}</a></li>
<li><a href="<c:url value='/register/add'/>">Sign in</a></li>
<li><a href=""><i class="fa fa-search"></i></a></li>
</ul>
</div>
<script>
let msg = "${msg}";
if(msg=="LIST_ERR") alert("게시물 목록을 가져오는데 실패했습니다. 다시 시도해 주세요.");
if(msg=="READ_ERR") alert("삭제되었거나 없는 게시물입니다.");
if(msg=="DEL_ERR") alert("삭제되었거나 없는 게시물입니다.");
if(msg=="DEL_OK") alert("성공적으로 삭제되었습니다.");
if(msg=="WRT_OK") alert("성공적으로 등록되었습니다.");
if(msg=="MOD_OK") alert("성공적으로 수정되었습니다.");
</script>
<div style="text-align:center">
<div class="board-container">
<div class="search-container">
<form action="<c:url value="/board/list"/>" class="search-form" method="get">
<select class="search-option" name="option">
<option value="A" ${ph.sc.option=='A' || ph.sc.option=='' ? "selected" : ""}>제목+내용</option>
<option value="T" ${ph.sc.option=='T' ? "selected" : ""}>제목만</option>
<option value="W" ${ph.sc.option=='W' ? "selected" : ""}>작성자</option>
</select>
<input type="text" name="keyword" class="search-input" type="text" value="${ph.sc.keyword}" placeholder="검색어를 입력해주세요">
<input type="submit" class="search-button" value="검색">
</form>
<button id="writeBtn" class="btn-write" onclick="location.href='<c:url value="/board/write"/>'"><i class="fa fa-pencil"></i> 글쓰기</button>
</div>
<table>
<tr>
<th class="no">번호</th>
<th class="title">제목</th>
<th class="writer">이름</th>
<th class="regdate">등록일</th>
<th class="viewcnt">조회수</th>
</tr>
<c:forEach var="boardDto" items="${list}">
<tr>
<td class="no">${boardDto.bno}</td>
<td class="title"><a href="<c:url value="/board/read${ph.sc.queryString}&bno=${boardDto.bno}"/>">${boardDto.title}</a></td>
<td class="writer">${boardDto.writer}</td>
<c:choose>
<c:when test="${boardDto.reg_date.time >= startOfToday}">
<td class="regdate"><fmt:formatDate value="${boardDto.reg_date}" pattern="HH:mm" type="time"/></td>
</c:when>
<c:otherwise>
<td class="regdate"><fmt:formatDate value="${boardDto.reg_date}" pattern="yyyy-MM-dd" type="date"/></td>
</c:otherwise>
</c:choose>
<td class="viewcnt">${boardDto.view_cnt}</td>
</tr>
</c:forEach>
</table>
<br>
<div class="paging-container">
<div class="paging">
<c:if test="${totalCnt==null || totalCnt==0}">
<div> 게시물이 없습니다. </div>
</c:if>
<c:if test="${totalCnt!=null && totalCnt!=0}">
<c:if test="${ph.showPrev}">
<a class="page" href="<c:url value="/board/list${ph.sc.getQueryString(ph.beginPage-1)}"/>"><</a>
</c:if>
<c:forEach var="i" begin="${ph.beginPage}" end="${ph.endPage}">
<a class="page ${i==ph.sc.page? "paging-active" : ""}" href="<c:url value="/board/list${ph.sc.getQueryString(i)}"/>">${i}</a>
</c:forEach>
<c:if test="${ph.showNext}">
<a class="page" href="<c:url value="/board/list${ph.sc.getQueryString(ph.endPage+1)}"/>">></a>
</c:if>
</c:if>
</div>
</div>
</div>
</div>
</body>
</html>
board.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%@ page session="true"%>
<c:set var="loginId" value="${sessionScope.id}"/>
<c:set var="loginOutLink" value="${loginId=='' ? '/login/login' : '/login/logout'}"/>
<c:set var="loginOut" value="${loginId=='' ? 'Login' : 'ID='+=loginId}"/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>fastcampus</title>
<link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Noto Sans KR", sans-serif;
}
.container {
width : 50%;
margin : auto;
}
.writing-header {
position: relative;
margin: 20px 0 0 0;
padding-bottom: 10px;
border-bottom: 1px solid #323232;
}
input {
width: 100%;
height: 35px;
margin: 5px 0px 10px 0px;
border: 1px solid #e9e8e8;
padding: 8px;
background: #f8f8f8;
outline-color: #e6e6e6;
}
textarea {
width: 100%;
background: #f8f8f8;
margin: 5px 0px 10px 0px;
border: 1px solid #e9e8e8;
resize: none;
padding: 8px;
outline-color: #e6e6e6;
}
.frm {
width:100%;
}
.btn {
background-color: rgb(236, 236, 236); /* Blue background */
border: none; /* Remove borders */
color: black; /* White text */
padding: 6px 12px; /* Some padding */
font-size: 16px; /* Set a font size */
cursor: pointer; /* Mouse pointer on hover */
border-radius: 5px;
}
.btn:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div id="menu">
<ul>
<li id="logo">fastcampus</li>
<li><a href="<c:url value='/'/>">Home</a></li>
<li><a href="<c:url value='/board/list'/>">Board</a></li>
<li><a href="<c:url value='${loginOutLink}'/>">${loginOut}</a></li>
<li><a href="<c:url value='/register/add'/>">Sign in</a></li>
<li><a href=""><i class="fa fa-search"></i></a></li>
</ul>
</div>
<script>
let msg = "${msg}";
if(msg=="WRT_ERR") alert("게시물 등록에 실패하였습니다. 다시 시도해 주세요.");
if(msg=="MOD_ERR") alert("게시물 수정에 실패하였습니다. 다시 시도해 주세요.");
</script>
<div class="container">
<h2 class="writing-header">게시판 ${mode=="new" ? "글쓰기" : "읽기"}</h2>
<form id="form" class="frm" action="" method="post">
<input type="hidden" name="bno" value="${boardDto.bno}">
<input name="title" type="text" value="${boardDto.title}" placeholder=" 제목을 입력해 주세요." ${mode=="new" ? "" : "readonly='readonly'"}><br>
<textarea name="content" rows="20" placeholder=" 내용을 입력해 주세요." ${mode=="new" ? "" : "readonly='readonly'"}>${boardDto.content}</textarea><br>
<c:if test="${mode eq 'new'}">
<button type="button" id="writeBtn" class="btn btn-write"><i class="fa fa-pencil"></i> 등록</button>
</c:if>
<c:if test="${mode ne 'new'}">
<button type="button" id="writeNewBtn" class="btn btn-write"><i class="fa fa-pencil"></i> 글쓰기</button>
</c:if>
<c:if test="${boardDto.writer eq loginId}">
<button type="button" id="modifyBtn" class="btn btn-modify"><i class="fa fa-edit"></i> 수정</button>
<button type="button" id="removeBtn" class="btn btn-remove"><i class="fa fa-trash"></i> 삭제</button>
</c:if>
<button type="button" id="listBtn" class="btn btn-list"><i class="fa fa-bars"></i> 목록</button>
</form>
</div>
<script>
$(document).ready(function(){
let formCheck = function() {
let form = document.getElementById("form");
if(form.title.value=="") {
alert("제목을 입력해 주세요.");
form.title.focus();
return false;
}
if(form.content.value=="") {
alert("내용을 입력해 주세요.");
form.content.focus();
return false;
}
return true;
}
$("#writeNewBtn").on("click", function(){
location.href="<c:url value='/board/write'/>";
});
$("#writeBtn").on("click", function(){
let form = $("#form");
form.attr("action", "<c:url value='/board/write'/>");
form.attr("method", "post");
if(formCheck())
form.submit();
});
$("#modifyBtn").on("click", function(){
let form = $("#form");
let isReadonly = $("input[name=title]").attr('readonly');
// 1. 읽기 상태이면, 수정 상태로 변경
if(isReadonly=='readonly') {
$(".writing-header").html("게시판 수정");
$("input[name=title]").attr('readonly', false);
$("textarea").attr('readonly', false);
$("#modifyBtn").html("<i class='fa fa-pencil'></i> 등록");
return;
}
// 2. 수정 상태이면, 수정된 내용을 서버로 전송
form.attr("action", "<c:url value='/board/modify${searchCondition.queryString}'/>");
form.attr("method", "post");
if(formCheck())
form.submit();
});
$("#removeBtn").on("click", function(){
if(!confirm("정말로 삭제하시겠습니까?")) return;
let form = $("#form");
form.attr("action", "<c:url value='/board/remove${searchCondition.queryString}'/>");
form.attr("method", "post");
form.submit();
});
$("#listBtn").on("click", function(){
location.href="<c:url value='/board/list${searchCondition.queryString}'/>";
});
});
</script>
</body>
</html>
쿼리 보기
https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1
<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
resources > log4jdbc.log4j2.properties.xml
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
resources > logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- log4jdbc-log4j2 -->
<logger name="jdbc.sqlonly" level="INFO"/>
<logger name="jdbc.sqltiming" level="INFO"/>
<logger name="jdbc.audit" level="WARN"/>
<logger name="jdbc.resultset" level="INFO"/>
<logger name="jdbc.resultsettable" level="INFO"/>
<logger name="jdbc.connection" level="INFO"/>
</configuration>
root-context.xml
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<property name="url" value="jdbc:log4jdbc:mysql://localhost:3306/springbasic?useUnicode=true&characterEncoding=utf8"></property>
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/springbasic?useUnicode=true&characterEncoding=utf8"></property>-->
<property name="username" value="back"></property>
<property name="password" value="rhrlekd1+"></property>
</bean>
728x90