springPage
AdminCheckInterceptor
생성package kr.spring.interceptor;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class AdminCheckInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LoggerFactory.getLogger(AdminCheckInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("<<AdminCheckInterceptor 진입>>");
HttpSession session = request.getSession();
Integer user_auth = (Integer)session.getAttribute("user_auth");
if(user_auth==null || user_auth!=3) { // 관리자 권한으로 접속하지 않은 경우
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/common/notice.jsp");
dispatcher.forward(request, response);
return false;
}
return true;
}
}
servlet-context.xml
에서 <interceptor>
태그 사이에 다음의 내용을 추가 <mapping path="/member/admin_list.do"/>
<mapping path="/member/admin_update.do"/>
<mapping path="/board/write.do"/>
<mapping path="/board/update.do"/>
<mapping path="/board/delete.do"/>
servlet-context.xml
에서 <interceptors>
태그 사이에 다음의 내용을 추가 <interceptor>
<mapping path="/member/admin_list.do"/>
<mapping path="/member/admin_update.do"/>
<beans:bean class="kr.spring.interceptor.AdminCheckInterceptor"/>
</interceptor>
root-context.xml
에서 <beans>
태그 사이의 내용을 다음처럼 수정 <!--
빈 자동 스캔 - servlet-context.xml에서 Controller를 자동 스캔 설정해서 아래 설정에서는
Controller 자동 스캔 제외
-->
<context:component-scan base-package="kr.spring.**">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- mybatis 설정 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="kr.spring.member.vo,kr.spring.board.vo"/> <!-- **을 인식하지 못해서 ,로 패키지명을 나열해야 함 -->
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="kr.spring.**.dao"/>
</bean>
servlet-context.xml
에서 <beans:property name="definitions">
태그 사이의 내용을 다음처럼 수정 <beans:list>
<beans:value>/WEB-INF/tiles-def/main.xml</beans:value>
<beans:value>/WEB-INF/tiles-def/member.xml</beans:value>
<beans:value>/WEB-INF/tiles-def/board.xml</beans:value>
</beans:list>
servlet-context.xml
에서 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- 파일 다운로드 -->
<beans:bean id="downloadView" class="kr.spring.view.DownloadView"/>
css
폴더의 style.css
하단에 다음의 내용을 추가/* 본문 */
.page-one {
width: 600px;
margin: 0 auto;
}
table.sql
하단에 다음의 내용을 추가/* 게시판 */
CREATE TABLE spboard(
board_num NUMBER NOT NULL,
title VARCHAR2(100) NOT NULL,
content CLOB not null,
hit NUMBER(5) DEFAULT 0 NOT NULL,
reg_date DATE DEFAULT SYSDATE NOT NULL,
modify_date DATE,
uploadfile BLOB,
filename VARCHAR2(100),
ip VARCHAR2(40) NOT NULL,
mem_num NUMBER NOT NULL,
CONSTRAINT spboard_pk PRIMARY KEY (board_num),
CONSTRAINT spboard_spmember_fk FOREIGN KEY (mem_num) REFERENCES spmember (mem_num)
);
CREATE SEQUENCE spboard_seq;
tiles-def
폴더에 새 XML 파일 board.xml
생성<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="boardList" extends="main">
<put-attribute name="title" value="게시판 목록"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardList.jsp"/>
</definition>
<definition name="boardWrite" extends="main">
<put-attribute name="title" value="글쓰기"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardWrite.jsp"/>
</definition>
<definition name="boardView" extends="main">
<put-attribute name="title" value="게시판 상세 정보"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardView.jsp"/>
</definition>
<definition name="boardModify" extends="main">
<put-attribute name="title" value="글 상세 정보 수정"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardModify.jsp"/>
</definition>
</tiles-definitions>
validation.properties
하단에 다음의 내용을 추가# 게시판
NotEmpty.title=제목은 필수 항목입니다
NotEmpty.content=내용은 필수 항목입니다
views
폴더의 하위 폴더로 common
생성하고 새 JSP 파일 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" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>안내</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css">
</head>
<body>
<div class="page-one">
<h2>안내</h2>
<div class="result-display">
<div class="align-center">
<c:if test="${!empty accessMsg}">
${accessMsg}
</c:if>
<c:if test="${empty accessMsg}">
잘못된 접근입니다.
</c:if>
</div>
<p>
<c:if test="${!empty accessUrl}">
<input type="button" value="이동" onclick="location.href = '${accessUrl}';">
</c:if>
<c:if test="${empty accessUrl}">
<input type="button" value="홈으로" onclick="location.href = '${pageContext.request.contextPath}/main/main.do';">
</c:if>
</div>
</div>
</body>
</html>
kr.spring.board.vo
생성하고 새 클래스 BoardVO
생성package kr.spring.board.vo;
import java.io.IOException;
import java.sql.Date;
import javax.validation.constraints.NotEmpty;
import org.springframework.web.multipart.MultipartFile;
public class BoardVO {
private int board_num; // 게시글 번호
@NotEmpty
private String title; // 제목
@NotEmpty
private String content; // 내용
private int hit; // 조회수
private Date reg_date; // 등록일
private Date modify_date; // 수정일
private MultipartFile upload;
private byte[] uploadfile; // 파일
private String filename; // 파일명
private String ip; // IP 주소
private int mem_num; // 작성자 회원 번호
private String id; // 작성자 아이디
// 업로드 파일 처리
public void setUpload(MultipartFile upload) throws IOException {
this.upload = upload;
// MultipartFile을 byte[]로 변환
setUploadfile(upload.getBytes());
// 파일명 구하기
setFilename(upload.getOriginalFilename());
}
public int getBoard_num() {
return board_num;
}
public void setBoard_num(int board_num) {
this.board_num = board_num;
}
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 int getHit() {
return hit;
}
public void setHit(int hit) {
this.hit = hit;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
public Date getModify_date() {
return modify_date;
}
public void setModify_date(Date modify_date) {
this.modify_date = modify_date;
}
public MultipartFile getUpload() {
return upload;
}
public byte[] getUploadfile() {
return uploadfile;
}
public void setUploadfile(byte[] uploadfile) {
this.uploadfile = uploadfile;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getMem_num() {
return mem_num;
}
public void setMem_num(int mem_num) {
this.mem_num = mem_num;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// byte[] 자료형의 데이터를 출력시 양이 많아 프로그램이 느려지므로 uploadfile을 제외한 다른 필드들만 출력하도록 toString() 재정의
@Override
public String toString() {
return "BoardVO [board_num=" + board_num + ", title=" + title + ", content=" + content + ", hit=" + hit
+ ", reg_date=" + reg_date + ", modify_date=" + modify_date + ", upload=" + upload + ", filename="
+ filename + ", ip=" + ip + ", mem_num=" + mem_num + ", id=" + id + "]";
}
}
kr.spring.board.dao
생성하고 새 인터페이스 BoardMapper
생성package kr.spring.board.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import kr.spring.board.vo.BoardVO;
public interface BoardMapper {
// 부모글
public List<BoardVO> selectList(Map<String, Object> map);
public int selectRowCount(Map<String, Object> map);
@Insert("INSERT INTO spboard (board_num, title, content, uploadfile, filename, ip, mem_num) "
+ "VALUES (spboard_seq.NEXTVAL, #{title}, #{content}, #{uploadfile}, #{filename}, #{ip}, #{mem_num})")
public void insertBoard(BoardVO board);
@Select("SELECT * FROM spboard JOIN spmember USING (mem_num) WHERE board_num=#{board_num}")
public BoardVO selectBoard(Integer board_num);
@Update("UPDATE spboard SET hit=hit+1 WHERE board_num=#{board_num}")
public void updateHit(Integer board_num);
public void updateBoard(BoardVO board);
@Delete("DELETE FROM spboard WHERE board_num=#{board_num}")
public void deleteBoard(Integer board_num);
@Update("UPDATE spboard SET uploadfile='', filename='' WHERE board_num=#{board_num}")
public void deleteFile(Integer board_num);
}
kr.spring.board.dao
패키지에 새 XML 파일 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="kr.spring.board.dao.BoardMapper">
<!-- 목록 -->
<select id="selectList" parameterType="map" resultType="boardVO">
SELECT
*
FROM (SELECT
a.*,
ROWNUM AS rnum
FROM (SELECT
board_num,
<![CDATA[
REPLACE(REPLACE(title, '<', '<'), '>', '>') AS title,
]]>
hit,
reg_date,
id
FROM spboard
JOIN spmember
USING (mem_num)
<where>
<if test="keyword!='' and keyfield==1">
title LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==2">
id LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==3">
content LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==4">
title LIKE '%' || #{keyword} || '%'
OR
content LIKE '%' || #{keyword} || '%'
</if>
</where>
ORDER BY board_num DESC) a)
<![CDATA[
WHERE rnum>=#{start} AND rnum<=#{end}
]]>
</select>
<select id="selectRowCount" parameterType="map" resultType="integer">
SELECT
COUNT(*)
FROM spboard
JOIN spmember
USING (mem_num)
<where>
<if test="keyword!='' and keyfield==1">
title LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==2">
id LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==3">
content LIKE '%' || #{keyword} || '%'
</if>
<if test="keyword!='' and keyfield==4">
title LIKE '%' || #{keyword} || '%'
OR
content LIKE '%' || #{keyword} || '%'
</if>
</where>
</select>
<!-- 수정 -->
<update id="updateBoard" parameterType="boardVO">
UPDATE spboard SET
<if test="filename!=''">
uploadfile=#{uploadfile},
filename=#{filename},
</if>
title=#{title},
content=#{content},
ip=#{ip},
modify_date=SYSDATE
WHERE board_num=#{board_num}
</update>
</mapper>
kr.spring.board.service
생성하고 새 인터페이스 BoardService
생성package kr.spring.board.service;
import java.util.List;
import java.util.Map;
import kr.spring.board.vo.BoardVO;
public interface BoardService {
// 부모글
public List<BoardVO> selectList(Map<String, Object> map);
public int selectRowCount(Map<String, Object> map);
public void insertBoard(BoardVO board);
public BoardVO selectBoard(Integer board_num);
public void updateHit(Integer board_num);
public void updateBoard(BoardVO board);
public void deleteBoard(Integer board_num);
public void deleteFile(Integer board_num);
}
BoardServiceImpl
생성package kr.spring.board.service;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.spring.board.dao.BoardMapper;
import kr.spring.board.vo.BoardVO;
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardMapper boardMapper;
@Override
public List<BoardVO> selectList(Map<String, Object> map) {
return boardMapper.selectList(map);
}
@Override
public int selectRowCount(Map<String, Object> map) {
return boardMapper.selectRowCount(map);
}
@Override
public void insertBoard(BoardVO board) {
boardMapper.insertBoard(board);
}
@Override
public BoardVO selectBoard(Integer board_num) {
return boardMapper.selectBoard(board_num);
}
@Override
public void updateHit(Integer board_num) {
boardMapper.updateHit(board_num);
}
@Override
public void updateBoard(BoardVO board) {
boardMapper.updateBoard(board);
}
@Override
public void deleteBoard(Integer board_num) {
boardMapper.deleteBoard(board_num);
}
@Override
public void deleteFile(Integer board_num) {
boardMapper.deleteFile(board_num);
}
}
kr.spring.board.controller
생성하고 새 클래스 BoardController
생성package kr.spring.board.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import kr.spring.board.service.BoardService;
import kr.spring.board.vo.BoardVO;
import kr.spring.util.PagingUtil;
import kr.spring.util.StringUtil;
@Controller
public class BoardController {
private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
@Autowired
private BoardService boardService;
@ModelAttribute
public BoardVO initCommand() {
return new BoardVO();
}
// 글 등록 폼
@GetMapping("/board/write.do")
public String form() {
// Tiles 설정 반환
return "boardWrite";
}
// 글 등록 폼에서 전송된 데이터 처리
@PostMapping("/board/write.do")
public String submit(@Valid BoardVO boardVO, BindingResult result, HttpSession session, HttpServletRequest request) {
logger.info("<<게시판 글 저장>> : " + boardVO);
// 유효성 검증 결과 오류가 있으면 폼 호출
if(result.hasErrors()) {
return form();
}
Integer user_num = (Integer)session.getAttribute("user_num");
// 작성자 회원 번호 세팅
boardVO.setMem_num(user_num);
// IP 주소 세팅
boardVO.setIp(request.getRemoteAddr());
// 글쓰기
boardService.insertBoard(boardVO);
return "redirect:/board/list.do";
}
// 게시판 글 목록
@RequestMapping("/board/list.do")
public ModelAndView process(@RequestParam(value="pageNum", defaultValue="1") int currentPage,
@RequestParam(value="keyfield", defaultValue="") String keyfield,
@RequestParam(value="keyword", defaultValue="") String keyword) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("keyfield", keyfield);
map.put("keyword", keyword);
// 총 게시글 수 또는 검색된 게시글 수
int count = boardService.selectRowCount(map);
// 페이지 처리
PagingUtil page = new PagingUtil(keyfield, keyword, currentPage, count, 20, 10, "list.do");
map.put("start", page.getStartCount());
map.put("end", page.getEndCount());
List<BoardVO> list = null;
if(count>0) {
list = boardService.selectList(map);
}
ModelAndView mav = new ModelAndView();
mav.setViewName("boardList"); // Tiles 설정명을 view명으로 지정
mav.addObject("count", count);
mav.addObject("list", list);
mav.addObject("pagingHtml", page.getPagingHtml());
return mav;
}
// 게시판 글 상세
@RequestMapping("/board/detail.do")
public ModelAndView process(@RequestParam int board_num) {
logger.info("<<게시판 글 상세 : 글 번호 >> : " + board_num);
// 해당 글의 조회수 증가
boardService.updateHit(board_num);
BoardVO board = boardService.selectBoard(board_num);
// 타이틀 HTML 불허
board.setTitle(StringUtil.useNoHtml(board.getTitle()));
return new ModelAndView("boardView", "board", board); // Tiles 설정명, 속성명, 속성값을 저장해 반환
}
// 이미지 출력
@RequestMapping("/board/imageView.do")
public ModelAndView viewImage(@RequestParam int board_num) {
BoardVO board = boardService.selectBoard(board_num);
ModelAndView mav = new ModelAndView();
mav.setViewName("imageView");
mav.addObject("imageFile", board.getUploadfile());
mav.addObject("filename", board.getFilename());
return mav;
}
// 파일 다운로드
@RequestMapping("/board/file.do")
public ModelAndView download(@RequestParam int board_num) {
BoardVO board = boardService.selectBoard(board_num);
ModelAndView mav = new ModelAndView();
mav.setViewName("downloadView");
mav.addObject("downloadFile", board.getUploadfile());
mav.addObject("filename", board.getFilename());
return mav;
}
// 수정 폼
@GetMapping("/board/update.do")
public String formUpdate(@RequestParam int board_num, Model model) {
BoardVO boardVO = boardService.selectBoard(board_num);
model.addAttribute("boardVO", boardVO);
return "boardModify";
}
// 수정 폼에서 전송된 데이터 처리
@PostMapping("/board/update.do")
public String submitUpdate(@Valid BoardVO boardVO, BindingResult result, HttpServletRequest request, Model model) {
logger.info("<<글 정보 수정>> : " + boardVO);
// 유효성 검증 결과에 오류가 있으면 폼 호출
if(result.hasErrors()) {
// title 또는 content가 입력되지 않으면 유효성 검증시 오류가 발생하고 파일 정보를 잃어버리기 때문에 폼을 호출할 때 다시 세팅
BoardVO vo = boardService.selectBoard(boardVO.getBoard_num());
boardVO.setFilename(vo.getFilename());
return "boardModify";
}
// ip 세팅
boardVO.setIp(request.getRemoteAddr());
// 글 수정
boardService.updateBoard(boardVO);
// view에 표시할 메시지
model.addAttribute("message", "글 수정 완료!");
model.addAttribute("url", request.getContextPath() + "/board/list.do");
return "common/resultView"; // JSP 반환
}
// 게시판 글 삭제
@RequestMapping("/board/delete.do")
public String submitDelete(@RequestParam int board_num) {
boardService.deleteBoard(board_num);
return "redirect:/board/list.do";
}
}
BoardAjaxController
생성package kr.spring.board.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import kr.spring.board.service.BoardService;
@Controller
public class BoardAjaxController {
private static final Logger logger = LoggerFactory.getLogger(BoardAjaxController.class);
@Autowired
private BoardService boardService;
// 부모글 업로드된 파일 삭제
@RequestMapping("/board/deleteFile.do")
@ResponseBody
public Map<String, String> processFile(int board_num, HttpSession session) {
Map<String, String> map = new HashMap<String, String>();
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num==null) { // 로그인되어 있지 않은 경우
map.put("result", "logout");
}
else {
boardService.deleteFile(board_num);
map.put("result", "success");
}
return map;
}
}
views
폴더의 하위 폴더로 board
생성하고 새 JSP 파일 boardList.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!-- 중앙 컨텐츠 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
$('#search_form').submit(function() {
if($('#keyword').val().trim()=='') {
alert('검색어를 입력하세요!');
$('#keyword').val('').focus();
return false;
}
}); // end of submit
});
</script>
<div class="page-main">
<h2>게시판 목록</h2>
<form action="list.do" id="search_form" method="get">
<ul class="search">
<li>
<select name="keyfield" id="keyfield">
<option value="1" <c:if test="${param.keyfield==1}">selected</c:if>>제목</option>
<option value="2" <c:if test="${param.keyfield==2}">selected</c:if>>아이디</option>
<option value="3" <c:if test="${param.keyfield==3}">selected</c:if>>내용</option>
<option value="4" <c:if test="${param.keyfield==4}">selected</c:if>>제목+내용</option>
</select>
</li>
<li>
<input type="search" name="keyword" id="keyword">
</li>
<li>
<input type="submit" value="검색">
<input type="button" value="목록" onclick="location.href = 'list.do';">
</li>
</ul>
</form>
<c:if test="${!empty user_num}">
<div class="align-right">
<input type="button" value="글쓰기" onclick="location.href = 'write.do';">
</div>
</c:if>
<c:if test="${count==0}">
<div class="result-display">표시할 게시물이 없습니다.</div>
</c:if>
<c:if test="${count>0}">
<table>
<tr>
<th>번호</th>
<th width="400">제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회수</th>
</tr>
<c:forEach var="board" items="${list}">
<tr>
<td>${board.board_num}</td>
<td><a href="detail.do?board_num=${board.board_num}">${board.title}</a></td>
<td>${board.id}</td>
<td>${board.reg_date}</td>
<td>${board.hit}</td>
</tr>
</c:forEach>
</table>
<div class="align-center">${pagingHtml}</div>
</c:if>
</div>
<!-- 중앙 컨텐츠 끝 -->
board
폴더에 새 JSP 파일 boardWrite.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!-- 중앙 컨텐츠 시작 -->
<div class="page-main">
<h2>글쓰기</h2>
<form:form modelAttribute="boardVO" action="write.do" enctype="multipart/form-data" id="register_form">
<form:errors element="div" cssClass="error-color"/>
<ul>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="content">내용</form:label>
<form:textarea path="content"/>
<form:errors path="content" cssClass="error-color"/>
</li>
<li>
<form:label path="upload">파일 업로드</form:label>
<input type="file" name="upload" id="upload">
<form:errors path="upload" cssClass="error-color"/>
</li>
</ul>
<div class="align-center">
<form:button>전송</form:button>
<input type="button" value="목록" onclick="location.href = 'list.do';">
</div>
</form:form>
</div>
<!-- 중앙 컨텐츠 끝 -->
board
폴더에 새 JSP 파일 boardView.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://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!-- 중앙 컨텐츠 시작 -->
<div class="page-main">
<h2>${board.title}</h2>
<ul>
<li>번호 : ${board.board_num}</li>
<li>작성자 : ${board.id}</li>
<li>조회수 : ${board.hit}</li>
<c:if test="${!empty board.modify_date}">
<li>최근 수정일 : ${board.modify_date}</li>
</c:if>
<li>작성일 : ${board.reg_date}</li>
<c:if test="${!empty board.filename}">
<li>첨부 파일 : <a href="file.do?board_num=${board.board_num}">${board.filename}</a></li>
</c:if>
</ul>
<hr size="1" width="100%" noshade>
<c:if test="${fn:endsWith(board.filename, '.jpg') ||
fn:endsWith(board.filename, '.JPG') ||
fn:endsWith(board.filename, '.png') ||
fn:endsWith(board.filename, '.PNG') ||
fn:endsWith(board.filename, '.gif') ||
fn:endsWith(board.filename, '.GIF')}">
<div class="align-center">
<img src="imageView.do?board_num=${board.board_num}" style="max-width: 500px">
</div>
</c:if>
<p>
${board.content}
</p>
<hr size="1" width="100%" noshade>
<div class="align-right">
<c:if test="${!empty user_num && user_num==board.mem_num}">
<input type="button" value="수정" onclick="location.href = 'update.do?board_num=${board.board_num}';">
<input type="button" value="삭제" id="delete_btn">
<script type="text/javascript">
let delete_btn = document.getElementById('delete_btn');
delete_btn.onclick = function() {
let choice = confirm('삭제하시겠습니까?');
if(choice) {
location.replace('delete.do?board_num=${board.board_num}');
}
};
</script>
</c:if>
<input type="button" value="목록" onclick="location.href = 'list.do';">
</div>
</div>
<!-- 중앙 컨텐츠 끝 -->
board
폴더에 새 JSP 파일 boardModify.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!-- 중앙 컨텐츠 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>
<div class="page-main">
<h2>글 수정</h2>
<form:form modelAttribute="boardVO" action="update.do" enctype="multipart/form-data" id="update_form">
<form:hidden path="board_num"/>
<form:errors element="div" cssClass="error-color"/>
<ul>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="content">내용</form:label>
<form:textarea path="content"/>
<form:errors path="content" cssClass="error-color"/>
</li>
<li>
<form:label path="upload">파일 업로드</form:label>
<input type="file" name="upload" id="upload">
<c:if test="${!empty boardVO.filename}">
<br>
<span id="file_detail">(${boardVO.filename}) 파일이 등록되어 있습니다.
다시 업로드하면 기존 파일은 삭제됩니다.
<input type="button" value="파일 삭제" id="file_del">
</span>
<script type="text/javascript">
$(function() {
$('#file_del').click(function() {
let choice = confirm('삭제하시겠습니까?');
if(choice) {
$.ajax({
url:'deleteFile.do',
data:{board_num:${boardVO.board_num}},
type:'post',
dataType:'json',
cache:false,
timeout:30000,
success:function(param) {
if(param.result=='logout') {
alert('로그인 후 사용하세요!');
}
else if(param.result=='success') {
$('#file_detail').hide();
}
else {
alert('파일 삭제 오류 발생!');
}
},
error:function() {
alert('네트워크 오류 발생!');
}
}); // end of ajax
}
}); // end of click
});
</script>
</c:if>
<form:errors path="upload" cssClass="error-color"/>
</li>
</ul>
<div class="align-center">
<form:button>전송</form:button>
<input type="button" value="목록" onclick="location.href = 'list.do';">
</div>
</form:form>
</div>
<!-- 중앙 컨텐츠 끝 -->
common
폴더에 새 JSP 파일 resultView.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script type="text/javascript">
alert('${message}');
location.href = '${url}';
</script>
kr.spring.util
패키지에 새 클래스 StringUtil
생성package kr.spring.util;
public class StringUtil {
//HTML 태그를 허용하면서 줄바꿈
public static String useBrHtml(String str) {
if(str == null) return null;
return str.replaceAll("\r\n", "<br>")
.replaceAll("\r", "<br>")
.replaceAll("\n", "<br>");
}
//HTML 태그를 허용하지 않으면서 줄바꿈
public static String useBrNoHtml(String str) {
if(str == null) return null;
return str.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\r\n", "<br>")
.replaceAll("\r", "<br>")
.replaceAll("\n", "<br>");
}
//HTML 태그를 허용하지 않음
public static String useNoHtml(String str) {
if(str == null) return null;
return str.replaceAll("<", "<")
.replaceAll(">", ">");
}
//특정 문자열 이후에 ...으로 처리
public static String shortWords(int length, String content) {
if(content == null) return null;
if(content.length() > length) {
return content.substring(0,length) + " ...";
}
return content;
}
}
DownloadView
생성package kr.spring.view;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.web.servlet.view.AbstractView;
public class DownloadView extends AbstractView {
public DownloadView() {
setContentType("application/download; charset=utf-8");
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
byte[] file = (byte[])model.get("downloadFile");
String filename = (String)model.get("filename");
String userAgent = request.getHeader("User-Agent");
boolean ie = userAgent.indexOf("MSIE") > -1;
String fileName = null;
if (ie) {
fileName = URLEncoder.encode(filename, "utf-8");
} else {
fileName = new String(filename.getBytes("utf-8"),
"iso-8859-1");
}
response.setHeader("Content-Disposition", "attachment; filename=\""
+ fileName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
OutputStream out = response.getOutputStream();
InputStream input = new ByteArrayInputStream(file);
IOUtils.copy(input, out);
out.flush();
out.close();
input.close();
}
}