본문 바로가기
[ View ]/JSP

[ JSP ] 게시판 만들기 2 - 글쓰기 페이지 구현(다운로드,조회수 기능 추가)

by 환이s 2023. 3. 29.
728x90

이전 포스팅에서 DB 설정 및 게시판 목록을 구현해 봤습니다. 

오늘은 글쓰기 페이지를 만들어서 다운로드, 조회수 기능까지 추가해 보겠습니다.

 

 

게시판 기능/ 파일 구조

 

< 기능 >

1 ) CRUD(글쓰기, 목록/상세, 수정, 삭제)

2 ) 검색 기능

3 ) 페이지 나누기

4) 파일 업로드, 다운로드

5 ) 댓글 달기

6 ) 답변 달기

 

< 파일 구조 >

Contoller

BoardController.java

Model

Pager.java Constants.java (상수값들)
BoardDTO.java
BoardCommentDTO.java(댓글 관련)
BoardDAO.java
board.xml

View

index.jsp : 시작 페이지
list.jsp : 게시판 목록
write.jsp : 글쓰기
comment_list.jsp : 댓글 목록
edit.jsp : 수정, 삭제 기능
reply.jsp : 답변 달기
search.jsp : 검색 페이지


■ 글쓰기 페이지

 

list.jsp

 

이전 포스팅까지 작업했던 게시판 목록 페이지에 버튼을 추가해서 글쓰기 페이지를 호출시키려고 합니다. 

그럼 자바스크립트에서 click() 이벤트를 활용해서 페이지 호출을 합니다. 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<script type="text/javascript">
$(function() {
	$("#btnWriter").click(function() {
		location.href="${path}/board/writer.jsp"; //글쓰기 페이지 호출
	});
});
</script>
</head>
<body>
<h2>게시판</h2>
<button id="btnWriter">글쓰기</button> <!-- 글쓰기 페이지 버튼 -->
<table border="1" style="width: 100%;">
	<tr>
	<th>번호</th>
	<th>이름</th>
	<th>제목</th>
	<th>날짜</th>
	<th>조회수</th>
	<th>첨부파일</th>
	<th>파일사이즈</th>
	<th>다운로드</th>
	<th>IP주소</th>
	</tr>
	<c:forEach var="dto" items="${list}">
	<tr>
		<td>${dto.num}</td>
		<td>${dto.writer}</td>
		<td>${dto.subject}</td>
		<td>${dto.reg_date}</td>
		<td>${dto.readcount}</td>
		<td>${dto.filename }</td>
		<td>${dto.filesize }</td>
		<td>${dto.down }</td>
		<td>${dto.ip }</td>
	</c:forEach>
	</tr>
</table>

</body>
</html>

 

다음으로 글쓰기 페이지를 생성합니다.

 

write.jsp 

 

글쓰기 페이지 기능은 이름, 제목, 본문 작성을 하고 추가로 첨부파일까지 등록할 수 있는 기능을 추가하려고 합니다. 그럼 파일 업로드를 위한 MultipartRequest 객체를 활용하고 파일 객체를 사용하기 위해 타입 설정 "multpart/form-data"를 해줘야 합니다. 

 

(MultipartRequest 객체 및 파일 업로드 부분을 알아보시는 분들은 아래 포스팅을 참고하시면 도움 될 거 같습니다!)

 

[ JSP ] 파일 업로드

오늘은 게시판 만들기 전에 파일 업로드 할 때 필요한 기능에 대해서 알아보겠습니다. ■ 파일 업로드 jsp에는 파일 업로드 기능이 기본적으로 포함되어 있지 않으므로 외부 라이브러리를 사용

drg2524.tistory.com

 

그럼 <form> 태그를 사용해서 html 화면을 구현하고 전송 버튼은 <input type="button"> 타입으로 설정해서 자바 스크립트에서 Controller로 보내는 작업을 합니다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<script type="text/javascript">
$(function() {
	$("#btnSave").click(function() {
		var writer =$("#writer").val();
		var subject = $("#subject").val();
		var content = $("#contetn").val();
		var passwd = $("#passwd").val();
		
		if(writer == ""){
			alert("이름을 입력하세요.");
			$("#writer").focus();
			return;
		}
		if(subject == ""){
			alert("제목을 입력하세요.");
			$("#writer").focus();
			return;
		}
		if(content == ""){
			alert("내용을 입력하세요.");
			$("#writer").focus();
			return;
		}
		if(passwd == ""){
			alert("비밀번호를 입력하세요.");
			$("#writer").focus();
			return;
		}
		document.form1.submit(); // 수동 서밋
	});
});

