본문 바로가기

JSP&Servlet

계층형 게시판(4)

오늘은 게시판에 필요한 자바와 빈즈와 관련된 소스 코드를 첨부하겠다.

 

BoardBean의 사용은 여러 가지가 있지만 가장 큰 흐름은 JSP에서 입력받은 게시물의 내용들을 setXxx 메소드로 빈즈에 저장한다. BoardMgr.java에서는 저장되어 있는 게시물의 내용을 getXxx 메소드로 가져와서 tblBoard 테이블에 저장한다.

 

그리고 tblBoard 테이블에 저장되어 있는 게시물은 BoardMgr.java에서 가져와서 setXxx 메소드로 BoardBean에 저장하고, JSP에서는 빈즈에 저장되어 있는 내용들을 getXxx 메소드로 반환을 받아서 브라우저에 보여주게 되는 것이다.

 

[그림 1] JSP와 서블릿 그리고 빈즈의 사용 흐름도

 

[BoardBean.java]

package BoardPack;

public class BoardBean {
	
	private int num;
	private String name;        
	private String subject;      
	private String content;     
	private int pos;            
	private int depth;        
	private int ref;        
	private String regdate;     
	private String pass;          
	private String  ip;
	private int count;        
	private String filename; 

	private int filesize;
	
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public int getPos() {
		return pos;
	}
	public void setPos(int pos) {
		this.pos = pos;
	}
	public int getDepth() {
		return depth;
	}
	public void setDepth(int depth) {
		this.depth = depth;
	}
	public int getRef() {
		return ref;
	}
	public void setRef(int ref) {
		this.ref = ref;
	}
	public String getRegdate() {
		return regdate;
	}
	public void setRegdate(String regdate) {
		this.regdate = regdate;
	}
	public String getPass() {
		return pass;
	}
	public void setPass(String pass) {
		this.pass = pass;
	}
	public String getIp() {
		return ip;
	}
	public void setIp(String ip) {
		this.ip = ip;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	public String getFilename() {
		return filename;
	}
	public void setFilename(String filename) {
		this.filename = filename;
	}
	public int getFilesize() {
		return filesize;
	}
	public void setFilesize(int filesize) {
		this.filesize = filesize;
	}  
}

 

[BoardMgr.java]

package BoardPack;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;


// 파일 업로드 기능에 필요한 클래스들임
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

public class BoardMgr {

	// DBConnectionMgr 변수를 선언함
	private DBConnectionMgr pool;
	// 파일 업로드 기능에 파일이 저장될 폴터 위치임
	private static final String  SAVEFOLDER = "C:/Jsp/myapp/WebContent/fileupload";
	private static final String ENCTYPE = "UTF-8";
	private static int MAXSIZE = 5*1024*1024;

