첨부파일 게시글
첨부파일이 있을 수도 있는 게시글을 조회하면서
첨부된 파일을 다운로드할 수 있는 페이지를 구성해보자
<< 이전포스팅에서 완료된 요청 | 이번포스팅에서 구현할 요청 >>
| 기능 | url요청 | 요청시 전달값 | 응답페이지 또는 url재요청 | |
| /web | index.jsp | |||
| 로그인요청 | /login.me | userId=?&userPwd=? | 실패시 | views/common/errorPage.jsp |
| 성공시 | /web url재요청=> index.jsp | |||
| 로그아웃요청 | /logout.me | /web url재요청=> index.jsp | ||
| 회원가입페이지 | /enrollForm.me | views/member/memberEnrollForm.jsp | ||
| 회원가입요청 | /insert.me | userId=? ... 등 | 실패시 | views/common/errorPage.jsp |
| 성공시 | /web url재요청 => index.jsp | |||
| 마이페이지요청 | /myPage.me | 로그인전 | /web url재요청 => index.jsp | |
| 로그인후 | views.member/myPage.jsp | |||
| 정보변경요청 |
/update.me |
userId=? ... 등 | 실패시 | view/common/errorPage.jsp |
| 성공시 | /myPage.me url재요청 | |||
| 공지사항 목록 | /list.no | views/notice/noticeListView.jsp | ||
| 공지사항글쓰기 | /enrollForm.no | views/noticeEnrollForm.jsp | ||
| 공지사항등록 | /insert.no | title=?&content=? | 실패시 | views/common/errorPage.jsp |
| 성공시 | /list.no url재요청 | |||
| 공지 - 상세페이지 | /detail.no | no=? (수기기재) | 실패시 | views/common/errorPage.jsp |
| 성공시 | views/notice/noticeDetailView.jsp | |||
| 공지 - 수정페이지 | /updateForm.no | no=? | views/notice/noticeUpdateForm.jsp | |
| 공지 - 수정요청 | /update.no | no=?&title=?&content=? | 실패시 | views/common/errorPage.jsp |
| 성공시 | /detail.no?no=XX url재요청 | |||
| 일반게시판 목록 | /list.bo | cpage=? | views/board/boardListView.jsp | |
| 일반 글쓰기 | /enrollForm.bo | views/board/boardEnrollForm.jsp | ||
| 일반글 등록 | /insert.bo | 실패시 | views/common/errorPage.jsp | |
| 성공시 | /list.bo url 재요청 | |||
| 일반 - 상세페이지 | /detail.bo | no=? | 실패시 | view/common/errorPage.jsp |
| 성공시 | views/board/boardDetailView.jsp | |||
| 일반 - 수정페이지 | /updateForm.bo | no= | view/board/boardUpdateForm.jsp | |
| 일반 - 수정요청 | /update.bo | 실패시 | view/common/errorPage.jsp | |
| 성공시 | /detail.bo url 재요청 |
목록클릭시 상세페이지
게시글 목록에서 한줄을 클릭하면 해당글의 상세페이지가 뜨도록 한다.
STEP1) boardDetailView.jsp
- 생성위치 : webProject/src/main/webapp/views/board
- 게시판 목록을 클릭했을때 뜨는 상세페이지를 가작성한다.
.....
<h2>일반게시판 상세보기</h2>
카테고리 : ~~~
제목 : ~제목자리~
작성자 : ~아이디~
작성일 : ~yyyy/mm/dd~
내용 : ~내용자리~
첨부파일 : ~~
<!-- case1. 첨부파일이 없을경우-->
첨부파일이 없습니다
<!-- case2. 첨부파일이 있을 경우-->
<a href="첨부파일의 저장경로, 첨부파일의 실제 저장된 파일명">~원본명~</a>
<a href="">목록가기</a>
<!-- 로그인한 회원이 게시글을 쓴 회원일경우에만 보이는 버튼 -->
<a href="">수정하기</a>
<a href="">삭제하기</a>
STEP2) board-mapper.xml (수정)
- 첨부파일이 있는 게시글을 조회할 때는 세가지 sql문이 필요하다
- 1) 조회수 증가시키기
- 정상적으로 조회할 수 있는 게시글인가를 판단 후 조회수를 1 증가시킨다 - 2) Board로부터 게시글 정보 조회
- 함수를 쓴 칼럼은 반드시 별칭을 지어준다(ResultSet에 칼럼명으로 저장되기 때문에)
- 게시글번호는 노출되지 않지만 수정 삭제에 쓰이므로 가져온다.
- 1번이 수행된 상황에서만 수행될것이기때문에 status조건은 넣지 않아도 된다. - 3) Attachment로부터 첨부파일 정보 조회
- 0행이 조회된다면 첨부파일이 없는 것.
--1) 조회수 증가시키기
UPDATE
BOARD
SET COUNT = COUNT +1
WHERE BOARD_NO = 조회할 글번호
AND STATUS = 'Y'
--2) BOARD로부터 게시글 정보 조회
SELECT
BOARD_NO
, CATEGORY_NAME
, BOARD_TITLE
, BOARD_CONTENT
, USER_ID
, TO_CHAR(CREATE_DATE, 'YYYY/MM/DD') "CREATE_DATE"
FROM BOARD
JOIN CATEGORY USING (CATEGORY_NO)
JOIN MEMBER ON (BOARD_WRITER=USER_NO)
WHERE BOARD_NO = 조회할 글번호
--3) ATTACHMENT로부터 첨부파일 정보 조회
SELECT
FILE_NO
, ORIGIN_NAME
, CHANGE_NAME
, FILE_PATH
FROM ATTACHMENT
WHERE REF_BNO = 조회할 글번호
STEP3) boardListView.jsp (수정)
- 게시글을 클릭했을때 detail.bo서블릿 호출
>>이때, 클릭한 게시글의 첫번째 td태그에 담겨있는 글번호를 쿼리스트링으로 함께 넘긴다.
.....
<% for(Board b : list) { %>
<tr>
<td><%=b.getBoardNo() %></td>
<td><%=b.getCategory() %></td>
.....
</tr>
<%} %>
.....
<script>
$(function(){
$(".list-area>tbody>tr").click(function(){
location.href = '<%=contextPath%>/detail.bo?no=' + $(this).children().eq(0).text();
})
})
</script>
STEP4) BoardDetailController.java
- 생성위치 : com.br.board.controller
서블릿 / 매핑값 detail.bo - 게시글 목록에서 게시글1줄을 선택했을 때 호출되며, 해당 게시글의 상세페이지를 포워딩한다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//요청시 전달값 뽑기 (no키값으로 조회할 글번호)
int boardNo = Integer.parseInt(request.getParameter("no"));
//서비스 객체 3군데서 필요하니 미리 정의
BoardService bService = new BoardService();
// 1) 조회수 증가 요청
int result = bService.increseCount(boardNo);
if(result>0) {
// >> 조회수 증가 성공시 (유효한 글번호)
// 2-1) Board 테이블로부터 게시글 정보 조회 요청
Board b= bService.selectBoard(boardNo);
// 2-2) Attachment 테이블로부터 첨부파일 정보조회 요청(없으면 null로 돌아오게)
Attachment at = bService.selectAttachment(boardNo);
// 2-3) 두 객체 담아서 boardDetailView.jsp로 응담
request.setAttribute("b", b);
request.setAttribute("at", at); //null이 담길 수 있다.(검사해서 첨부파일유부판단)
request.getRequestDispatcher("views/board/boardDetailView.jsp").forward(request, response);
}else {
// >> 조회수 증가 실패시 (유효하지 않은 글번호)
// 에러페이지
request.setAttribute("errorMsg", "상세조회 실패");
request.getRequestDispatcher("view/common/errorPage.jsp").forward(request, response);
}
}
STEP5) BoardService.java (수정)
- Controller에 쓰인 세가지 메소드를 작성한다
- increseCount(boardNo)
- selectBoard(boardNo)
- selectAttachment(boardNo)
public int increseCount(int boardNo) {
Connection conn = getConnection();
int result = new BoardDao().increseCount(conn, boardNo);
if(result>0) {
commit(conn);
}else {
rollback(conn);
}
close(conn);
return result;
}
public Board selectBoard(int boardNo) {
Connection conn = getConnection();
Board b = new BoardDao().selectBoard(conn, boardNo);
close(conn);
return b;
}
public Attachment selectAttachment(int boardNo) {
Connection conn = getConnection();
Attachment at = new BoardDao().selectAttachment(conn, boardNo);
close(conn);
return at;
}
STEP6) BoardDao.java (수정)
- Service에 쓰인 세가지 메소드를 작성한다.
- increseCount(conn, boardNo)
- selectBoard(conn, boardNo)
- selectAttachment(conn, boardNo)
public int increseCount(Connection conn, int boardNo) {
PreparedStatement pstmt = null;
int result = 0;
String sql = prop.getProperty("increseCount");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, boardNo);
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public Board selectBoard(Connection conn, int boardNo) {
PreparedStatement pstmt = null;
ResultSet rset = null;
Board b = null;
String sql = prop.getProperty("selectBoard");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, boardNo);
rset = pstmt.executeQuery();
if(rset.next()) {
b = new Board(rset.getInt("board_no")
, rset.getString("category_name")
, rset.getString("board_title")
, rset.getString("board_content")
, rset.getString("user_id")
, rset.getString("create_date"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return b;
}
public Attachment selectAttachment(Connection conn, int boardNo) {
PreparedStatement pstmt = null;
ResultSet rset = null;
Attachment at = null;
String sql = prop.getProperty("selectAttachment");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, boardNo);
rset = pstmt.executeQuery();
if(rset.next()) {
at = new Attachment(rset.getInt("file_no")
, rset.getString("origin_name")
, rset.getString("change_name")
, rset.getString("file_path"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return at;
}
STEP7) BoardDetailView.jsp (수정)
- 넘겨받은 데이터들로 페이지 구성하기
- 파일은 href에 파일경로를 입력하면 다운받는것이 가능하다.
- 파일다운로드하기 :
- href에 파일의 저장위치를 입력한다
ex) http://localhost:8887/web/resources/board_upfiles/2023011212082884091.jpg
- <%=contextPath%> : /web
- <%=at.getFilePath() + at.getChangeName()%> : resources.board_upfiles/2023011212082884091.jpg
- 두 출력식 사이에 반드시 ' / ' 를 넣어준다.
>> 여기까지 해주면 브라우저를 통해서 이미지가 열림
다운까지 받으려면 a태그 속성에 download 를 넣어준다.
>>여기까지만 하면 다운받을때는 실제파일명으로 다운로드 받아짐.
download 속성으로 다운로드받아질 이름을 넣는다.
.....
<%@ page import="com.br.board.model.vo.*" %>
<%
Board b = (Board)request.getAttribute("b");
Attachment at = (Attachment)request.getAttribute("at");
%>
.....
카테고리 : <%=b.getCategory()%>
제목 : <%=b.getBoardTitle()%>
작성자 : <%= b.getBoardWriter()%>
작성일 : <%= b.getCreateDate()%>
내용 : <%=b.getBoardContent()%>
첨부파일 :
<% if( at==null ){ %>
첨부파일이 없습니다
<% } else { %>
<a download="다운받아질 이름.jpg" href="<%=contextPath%>/<%=at.getFilePath() + at.getChangeName()%>"><%= at.getOriginName() %></a>
<% } %>
<a href="<%=contextPath%>/list.bo?cpage=1">목록가기</a>
<% if( loginUser != null && loginUser.getUserId().equals(b.getBoardWriter())) { %>
<!-- 로그인한 회원이 게시글을 쓴 회원일경우에만 보이는 버튼 -->
<a href="">수정하기</a>
<a href="">삭제하기</a>
<% } %>
수정
첨부파일이 있는 경우는 수정하는 경우가 다소 까다롭다
STEP1) boardDetailView.jsp (수정)
- 수정하기 버튼 클릭시 updateForm.bo로 요청
>> 수정할때 가져가야 할 데이터는 게시글 번호(쿼리스트링으로 전달)
<a href="<%=contextPath%>/updateForm.bo?no=<%=b.getBoardNo()%>">수정하기</a>
STEP2) BoardUpdateFormController.java
- 생성위치 : com.br.board.controller
서블릿 / 매핑값 updateForm.bo - 수정하기 눌렀을 때, 필요한 정보들을 확보하여 수정페이지로 넘기는 서블릿
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int boardNo = Integer.parseInt(request.getParameter("no"));
BoardService bService = new BoardService();
ArrayList<Category> list = bService.selectCategoryList();
Board b = bService.selectBoard(boardNo);
Attachment at = bService.selectAttachment(boardNo); // null가능
request.setAttribute("list", list);
request.setAttribute("b", b);
request.setAttribute("at", at);
request.getRequestDispatcher("view/board/boardUpdateForm.jsp").forward(request, response);
}
STEP3) boardUpdateForm.jsp
- 생성위치 : webProject/src/main/webapp/views/board
- 수정하기 버튼을 눌렀을 때 보여지는 화면설계
>> 작성하기 페이지 골격 + 기존의 데이터 뿌려주기 - 수정주요내역
>> 카테고리 option들 중 기존데이터값에 맞는 곳에 selected속성주기(jQuery사용)
>> 제목입력 태그에 기존값을 value로 넣기
>> 내용입력 태그 content부에 기존값 넣기
>> input type=file은 value값 둘 수 없으므로 버튼앞에 기존 첨부파일의 원본명 표기되게 하기
(다운로드 기능 원하면 a태그 붙이면 됨)
<%@ page import="com.br.board.model.vo.*, java.util.ArrayList" %>
<%
ArrayList<Category> list = (ArrayList<Category>)request.getAttribute("list");
Board b = (Board)request.getAttribute("b");
Attachment at = (Attachment)request.getAttribute("at");
%>
.....
<select name="category">
<% for(Category c : list) { %>
<option value="<%=c.getCategoryNo()%>"><%= c.getCategoryName() %></option>
<% } %>
</select>
<script>
$(function(){
$("#update-form option").each(function(){
if($(this).text()== '<%=b.getCategory()%>') {
$(this).attr("selected", true)
}
})
})
</script>
.....
STEP4) board-mapper.xml (수정)
- 세가지 sql문을 작성해야한다.
- 게시글 정보를 업데이트 하는 sql문
- 새로운 첨부파일이 있는데 기존 첨부파일이 있을 경우
Attachment테이블을 update하는 sql문
이때, primary key로 지정된 컬럼으로 비교하는 것이 검색속도가 빠르다고 한다. - 새로운 첨부파일이 있는데 기존첨부파일이 없는 경우
Attachment테이블에 insert하는 sql문
기존에 썼던 insert문은 참조하는 게시글번호를 시퀀스.currval로 썼기때문에 재새용이 불가하다
-- 수정하기 요청시 실행할 sql문
--1) 게시글 정보 update (Board)
UPDATE
BOARD
SET CATEGORY_NO = 사용자가선택한 카테고리번호
, BOARD_TITLE = 사용자가 입력한 제목
, BOARD_CONTENT = 사용자가 입력한 내용
WHERE BOARD_NO = 수정하고자하는 게시글번호;
--2_1) 새로운첨부파일O, 기존첨부파일O
UPDATE
ATTACHMENT
SET ORIGIN_NAME = 새로운 첨부파일의 원본명
, CHANGE_NAME = 새로운 첨부파일의 저장명
, FILE_PATH = 새로운 첨부파일의 저장경로
WHERE FILE_NO = 기존의 첨부파일의 고유번호;
--2_2) 새로운첨부파일X, 기존첨부파일X
INSERT
INTO ATTACHMENT
(
FILE_NO
, REF_BNO
, ORIGIN_NAME
, CHANGE_NAME
, FILE_PATH
)
VALUES
(
SEQ_FNO.NEXTVAL
, 현재수정하는 게시글번호
, 새로운 첨부파일의 원본명
, 새로운 첨부파일의 저장명
, 새로운 첨부파일의 저장경로
)
STEP5) boardUpdateForm.jsp (수정)
- sql문 확인시 게시글 번호가 필요하다는것 확인
>> 폼태그내에 input type=hidden으로 숨겨서 넘기기 - 마찬가지로 기존에 첨부파일 있을경우 원래명칭 필요함 확인
>> input type=hidden으로 숨겨서 넘기 - 폼을 제출할 때 update.bo 서블릿을 호출하도록 action값을 입력한다.
<form id="update-form" action="<%=contextPath%>/update.bo" method="post" enctype="multipart/form-data">
<input type="hidden" name="no" value="<%=b.getBoardNo()%>">
.....
첨부파일
<% if(at != null ) { %>
<!-- 기존의 첨부파일이 있었을 경우 -->
<input type="hidden" name="originFileNo" value="<%=at.getFileNo()%>">
<%=at.getOriginName()%>
<% } %>
.....
STEP6) BoardUpdateController.java
- 생성위치 : com.br.board.controller
서블릿 / 매핑값 update.bo - 게시글수정요청이 들어왔을때 db에 요청내용을 반영한 후 게시글 목록페이지로 포워딩하는 서블릿
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
if(ServletFileUpload.isMultipartContent(request)) {
//1-1) 파일의 용량제한
int maxSize = 10*1024*1024;
//1-2) 물리적인 파일저장위치
// session객체로부터 getServletContext로 application 객체를 얻어낸다음 getRealPath 사용
String savePath = request.getSession().getServletContext().getRealPath("/resources/board_upfiles/");
//2) 전달된 파일 서버에 업로드(한줄로 완료)
// request에 담긴것도 multiReqest로 옮긴다.
MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
//3) db에 데이터기록
// 첨부파일이 있든없든 수행하는것 : Board 테이블 업데이트
int boardNo = Integer.parseInt(multiRequest.getParameter("no"));
String category = multiRequest.getParameter("category");
String boardTitle = multiRequest.getParameter("title");
String boardContent = multiRequest.getParameter("content");
Board b = new Board();
b.setBoardNo(boardNo);
b.setCategory(category);
b.setBoardTitle(boardTitle);
b.setBoardContent(boardContent);
// 새로 넘어온 첨부파일 있는지 여부 확인할때 : at의 null여부
Attachment at = null;
if(multiRequest.getOriginalFileName("upfile") != null) {
//새로운 첨부파일이 있을경우 객체생성
at = new Attachment();
//기존 첨부파일이 있건없건 필요한 데이터들 = 새로운 첨부파일 원본명, 저장명, 저장경로
at.setOriginName(multiRequest.getOriginalFileName("upfile"));
at.setChangeName(multiRequest.getFilesystemName("upfile"));
at.setFilePath("resources/board_upfiles/");
//숨겨서 넘긴 정보를 활용해서 기존파일여부를 확인한다.
if(multiRequest.getParameter("originFileNo") != null) {
// 기존의 첨부파일이 있었을 경우 => UPDATE ATTACHMENT (기존파일번호필요)
at.setFileNo(Integer.parseInt(multiRequest.getParameter("originFileNo")));
}else {
// 기존의 첨부파일이 없었을 경우 => INSERT ATTACHMENT (현재게시글번호필요)
at.setRefBoardNo(boardNo);
}
}
int result = new BoardService().updateBoard(b,at);
if(result>0) {
//성공
request.getSession().setAttribute("alertMsg", "성공적으로 수정되었습니다.");
response.sendRedirect(request.getContextPath()+"/detail.bo?no="+boardNo);
}else {
//실패 => 에러페이지
}
}
}
STEP7) BoardService.java (수정)
- updateBoard(b,at) 메소드 생성
- at에 담긴 정보에 따라 세가지 경우를 구분할 수 있음.
1) new첨부 X == at는 null
2) new첨부 O, old첨부 O == fileNo에 기존파일정보있음
3) new첨부 O, old첨부 X == refBoardNo에 정보 있음 - Board업데이트를 진행한 후 결과를 result1에 담고, Attachment엡데이트를 진행한 결과를 result2에 담은 후 두 결과의 곱을 리턴한다(두 결과 모두 성공일때만 result>0이다.)
public int updateBoard(Board b,Attachment at) {
Connection conn = getConnection;
int result1 = new BoardDao().updateBoard(conn, b);
int result2 = 1;
//0으로 초기화해놓으면 첨부파일이 없는 경우에는 그대로 0이므로 1로 초기화
if(at != null) { //새로운 첨부파일이 있으면서,
if(at.getFileNo() != 0) {
// 기존첨부파일도 있는 경우
// (fileNo 타입은 숫자이므로 null이 아닌 기본값 0으로 비교)
// => UPDATE ATTACHMENT
result2 = new BoardDao().updateAttachment(conn, at);
}else {
// 기존 첨부파일은 없는경우
// ==> INSERT ATTACHMENT
result2 = new BoardDao().insertNewAttachment(conn, at);
}
}
if(result1>0 && result2>0) {
commit(conn);
}else {
rollback(conn);
}
close(conn);
return result1*result2;
}
STEP8) BoardDao.java (수정)
- updateBoard(conn, b) 메소드와 첨부파일이 있는경우 수행될
updateAttachment(conn, at) 메소드와
insertNewAttachment(conn, at) 메소드를 작성한다.
public int updateBoard(Connection conn, Board b) {
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateBoard");
int result = 0;
try {
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, b.getCategory());
.....
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public int updateAttachment(Connection conn, Attachment at) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateAttachment");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, at.getOriginName());
.....
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public int insertNewAttachment(Connection conn, Attachment at) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertNewAttachment");
try {
pstmt = conn.prepareStatement(sql);
.....
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}

'다양한 기술들 > 레거시 Web 실습' 카테고리의 다른 글
| [12] 게시판심화2 : 상세보기(+첨부된 이미지 띄우기) (0) | 2023.01.18 |
|---|---|
| [11] 게시판심화2 : 썸네일목록, 글작성(+첨부파일 미리보기) (0) | 2023.01.16 |
| [09] 게시판심화1 : 글쓰기(+파일첨부) (0) | 2023.01.11 |
| [08] 게시판심화1 : 페이징처리 (1) | 2023.01.11 |
| [07] 게시판기초 : 상세조회, 수정, 삭제 (0) | 2023.01.09 |
댓글