48 분 소요

비회원 문의 게시판

  • 회원 가입을 하지 않아도 사용자는 문의 게시판을 이용할 수 있다.
  • 비밀번호를 통해 본인이 쓴 글만 삭제할 수 있다.
  • 최대 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;
    }

첫 페이지 만들기

  • 로그인 페이지와 문의 게시판 페이지를 구분하기 위한 첫 페이지를 만들어 주었다.
    Pasted image 20230622124123
  • 이 페이지는 로그인을 하지 않아도 방문할 수 있다. 즉 회원과 비회원 모두 이용할 수 있다.

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;
        }

문의 게시판 페이지

  • 문의 게시판은 비회원, 회원 누구나 이용이 가능하다.
  • 문의 게시글을 저장을 할려면 비밀번호 입력은 필수이다.
  • 문의 게시글의 제목은 누구나 볼 수 있지만 본문 내용은 문의 글 작성자 또는 관리자만이 볼 수 있다.
    Pasted image 20230622125018
  • 문의 게시판은 전에 만들었던 사용자 게시판 기능 코드와 거의 유사하다.
  • 게시글을 클릭하면 비밀번호 입력창이 뜬다. 여기서 비밀번호를 맞게 입력해야 문의 게시글 확인이 가능하다.
    Pasted image 20230622125256

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

문의 게시글 새로 작성하기 페이지

  • 기존의 게시글 작성 폼을 이용하였다.
  • 문의 게시글을 저장하기 위해서는 빈칸이 없어야 한다. 그리고 비밀번호 입력은 필수이다.
    Pasted image 20230622130426
    Pasted image 20230622130438

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

문의 게시글 자세히 보기 페이지

  • 문의 게시글을 클릭하고 비밀번호를 입력하면 작성한 문의 게시글을 자세히 볼 수 있다.
    Pasted image 20230622132306
  • 문의 게시글을 삭제하거나 수정할 수 있다.

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

댓글남기기