	public BoardMgr() {
		try {
			// getInstance() 메소드로 DBConnectionMgr 객체를 선언함
			pool = DBConnectionMgr.getInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 게시판 리스트
	public Vector<BoardBean> getBoardList(String keyField, String keyWord,
			int start, int end) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		Vector<BoardBean> vlist = new Vector<BoardBean>();
		try {
			con = pool.getConnection();
			if (keyWord.equals("null") || keyWord.equals("")) {
				// 모든 게시물을 가져오기 위한 SQL문
				sql = "select * from tblBoard order by ref desc, pos limit ?, ?";
				pstmt = con.prepareStatement(sql);
				pstmt.setInt(1, start);
				pstmt.setInt(2, end);
			} else {
				// 아래 두 줄은 keyField 컬럼에 keyWord 단어를 검색하기 위한 SQL문
				sql = "select * from  tblBoard where " + keyField + " like ? ";
				sql += "order by ref desc, pos limit ? , ?";
				
				pstmt = con.prepareStatement(sql);
				pstmt.setString(1, "%" + keyWord + "%");
				pstmt.setInt(2, start);
				pstmt.setInt(3, end);
			}
			// SQL문을 실행하고 결괏값을 ResultSet 객체 타입으로 반환함
			rs = pstmt.executeQuery();
			
			while (rs.next()) {
				BoardBean bean = new BoardBean();
				bean.setNum(rs.getInt("num"));
				bean.setName(rs.getString("name"));
				bean.setSubject(rs.getString("subject"));
				bean.setPos(rs.getInt("pos"));
				bean.setRef(rs.getInt("ref"));
				bean.setDepth(rs.getInt("depth"));
				bean.setRegdate(rs.getString("regdate"));
				bean.setCount(rs.getInt("count"));
				vlist.add(bean);
			}
		} catch (Exception e) {
			// 예외가 발생할 경우 String으로 출력함
			e.printStackTrace();
		} finally {
			// Connection 객체를 재사용하기 위해서 닫지 않고 pool에 반환하고
			// pstmt와 rs를 close함
			pool.freeConnection(con, pstmt, rs);
		}
		return vlist;
	}
	
	//총 게시물수
	public int getTotalCount(String keyField, String keyWord) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		int totalCount = 0;
		try {
			con = pool.getConnection();
			if (keyWord.equals("null") || keyWord.equals("")) {
				// 모든 게시물의 개수를 구하는 SQL문
				sql = "select count(num) from tblBoard";
				pstmt = con.prepareStatement(sql);
			} else {
				// 검색된 게시물의 개수를 구하는 SQL문
				sql = "select count(num) from  tblBoard where " + keyField + " like ? ";
				pstmt = con.prepareStatement(sql);
				pstmt.setString(1, "%" + keyWord + "%");
			}
			rs = pstmt.executeQuery();
			if (rs.next()) {
				totalCount = rs.getInt(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt, rs);
		}
		return totalCount;
	}
	
	// 게시물 입력
	public void insertBoard(HttpServletRequest req) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		MultipartRequest multi = null;
		int filesize = 0;
		String filename = null;
		try {
			con = pool.getConnection();
			sql = "select max(num) from tblBoard";
			pstmt = con.prepareStatement(sql);
			rs = pstmt.executeQuery();
			int ref = 1;
			if (rs.next())
				ref = rs.getInt(1) + 1;
			File file = new File(SAVEFOLDER);
			if (!file.exists())
				file.mkdirs();
			multi = new MultipartRequest(req, SAVEFOLDER,MAXSIZE, ENCTYPE,
					new DefaultFileRenamePolicy());

			if (multi.getFilesystemName("filename") != null) {
				filename = multi.getFilesystemName("filename");
				filesize = (int) multi.getFile("filename").length();
			}
			String content = multi.getParameter("content");
			if (multi.getParameter("contentType").equalsIgnoreCase("TEXT")) {
				content = UtilMgr.replace(content, "<", "&lt;");
			}
			sql = "insert tblBoard(name,content,subject,ref,pos,depth,regdate,pass,count,ip,filename,filesize)";
			sql += "values(?, ?, ?, ?, 0, 0, now(), ?, 0, ?, ?, ?)";
			
			// 매개변수로 받은 BoardBean 객체를 getXxx 메소드로 위 쿼리문의 ?에 대입함
			// 괄호 안에 있는 1에서 8은 ?의 순서를 지정함
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, multi.getParameter("name"));
			pstmt.setString(2, content);
			pstmt.setString(3, multi.getParameter("subject"));
			pstmt.setInt(4, ref);
			pstmt.setString(5, multi.getParameter("pass"));
			pstmt.setString(6, multi.getParameter("ip"));
			pstmt.setString(7, filename);
			pstmt.setInt(8, filesize);
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt, rs);
		}
	}
	
	// 게시물 리턴
	public BoardBean getBoard(int num) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		
		// SQL문의 결과를 저장하기 위해서 BoardBean 객체 생성
		BoardBean bean = new BoardBean();
		
		try {
			con = pool.getConnection();
			sql = "select * from tblBoard where num=?";
			pstmt = con.prepareStatement(sql);
			// 매개변수로 받은 num 값을 위 쿼리문의 ?에 대입
			pstmt.setInt(1, num);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				bean.setNum(rs.getInt("num"));
				bean.setName(rs.getString("name"));
				bean.setSubject(rs.getString("subject"));
				bean.setContent(rs.getString("content"));
				bean.setPos(rs.getInt("pos"));
				bean.setRef(rs.getInt("ref"));
				bean.setDepth(rs.getInt("depth"));
				bean.setRegdate(rs.getString("regdate"));
				bean.setPass(rs.getString("pass"));
				bean.setCount(rs.getInt("count"));
				bean.setFilename(rs.getString("filename"));
				bean.setFilesize(rs.getInt("filesize"));
				bean.setIp(rs.getString("ip"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt, rs);
		}
		return bean;
	}

	// 조회수 증가
	public void upCount(int num) {
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		try {
			con = pool.getConnection();
			// 읽은 게시물의 count 값을 증가
			sql = "update tblBoard set count=count+1 where num=?";
			pstmt = con.prepareStatement(sql);
			// 매개변수로 받은 num 값을 위 쿼리문의 ?에 대입
			pstmt.setInt(1, num);
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt);
		}
	}

	// 게시물 삭제
	public void deleteBoard(int num) {
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		ResultSet rs = null;
		try {
			con = pool.getConnection();
			// 게시물 삭제를 위한 SQL문 선언
			sql = "select filename from tblBoard where num = ?";
			pstmt = con.prepareStatement(sql);
			// 매개변수로 받은 num 값을 위 쿼리문의 ?에 대입
			pstmt.setInt(1, num);
			rs = pstmt.executeQuery();
			
			// 결괏값과 받은 문자열이 null인지 검사 
			if (rs.next() && rs.getString(1) != null) {
				if (!rs.getString(1).equals("")) {
					File file = new File(SAVEFOLDER + "/" + rs.getString(1));
					// 파일이 존재하는지 검사
					if (file.exists())
						// 파일이 존재하면 지워주는 기능
						// But, 문자열을 아래와 같이 합치면 퍼포먼스가 매우 떨어짐
						UtilMgr.delete(SAVEFOLDER + "/" + rs.getString(1));
				}
			}
			sql = "delete from tblBoard where num=?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, num);
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt, rs);
		}
	}

	// 게시물 수정
	// 다 바뀔 필요가 없고, 필요한 것만 처리되면 됨
	public void updateBoard(BoardBean bean) {
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		try {
			con = pool.getConnection();
			// 게시물 수정을 위한 SQL문 선언
			sql = "update tblBoard set name = ?, subject=?, content = ? where num = ?";
			
			// 매개변수로 받은 bean 객체를 getXxx 메소드로 위 쿼리문의 ?에 대입
			// 1에서 4는 ?의 순선를 지정함
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, bean.getName());
			pstmt.setString(2, bean.getSubject());
			pstmt.setString(3, bean.getContent());
			pstmt.setInt(4, bean.getNum());
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt);
		}
	}

	// 게시물 답변
	public void replyBoard(BoardBean bean) {
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		try {
			con = pool.getConnection();
			// 답변 게시물을 저장하기 위한 SQL문 선언
			sql = "insert tblBoard (name,content,subject,ref,pos,depth,regdate,pass,count,ip)";
			sql += "values(?,?,?,?,?,?,now(),?,0,?)";
			
			// 매개변수로 받은 depth 값에 1을 증가(답변의 단계를 나타냄)
			int depth = bean.getDepth() + 1;
			
			// 매개변수로 받은 pos 값에 1을 증가
			int pos = bean.getPos() + 1;
			
			// 매개변수로 받은 bean 객체를 getXxx 메소드로 위 쿼리문의 ?에 대입
			// 1에서 8은 ?의 순서를 뜻함
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, bean.getName());
			pstmt.setString(2, bean.getContent());
			pstmt.setString(3, bean.getSubject());
			pstmt.setInt(4, bean.getRef());
			pstmt.setInt(5, pos);
			pstmt.setInt(6, depth);
			pstmt.setString(7, bean.getPass());
			pstmt.setString(8, bean.getIp());
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt);
		}
	}

	// 답변에 위치값 증가
	public void replyUpBoard(int ref, int pos) {
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		try {
			con = pool.getConnection();
			// 매개변수로 받은 ref와 ref 값이 같은 게시물 중 매개변수로 받은 pos 값보다 
			// 게시물의 pos 값이 큰 게시물은 pos 값을 1씩 증가
			sql = "update tblBoard set pos = pos + 1 where ref = ? and pos > ?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, ref);
			pstmt.setInt(2, pos);
			pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt);
		}
	}

	//파일 다운로드
		public void downLoad(HttpServletRequest req, HttpServletResponse res,
				JspWriter out, PageContext pageContext) {
			try {
				// 매개변수로 받은 req 요청 객체에서 getParameter() 메소드를 이용하여
				// filename 파라미터 값을 String filename에 할당함
				String filename = req.getParameter("filename");
				File file = new File(UtilMgr.con(SAVEFOLDER + File.separator+ filename));
				
				// 응답 객체 res 헤더필드 Accept-Ranges에 bytes 단위로 설정함
				byte b[] = new byte[(int) file.length()];
				// 요청 객체인 res에서 클라이언트의 User-Agent 정보를 리턴받음
				res.setHeader("Accept-Ranges", "bytes");
				// 브라우저의 버전과 정보를 구분해서 각각 res 헤더필드와 contentType을 설정함
				String strClient = req.getHeader("User-Agent");
				
				if (strClient.indexOf("MSIE6.0") != -1) {
					res.setContentType("application/smnet;charset=euc-kr");
					res.setHeader("Content-Disposition", "filename=" + filename + ";");
				} else {
					res.setContentType("application/smnet;charset=euc-kr");
					res.setHeader("Content-Disposition", "attachment;filename="+ filename + ";");
				}
				out.clear();
				
				// 파일 존재 여부에 따라 스트링 방식으로 브라우저로 파일을 전송함
				out = pageContext.pushBody();
				if (file.isFile()) {
					BufferedInputStream fin = new BufferedInputStream(
							new FileInputStream(file));
					BufferedOutputStream outs = new BufferedOutputStream(
							res.getOutputStream());
					int read = 0;
					while ((read = fin.read(b)) != -1) {
						outs.write(b, 0, read);
					}
					outs.close();
					fin.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	//페이징 및 블럭 테스트를 위한 게시물 저장 메소드 
	public void post1000(){
		Connection con = null;
		PreparedStatement pstmt = null;
		String sql = null;
		try {
			con = pool.getConnection();
			sql = "insert tblBoard(name,content,subject,ref,pos,depth,regdate,pass,count,ip,filename,filesize)";
			sql+="values('aaa', 'bbb', 'ccc', 0, 0, 0, now(), '1111',0, '127.0.0.1', null, 0);";
			pstmt = con.prepareStatement(sql);
			for (int i = 0; i < 1000; i++) {
				pstmt.executeUpdate();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			pool.freeConnection(con, pstmt);
		}
	}
	
	//main
	public static void main(String[] args) {
		new BoardMgr().post1000();
		System.out.println("SUCCESS");
	}
}

 

게시판 리스트의 getBoardList 메소드(list.jsp에서 사용)

 

: getBoardList() 메소드의 매개변수는 두 개의 String 객체이고 반환형은 Vector<BoardBean> 타입이다. getBoardList() 메소드의 역할은 list.jsp에서 전체 게시물을 가져오는 역할을 담당한다. 검색을 하기 위해서 검색 키워드와 검색 단어 두 개의 매개변수로 두 번째 SQL문이 실행되고 만약 검색으로 하지 않고 모든 게시물을 호출할 때는 첫 번째 쿼리문이 실행된다. 검색한 결괏값들은 BoardBean에 담아 두었다가 Vector 객체에 저장하고 이 Vector를 반환한다.

 

총 게시물 수의 getTotalCount 메소드(list.jsp에서 사용)

 

: getTotalCount() 메소드의 매개변수는 keyField, keyWord이며 반환하는 값은 totalCount이다. getTotalCount() 메소드는 list.jsp에서 전체 페이지 수를 계산하는 데 필요한 전체 게시물 수를 반환하는 역할을 수행한다. 매개변수인 keyField와 keyWord가 null 값일 때는 tblBoard 테이블에 등록되어 있는 모든 게시물의 개수를 반환하고 사용자가 검색어를 입력해서 매개변수인 keyField와 keyWord에 값이 넘어왔을 때는 SQL문에서 where 절을 사용하여 이 값에 해당하는 게시물의 개수만 반환한다.

 

게시판 입력의 insertBoard 메소드(BoardPostServlet.java에서 사용)

 

: insertBoard() 메소드의 매개변수는 HttpServletRequest 형의 req 객체이며 void 타입 메소드이기 때문에 반환 값은 없다. insertBoard() 메소드의 역할을 게시물 입력 폼(post.jsp)에서 입력받은 게시물의 내용을 BoardPostServlet.java 서블릿을 통해 tblBoard 테이블에 저장하는 역할을 하는 메소드이다. 매개변수로 받은 HttpServletRequest req에 답겨져서 넘어온 입력 값들을 insert문의 values 값에 대입하여 tblBoard 테이블에 입력한다.

 

게시물 리턴의 getBoard 메소드(read.jsp에서 사용)

 

: getBoard() 메소드의 매개변수 int 형의 변수이고 반환형은 BoardBean 객체이다. list.jsp에서 제목을 클릭하면 read.jsp로 이동하면서 넘겨받은 num 값으로 tblBoard 테이블에서 읽고자 하는 게시물을 반환한다. SQL문을 실행한 결괏값을 setXxx 메소드로 BoardBean에 저장하였다가 이 빈즈를 반환한다.

 

조회수 증가의 upCount 메소드(read.jspd에서 사용)

 

: upCount() 메소드의 매개변수는 int 형 변수이며, 반환형은 반환하는 값이 없는 void이다. 조회수 증가를 위한 upCount() 메소드는 list.jsp에서 글 제목을 클릭하여 read.jsp로 이동하면 자동적으로 호출되는 메소드이다. 조회하는 게시물의 num 값을 매개변수로 받아서 SQL문의 조건 값으로 쓰이며 해당 게시물의 count 값을 1씩 증가시킨다.

 

게시물 삭제의 deleteBoard 메소드(delete.jsp에서 사용)

 

: deleteBoard() 메소드는 삭제하고 싶은 게시물의 num 값을 매개변수로 받아서 SQL문의 조건 값으로 쓰여진다. 게시물을 삭제하기 이전에 num 값으로 tblBoard 테이블의 filename 필드를 조회하여 filename이 있을 경우 첨부파일을 삭제한다.

 

게시물 수정의 updateBoard 메소드(BoardUpdateServlet.java에서 사용)

 

: updateBoard() 메소드는 수정하고 싶은 게시물의 num 값을 매개변수로 받아서 SQL문의 조건 값으로 쓰여진다.

 

게시물 답변의 replyBoard 메소드(BoardReplyServlet.java에서 사용)

 

: replyBoard() 메소드는 매개변수로 받은 BoardBean 객체에 답변 게시물의 내용을 답고 있으며 pos 값과 depth 값은 답변하고자 하는 게시물의 값을 가져왔기 때문에, 각각 1씩 증가시킨 값으로 tblBoard 테이블에 저장한다.

 

답변의 위치값 증가 부분의 replyUpBoard 메소드(BoardReplyServlet.java에서 사용)

 

: replyUpBoard() 메소드는 답변 게시물을 저장하기 전에 원 글에 소속된 답변 글 중 답변하고자 하는 게시물보다 pos 값이 큰 게시물의 pos 값을 1씩 증가시키는 역할을 한다. 매개변수로 받은 ref는 답변 글이 소속될 원 글을 뜻하며 pos는 원 글에 소속된 답변 글 사이의 순서를 뜻한다.

 

파일 다운로드의 downLoad 메소드(download.jsp에서 사용)

 

: downLoad() 메소드의 매개변수는 HttpServletRequest, HttpServletResponse, JspWriter, PageContext 타입의 객체이다. donwLoad() 메소드는 사용자가 read.jsp에서 첨부파일을 클릭했을 때 넘겨받은 파일명을 매개변수로 하여 사용자가 해당 파일을 다운로드할 수 있도록 제공해 주는 역할을 한다.

 

이것으로 계층형 게시판 만들기를 모두 마무리하도록 하겠다.

 

 

'JSP&Servlet' 카테고리의 다른 글

CSS 파일 변경 후 적용이 안 될 때  (0) 2022.04.19
import와 useBean의 차이  (0) 2022.04.18
계층형 게시판(3)  (0) 2022.04.11
계층형 게시판(2)  (0) 2022.04.05
계층형 게시판(1)  (0) 2022.04.04