</script>
</head>
<body>
<h2>글쓰기</h2>
<form action="${path}/board_servlet/insert.do" method="post" name="form1" enctype="multpart/form-data"><!-- multpart 객체타입 -->
	<table border="1" style="width: 100%;">
		<tr>
			<td>이름</td>
			<td><input name="writer" id="writer"></td>
		</tr>
		<tr>
			<td>제목</td>
			<td><input name="subject" id="subject" size="60"></td>
		</tr>
		<tr>
			<td>본문</td>
			<td><textarea rows="5" cols="60" name="content" id="content"></textarea> </td>
		</tr>
		<tr>
			<td>첨부파일</td>
			<td><input type="file" name="file1" id="file1"></td>
		</tr>
		<tr>
			<td>비밀번호</td>
			<td><input type="password" name="passwd" id="passwd"></td>
		</tr>
		<tr>
		<td colspan="2" align="center">
			<input type="button" value="확인" id="btnSave">
		</td>
		</tr>
	
	
	</table>
</form>
</body>
</html>

 

 

다음으로 <form> 태그에서 insert.do를 호출을 했으므로 Controller에서 코드 구현을 합니다.

 

Controller

 

글쓰기 페이지에서 이름, 제목, 본문만 처리하면 코드량이 적지만.. 파일 업로드 기능 하나 때문에 코드량이 많아집니다.

먼저 MultpartRequest 객체를 사용해야 하고 파일 디렉터리도 만들어줘야 하며, 사용자 입장에서 무조건 파일을 업로드하는 게 아니라서 if문 처리도 해줘야 합니다.

 

제가 간단하게 코드에 해당 역할에 대해서 주석 처리를 했습니다.

 

