오늘이라도
[Spring] 17. 방명록 만들기 ③ : 글 상세 조회, 수정 본문
반응형
https://github.com/upcake/Class_Examples
교육 중에 작성한 예제들은 깃허브에 올려두고 있습니다.
gif 파일은 클릭해서 보는 것이 정확합니다.
- 방명록 만들기 ③ : 글 상세 조회, 수정 -
@charset "UTF-8";
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap');
body {
margin: 0 auto;
text-align: center;
font-size: 16px;
font-family: 'Noto Sans KR', sans-serif;
}
a:link, a:visited {
text-decoration: none;
color: #000;
}
#content {
padding: 20px 0;
min-width: 1024px; /* 창의 최소 크기 지정 */
}
img {
vertical-align: middle; /* 세로축 가운데 정렬 */
}
table {
width: 80%;
margin: 0 auto;
border: 1px solid;
border-collapse: collapse; /* 테두리 겹침 설정 collapse: 겹치지 않게 처리 */
}
table th, table td {
border: 1px solid;
padding: 5px 10px;
}
table td a:hover { font-weight: bold; }
.btnSet { margin-top: 20px; }
a.btn-fill, a.btn-empty {
text-align: center;
padding: 3px 10px;
border:1px solid #3367d6;
border-radius: 3px;
box-shadow: 2px 2px 3px #022d72;
/* 오른쪽, 아래쪽, 번진 정도 */
}
a.btn-fill {
background-color: #3367d6;
color: #fff;
}
a.btn-empty {
background-color: #fff;
color: #3367d6
}
a.btn-fill-s, a.btn-empty-s {
text-align: center;
padding: 1px 10px;
border:1px solid #c4dafc
border-radius: 3px;
box-shadow: 2px 2px 3px #022d72;
color: #0000cd;
font-size: 13px;
}
a.btn-fill-s {
background-color: #bacdfa;
}
a.btn-empty-s {
background-color: #fff;
}
.btnSet a:not(:first-child) {
margin-left: 3px;
}
a:hover { cursor:pointer; }
input {
height: 22px;
padding: 3px 5px;
font-size: 15px;
}
input[type=radio] {
width: 18px;
margin: 0 5px 3px;
vertical-align: middle;
}
table tr td label:not(:last-child) {
margin-right: 20px;
}
.w-pct60 { width: 60% }
.w-pct70 { width: 70% }
.w-pct80 { width: 80% }
.w-px40 { width: 40px }
.w-px60 { width: 60px }
.w-px80 { width: 80px }
.w-px100 { width: 100px }
.w-px120 { width: 120px }
.w-px140 { width: 140px }
.w-px160 { width: 160px }
.w-px180 { width: 180px }
.w-px200 { width: 200px }
.w-px300 { width: 300px }
.left { text-align: left }
.right { text-align: right }
.font-img { cursor: pointer; }
ul { list-style: none; padding: 0; }
#list-top { width: 80%; padding: 20px 10%;}
#list-top ul { margin:0; display:flex; }
#list-top ul:last-child { float: right; }
#list-top ul:first-child { float: left; }
#list-top ul li:not(:first-child) { margin-left:2px }
#list-top div { width: 100%; height: 32px;}
#list-top ul li * { vertical-align:middle; }
input[name=title] { width:calc(100% - 14px) }
textarea[name=content] { width:calc(100% - 6px); height: 150px; resize: none;}
/* 파일 첨부 */
.file-img { width: 18px; height:18px; cursor:poinrter; }
#attach-file, #delete-file { display:none; }
select { height: 32px }
ul.grid { width: 80%; margin: 0 10%; }
.grid li { float: left; width: 19%; height: 150px; border: 1px solid gray; box-sizing: border-box; }
.grid li:not(:nth-child(5n)) { margin: 0 1.25% 20px 0 }
.grid li div{ text-align: left; padding: 10px; }
.grid li div:nth-child(1) {
height:60px;
line-height: 23px;
overflow:hidden;
display:-webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical; /* 말 줄임 표시 */
word-Wrap: break-word; /* 영문 여러 줄 */
}
.grid li div:nth-child(2) { padding: 0 10px; }
▲common.css
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>board list jsp</title>
<style type="text/css">
table { table-layout:fixed; }
table td { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.grid li div span{ float: right; }
</style>
</head>
<body>
<h3>방명록</h3>
<form id="list" method="post" action="">
<input type="hidden" name="curPage" value="1" />
<input type="hidden" name="id" />
<div id="list-top">
<div>
<!-- 검색 -->
<ul>
<li>
<select name="search" class="w-px80">
<option value="all" ${page.search eq 'all' ? 'selected' : '' }>전체</option>
<option value="title" ${page.search eq 'title' ? 'selected' : '' }>제목</option>
<option value="content" ${page.search eq 'content' ? 'selected' : '' }>내용</option>
<option value="writer" ${page.search eq 'writer' ? 'selected' : '' }>작성자</option>
</select>
</li>
<li>
<input type="text" name="keyword" class="w-px300"/>
</li>
<li>
<a class="btn-fill" onclick="$('form').submit()">검색</a>
</li>
</ul>
<ul>
<li>
<select name="pageList" class="w-px80" onchange="$('[name=curPage]').val(1); $('form').submit()">
<option value="10" ${page.pageList eq 10 ? 'selected' : '' }>10개씩</option>
<option value="20" ${page.pageList eq 20 ? 'selected' : '' }>20개씩</option>
<option value="30" ${page.pageList eq 30 ? 'selected' : '' }>30개씩</option>
</select>
</li>
<li>
<select name="viewType" class="w-px100" onchange="$('form').submit()">
<option value="list" ${page.viewType eq 'list' ? 'selected' : '' }>리스트 형태</option>
<option value="grid" ${page.viewType eq 'grid' ? 'selected' : '' }>바둑판 형태</option>
</select>
</li>
<!-- 로그인되어 있으면 글쓰기 가능 -->
<core:if test="${!empty login_info }">
<li>
<!-- 글쓰기 버튼 -->
<a class="btn-fill" href="new.bo">글쓰기</a>
</li>
</core:if>
</ul>
</div>
</div>
</form>
<div id="data-list">
<core:if test="${page.viewType eq 'list' }">
<table>
<tr>
<th class="w-px60">번호</th>
<th>제목</th>
<th class="w-px100">작성자</th>
<th class="w-px120">작성일자</th>
<th class="w-px60">첨부 파일</th>
</tr>
<core:forEach items="${page.list }" var="vo">
<tr>
<td>${vo.no }</td>
<td class="left"><a onclick="go_detail(${vo.id})">${vo.title }</a></td>
<td>${vo.name }</td>
<td>${vo.writedate }</td>
<td>
<core:if test="${!empty vo.filename }">
<img src="img/attach.png" class="file-img"/>
</core:if>
</td>
</tr>
</core:forEach>
</table>
</core:if>
<core:if test="${page.viewType eq 'grid' }">
<ul class="grid">
<core:forEach items="${page.list }" var="vo">
<li>
<div><a onclick="go_detail(${vo.id})">${vo.title }</a></div>
<div>${vo.name }</div>
<div>
${vo.writedate }
<span>${empty vo.filename ? '' : '<img src="img/attach.png" class="file-img" />' }</span>
</div>
</li>
</core:forEach>
</ul>
</core:if>
</div>
<div class="btnSet">
<jsp:include page="/WEB-INF/views/include/page.jsp"/>
</div>
<script type="text/javascript">
$(function(){
$('#data-list ul').css('height',
( ( $('.grid li').length % 5 > 0 ? 1 : 0 ) + Math.floor($('.grid li').length / 5) )
* $('.grid li').outerHeight(true) - 20);
})
function go_detail(id) {
$('[name=id]').val(id);
$('form').attr('action', 'detail.bo');
$('form').submit();
}
</script>
</body>
</html>
▲list.jsp
package com.hanul.iot;
import java.io.File;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import board.BoardPage;
import board.BoardServiceImpl;
import board.BoardVO;
import common.CommonService;
import member.MemberVO;
@Controller
public class BoardController {
@Autowired private BoardServiceImpl service;
@Autowired private BoardPage page;
@Autowired private CommonService common;
//방명록 목록 화면 요청================================================================
@RequestMapping("/list.bo")
public String list(HttpSession session, Model model, @RequestParam(defaultValue = "1") int curPage,
String search, String keyword, @RequestParam(defaultValue = "10") int pageList,
@RequestParam(defaultValue = "list") String viewType) {
//DB에서 방명록 정보를 조회해와 목록 화면에 출력
session.setAttribute("category", "bo");
page.setCurPage(curPage);
page.setSearch(search);
page.setKeyword(keyword);
page.setPageList(pageList);
page.setViewType(viewType);
model.addAttribute("page", service.board_list(page));
return "board/list";
} //list()
//방명록 신규 화면 요청================================================================
@RequestMapping("/new.bo")
public String board() {
//방명록 글쓰기 화면으로 연결
return "board/new";
} // board()
//신규 방명록 저장 처리 요청================================================================
@RequestMapping("/insert.bo")
public String insert(BoardVO vo, MultipartFile file, HttpSession session) {
//화면에서 입력한 정보를 DB에 저장한 후 목록 화면으로 연결
if( !file.isEmpty() ) {
vo.setFilename( file.getOriginalFilename() );
vo.setFilepath(common.upload("board", file, session));
}
vo.setWriter( ((MemberVO) session.getAttribute("login_info")).getId() );
service.board_insert(vo);
return "redirect:list.bo";
} //insert()
//방명록 상세 화면 요청====================================================================
@RequestMapping("/detail.bo")
public String detail(int id, Model model) {
//선택한 방명록 글을 DB에서 조회해와 상세 화면에 출력
service.board_read(id);
model.addAttribute("vo", service.board_detail(id));
model.addAttribute("page", page);
model.addAttribute("crlf", "\r\n");
return "board/detail";
} //detail()
//방명록 상세 화면 요청====================================================================
@ResponseBody @RequestMapping("/download.bo")
public void download(int id, HttpSession session, HttpServletResponse response) {
//해당 글의 첨부 파일 정보를 조회해와 다운로드한다.
BoardVO vo = service.board_detail(id);
common.download(vo.getFilename(), vo.getFilepath(), session, response);
} //download()
//방명록 수정 화면 요청====================================================================
@RequestMapping("/modify.bo")
public String modify(int id, Model model) {
//선택한 방명록 글의 정보를 DB에서 조회해와 수정 화면에 출력
model.addAttribute("vo", service.board_detail(id));
return "board/modify";
} //modify()
//방명록 수정 화면 요청====================================================================
@RequestMapping("/update.bo")
public String update(BoardVO vo, MultipartFile file, HttpSession session, String attach, Model model) {
//화면에서 입력한 정보를 DB에 변경, 저장한 후 상세 화면으로 연결
BoardVO board = service.board_detail(vo.getId());
String uuid = session.getServletContext().getRealPath("resources") + board.getFilepath();
//파일을 첨부한 경우 - 없었는데 새로 첨부, 있었는데 바꿔 첨부
if( !file.isEmpty() ) {
vo.setFilename(file.getOriginalFilename());
vo.setFilepath(common.upload("board", file, session));
if( board.getFilename() != null ) {
File f = new File(uuid);
if( (f.exists()) ) { f.delete(); }
}
} else {
//파일 첨부가 없는 경우 - if 없었고, else 있었는데 그대로 사용하는 경우
if( attach.isEmpty() ) {
File f = new File(uuid);
if( (f.exists()) ) { f.delete(); }
} else {
vo.setFilename(board.getFilename());
vo.setFilepath(board.getFilepath());
}
}
service.board_update(vo);
//기존 방법
//return "redirect:detail.bo?id=" + vo.getId();
//다른 방법
model.addAttribute("url", "detail.bo");
model.addAttribute("id", vo.getId());
return "board/redirect";
}
} //class
▲BoardController.java
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>board detail jsp</title>
<style type="text/css">
table td { word-break:break-all; } /* 영문으로'만' 이루어진 글 줄바꿈 되게끔 처리 */
</style>
</head>
<body>
<h3>방명록 상세 조회</h3>
<table>
<tr>
<th class="w-px160">제목</th>
<td class="left" colspan="5" class="left">${vo.title }</td>
</tr>
<tr>
<th>작성자</th>
<td>${vo.name }</td>
<th class="w-px100">작성일자</th>
<td class="w-px100">${vo.writedate }</td>
<th class="w-px80">조회수</th>
<td class="w-px60">${vo.readcnt }</td>
</tr>
<tr>
<th>내용</th>
<td class="left" colspan="5">${fn:replace(vo.content, crlf, '<br>') }</td>
</tr>
<tr>
<th>첨부 파일</th>
<td class="left" colspan="5">
<core:if test="${!empty vo.filename }">
${vo.filename }
<a href="download.bo?id=${vo.id }"><i class="fas fa-download font-img"></i></a>
</core:if>
</td>
</tr>
</table>
<div class="btnSet">
<a class="btn-fill" onclick="go_list()">목록으로</a>
<!-- 작성자로 로그인한 경우만 수정/삭제 가능, 관리자는 삭제 가능 -->
<core:if test="${login_info.id eq vo.writer}">
<a class="btn-fill" onclick="$('form').attr('action', 'modify.bo'); $('form').submit()">수정</a>
</core:if>
<core:if test="${login_info.id eq vo.writer or login_info.admin eq 'Y' }">
<a class="btn-fill">삭제</a>
</core:if>
</div>
<form method="post" action="list.bo">
<input type="hidden" name="id" value="${vo.id }" />
<input type="hidden" name="curPage" value="${page.curPage }" />
<input type="hidden" name="search" value="${page.search }" />
<input type="hidden" name="keyword" value="${page.keyword }" />
<input type="hidden" name="viewType" value="${page.viewType }" />
<input type="hidden" name="pageList" value="${page.pageList }" />
</form>
<script type="text/javascript">
function go_list() {
$('form').submit();
}
</script>
</body>
</html>
▲detail.jsp
package board;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BoardServiceImpl implements BoardService {
@Autowired private BoardDAO dao;
@Override
public int board_insert(BoardVO vo) {
return dao.board_insert(vo);
}
@Override
public BoardPage board_list(BoardPage page) {
return dao.board_list(page);
}
@Override
public BoardVO board_detail(int id) {
return dao.board_detail(id);
}
@Override
public void board_read(int id) {
dao.board_read(id);
}
@Override
public int board_update(BoardVO vo) {
return dao.board_update(vo);
}
@Override
public int board_delete(int id) {
// TODO Auto-generated method stub
return 0;
}
}
▲BoardServiceImpl.java
package board;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class BoardDAO implements BoardService {
@Autowired private SqlSession sql;
@Override
public int board_insert(BoardVO vo) {
return sql.insert("board.mapper.insert", vo);
}
@Override
public BoardPage board_list(BoardPage page) {
page.setTotalList((Integer) sql.selectOne("board.mapper.total", page));
page.setList(sql.selectList("board.mapper.list", page));
return page;
}
@Override
public BoardVO board_detail(int id) {
return sql.selectOne("board.mapper.detail", id);
}
@Override
public void board_read(int id) {
sql.update("board.mapper.read", id);
}
@Override
public int board_update(BoardVO vo) {
return sql.update("board.mapper.update", vo);
}
@Override
public int board_delete(int id) {
// TODO Auto-generated method stub
return 0;
}
}
▲BoardDAO.java
<?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="board.mapper">
<select id="total" resultType="integer">
SELECT COUNT(*) FROM board <include refid="search"/>
</select>
<select id="list" resultType="board.BoardVO">
SELECT n.*, (SELECT name FROM member WHERE member.id=writer) name
FROM (SELECT b.*, ROWNUM no
FROM (SELECT * FROM board <include refid="search"/> ORDER by id) b
ORDER BY no DESC) n
WHERE no BETWEEN #{beginList } AND #{endList }
</select>
<sql id="search">
<if test="search == 'title' or search == 'content'" >
WHERE ${search } LIKE '%' || #{keyword } || '%'
</if>
<if test="search == 'writer'" >
WHERE <include refid="writer" />
</if>
<if test="search == 'all'">
WHERE TITLE LIKE '%' || #{keyword } || '%'
OR content LIKE '%' || #{keyword } || '%'
OR <include refid="writer" />
</if>
</sql>
<sql id="writer">
writer IN (SELECT id FROM member WHERE name LIKE '%' || #{keyword } || '%')
</sql>
<insert id="insert">
INSERT INTO board(title, content, writer, filename, filepath)
VALUES (#{title }, #{content}, #{writer}, #{filename, jdbcType=VARCHAR}, #{filepath, jdbcType=VARCHAR} )
</insert>
<update id="read">
UPDATE BOARD SET readcnt= readcnt + 1 WHERE id= #{id }
</update>
<select id="detail" resultType="board.BoardVO">
SELECT b.*, (SELECT name FROM member m WHERE m.id= b.writer) name
FROM board b
WHERE id=#{id }
</select>
<update id="update">
UPDATE board SET title=#{title }, content=#{content }, filename=#{filename, jdbcType=VARCHAR}, filepath=#{filepath, jdbcType=VARCHAR}
WHERE id=#{id }
</update>
</mapper>
▲board-mapper.xml
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>board modify jsp</title>
</head>
<body>
<h3>방명록 수정</h3>
<form method="post" action="update.bo" enctype="multipart/form-data">
<table>
<tr>
<th class="w-px160">제목</th>
<td><input type="text" name="title" value="${vo.title }" class="need" title="제목"/></td>
</tr>
<tr>
<th>작성자</th>
<td>${vo.name }</td>
</tr>
<tr>
<th>내용</th>
<td><textarea name="content" class="need" title="내용">${vo.content }</textarea></td>
</tr>
<tr>
<th>첨부 파일</th>
<td class="left">
<label>
<input type="file" name="file" id="attach-file" />
<img src="img/select.png" class="file-img" />
</label>
<span id="file-name">${vo.filename }</span>
<span id="delete-file" style="color:red"><i class="fas fa-times font-img"></i></span>
</td>
</tr>
</table>
<input type="hidden" name="attach"/>
<input type="hidden" name="id" value="${vo.id }" />
</form>
<div class="btnSet">
<a class="btn-fill" onclick="if( necessary() ) { $('[name=attach]').val( $('#file-name').text() ); $('form').submit(); }">저장</a>
<a class="btn-empty" href="javascript:history.go(-1)">취소</a>
</div>
<script type="text/javascript" src="js/file_attach.js"></script>
<script type="text/javascript" src="js/need_check.js"></script>
<script type="text/javascript">
if(${!empty vo.filename}) {
$('#delete-file').css("display", "inline");
}
</script>
</body>
</html>
▲modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<form method="post" action="${url }">
<input type="text" name="id" value="${id }"/>
</form>
<script>
$('form').submit();
</script>
▲redirect.jsp
반응형
'취업성공패키지 SW 개발자 교육 > Spring' 카테고리의 다른 글
[Spring] 19. 방명록 만들기 ⑤ : 덧글 수정, 삭제 / 공공 데이터 API 가져오기 (0) | 2020.07.27 |
---|---|
[Spring] 18. 방명록 만들기 ④ : 사진 미리보기, 덧글 기능 (0) | 2020.07.24 |
[Spring] 16. 방명록 만들기 ② : 첨부파일 미리보기, 게시글 표시 갯수, 게시판 표시 형태 (0) | 2020.07.22 |
[Spring] 15. 미니 프로젝트 : Q&A 게시판 만들기 / 방명록 만들기 (1) | 2020.07.21 |
[Spring] 14. 웹사이트 만들기 ⑬ : 답글 처리, 검색 기능 (0) | 2020.07.17 |