사진게시판
파일첨부 기능을 활용하여
파일 첨부시 미리보기가 되는 글쓰기 페이지와
첨부된 파일을 썸네일로 활용하는 목록 페이지를 만들어보자
<< 이전포스팅에서 완료된 요청 | 이번포스팅에서 구현할 요청 >>
| 기능 | 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 재요청 | |||
| 사진게시판목록 | /list.th | views/board/thumbnailListView.jsp | ||
| 사진 글쓰기 | /enrollForm.th | views/board/thumbnailEnrollForm.jsp | ||
| 사진글 등록 | /insert.th | 실패시 | views/common/errorPage.jsp | |
| 성공시 | /list.th url 재요청 |
사진게시글 등록
사진게시판의 경우,
사용자가 사진을 첨부했을때 미리보기 기능이 있으면 유용하다.

STEP1) thumbnailEnrollForm.jsp
- 생성위치 : webProject/src/main/webapp/views/board
- 사용자가 사진게시글을 올릴 수 있는 글작성 페이지를 구성해보자.
이때, input type=file 버튼은 숨기고, 사진을 올렸을때 미리보기가 가능하도록 한다. - input type file 버튼 숨기기(이미지 영역을 클릭했을 때 파일선택 링크 팝업)
1) 버튼을 감싸는 div에 style="display:none" 부여
2) 이미지 태그에 onclick="clickFile(해당 이미지 순번)" 이벤트 부여
3) clickFile 함수를 정의할때, 넘어온 순번에 해당하는 input type=file 버튼에 click() 이벤트를 발생시킨다.
>> 본래 file타입의 클릭이벤트는 파일을 선택할 수 있는 창을 띄우는것.
따라서 이미지 영역을 클릭하는 순간 파일을 선택할 수 있는 창을 띄워진다. - 파일선택시 해당 파일 img 태그에 띄우기
1) input type="file"태그에 onchange="loadImg(this,순번)"을 부여한다.
>>첨부파일을 등록하거나 변경, 취소할때마다 change 이벤트가 발생하여 함수발동
>>this는 이벤트가 발생한 요소에 대한 정보를 전달하고, 순번은 그에 맞는 이미지 영역으로 가기위해 전달한다.
2) 객체.files : 파일이 선택되었을때 객체.files[0]에 선택된 파일의 정보가 담기며, 배열로 반환된다.
선택된 파일이 있다면 배열의 길이가 1, 없다면 0이다.
>> if(객체.files.length==1) 로 change가 취소인지 등록인지 판별한다.
3) const reader = new FileReader
파일을 읽어들이려면 자바스크립트 객체 FileReader가 필요하다.
4) reader.readAsDataURL(객체.files[0])
파일을 읽어들이는 메소드로 내부적으로 파일만의 고유한 url을 부여한다.
(이미지를 찾을 수 있게 한다)
5) reader.onload = function(e){...}
파일 읽어들이는것이 완료되었을 때 호출될 함수이다.
해당 함수에 img태그의 src속성에 e.target.result를 부여하면, 이미지가 띄워진다.
<form action="<%=contextPath%>/insert.th" id="enroll-form" method="post" enctype="multipart/form-data">
제목 : <input type="text" name="title" required></td>
내용 :
<textarea name="content" rows="5" style="resize:none;" required>
</textarea>
대표이미지 :
<img id="titleImg" width="250" height="170" onclick="clickFile(1);">
상세이미지 :
<img id="contentImg1" width="150" height="120" onclick="clickFile(2);">
<img id="contentImg2" width="150" height="120" onclick="clickFile(3);">
<img id="contentImg3" width="150" height="120" onclick="clickFile(4);"></td>
<div id="file-area" style="display:none">
<input type="file" name="file1" onchange="loadImg(this, 1);" required>
<input type="file" name="file2" onchange="loadImg(this, 2);" >
<input type="file" name="file3" onchange="loadImg(this, 3);" >
<input type="file" name="file4" onchange="loadImg(this, 4);" >
</div>
<script>
//clickFile(num) 함수 : 클릭이 된 영역에 대한 정보를 매개변수로 받으며,
// 해당 영역에 해당하는 input type=file에 클릭 이벤트를 준다.
function clickFile(num) {
$('input[name=file'+num+']').click();
}
//loadImg(this, num) 함수 : 파일을 첨부 혹은 취소하였을때
// 해당하는 img 태그에 이미지를 표현하거나 없앤다.
function loadImg(inputFile, num){
// inputFile : 현재 변화가 생긴 input type="file" 요소객체
// num : 몇번째 input요소에 변화가 생겼는지 구분하기 위한 숫자
// 해당되는 이미지 영역을 찾기위해 사용
// input객체.files 는 배열로 반환되는 속성으로, 선택된 파일이 있다면
// 1)inputFile.files[0] 에 선택된 파일에 대한 정보 담기면서 배열길이가 1이된다.
// 2)취소하면 length가 0이 됨
if(inputFile.files.length == 1) {
//파일이 선택된 경우로 미리보기 효과를 만들자.
//파일을 읽어들일 FileReader 객체 생성
const reader = new FileReader();
//파일 읽어들이는 메소드 실행
//아래 코드가 실행되는 순간 내부적으로 파일만의 고유한 url 부여
reader.readAsDataURL(inputFile.files[0]);
//파일 읽어들이기가 완료됐을 때 미리보기가 뜨도록 함수 정의
reader.onload = function(e){
//e.target.result = 읽어들인 파일의 고유한 url
switch(num) {
case 1 : $('#titleImg').attr("src", e.target.result); break;
case 2 : $('#contentImg1').attr("src", e.target.result); break;
case 3 : $('#contentImg2').attr("src", e.target.result); break;
case 4 : $('#contentImg3').attr("src", e.target.result); break;
}
}
} else {
//파일이 취소된 경우로 미리보기 됐던게 사라지게끔 하자
switch(num) {
case 1 : $('#titleImg').attr("src", null); break;
case 2 : $('#contentImg1').attr("src", null); break;
case 3 : $('#contentImg2').attr("src", null); break;
case 4 : $('#contentImg3').attr("src", null); break;
}
}
}
</script>
STEP2) board-mapper.xml (수정)
- 사진게시글 작성 요청 시 실행될 sql문을 짠다.
-- BOARD INSERT 먼저 진행한다.
INSERT
INTO BOARD
(
BOARD_NO
, BOARD_TYPE
, BOARD_TITLE
, BOARD_CONTENT
, BOARD_WRITER
)
VALUES
(
SEQ_BNO.NEXTVAL
, 2
, 사용자가 입력한 제목
, 사용자가 입력한 내용
, 로그인한 회원의 번호
)
--ATTACHMENT INSERT하기, 첨부파일 갯수만큼 돌린다.
INSERT
INTO ATTACHMENT
(
FILE_NO
, REF_BNO
, ORIGIN_NAME
, CHANGE_NAME
, FILE_PATH
, FILE_LEVEL
)
VALUES
(
SEQ_FNO.NEXTVAL
, SEQ_BNO.CURRVAL
, 첨부파일의원본명
, 첨부파일의 실제 업로드명
, 저장경로
, 대표이미지면1 / 상세이미지면2
)
STEP3) ThumbnailInsertController.java
- 작성위치 : com.br.board.controller
서블릿/매핑값 insert.th - 사진게시글에서 작성을 눌렀을때 요청되어 db에 데이터를 기록하는 서블릿
- enctype="~"로 넘겨받았을 경우 multipartRequest 로 변경해야만 값을 뽑을 수 있
- 사진게시글용 폴더 만들기 thumbnail_upfiles (webProject/src/main/webapp/resources)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
if(ServletFileUpload.isMultipartContent(request)) {
int maxSize = 10*1024*1024;
//request.getSession().getServletContext() = 웹 어플리케이션 객체 (전역객체)
//.getRealPath("/~~")에서 / == webapp이므로 이후 경로 기재
String savePath = request.getSession().getServletContext().getRealPath("/resources/thumbnail_upfiles/");
MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
//업로드완
//DB에 기록하기 위해 데이터 담기
HttpSession session = request.getSession();
int userNo = ((Member)session.getAttribute("loginUser")).getUserNo();
Board b = new Board();
b.setBoardTitle(multiRequest.getParameter("title"));
b.setBoardContent(multiRequest.getParameter("content"));
b.setBoardWriter(String.valueOf(userNo));
// Attachment테이블에 insert할 데이터 담기 => 여러개일수 있으므로 ArrayList
ArrayList<Attachment> list = new ArrayList<>();
for(int i=1; i<=4; i++) {
String key = "file"+i; // 해당 키값을 가진 input을 하나씩 확인한다.
if(multiRequest.getOriginalFileName(key) != null) {
//첨부파일이 있을 경우
Attachment at = new Attachment();
at.setOriginName(multiRequest.getOriginalFileName(key));
at.setChangeName(multiRequest.getFilesystemName(key));
at.setFilePath("resources/thumbnail_upfiles/");
//대표이미지면 1, 아니면 2 입력
at.setFileLevel(i==1? 1 : 2);
list.add(at);
}
}
//요청처리 하고오기
int result = new BoardService().insertThumbnailBoard(b, list);
if(result>0) {
//성공 => 목록페이지 이미 포워딩하는 url 존재 => url재요청진행
session.setAttribute("alertMsg", "성공적으로 게시글이 등록되었습니다");
response.sendRedirect(request.getContextPath()+"/list.th");
}else {
//에러페이지
}
}
}
STEP4) BoardService.java (수정)
- insertThumbnailBoard(b, list) 메소드 작성
public int insertThumbnailBoard(Board b, ArrayList<Attachment> list) {
Connection conn = getConnection();
//Board 테이블 insert
int result1 = new BoardDao().insertThBoard(conn,b);
//Attachment테이블 insert
int result2 = new BoardDao().insertAttachmentList(conn, list);
if(result1*result2>0) {
commit(conn);
}else {
rollback(conn);
}
return result1*result2;
}
STEP5) BoardDao.java (수정)
- insertThBoard(conn,b) 메소드 작성
- insertAttachmentList(conn, list) 메소드 작성
public int insertThBoard(Connection conn,Board b) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertThBoard");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, b.getBoardTitle());
pstmt.setString(2, b.getBoardContent());
pstmt.setString(3, b.getBoardWriter());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public int insertAttachmentList(Connection conn, ArrayList<Attachment> list) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertAttachmentList");
try {
//list의 갯수만큼 반복문으로돌려서 insert하기
for(Attachment at : list) {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, at.getOriginName());
pstmt.setString(2, at.getChangeName());
pstmt.setString(3, at.getFilePath());
pstmt.setInt(4, at.getFileLevel());
result = pstmt.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
썸네일 목록페이지
일반 목록과 다르게 첨부된 사진으로 썸네일 목록을 만들어보자
페이징 처리는 생략한다.
![]() |
STEP1) thumbnailListView.jsp
- 생성위치 : webProject/src/main/webapp/views/board
- 썸네일이 보이는 표현하는 사진게시판목록을 구성한다.
<style>
.....
.thumbnail {
border: 1px solid white;
width: 220px;
/*옆으로 배치하기 위해 인라인, 가로세로 지정하기위해 블럭*/
display: inline-block;
margin:14px;
}
</style>
.....
<div class="thumbnail" align="center">
<!-- 사용자가 입력한 이미지는 크기가 제각각일것 > 이미지 크기 고정-->
<img src="" width="200" height="150">
<p>
No.23 제목자리 <br>
조회수 : 120
</p>
</div>
<div class="thumbnail" align="center">
<img src="" width="200" height="150">
<p>
No.23 제목자리 <br>
조회수 : 120
</p>
</div>
......
STEP2) board-mapper.xml (수정)
- 목록을 표현하는데 필요한 데이터들을 sql문을 작성하며정리해본다.
- file_path와 change_name을 연이어서 하나의 문자열로 가져온다
이때 별칭 필수 (데이터 꺼낼때 사용할 명칭)
SELECT
BOARD_NO
, BOARD_TITLE
, COUNT
, FILE_PATH || CHANGE_NAME "TITLEIMG"
FROM BOARD B
JOIN ATTACHMENT ON (BOARD_NO = REF_BNO)
WHERE BOARD_TYPE = 2
AND B.STATUS = 'Y'
AND FILE_LEVEL = 1
ORDER
BY BOARD_NO DESC;
STEP3) Board.java (수정)
- 상단의 쿼리문 확인시, Board에 필요한 데이터를 다 담을 수 없음(TITLEIMG )
>> 필드를 하나 추가해준다. - 이처럼 필드는 얼마든지 추가해 줄 수 있다.
기존의 필드를 지우는것과 달리 문제가 안됨. getter, setter 메소드만 추가로 잘 생성해준다.
private String titleImg; // 대표이미지의경로
STEP4) menubar.jsp (수정)
- 메뉴바의 사진게시판 버튼을 클릭하면 이동하도록 매핑값을 입력한다.
<a href="<%=contextPath%>/list.th">사진게시판</a></div>
STEP5) ThumbnailListController.java
- 생성위치 : com.br.board.controller
서블릿/매핑값 list.th - 사진게시판 목록으로 포워딩하는 서블릿이다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//thumbnailListView.jsp 상에 필요한 데이터 조회해서 가야됨
ArrayList<Board> list = new BoardService().selectThumbnailList();
request.setAttribute("list", list);
//응답페이지
request.getRequestDispatcher("views/board/thumbnailListView.jsp").forward(request, response);
}
STEP6) BoardService.java (수정)
- selectThumbnailList() 메소드 생성
public ArrayList<Board> selectThumbnailList(){
Connection conn = getConnection();
ArrayList<Board> list = new BoardDao().selectThumbnailList(conn);
close(conn);
return list;
}
STEP7) BoardDao.java (수정)
- selectThumbnailList(conn) 메소드 생성
public ArrayList<Board> selectThumbnailList(Connection conn) {
ArrayList<Board> list = new ArrayList<>();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectThumbnailList");
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
while(rset.next()) {
Board b = new Board();
b.setBoardNo(rset.getInt("board_no"));
b.setBoardTitle(rset.getString("board_title"));
b.setCount(rset.getInt("count"));
b.setTitleImg(rset.getString("titleimg"));
list.add(b);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
STEP8) thumbnailListView.jsp (수정)
- 상단에 전달받은 데이터를 뽑아놓는다
- list에 담긴 Board 갯수만큼 썸네일 div 뿌린다.
<%@ page import="com.br.board.model.vo.Board, java.util.ArrayList" %>
<%
ArrayList<Board> list = (ArrayList<Board>)request.getAttribute("list");
%>
.....
<% for(Board b : list) { %>
<div class="thumbnail" align="center">
<img src="<%=contextPath%>/<%=b.getTitleImg()%>" width="200" height="150">
<p>
No.<%=b.getBoardNo()%> <%=b.getBoardTitle()%><br>
조회수 : <%=b.getCount()%>
</p>
</div>
<% } %>
.....
글작성 페이지 가기
목록에서 글작성 버튼을 누르면
사진게시글을 작성할수 있는 페이지로 이동한다
STEP1) thumbnailListView.jsp(수정)
- 글작성 버튼에 enrollForm.th 서블릿을 연결한다.
<a href="<%=contextPath%>/enrollForm.th">글작성</a>
STEP2) ThumbnailEnrollFormController.java
- 생성위치 : com.br.board.controller
서블릿/매핑값 enrollForm.th - 글작성 페이지로 포워딩한다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//조회해갈 데이터 없이 포워딩만 해가면 됨.
request.getRequestDispatcher("views/board/thumbnailEnrollForm.jsp").forward(request, response);
}

'다양한 기술들 > 레거시 Web 실습' 카테고리의 다른 글
| [13] 아이디 중복체크 : Ajax 활용 (0) | 2023.01.18 |
|---|---|
| [12] 게시판심화2 : 상세보기(+첨부된 이미지 띄우기) (0) | 2023.01.18 |
| [10] 게시판심화1 : 상세보기(+첨부파일 조회 및 다운로드) (0) | 2023.01.12 |
| [09] 게시판심화1 : 글쓰기(+파일첨부) (0) | 2023.01.11 |
| [08] 게시판심화1 : 페이징처리 (1) | 2023.01.11 |

댓글