else if(url.indexOf("insert.do") != -1) {
			//파일업로드 처리
			File uploadDir=new File(Constants.UPLOAD_PATH);
			if(!uploadDir.exists()) {//업로드디렉토리가 존재하지 않으면
				uploadDir.mkdir();//디렉토리를 만듬
			}
			//request를 확장시킨 MultipartRequest생성
			MultipartRequest multi=new MultipartRequest(request, Constants.UPLOAD_PATH, 
					Constants.MAX_UPLOAD, "utf-8", new DefaultFileRenamePolicy());
			String writer=multi.getParameter("writer");
			String subject=multi.getParameter("subject");
			String content=multi.getParameter("content");
			String passwd=multi.getParameter("passwd");
			//클라이언트 ip주소 가져오기
			String ip=request.getRemoteAddr();
			if(ip.equalsIgnoreCase("0:0:0:0:0:0:0:1")) {
				InetAddress inetAddress=InetAddress.getLocalHost();
				ip=inetAddress.getHostAddress();
			}
			System.out.println("클라이언트IP주소 :"+ip);
			String filename=" ";//공백 1개
			int filesize=0;
			try {
				//첨부파일집합
				Enumeration files=multi.getFileNames();
				//다음 요소가 있으면
				while(files.hasMoreElements()) {
					//첨부파일의 이름
					String file1=(String)files.nextElement();
					filename=multi.getFilesystemName(file1);
					File f1=multi.getFile(file1);
					if(f1 != null) {
						filesize=(int)f1.length();//파일 사이즈 저장
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			BoardDTO dto=new BoardDTO();
			dto.setWriter(writer);
			dto.setSubject(subject);
			dto.setContent(content);
			dto.setPasswd(passwd);
			dto.setIp(ip);
			//파일 첨부를 하지 않을 경우
			if(filename == null || filename.trim().equals("")) {
				filename="-";
			}
			dto.setFilename(filename);
			dto.setFilesize(filesize);
			
			dao.insert(dto);
			String page="/board_servlet/list.do";
			response.sendRedirect(contextPath+page);

 

DAO

 

다음으로 Controller에서 미리 만들어둔 insert()DAO로 가서 코드 구현을 합니다.

DAO에서는 mapper 파일을 호출해 줍니다.

 

그럼 mapper 파일을 호출하기 위해서 session 객체에서 제공해 주는 insert를 사용합니다.

 

	//게시물 저장
	public void insert(BoardDTO dto) {
		SqlSession session=null;
		try {
			session=MybatisManager.getInstance().openSession();
			session.insert("board.insert", dto);
			session.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(session != null) session.close();
		}
	}

 

 

board.xml(mapper)

SQL에서 데이터를 추가했을 때 insert문을 사용합니다. mapper에서도 동일하게 insert문을 작성해서 호출하면 됩니다. 

단, 화면에서 입력받은 데이터를 추가해야 하기 때문에 해당 칼럼 데이터는 함수 처리를 해주셔야 합니다.

 

 

mapper 파일까지 끝나면 글쓰기 페이지는 끝났습니다.

다음으로 다운로드, 조회수 기능을 구현합니다.


■ 파일 다운로드 및 조회수 기능 추가  

 

파일 다운로드를 하려면 게시물에 파일을 등록시킨 사용자가 있어야 하므로 해당 글 제목을 클릭 시 상세 화면 페이지로 이동시킬 수 있게 해야 합니다. 물론 조회수도 동일합니다.

 

그렇다면 처음에 구현했던 게시물 목록 페이지에서 작업을 해야 합니다. 

 

list.jsp

 

 

현재 게시물 목록 페이지 코드입니다. 

여기서 제목(subject)과 첨부파일(filename) 코드를 Controller에서 작업할 수 있게 수정해 줍니다.

 

 

전/후 코드를 비교해 보면 코드가 많이 수정된 부분을 확인할 수 있는데, 간단하게 말씀드리자면 제목과 첨부파일 데이터에 <a> 태그를 추가하고 각 기능을 Controller에 보내서 처리하는 코드입니다.

 

다운로드 코드의 filesize 즉, 파일이 0개가 아니면 다운로드할 수 있게 코드 구현을 했습니다. 

 

다음으로 Controller로 넘어갑니다.

 

Controller

 

기능을 하나씩 추가했으므로 esle if 문으로 해당 기능들을 처리합니다.

 

먼저 첨부파일 다운로드 처리를 하는데, 다운로드를 하려면 업로드되었던 파일의 위치 정보값을 저장해야 하고 한글 파일명 처리도 해야 합니다. 

 

여기서 잠깐 Controller 작업을 멈추고 필요한 파일이 있습니다.  

글쓰기 페이지에서 파일 업로드를 하면 보관해야 하는 디렉터리 설정이 필요합니다.

 

그럼 파일 업로드 제한 용량을 설정해야 하고 파일이 업로드되고 관리되는 디렉터리를 설정해줘야 합니다.

앞으로 자주 사용하므로 파일 생성해서 상수 타입으로 설정해 줍니다.

 

 

설정이 끝났다면 다운로드 처리를 합니다.

 

다운로드 부분은 어렵습니다.

코드마다 설명을 주석처리 했습니다.

 

}else if(url.indexOf("download.do") != -1) {
			int num=Integer.parseInt(request.getParameter("num"));
			String filename=dao.getFileName(num);
			System.out.println("첨부파일 이름 : " + filename);
			
			//업로드되었던 파일의 위치정보값을 path에 저장
			String path=Constants.UPLOAD_PATH+filename;
			byte b[]=new byte[4096]; //바이트 배열 생성
			//업로드 폴더에 저장된 파일을 읽기 위한 스트림 생성
			FileInputStream fis=new FileInputStream(path);
			//mimeType(파일의 종류- img, mp3, txt..등)
			String mimeType=getServletContext().getMimeType(path);
			//스트림 방식의 파일 다운로드시 한글 파일명 관련 브라우저 헤더 처리 
			//octet-stream : 8비트로 된 일련의 데이터를 뜻하며 모든 종류의 이진데이터를 처리하겠다는 의미
			if(mimeType==null){
				mimeType="application/octet-stream;charset=utf-8";//다운로드할 mime 유형을 나타냄
			}
			//파일 이름에 한글이 포함된 경우 header로 값을 보내게 되는데 header에는
			//한글이나 특수문자가 올 수 없기 때문에 톰캣 서버의 기본셋팅언어인 서유럽언어
			//8859_1을 한글처리가 가능한 utf-8로 인코딩처리하여 에러를 해결
			filename = new String(filename.getBytes("utf-8"),"8859_1");//추가코드
			response.setHeader("Content-Disposition", "attachment;filename="+filename);
			
			//OutputStream생성 (서버에서 클라이언트에 쓰기)
			ServletOutputStream out=response.getOutputStream();
			int numRead;
			while (true) {
				numRead = fis.read(b, 0, b.length);//데이터를 읽음
				if(numRead == -1) break; //더 이상 내용이 없으면
				out.write(b, 0, numRead);//데이터 쓰기
			}
			//파일 처리 관련 리소스 정리
			out.flush();
			out.close();
			fis.close();
			
			//다운로드 횟수 증가 처리
			dao.plusDown(num);

 

글쓴이도 이 부분에서 시간을 많이 투자했었는데, 바로 한글, 특수문자 파일이 들어오면 깨져서 들어와서 해결하는데 오래 걸렸습니다..^^ 

 

톰캣 서버에서 인코딩 처리하는 방법까지 주석으로 처리해서 적어두었습니다. 참고하시면 좋습니다.

 

Controller 작업을 하고 있어서 조회수 처리까지 같이 해서 DAO에 보내주겠습니다.

 

 }else if(url.indexOf("view.do") != -1) {
	int num=Integer.parseInt(request.getParameter("num"));
	HttpSession session=request.getSession();
			
	//조회수 증가 처리
	dao.plusReadCount(num, session);
		
	BoardDTO dto=dao.view(num);
	request.setAttribute("dto", dto);
	String page="/board/view.jsp";
	RequestDispatcher rd=request.getRequestDispatcher(page);
	d.forward(request, response);

 

다음으로 DAO에 요청 보낸 getFileName(), plusDown(), plusReadCount(), view() 코드 작업을 합니다.

 

DAO

 

< getFileName() >

 

< plusDown() >

 

< plusReadCount() >

 

< view() >

 

session 객체에서 제공해 주는 각 메서드를 활용해서 mapper 파일에 보내줍니다. 

그 외 코드들은 지금까지 포스팅했던 DAO 코드 패턴 그대로 적용합니다.

 

board.xml(mapper)

 

요청 들어온 각 타입에 맞춰서 SQL문 처리를 해줍니다.

 

 

여기서 주의할 점은 SELECT는 꼭 resultType 설정을 해주셔야 합니다.

resultType은 DAO에서 메서드 생성 했을 때 DTO 타입인지, Int, String 타입인지 구분해서 설정해 줍니다.

 

마지막으로 view 단 상세 화면 페이지 생성합니다.

 

view.jsp

 

상세 화면 페이지에서 기능 구현을 하는데, 다음 포스팅을 위해 수정/삭제/댓글 테이블까지 추가했습니다.

 

<body>
<h2>상세 화면</h2>
<form name="form1" method="post">
<table border="1" style="width: 100%;">
 <tr>
  <td>날짜</td>
  <td>${dto.reg_date}</td>
  <td>조회수</td>
  <td>${dto.readcount}</td>
 </tr>
 <tr>
  <td>이름</td>
  <td colspan="3">${dto.writer}</td>
 </tr>
 <tr>
  <td>제목</td>
  <td colspan="3">${dto.subject}</td>
 </tr>
 <tr>
  <td>본문</td>
  <td colspan="3">${dto.content}</td>
 </tr>
 <tr>
  <td>비밀번호</td>
  <td colspan="3">
   <input type="password" name="passwd" id="passwd">
   <c:if test="${param.message == 'error'}">
    <span style="color: red;">비밀번호가 일치하지 않습니다.</span>
   </c:if>
  </td>
 </tr>
 <tr>
  <td>첨부파일</td>
  <td colspan="3">
   <c:if test="${dto.filesize > 0}">${dto.filename}(${dto.filesize} bytes )
    <a href="${path}/board_servlet/download.do?num=${dto.num}">[다운로드]</a>
   </c:if>
  </td>
 </tr>
 <tr>
  <td colspan="4" align="center">
   <input type="hidden" name="num" value="${dto.num}">
   <input type="button" value="수정/삭제" id="btnEdit">
   <input type="button" value="답변" id="btnReply">
   <input type="button" value="목록" id="btnList">
  </td>
 </tr>
</table>
</form>
<!-- 댓글 쓰기 폼 -->
<table border="1" style="width: 100%;">
 <tr>
  <td><input id="writer" placeholder="이름"></td>
  <td rowspan="2">
   <button id="btnSave" type="button">확인</button>
  </td>
 </tr>
 <tr>
  <td><textarea rows="5" cols="80" placeholder="내용을 입력하세요" id="content"></textarea></td>
 </tr>
</table>

<!-- 댓글 목록을 출력할 영역 -->
<div id="commentList"></div>

</body>
</html>

 

상세화면 페이지 생성까지 끝났습니다. 결과를 확인해 봅시다.

 

<게시물 추가>

 

< 게시물 목록 >

 

글쓰기 페이지가 제대로 작동되는 걸 확인했다면 첨부파일을 다운로드해 보겠습니다.

 

 

< 첨부파일 다운로드>

 

 

첨부파일 이미지를 클릭했더니 다운로드 횟수가 증가했습니다.

 

 

다행히 한글이 깨지지 않고 제대로 다운로드 됐습니다. 다음으로 설정했던 디렉터리에서 확인해 봅니다.

 

 

 

다음으로 확인해야 할 건 상세화면 페이지입니다.

 

< 상세 화면 >

 

조회수도 제대로 확인했습니다.

게시물 목록에도 정상적으로 작동하는지 확인하고 마무리하겠습니다.

 

< 조회수 >

 


마치며

 

오늘은 글쓰기, 상세화면 페이지를 생성해서 다운로드, 조회수 기능을 추가했습니다.

다음 포스팅 댓글 기능을 추가하겠습니다.

728x90