웹 서버 개발 (19) 비회원 문의 게시판
비회원 문의 게시판
- 회원 가입을 하지 않아도 사용자는 문의 게시판을 이용할 수 있다.
- 비밀번호를 통해 본인이 쓴 글만 삭제할 수 있다.
- 최대 3개의 첨부파일을 업로드할 수 있다.
SQL 비회원 문의 게시글
Table 만들기
- sid
- gen_time ( 생성 시간 )
- title
- main_text
- salt
- passwd
- file cnt
- file sids
create table no_member_notice_board (
sid NUMBER(20) PRIMARY KEY,
gen_time number(20),
title varchar2(100),
main_text clob,
salt varchar2(30),
password varchar2(150),
file_cnt number(20) default 0 not null,
file_sids nvarchar2(200) default '[]' not null
);
비회원 문의글 작성
- 일단 문의글부터 작성한다. 파일 저장은 update 를 이용하여 저장해 줄 것이다.
- 문의글 생성일, 제목, 본문, 비밀번호를 입력하여야 한다.
insert into
no_member_notice_board (sid, gen_time, title, main_text, salt, password)
select ?,?,?,?,salt,standard_hash(salt||?,'SHA512') from (select DBMS_RANDOM.STRING('P', 30) as salt from dual);
- 파일 sid 목록들과 첨부파일 수를 동시에 저장한다.
insert into
no_member_notice_board (sid, gen_time, title, main_text, salt, password, file_cnt,file_sids)
select ?,?,?,?,salt,standard_hash(salt||?,'SHA512'),?,? from (select DBMS_RANDOM.STRING('P', 30) as salt from dual);
DAO 코드
public class NoMemberNoticeBoardDAO {
... 생략 ...
public long addNotice(String title, String mainText, String password){
long re = -1;
var query = new StringBuilder();
query.append("insert into ");
query.append(tableName);
query.append(" (sid, gen_time, title, main_text, salt, password) ");
query.append("select ");
query.append(" ?,?,?,?,salt,standard_hash(salt||?,'SHA512') from (select DBMS_RANDOM.STRING('P', 30) as salt from dual)");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
re = getSid();
pstmt.setLong(1, re);
pstmt.setLong(2, System.currentTimeMillis());
pstmt.setString(3, title);
pstmt.setString(4, mainText);
pstmt.setString(5, password);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
re = -1;
} finally {
Close();
}
return re;
}
public long addNotice2(String title, String mainText, String password,ArrayList<Long> arr){
long re = -1;
var query = new StringBuilder();
query.append("insert into ");
query.append(tableName);
query.append(" (sid, gen_time, title, main_text, salt, password, file_cnt,file_sids) ");
query.append("select ");
query.append(" ?,?,?,?,salt,standard_hash(salt||?,'SHA512'),?,? from (select DBMS_RANDOM.STRING('P', 30) as salt from dual)");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
re = getSid();
pstmt.setLong(1, re);
pstmt.setLong(2, System.currentTimeMillis());
pstmt.setString(3, title);
pstmt.setString(4, mainText);
pstmt.setString(5, password);
pstmt.setLong(6, arr.size());
pstmt.setString(7, arr.toString());
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
re = -1;
} finally {
Close();
}
return re;
}
문의글 목록 가져오기
- 문의글 생성 시간으로 정렬해준다.
- 모든 문의 글을 가져오지 않고 특정 갯수의 문의 글을 가지고 온다.
select * from no_member_notice_board order by gen_time desc OFFSET ? ROWS FETCH NEXT ? ROWS ONLY;
DAO 코드
- 문의 게시글 목록을 가지고 온다.
public ArrayList<NoMemberNoticeBoard> getNoticeList(long start, long number){
var re = new ArrayList<NoMemberNoticeBoard>();
var query = new StringBuilder();
query.append("select * from ");
query.append(tableName);
query.append(" order by gen_time desc OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
try{
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, start);
pstmt.setLong(2, number);
var result = pstmt.executeQuery();
while(result.next()){
re.add(
new NoMemberNoticeBoard(
result.getLong(1),
result.getLong(2),
result.getString(3),
result.getString(4)
)
);
}
}catch(Exception e){
e.printStackTrace();
re = new ArrayList<NoMemberNoticeBoard>();
}finally{
Close();
}
return re;
}
문의글 파일 sid 목록 가지고 오기
- 문의글의 첨부파일 sid 목록을 리스트 형태로 가지고온다.
select * from no_member_notice_board where sid=?;
DAO 코드
// get file sid list
public ArrayList<Long> getFileSidList(long noticeSid){
ArrayList<Long> fileSidList = null;
var query = new StringBuilder();
query.append("select file_sids from ");
query.append(tableName);
query.append(" where sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, noticeSid);
var re = pstmt.executeQuery();
String listStr = "[]";
if(re.next()){
listStr = re.getString(1);
}
fileSidList = strToArrayList(listStr);
} catch (Exception e) {
e.printStackTrace();
fileSidList = new ArrayList<Long>();
} finally {
Close();
}
return fileSidList;
}
private ArrayList<Long> strToArrayList(String listStr){
var arr = new ArrayList<Long>();
if(listStr.length() > 2){
arr.addAll(Arrays.stream(listStr.substring(1,listStr.length()-1).split(", ")).map(Long::parseLong).collect(Collectors.toList()));
}
return arr;
}
문의글 갯수 구하기
select count(sid) from no_member_notice_board;
DAO 코드
public int getCount(){
var cnt = 0;
var query = new StringBuilder();
query.append("select count(sid) from ");
query.append(tableName);
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
var result = pstmt.executeQuery();
if(result.next()){
cnt = result.getInt(1);
}
} catch (Exception e) {
e.printStackTrace();
cnt = 0;
} finally {
Close();
}
return cnt;
}
문의글 읽기
- 비밀번호를 맞게 입력해야 비회원이 작성한 게시글을 읽을 수 있다.
select * from no_member_notice_board where sid=? and password=standard_hash(salt||?,'SHA512');
DAO 코드
public NoMemberNoticeBoard getNoitceBoard(long sid, String password){
NoMemberNoticeBoard noMemberNoticeBoard = null;
var query = new StringBuilder();
query.append("select * from ");
query.append(tableName);
query.append(" where sid=? and password=standard_hash(salt||?,'SHA512')");
try{
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, sid);
pstmt.setString(2, password);
var result = pstmt.executeQuery();
if(result.next()){
noMemberNoticeBoard = new NoMemberNoticeBoard(
result.getLong(1),
result.getLong(2),
result.getString(3),
result.getString(4)
);
}
}catch(Exception e){
noMemberNoticeBoard = null;
}finally {
Close();
}
return noMemberNoticeBoard;
}
문의글 삭제
- 비밀번호를 맞게 입력해야 비회원 게시글을 삭제할 수 있다.
delete from no_member_notice_board where sid=? and password=standard_hash(salt||?,'SHA512');
DAO 코드
public boolean noticeDelete(long sid, String password){
// 첨부 파일 삭제
new NoMemberNoticeBoardFileDAO(context).deleteNoticeFile(sid);
var re = false;
var query = new StringBuilder();
query.append("delete from ");
query.append(tableName);
query.append(" where sid=? and password=standard_hash(salt||?,'SHA512')");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, sid);
pstmt.setString(2, password);
pstmt.executeUpdate();
re = true;
} catch (Exception e) {
e.printStackTrace();
re = false;
} finally {
Close();
}
return re;
}
문의글 수정하기
- 제목과 본문 수정하기.
update no_member_notice_board set gen_time=?,title=?,main_text=? where sid=? and password=standard_hash(salt||?,'SHA512');
- 업로드 파일 sid를 문의 글 리스트에 추가하기.
update no_member_notice_board set file_cnt=?,file_sids=? where sid=? and password=standard_hash(salt||'a','SHA512');
DAO 코드
public long noticeModifySave(long sid, String prePassword, String title, String mainText, String newPassword){
// 비번 일치 확인
long newSid = -1;
var preNotice = getNoitceBoard(sid, prePassword);
System.out.println("일치 확인"+preNotice+" "+sid+" "+prePassword);
if(preNotice == null){ // 비번 일치 안함
return -1;
}
var arr = getFileSidList(sid);
// 새로운 문의 글 생성
newSid = addNotice2(title, mainText, newPassword,arr);
if(newSid == -1){
return -1;
}
// 이전에 저장된 파일 새로운 글로 이전
new NoMemberNoticeBoardFileDAO(context).changeNoticeSid(sid, newSid);
// 이전 문의 글 삭제
System.out.println("이전 글 삭제");
if(!noticeDelete(sid,prePassword)){
return -1;
}
return newSid;
}
// file list 저장 및 저장된 파일 수 저장
public boolean updateFileListAndCnt(ArrayList<Long> fileSidList, long noticeSid){
var re = false;
var query = new StringBuilder();
query.append("update ");
query.append(tableName);
query.append(" set file_cnt=?,file_sids=? where sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1,fileSidList.size());
pstmt.setString(2, fileSidList.toString());
pstmt.setLong(3, noticeSid);
pstmt.executeUpdate();
re = true;
} catch (Exception e) {
e.printStackTrace();
re = false;
} finally {
Close();
}
return re;
}
SQL 파일 저장
Table 만들기
- sid
- 비 회원 게시글 sid ( 외래키 )
- 파일 이름
- 파일 바이너리
create table no_member_notice_file (
sid NUMBER(20) PRIMARY KEY,
notice_board_sid NUMBER(20),
file_name NVARCHAR2(200),
file_content blob,
foreign key (notice_board_sid) references no_member_notice_board (sid)
);
파일 추가하기
- 바이너리 형태로 DB 테이블에 파일을 저장한다.
insert into no_member_notice_file (sid, notice_board_sid, file_name, file_content) values(?,?,?,?);
DAO 코드
public class NoMemberNoticeBoardFileDAO {
public long saveNoticeFile(long noticeBoardSid, Part part){
long re = -1;
var sid = genSid();
var query = new StringBuilder();
query.append("insert into ");
query.append(tableName);
query.append(" (sid, notice_board_sid, file_name, file_content) values(?,?,?,?)");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, sid);
pstmt.setLong(2, noticeBoardSid);
pstmt.setString(3, part.getSubmittedFileName());
pstmt.setBinaryStream(4, part.getInputStream(), part.getSize());
pstmt.executeUpdate();
re = sid;
} catch (Exception e) {
e.printStackTrace();
re = -1;
} finally {
Close();
}
return re;
}
파일 이름 읽어오기
- 테이블에 저장된 파일이름을 읽어 온다.
- 다운로드 링크를 만들때 사용된다.
select file_name from no_member_notice_file where sid=?;
DAO 코드
public String getFileName(long sid){
String fileName = "none";
var query = new StringBuilder();
query.append("select file_name from ");
query.append(tableName);
query.append(" where sid=?");
try{
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, sid);
var re = pstmt.executeQuery();
if(re.next()){
fileName = re.getString(1);
}
}catch(Exception e){
e.printStackTrace();
fileName="none";
}finally {
Close();
}
return fileName;
}
파일 가지고 오기
- 파일 sid를 이용하여 테이블에 저장된 파일을 가지고 온다.
select file_content from no_member_notice_file where sid=?;
DAO 코드
// 파일 찾기
public InputStream getFile(long fileSid){
InputStream re = null;
var query = new StringBuilder();
query.append("select file_content from ");
query.append(tableName);
query.append(" where sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, fileSid);
var result = pstmt.executeQuery();
if(result.next()){
var blob = result.getBlob(1);
re = blob.getBinaryStream();
}
} catch (Exception e) {
e.printStackTrace();
re = null;
}
return re;
}
문의글에 속하는 파일 삭제하기
- 문의글 sid를 이용하여 문의글에 속하는 파일을 삭제해 준다.
delete from no_member_notice_file where notice_board_sid=?;
DAO 코드
// 게시글 에 속한 파일 삭제하기.
public void deleteNoticeFile(long noticeSid){
var query = new StringBuilder();
query.append("delete from ");
query.append(tableName);
query.append(" where notice_board_sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, noticeSid);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
Close();
}
}
파일 삭제하기
- 파일 sid를 이용하여 파일을 삭제한다.
delete from no_member_notice_file where sid=?;
DAO 코드
// 파일 sid 로 삭제
public boolean deleteFile(long fileSid){
var re = false;
var query = new StringBuilder();
query.append("delete from ");
query.append(tableName);
query.append(" where sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, fileSid);
pstmt.executeUpdate();
re = true;
} catch (Exception e) {
e.printStackTrace();
re = false;
}finally{
Close();
}
return re;
}
문의글 번호 변경
- 문의글을 수정할 때 새로 만들어진 문의글 sid로 변경한다.
update no_member_notice_file set notice_board_sid=? where notice_board_sid=?;
DAO 코드
// 새로운 문의글 sid 로 변경
public boolean changeNoticeSid(long oldSid, long newSid){
var re = false;
var query = new StringBuilder();
query.append("update ");
query.append(tableName);
query.append(" set notice_board_sid=? where notice_board_sid=?");
try {
Connection();
var pstmt = conn.prepareStatement(query.toString());
pstmt.setLong(1, newSid);
pstmt.setLong(2, oldSid);
pstmt.executeUpdate();
re = true;
} catch (Exception e) {
e.printStackTrace();
re = false;
} finally {
Close();
}
return re;
}
첫 페이지 만들기
- 로그인 페이지와 문의 게시판 페이지를 구분하기 위한 첫 페이지를 만들어 주었다.
- 이 페이지는 로그인을 하지 않아도 방문할 수 있다. 즉 회원과 비회원 모두 이용할 수 있다.
JSP 코드 ( first_page.jsp )
- servlet 로 부터 문의 게시판 링크와 로그인 링크를 받아온다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
h1 {
font-size: 100px;
width: 100%;
text-align: center;
margin:0px;
padding-bottom: 100px;
}
.button-design {
text-decoration: none;
border-radius: 10px / 10px;
width: 240px;
font-size: 20px;
margin-right: 25px;
margin-left: 25px;
margin-bottom: 10px;
}
.button-design:link, .button-design:visited {
background-color: #d0d0d0;
color: #000000;
padding: 5px 25px;
display: inline-block;
text-decoration: none;
text-align: center;
}
.button-design:hover{
background-color: #909090;
}
</style>
</head>
<body style="display: flex;">
<div style="width: 70%;margin:auto;">
<h1><b>Happy Hacking</b></h1>
<div style="width:100%;text-align: center;margin-top: 50px;">
<a class="button-design" href="${noMemberNoticeBoard}">문의 게시판</a>
<a class="button-design" href="${signInPage}">로그인</a>
</div>
</div>
</body>
</html>
Servlet 코드 ( MainPage.java )
- 로그인 페이지 링크와 문의 게시글 링크를 만들어 보내준다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean noMemberSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
var uri = request.getRequestURI();
... 생략 ...
if(uri.equals("/")){
request.setAttribute("signInPage", "sign_in_page");
request.setAttribute("noMemberNoticeBoard", noMemberNoticeBoard+"?page=1");
gotoForwardPage(request, response, "/WEB-INF/no_login/first_page.jsp");
return true;
}
문의 게시판 페이지
- 문의 게시판은 비회원, 회원 누구나 이용이 가능하다.
- 문의 게시글을 저장을 할려면 비밀번호 입력은 필수이다.
- 문의 게시글의 제목은 누구나 볼 수 있지만 본문 내용은 문의 글 작성자 또는 관리자만이 볼 수 있다.
- 문의 게시판은 전에 만들었던 사용자 게시판 기능 코드와 거의 유사하다.
- 게시글을 클릭하면 비밀번호 입력창이 뜬다. 여기서 비밀번호를 맞게 입력해야 문의 게시글 확인이 가능하다.
JSP 코드 ( notice_board.jsp )
- 전에 만들었던 게시판 코드와 거의 유사하다는 것을 확인할 수 있다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.ArrayList"%>
<%@ page import="com.mingyu2.happyhackingmain.noMemberNoticeBoard.NoMemberNoticeBoard"%>
<%
var arr = (ArrayList<NoMemberNoticeBoard>)request.getAttribute("noticeBoard");
if(arr == null){
arr = new ArrayList<NoMemberNoticeBoard>();
}
var pagination = (ArrayList<Integer>)request.getAttribute("pagination");
var currentPage = (Integer)request.getAttribute("currentPage");
var lastPage = (Integer)request.getAttribute("lastPage");
%>
<html>
<head>
<style>
.button-design {
text-decoration: none;
border-radius: 10px / 10px;
}
.button-design:link, .button-design:visited {
background-color: #d0d0d0;
color: #000000;
padding: 5px 25px;
display: inline-block;
text-decoration: none;
text-align: center;
}
.button-design:hover{
background-color: #909090;
}
.notice-board-list {
border: 1px solid #000000;
padding: 16px;
}
.notice-board-list:hover {
background: #e0e0e0;
}
.pagination-item{
text-decoration: none;
border : 1px solid #d0d0d0;
border-radius: 10px / 10px;
margin: 0 1px 0 1px;
padding: 0 10px 0 10px;
color : #000000;
&[aria-selected="true"] {
background-color: #6699FF;
};
}
.pagination-item:hover {
&[aria-selected="false"] {
background-color: #e0e0e0;
};
}
.modal{
display: none;
position: fixed;
z-index: 1;
left: 0;
right: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0,0, 0.4);
/* padding-top: 60px; */
}
.modal-content {
background-color: #fefefe;
margin: 30% auto 15% auto;
border: 3px solid;
width: 40%;
text-align: center;
}
</style>
<script>
window.onclick = function(event) {
if(event.target == document.getElementById('addr')){
document.getElementById('addr').style.display = 'none';
}
}
function noticeDetail(sid){
document.getElementById('addr').style.display='block';
document.getElementById('sid').value = sid;
}
</script>
</head>
<body>
<div id="addr" class="modal">
<div class="modal-content">
<form method="post" action="${noMemberNoitceDetail}" style="margin-top:10px;margin-bottom: 10px;font-size: 20px;">
<label><b>문의글 비밀번호</b></label>
<input id="sid" style="display: none;" name="detailsid" value="">
<input style="font-size: 20px;width: 90%;margin:auto;margin-top: 10px;margin-bottom: 10px;" type="password" name="password">
<input type="submit" value="전송">
</form>
</div>
</div>
<div style="text-align:right;">
<a class="button-design" href="/">뒤로가기</a>
</div>
<h1 style="width: 100%; text-align:center;">문의 게시판</h1>
<hr>
<div style="text-align:right;">
<a class="button-design" href="${noMemberNoticeWrite}">문의 글쓰기</a>
</div>
<div style="width: 70%;margin:auto;margin-top:10px">
<% for(var n : arr) { %>
<div class="notice-board-list" onclick="noticeDetail(<%=n.getSid() %>);">
<h2 style="margin:5px 0"><%=n.getTitle() %></h2>
<span>비밀글 입니다.</span><br>
</div>
<% } %>
<br>
<div class="pagination" style="text-align: center;margin-bottom: 30px;">
<% if(currentPage != 1){ %>
<a class="pagination-item" href="${noMemberNoticeBoard}?page=<%=(currentPage-1)%>">Prev</a>
<% } %>
<% for(int n:pagination) { %>
<% if(n == 0) {
out.print("<a>...</a>");
continue;
} %>
<% if(n != currentPage) { %>
<a class="pagination-item" aria-selected="false" href="${noMemberNoticeBoard}?page=<%=n%>"><%=n%></a>
<% } else { %>
<a class="pagination-item" aria-selected="true"><%=n%></a>
<% } %>
<% } %>
<% if(currentPage != lastPage){ %>
<a class="pagination-item" href="${noMemberNoticeBoard}?page=<%=(currentPage+1)%>">Next</a>
<% } %>
</div>
</div>
</body>
</html>
Servlet 코드 ( MainPage.java )
- 문의 게시글을 리스트로 만들어 JSP에 전달한다. 그리고 페이징을 시스템이 적용되어 한번에 5개의 게시글을 확인할 수 있다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean noMemberSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 비회원 문의 게시판 목록
if(uri.equals(noMemberNoticeBoard)){
var p = request.getParameter("page");
var page = 1;
try{
page = Integer.parseInt(p);
if(page < 1){
page = 1;
}
}catch(Exception e){
e.printStackTrace();
page = 1;
}
var noMemNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
int cnt = noMemNoticeBoardDAO.getCount();
if(cnt == 0){
cnt = 1;
}
// ****** 클라이언트가 수정 가능 *******
// 한 페이지 보여줄 데이터 갯수 정하기
int number = 5;
// ****** 클라이언트가 수정 가능 *******
// 마지막 페이지
int lastPage = (cnt/number)+((cnt%number == 0)?0:1);
// 최대 페이지 수를 초과하면 가장 마지막 페이지로 이동한다.
if(page > lastPage){
response.sendRedirect(noMemberNoticeWrite+"?page="+lastPage);
return true;
}
// 데이터 시작 위치
int start = (page-1)*number;
// 검색 데이터 가져오기.
var array = noMemNoticeBoardDAO.getNoticeList(start, number);
//***** 페이징 정보 *****
var pagination=new ArrayList<Integer>();
// ****** 클라이언트가 수정 가능 *******
var paginationBase = 5; // 기본이되는 페이징 홀수
// ****** 클라이언트가 수정 가능 *******
pagination.add(page);
// 페이징을 배열에 저장한다.
var leftEnd = false;
var rightEnd = false;
for(int i = 0, left=page, right=page;i<paginationBase-1;){
// left
if(left > 1){
left -=1;
pagination.add(0, left);
i++;
}else {
leftEnd=true;
}
// right
if(right < lastPage){
right+=1;
pagination.add(right);
i++;
}else {
rightEnd=true;
}
if(leftEnd && rightEnd){
break;
}
}
// 첫번째 페이지와 마지막 페이지를 포함해야 하는지 검사한다. 포함해야 하면 포함시킨다.
// 0 은 생략을 의미한다.
// prev 1 ... 3 4 5 6 next
var f = pagination.get(0);
if(f != 1){
if(f != 2){
pagination.add(0,0);
}
pagination.add(0,1);
}
var l = pagination.get(pagination.size()-1);
if(l != lastPage){
if(l != lastPage-1){
pagination.add(0);
}
pagination.add(lastPage);
}
//***** 페이징 정보 end *****
//***** 정보 넘겨주기 *****
request.setAttribute("noticeBoard", array);
// 페이징 번호 리스트
request.setAttribute("pagination", pagination);
// 현재 페이지 정보 page
request.setAttribute("currentPage", page);
// 마지막 Page
request.setAttribute("lastPage", lastPage);
request.setAttribute("noMemberNoitceDetail", noMemberNoitceDetail);
request.setAttribute("noMemberNoticeWrite",noMemberNoticeWrite);
request.setAttribute("noMemberNoticeBoard",noMemberNoticeBoard);
gotoForwardPage(request, response, "/WEB-INF/no_login/notice_board/notice_board.jsp");
return true;
}
// 비회원 문의 게시판 목록 end
문의 게시글 새로 작성하기 페이지
- 기존의 게시글 작성 폼을 이용하였다.
- 문의 게시글을 저장하기 위해서는 빈칸이 없어야 한다. 그리고 비밀번호 입력은 필수이다.
JSP 코드 ( notice_write.jsp )
- 게시글 작성 코드와 동일하다.
- 문의 글 작성 모드일 때만 비밀번호 입력창이 나타난다.
- 코드가 완전히 동일하고 비밀번호 입력창이 문의 글 작성 때는 나오도록 다음 코드를 추가하였다.
<% if(hasPassword) { %>
<tr>
<th>비밀번호</th>
<td>
<input name="password" type="password" style="width:100%;font-size: 20px;"/>
<input style="display: none;" type="password" name="pre-password" value="${prePassword}" />
</td>
</tr>
<% } %>
Servlet 코드 ( MainPage.java )
- 비회원 문의 게시글 저장을 위한 링크를 추가해 주었다.
// 비회원 문의 게시판 글 쓰기
if(uri.equals(noMemberNoticeWrite)){
request.setAttribute("hasPassword",true); // 비회원 글쓰기
request.setAttribute("noticeBoardURL", noMemberNoticeSave);
gotoForwardPage(request, response, "/WEB-INF/main_page/notice_board/notice_write.jsp");
return true;
}
// 비회원 문의 게시판 글 쓰기 end
문의 게시글 저장하기
- 문의 게시글 새로작성 페이지에서 저장 버튼을 눌러 저장을 서버에 요청을 하면 빈칸이 있는지와 비밀번호 입력이 되었는지를 확인하여 모두 입력이 되어있다면 문의 게시글 테이블에 게시글을 저장한다.
- 첨부된 파일 역시 파일 테이블에 저장해준다.
// 비회원 문의 게시글 저장
if(request.getMethod().equals("POST") && uri.equals(noMemberNoticeSave)){
var charSet = "utf-8";
request.setCharacterEncoding(charSet);
if(!request.getContentType().toLowerCase().startsWith("multipart/form-data")){
//404 에러 발생
// application/x-www-form-urlencoded 타입이 아니라.
// 오직 multipart/form-data 타입만 받는다.
return false;
}
var title = request.getParameter("title");
var mainText = request.getParameter("main-text");
var password = request.getParameter("password");
// 실패
if(title.equals("")) {
simplePage(response, "<script>alert('title blink!');window.history.back();</script>");
return true;
}
if(mainText.equals("")){
simplePage(response, "<script>alert('main text blink!');window.history.back();</script>");
return true;
}
if(password.equals("")){
simplePage(response, "<script>alert('password blink!');window.history.back();</script>");
return true;
}
var noMemberNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
var noticeSid = noMemberNoticeBoardDAO.addNotice(title,mainText,password);
if(noticeSid == -1){
simplePage(response, "<script>alert('save fail!');window.history.back();</script>");
return true;
}
// 게시글 저장 성공
// ***** 파일 업로드 시작 *****
var noticeRe = "no file";
try{
// 올바른 파일인지 확인하고 db에 저장한다.
var parts = request.getParts();
var fileSidList = new ArrayList<Long>();
for(var part:parts){
if(!part.getHeader("Content-Disposition").contains("filename=")){
continue;
}
if(part.getSubmittedFileName().equals("")){
continue;
}
// 파일 이름에 금지된 문자가 사용되면 파일 업로드 안하기.
if(notCorrectFileName(part.getSubmittedFileName())){
continue;
}
if(fileSidList.size() > MAX_FILE_NUM){
noticeRe = "file max number";
break;
}
// 데이터 DB 테이블에 저장하기
var re = new NoMemberNoticeBoardFileDAO(getServletContext()).saveNoticeFile(noticeSid, part);
part.delete(); // 임시파일 삭제
if(re != -1){
System.out.println("업로드 성공");
noticeRe = "file upload success";
fileSidList.add(re);
}else{
System.out.println("파일 업로드 실패");
noticeRe = "file upload fail";
}
}
// 게시글 file list 와 저장된 파일 갯수 저장하기.
noMemberNoticeBoardDAO.updateFileListAndCnt(fileSidList, noticeSid);
}catch(Exception e){
e.printStackTrace();
System.out.println("파일 업로드 실패");
noticeRe = "file upload fail";
}
// ***** 파일 업로드 end *****
simplePage(response, "<script>alert('save success! "+noticeRe+" ');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
// 비회원 문의 게시글 저장 end
문의 게시글 자세히 보기 페이지
- 문의 게시글을 클릭하고 비밀번호를 입력하면 작성한 문의 게시글을 자세히 볼 수 있다.
- 문의 게시글을 삭제하거나 수정할 수 있다.
JSP 코드 ( notice_detail.jsp )
- 문의 게시글 내용을 확인할 수 있다. 그리고 첨부파일 역시 다운받을 수 있다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.mingyu2.happyhackingmain.noMemberNoticeBoard.NoMemberNoticeBoard"%>
<%@ page import="com.mingyu2.happyhackingmain.Pair" %>
<%@ page import="java.util.ArrayList" %>
<%
var notice = (NoMemberNoticeBoard)request.getAttribute("notice");
var fileNameList = (ArrayList<Pair<String,String>>)request.getAttribute("fileNameList");
%>
<html>
<head>
<style>
.main-div{
margin : 0 auto 0 auto;
width: 80%;
}
.notice_write, .notice_write th, .notice_write td {
border: 1px solid #bcbcbc;
}
</style>
</head>
<body>
<div class="main-div">
<table style="width: 100%;" class="notice_write">
<thead>
<tr>
<th scope="col" colspan="2">
<h2><%=notice.getTitle() %></h2>
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;" colspan="2">
<%=notice.getGenDate() %>
</td>
</tr>
<% if(fileNameList.size() > 0) { %>
<tr>
<td style="text-align: center;">
<span>첨부파일</span>
</td>
<td>
<% for(var file : fileNameList ) { %>
<a href="${noticeBoardFileDownload}?downlink=<%=file.getD2() %>&pageid=<%=notice.getSid() %>&password=${password}"><%=file.getD1() %></a><br>
<% } %>
</td>
</tr>
<%}%>
<tr>
<td colspan="2">
<div style="width:100%;word-break:break-all;word-wrap:break-word; overflow-y: auto; height:250px; resize: none;">
<%=notice.getMainText() %>
</div>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<input type="button" onclick="javascript:window.history.back();" value="뒤로가기"/>
<form method="post" style="margin:0;display: inline-block;" action="${noMemberNoitceDelete}">
<input style="display: none;" type="text" name="pageid" value="<%=notice.getSid() %>">
<input style="display: none;" type="password" name="password" value="${password}">
<input type="submit" value="삭제하기"/>
</form>
<form method="post" style="margin:0;display: inline-block;" action="${noMemberNoitceModify}">
<input style="display: none;" type="text" name="pageid" value="<%=notice.getSid() %>">
<input style="display: none;" type="password" name="password" value="${password}">
<input type="submit" value="수정하기"/>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Servlet 코드 ( MainPage.java )
- 비회원 문의 게시글을 테이블에서 가지고 온다.
- 비밀번호를 맞게 입력해야 문의 게시글을 가지고 온다.
// 비회원 비번 입력 문의 글 읽기
if(request.getMethod().equals("POST") && uri.equals(noMemberNoitceDetail)){
try{
var sid = Long.parseLong(request.getParameter("detailsid")); // 문의 게시글 sid
var password = request.getParameter("password"); // 문의 게시글 비밀번호
// 게시글 정보 가지고 오기.
var noMemberNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
var notice = noMemberNoticeBoardDAO.getNoitceBoard(sid, password);
if(notice == null){
simplePage(response, "<script>alert('password diff!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
var noticeSID = notice.getSid();
// *** 파일들 읽어 오기 ***
// 파일 이름 찾기 및 href 링크 만들기
var fileNameList = new ArrayList<Pair<String,String>>();
var fileSidList = noMemberNoticeBoardDAO.getFileSidList(noticeSID);
var noticeFileDAO = new NoMemberNoticeBoardFileDAO(getServletContext());
for(var fileSid:fileSidList){
var fileName = noticeFileDAO.getFileName(fileSid);
fileNameList.add(new Pair<String,String>(fileName,fileSid.toString()));
}
request.setAttribute("fileNameList", fileNameList);
request.setAttribute("noticeBoardFileDownload", noMemberNoticeFileDownload);
// *** 파일들 읽어 오기 end ***
request.setAttribute("notice", notice);
request.setAttribute("password",password);
request.setAttribute("noMemberNoitceDelete", noMemberNoitceDelete);
request.setAttribute("noMemberNoitceModify", noMemberNoitceModify);
gotoForwardPage(request, response, "/WEB-INF/no_login/notice_board/notice_detail.jsp");
}catch(Exception e){
e.printStackTrace();
simplePage(response, "<script>alert('password diff!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
}
return true;
}
// 비회원 비번 입력 문의 글 읽기 end
문의 게시글 삭제
- 문의 게시글에서 삭제 버튼을 누르면 문의 게시글이 삭제된다.
- 문의 게시글에 방문할 때 입력한 비밀번호를 이용하여 비밀번호와 게시글 번호가 모두 일치할 때 삭제가 가능하다.
Servlet 코드 ( MainPage.java )
- 삭제 버튼을 눌렀을 때 실행되는 코드이다.
// 비회원 글 삭제
if(request.getMethod().equals("POST") && uri.equals(noMemberNoitceDelete)){
try {
var sid = Long.parseLong(request.getParameter("pageid"));
var password = request.getParameter("password");
var noMemberNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
if(!noMemberNoticeBoardDAO.noticeDelete(sid, password)){
simplePage(response, "<script>alert('delete fail!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
} catch (Exception e) {
e.printStackTrace();
simplePage(response, "<script>alert('delete fail!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
simplePage(response, "<script>alert('delete success!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
// 비회원 글 삭제 end
첨부 파일 다운로드
- 첨부파일 링크를 눌렀을 때 첨부파일 다운로드가 가능해야 한다.
Servlet 코드 ( MainPage.java )
// 비회원 글 첨부파일 다운로드
if(uri.equals(noMemberNoticeFileDownload)){
long pageSid;
long fileSid;
try {
pageSid = Long.parseLong(request.getParameter("pageid"));
fileSid = Long.parseLong(request.getParameter("downlink"));
} catch (Exception e) {
e.printStackTrace();
return false;
}
var password = request.getParameter("password");
var noticeDAO = new NoMemberNoticeBoardDAO(getServletContext());
var notice = noticeDAO.getNoitceBoard(pageSid,password);
if(notice == null) {
return false;
}
var fileDAO = new NoMemberNoticeBoardFileDAO(getServletContext());
var is = fileDAO.getFile(fileSid);
if(is == null){
return false;
}
var fileSize = is.available();
var fileName = fileDAO.getFileName(fileSid);
// 사용자에게 다운로드 보내기
var sMimeType = getServletContext().getMimeType(fileName);
if(sMimeType == null || sMimeType.length() == 0){
sMimeType = "application/octet-stream";
}
BufferedOutputStream outs = null;
try {
response.setContentType(sMimeType+"; charset=utf-8");
if(fileSize > 0){
response.setContentLength(fileSize);
}
var userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);
if(userAgent != null && userAgent.contains("MSIE 5.5")){ // MSIE 5.5 이하
return false;
}else if(userAgent != null && userAgent.contains("MSIE")){ // MS IE
return false;
}else{ // 모질라
response.setHeader("Content-Disposition", "attachment; filename="+ new String(fileName.getBytes("utf-8"), "latin1") + ";");
}
// 사용자에게 파일을 전송한다.
outs = new BufferedOutputStream(response.getOutputStream());
int read = -1;
byte[] b = new byte[8192];
while((read = is.read(b)) != -1){
outs.write(b, 0, read);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (Exception e){
e.printStackTrace();
}
try {
outs.close();
} catch (Exception e){
e.printStackTrace();
}
// 다운로드 완료 된후 DB 연결을 해제해야한다.
fileDAO.Close();
}
System.out.println("사용자 파일 다운로드 "+ pageSid + " "+ fileSize);
return true;
}
// 비회원 글 첨부파일 다운로드 end
문의 글 수정 페이지
- 수정하기 버튼을 누르면 수정페이지로 넘어간다.
- 게시글 작성 코드를 이용하였다.
- 첨부파일을 삭제하거나 추가할 수 있다.
- 비밀번호와 게시글 번호가 일치해야 게시글 수정이 가능하다.
Servlet 코드 ( MainPage.java )
- 수정할 때 필요한 정보를 jsp에 넘겨준다.
// 비회원 글 수정 페이지
if(request.getMethod().equals("POST") && uri.equals(noMemberNoitceModify)){
try{
var sid = Long.parseLong(request.getParameter("pageid")); // 문의 게시글 sid
var password = request.getParameter("password"); // 문의 게시글 비밀번호
// 게시글 정보 가지고 오기.
var noMemberNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
var notice = noMemberNoticeBoardDAO.getNoitceBoard(sid, password);
if(notice == null){
simplePage(response, "<script>alert('password diff!');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
return true;
}
// *** 파일들 읽어 오기 ***
// 파일 이름 찾기 및 href 링크 만들기
var noticeSID = notice.getSid();
var fileNameList = new ArrayList<Pair<String,String>>();
var fileSidList = noMemberNoticeBoardDAO.getFileSidList(noticeSID);
var noticeFileDAO = new NoMemberNoticeBoardFileDAO(getServletContext());
for(var fileSid:fileSidList){
var fileName = noticeFileDAO.getFileName(fileSid);
fileNameList.add(new Pair<String,String>(fileName,fileSid.toString()));
}
request.setAttribute("fileNameList", fileNameList);
request.setAttribute("noticeBoardFileDownload", noMemberNoticeFileDownload);
request.setAttribute("noticeBoardFileDelete", noticeBoardFileDelete);
// *** 파일들 읽어 오기 end ***
request.setAttribute("notice", notice);
request.setAttribute("noticeID", notice.getSid());
request.setAttribute("prePassword", password);
request.setAttribute("title", notice.getTitle());
request.setAttribute("mainText", notice.getMainText());
request.setAttribute("fileNameList", fileNameList);
request.setAttribute("noticeBoardURL", noMemberNoitceModifySave);
request.setAttribute("hasPassword",true);
request.setAttribute("modifyMode", true);
}catch(Exception e){
e.printStackTrace();
return false;
}
gotoForwardPage(request, response, "/WEB-INF/main_page/notice_board/notice_write.jsp");
return true;
}
// 비회원 글 수정 페이지 end
수정된 문의 글 저장하기
- 문의 글 수정을 완료한 후 저장하기 버튼을 누르면 문의 글 수정이 반영된다. 여기서 새로운 비밀번호 입력은 필수이다.
Servlet 코드 ( MainPage.java )
// 비회원 글 수정 저장하기
if(request.getMethod().equals("POST") && uri.equals(noMemberNoitceModifySave)){
try{
var title = request.getParameter("title");
var mainText = request.getParameter("main-text");
var password = request.getParameter("password");
var sid = Long.parseLong(request.getParameter("noticeID"));
var prePassword = request.getParameter("pre-password");
// 실패
if(title.equals("")) {
simplePage(response, "<script>alert('title blink!');window.history.back();</script>");
return true;
}
if(mainText.equals("")){
simplePage(response, "<script>alert('main text blink!');window.history.back();</script>");
return true;
}
if(password.equals("")){
simplePage(response, "<script>alert('password blink!');window.history.back();</script>");
return true;
}
// 이전 비밀 번호 검사
var noMemberNoticeBoardDAO = new NoMemberNoticeBoardDAO(getServletContext());
var newNoticeId = noMemberNoticeBoardDAO.noticeModifySave(sid,prePassword,title,mainText,password);
if(newNoticeId == -1){
simplePage(response, "<script>alert('modify fail!');window.history.back();</script>");
return true;
}
// ***** 파일 업로드 시작 *****
var noticeRe = "no file";
try{
// 올바른 파일인지 확인하고 db에 저장한다.
var parts = request.getParts();
var fileSidList = noMemberNoticeBoardDAO.getFileSidList(newNoticeId);
for(var part:parts){
if(!part.getHeader("Content-Disposition").contains("filename=")){
continue;
}
if(part.getSubmittedFileName().equals("")){
continue;
}
// 파일 이름에 금지된 문자가 사용되면 파일 업로드 안하기.
if(notCorrectFileName(part.getSubmittedFileName())){
continue;
}
if(fileSidList.size() > MAX_FILE_NUM){
noticeRe = "file max number";
break;
}
// 데이터 DB 테이블에 저장하기
var re = new NoMemberNoticeBoardFileDAO(getServletContext()).saveNoticeFile(newNoticeId, part);
part.delete(); // 임시파일 삭제
if(re != -1){
System.out.println("업로드 성공");
noticeRe = "file upload success";
fileSidList.add(re);
}else{
System.out.println("파일 업로드 실패");
noticeRe = "file upload fail";
}
}
// 게시글 file list 와 저장된 파일 갯수 저장하기.
noMemberNoticeBoardDAO.updateFileListAndCnt(fileSidList, newNoticeId);
}catch(Exception e){
e.printStackTrace();
System.out.println("파일 업로드 실패");
noticeRe = "file upload fail";
}
// ***** 파일 업로드 end *****
simplePage(response, "<script>alert('modify success!"+noticeRe+"');location.href='"+noMemberNoticeBoard+"?page=1"+"';</script>");
}catch(Exception e){
e.printStackTrace();
return false;
}
return true;
}
// 비회원 글 수정 저장하기 end
첨부파일 삭제하기
- 문의글 수정 페이지에서 특정 첨부파일을 하나씩 삭제할 수 있다.
Servlet 코드 ( MainPage.java )
// 파일 삭제
if(request.getMethod().equals("GET") && uri.equals(noticeBoardFileDelete)){
var re = "fail";
try{
var noticeSid = Long.parseLong(request.getParameter("noticeID"));
var fileSid = Long.parseLong(request.getParameter("filename"));
var password = request.getParameter("password");
var noticeDAO = new NoMemberNoticeBoardDAO(getServletContext());
var notice = noticeDAO.getNoitceBoard(noticeSid, password);
if(notice == null){ // 비번 틀려서 다운 못받음
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
var fileDAO = new NoMemberNoticeBoardFileDAO(getServletContext());
if(!fileDAO.deleteFile(fileSid)){
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
re = "success";
var fileSidList = noticeDAO.getFileSidList(noticeSid);
fileSidList.remove(fileSid);
noticeDAO.updateFileListAndCnt(fileSidList, noticeSid);
System.out.println("삭제 완료!");
}catch(Exception e){
e.printStackTrace();
re = "fail";
}
simplePage(response, "{\"result\":\""+re+"\"}");
return true;
}
// 파일 삭제 end
댓글남기기