고래씌
[JSP] 5-2. 게시판 상세 페이지 만들기(+첨부파일 업로드) 본문
1. 게시글 클릭시 상세보기 페이지로 넘어가는 기능 추가
① boardListVIew.jsp 파일에 게시글 클릭하면 상세보기 페이지로 넘어가는 기능 추가
=> 로그인한 회원만 글작성 버튼이 보이도록 설정
② 게시글 상세페이지 디자인
■ boardDetailView
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.kh.board.model.vo.*"%>
<%
Board b = (Board)request.getAttribute("b");
Attachment at = (Attachment) request.getAttribute("at");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%@include file="../common/menubar.jsp" %>
<div class="outer">
<br><h2 align="center">일반게시판 상세보기</h2><br>
<table id="detail-area" align="center" border="1">
<tr>
<th width="70">카테고리</th>
<td width="70"><%= b.getCategoryName() %></td>
<th width="70">제목</th>
<td width="350"><%= b.getBoardTitle() %></td>
</tr>
<tr>
<th>작성자</th>
<td><%= b.getBoardWriter() %></td>
<th>작성일</th>
<td><%= b.getCreateDate() %></td>
</tr>
<tr>
<th>내용</th>
<td colspan="3"> <!--열병합3칸-->
<p style="height:200px;">
<%= b.getBoardContent() %>
</p>
</td>
</tr>
<tr>
<th>첨부파일</th>
<td colspan="3">
<% if(at == null) { %>
첨부파일이 없습니다.
<% } else { %>
<!-- 첨부파일이 있을경우 해당파일 다운로드 -->
<a download="<%= at.getOriginName() %>"
href="<%= contextPath %>/<%= at.getFilePath()+at.getChangName()%>">
<!-- 다운로드 파일을 받을 경로 -->
<%= at.getOriginName() %>
</a>
<!-- at.getFilePath()+at.getChangName() => +를 이용해 하나로 표현해줌 -->
<% } %>
</td>
</tr>
</table>
<br>
<div align="center">
<a href="<%=contextPath%>/list.bo?currentPage=1" class="btn btn-secondary btn-sm">목록</a>
<!-- 로그인한 사용자와 현재 게시글을 작성한 작성자가 맞는지 검사 -->
<% if(loginUser != null && loginUser.getUserId().equals(b.getBoardWriter())) { %>
<!-- 로그인한 사용자만 게시글 수정/삭제버튼이 보여야함 -->
<a href="<%=contextPath %>" class="btn btn-warning btn-sm">수정</a>
<a href="<%=contextPath %>" class="btn btn-danger btn-sm">삭제</a>
<% } %>
</div>
<br>
<div id="reply-area">
<table border="1" align="center">
<thead>
<tr>
<th>댓글작성</th>
<td>
<textarea rows="3" cols="50" style="resize:none" readonly>로그인후 이용가능한 서비스입니다</textarea>
</td>
<td><button disabled>댓글등록</button></td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</body>
</html>
▶ 첨부파일이 있는 경우 및 없는 경우 if 문 처리
<th>첨부파일</th> <td colspan="3"> <% if(at == null) { %> 첨부파일이 없습니다. <% } else { %> <!-- 첨부파일이 있을경우 해당파일 다운로드 --> <a download="<%= at.getOriginName() %>" href="<%= contextPath %>/<%= at.getFilePath()+at.getChangName()%>"> <!-- 다운로드 파일을 받을 경로 --> <%= at.getOriginName() %> </a> <!-- at.getFilePath()+at.getChangName() => +를 이용해 하나로 표현해줌 --> <% } %> </td> |
▶ 로그인한 사용자만 사용할 수 있도록 하고, 로그인한 사용자와 현재 게시글을 작성한 작성자가 맞는지 검사
<div align="center"> <a href="<%=contextPath%>/list.bo?currentPage=1" class="btn btn-secondary btn-sm">목록</a> <!-- 로그인한 사용자와 현재 게시글을 작성한 작성자가 맞는지 검사 --> <% if(loginUser != null && loginUser.getUserId().equals(b.getBoardWriter())) { %> <!-- 로그인한 사용자만 게시글 수정/삭제버튼이 보여야함 --> <a href="<%=contextPath %>" class="btn btn-warning btn-sm">수정</a> <a href="<%=contextPath %>" class="btn btn-danger btn-sm">삭제</a> <% } %> </div> |
③ 게시글 상세피지와 첨부파일, 조회수증가하는 SQL쿼리문 작성하기
■ board-mapper.xml
▶ 각 게시글을 작성한 작성자, 카테고리, 작성날짜등의 데이터 등을 가져오기
=> 각 게시판의 제목, 카테고리 이름, userid등 가져오기 위해 select 쿼리문 작성
▶ 첨부파일 가져오기
=> 첨부파일은 Attachment 테이블에 REF_BNO을 이용해 가져옴
▶ 게시글 상세보기 페이지로 이동하면 해당 게시글의 조회수 증가되도록 SQL 쿼리문 작성
④ 서버 전송하는 Servlet 작성
■ BoardDetailController.java
package com.kh.board.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.kh.board.model.service.BoardService;
import com.kh.board.model.vo.Attachment;
import com.kh.board.model.vo.Board;
/**
* Servlet implementation class BoardDetailController
*/
@WebServlet("/detail.bo")
public class BoardDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public BoardDetailController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 사용자가 bno라는 이름으로 전달한 boardNo값 추출
int boardNo = Integer.parseInt(request.getParameter("bno"));
BoardService bService = new BoardService();
// 조회수 증가 서비스 => ()
int result = bService.increaseCount(boardNo);
if(result>0) { // 성공적으로 조회수가 증가했다면
// 게시글 정보 조회
Board b = bService.selectBoard(boardNo);
// Attachment 정보 조회
Attachment at = bService.selectAttachment(boardNo);
System.out.println(b);
System.out.println(at);
request.setAttribute("b", b);
request.setAttribute("at", at);
request.getRequestDispatcher("views/board/boardDetailView.jsp").forward(request, response);
} else { // 에러페이지로 이동
request.setAttribute("errorMsg", "게시글 상세조회 실패");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
⑤ BoardService.java에서 Connection 객체 생성
▶ 조회수 증가 관련 메소드
▶ 게시글 불러오기(상세페이지) 메소드
▶ 첨부파일 메소드
⑥ BoardDao.java에서 SQL 쿼리문 실행
▶ 조회수 증가 관련 메소드
▶ 게시글 불러오기(상세페이지) 메소드
▶ 첨부파일 메소드
▶ 결과화면
2. 게시글 작성 만들고 업로드 파일 저장
board_upfiles => 사용자가 업로드한 파일들이 저장되는 폴더
① 글작성 클릭 후 이동하는 페이지 만들기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.ArrayList, com.kh.board.model.vo.Category"%>
<%
ArrayList<Category> list = (ArrayList<Category>) request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
#enroll-form>table{border:1px solid white;}
#enroll-form input, #enroll-form textarea{
width:100%;
box-sizing:border-box;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">일반게시판 작성</h2>
<form action="<%= contextPath %>/insert.bo" id="enroll-form" method="post"
enctype="multipart/form-data">
<!-- 만약 내가 보내야하는 파일에 첨부파일이 있는 경우 enctype="multipart/form-data"로 전달해야함
그래야 정상적으로 파일이 전송되어진다!
-->
<!-- 카테고리, 제목, 내용, 첨부파일 넘겨주어야 함(사용자가 선택하거나 입력해야하는 것) -->
<!-- 회원번호 → input type hidden으로 넘길예정 (사용자가 자기 회원번호를 입력하는 것은 이상하니까) -->
<input type="hidden" name="userNo" value="<%= loginUser.getUserNo() %>">
<table align="center">
<tr>
<th width="100">카테고리</th>
<td width="500">
<select name="category">
<!-- <option value="10">공통</option> -->
<!-- <option value="20">운동</option> -->
<!-- <option value="30">등산</option> -->
<!-- <option value="40">게임</option> -->
<!-- <option value="50">낚시</option> -->
<!-- <option value="60">요리</option> -->
<!-- <option value="70">기타</option> -->
<!-- DB에 저장되어있는 category_no는 value에 저장 -->
<% for(Category c : list) { %>
<option value="<%= c.getCategoryNo() %>"><%= c.getCategoryName() %></option>
<% } %>
</select>
</td>
</tr>
<tr>
<th>제목</th>
<td><input type="text" name="title" required></td>
</tr>
<tr>
<th>내용</th>
<td>
<textarea name="content" rows="10" required></textarea>
</td>
</tr>
<tr>
<th>첨부파일</th>
<td>
<input type="file" name="upfile">
</td>
</tr>
</table>
<br>
<div align="center">
<button type="submit">작성</button>
</div>
</form>
</div>
</body>
</html>
▶ 내가 보내야하는 파일에 첨부파일이 있는 경우
=> enctype="multipart/form-data"로 전달해야한다. 그래야 정상적으로 파일이 전송되어진다!
② DB에 저장되어 있는 카테고리 리스트들을 가져오기 위해 SQL 문 작성
▶ board-mapper.xml
③ Servlet 작성하여 서버 연결
▶ BoardInsertController.java
package com.kh.board.controller;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import com.kh.board.model.service.BoardService;
import com.kh.board.model.vo.Category;
import com.kh.common.MyFileRenamePolicy;
import com.oreilly.servlet.MultipartRequest;
/**
* Servlet implementation class BoardInsertController
*/
@WebServlet("/insert.bo")
public class BoardInsertController extends HttpServlet {
private static final long serialVersionUID = 1L;
public BoardInsertController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ArrayList<Category> list = new BoardService().selectCategoryList();
request.setAttribute("list", list);
request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
}
④ 게시글 작성하는 Connection 객체 생성
▶ BoardService.java
⑤ SQL 쿼리문 실행
▶ BoardDao.java
3. 게시글 작성할 때 첨부파일이 있을 경우 전송
☞ form태그의 전송방법을 multipart/form-data로 전송하는 경우 request로 부터 값을 뽑는게 불가능하다!
☞ multipart로 값을 이관시킨 후 다뤄줘야 한다.
=> 우리는 앞서 첨부파일을 전송할 때 multipart을 이용하여 전송하는 방법으로 하였었다.
(메이븐 레파지토리)
https://mvnrepository.com/artifact/com.jfinal/cos
=> 2022.02 들어가 jar 파일 다운.
다운 후, lib 파일에 복사한다.
① 게시판 추가 와 게시판 추가할 때 첨부파일이 있을 경우 SQL 쿼리문 생성
▶ board-mapper.xml
② if문을 이용해 enctype이 multipart/form-data로 전송되었는지 확인
▶ BoardInsertController.java
package com.kh.board.controller;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import com.kh.board.model.service.BoardService;
import com.kh.board.model.vo.Attachment;
import com.kh.board.model.vo.Board;
import com.kh.board.model.vo.Category;
import com.kh.common.MyFileRenamePolicy;
import com.oreilly.servlet.MultipartRequest;
/**
* Servlet implementation class BoardInsertController
*/
@WebServlet("/insert.bo")
public class BoardInsertController extends HttpServlet {
private static final long serialVersionUID = 1L;
public BoardInsertController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ArrayList<Category> list = new BoardService().selectCategoryList();
request.setAttribute("list", list);
request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
/*
* form태그의 전송방법을 multipart/form-data로 전송하는 경우
* request로 부터 값을 뽑는게 불가능하다!
*
* multipart로 값을 이관시킨 후 다뤄줘야 한다.
*/
// enctype이 multipart/form-data로 전송되었는지 확인
if(ServletFileUpload.isMultipartContent(request)) {
// Servlet으로 multipart 파일 형식으로 파일을 업로드했냐 안했냐
// System.out.println("실행중");
// 파일 업로드를 위한 라이브러리 : cos.jar
// 1. 전송되는 파일을 처리할 작업내용(전송되는 파일의 용량제한, 전달된 파일을 저장할 폴더경로 제시)
// 1_1. 전송파일 용량제한(byte단위로 제시)
/*
* 1kbyte == 1024byte
* 1mbyte == 1024kbyte == 1024*1024(2의 20승)
*/
int maxSize = 10 * 1024 * 1024; // 10Mbyte;
// 1_2. 전달된 파일을 저장할 서버의 폴더 경로 알아내기
// 어플리케이션 객체에서 제공하는 getRealPath메소드를 통해 알아내기
// 매개변수로 board_upfiles폴더까지의 경로를 제시해줘야한다.
String savePath = request.getSession().getServletContext().getRealPath("/resources/board_upfiles/");
// System.out.println(savePath);
// "/" => WebContent 의미
/*
* 2. 전달된 파일명 수정 및 서버에 업로드
* - HttpServeltRequest request => MultipartRequest multiRequest로 변환
*
* 매개변수 생성자로 MultpartRequest객체를 생성(cos.jar에서 제공하는 객체)
* MultipartRequest multiRequest = new MultipartRequest(request 객체,
* 저장할 폴더경로, 용량제한, 인코딩값, 파일명을 수정시켜주는 객체);
*
* 위 구문 한줄 실행만으로 넘어온 첨부파일들이 해당 폴더에 업로드됨.
* 그리고 사용자가 올린 파일명은 그대로 해당 폴더에 업로드하지 않는다.(같은 파일명이 있을 경우
* 덮어씌워질 수도 있고, 서버에 따라 문제가 발생할 수도 있다.)
*
* cos.jar에서 제공하는 파일명 수정작업을 해주는 객체
* - DefaultFileRenamePolicy 객체
* - 내부적으로 rename()이라는 메소드가 실행이 되면서 파일명 수정이 진행됨
* - 작동방식은 동일한 파일명이 존재할 경우 카운팅된 숫자를 붙여서 파일명을 수정해줌
* ex) aaa.jpb, aaa1.jpb, aaa2.jpg
*
* - 우리 입맛대로 파일명이 겹치지 않게끔 rename 메소드를 오버라이딩 해줄 예정
*/
MultipartRequest multiRequest = new MultipartRequest(request, savePath,
maxSize, "UTF-8", new MyFileRenamePolicy());
// 3. DB에 기록할 데이터들을 뽑기
// - Board 테이블에 추가할 데이터(카테고리 번호, 제목, 내용, 작성자 회원번호)
// - Attachment테이블에 추가할 데이터(원본명, 수정명, 저장경로)
String category = multiRequest.getParameter("category");
String boardTitle = multiRequest.getParameter("title");
String boardContent = multiRequest.getParameter("content");
String boardWriter = multiRequest.getParameter("userNo");
Board b = new Board.Builder().categoryNo(Integer.parseInt(category))
.boardTitle(boardTitle)
.boardContent(boardContent)
.boardWriter(boardWriter)
.bulid();
// 첨부파일은 필수가 아니기 때문에 우선 null로 초기화,
// 등록한 첨부파일이 있을 경우 객체 생성
Attachment at = null;
// multiRequest.getOriginlFileName("키")
// 첨부파일이 있었을 경우 원본 파일명, 없었을 경우 null 값이 반환
if(multiRequest.getOriginalFileName("upfile") != null) {
at = new Attachment();
at.setOriginName(multiRequest.getOriginalFileName("upfile")); // 원본명
at.setChangeName(multiRequest.getFilesystemName("upfile")); // 수정명(실제 서버에 올라간 파일명)
at.setFilePath("resources/board_upfiles/");
}
// 4. 서비스 요청(DML)
int result = new BoardService().insertBoard(b, at);
// 5. 응답화면 지정
if(result > 0) { // 성공 => list.bo
request.getSession().setAttribute("alertMsg", "게시글 작성에 성공했습니다.");
response.sendRedirect("list.bo?currentPage=1");
}else {
// 실패시 => 첨부파일이 있었을 경우 이미 업로드된 첨부파일을 서버에서 삭제후, 에러페이지로 포워딩
if(at != null) {
// 삭제시키고자하는 파일객체 생성후 delete 메소드 호출
new File(savePath+at.getChangeName()).delete();
}
request.setAttribute("errorMsg", "게시글 작성 실패!");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response); // 에러페이지로 포워딩
}
}
}
}
1. 전송되는 파일을 처리할 작업내용(전송되는 파일의 용량제한, 전달된 파일을 저장할 폴더경로 제시
1_1. 전송파일 용량제한(byte단위로 제시)
* 1kbyte == 1024byte
* 1mbyte == 1024kbyte == 1024*1024(2의 20승)
int maxSize = 10 * 1024 * 1024 // 10Mbyte;
=> 용량제한 걸어둠
1_2. 전달된 파일을 저장할 서버의 폴더 경로 알아내기
=> 어플리케이션 객체에서 제공하는 getRealPath메소드를 통해 알아내기
=> 매개변수로 board_upfiles폴더까지의 경로를 제시해줘야한다.(board_upfiles => 업로드된 파일이 저장된 폴더)
String savePath = request.getSession().getServletContext().getRealPath("/resources/board_upfiles/"); |
"/" => WebContent 의미
2. 전달된 파일명 수정 및 서버에 업로드
- HttpServeltRequest request => MultipartRequest multiRequest로 변환
매개변수 생성자로 MultpartRequest객체를 생성(cos.jar에서 제공하는 객체)
MultipartRequest multiRequest = new MultipartRequest(request 객체, 저장할 폴더경로, 용량제한, 인코딩값, 파일명을 수정시켜주는 객체);
=> 위 구문 한줄 실행만으로 넘어온 첨부파일들이 해당 폴더에 업로드됨.
=> 그리고 사용자가 올린 파일명은 그대로 해당 폴더에 업로드하지 않는다.(같은 파일명이 있을 경우 덮어씌워질 수도 있고, 서버에 따라 문제가 발생할 수도 있다.)
☞ cos.jar에서 제공하는 파일명 수정작업을 해주는 객체
- DefaultFileRenamePolicy 객체
- 내부적으로 rename()이라는 메소드가 실행이 되면서 파일명 수정이 진행됨
- 작동방식은 동일한 파일명이 존재할 경우 카운팅된 숫자를 붙여서 파일명을 수정해줌
ex) aaa.jpb, aaa1.jpb, aaa2.jpg
- 우리 입맛대로 파일명이 겹치지 않게끔 rename 메소드를 오버라이딩 해줄 예정
MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy()); |
3. DB에 기록할 데이터들을 뽑기
- Board 테이블에 추가할 데이터(카테고리 번호, 제목, 내용, 작성자 회원번호)
- Attachment테이블에 추가할 데이터(원본명, 수정명, 저장경로)
4. 서비스 요청(DML)
int result = new BoardService().insertBoard(b, at); |
5. 응답화면 지정
③ 사용자가 파일을 업로드할 때 board_upfiles 폴더에 저장되는데 같은 폴더명이 존재하면 덮어씌우기 되니까 날짜,시간을 합한 파일명으로 자동으로 생성되도록 설정
▶ MyFileFenamePolicy.java
package com.kh.common;
import java.io.File;
import com.oreilly.servlet.multipart.FileRenamePolicy;
public class MyFileRenamePolicy implements FileRenamePolicy {
public File rename(File originFile) {
// 기존의 파일을 매개변수로 전달받아서(originFile) 파일명 수정작업 후에 수정된 파일을 반환해주는 메소드
// 원본파일명("aaa.jpg")
String originName = originFile.getName();
// 수정파일명 : 파일업로드시간(년월일시분초)+5자리 랜덤값 => 최대한 안겹치게 함
// 확장자 : 원본파일의 확장자 그대로 사용
// aaa.jpg ------> 202312114374512345.jpg
// 1. 파일 업로드된 시간(년월일시분초)
String currentTime = new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date());
// 2. 5자리 랜덤값 => int ranNum;
int ranNum = (int)((Math.random() * 90000) + 10000); // 10000~99999
// 3. 원본파일 확장자(String ext)
String ext = originName.substring(originName.lastIndexOf(".")); // jpg
String changeName = currentTime + ranNum + ext;
// 원본파일을 수정된 파일명으로 적용시켜서 파일객체로 반환
return new File(originFile.getParent(), changeName);
}
}
④ 게시글 작성과 첨부파일 있을시 업로드 할 때 메소드 생성
▶ BoardService.java
⑤ 게시판 작성 후 추가와 첨부파일 있을 경우 추가에 관한 SQL 쿼리문 실행한 메소드 생성
▶ BoardDao.java
▶ 결과화면
=> 첨부파일을 클릭하면 다운로드까지 되는 것을 확인할 수 있다.
'Server > JSP과 Servlet' 카테고리의 다른 글
[JSP] 5-4. 사진게시판(썸네일) (0) | 2023.12.13 |
---|---|
[JSP] 5-3. 게시판 상세 페이지 수정, 삭제 (0) | 2023.12.12 |
[JSP] 5-1. 게시판 만들기(페이지) (0) | 2023.12.08 |
[JSP] 4-3. 마이페이지 만들기(회원정보 변경, 회원탈퇴, 비밀번호 변경) (0) | 2023.12.07 |
[JSP] 4-2. 로그아웃, 회원가입 페이지 만들기 (0) | 2023.12.07 |