본문 바로가기
Back-End

[jsp] MVC Model2을 이용하여 게시판 만들기 [1편]

by LeeGangEun 2022. 5. 4.


사전 준비물

1. 개발툴(이클립스, 인텔리제이 등)
2. oracle DB
3. jdk 및 각종 lib(jstl,  cos, ojdbc) !

JSTL 이란 ? 
=> JSP에서 빈번하게 사용되는 여러가지 처리문들을 편하게 처리해주는 태그를 모아
표준으로 만들어 놓은 라이브러리 이다. JSTL을 사용하면 스크립틀릿 없이 태그만으로 작성할 수 있다. 
(MVC 패턴에서는 스크립틀릿 태그를 사용하지 않기 때문에 필요 !)

jstl 다운로드 링크  : https://mvnrepository.com/artifact/javax.servlet/jstl/1.2  

COS 란 ?
=> 파일 다운로드 및 다운로드에 도움을 주는 라이브러리 이다.  

cos 다운로드 링크 : http://servlets.com/cos/

ojdbc : 다운받은 오라클의 폴더에 들어가서 찾는다 !
내 컴퓨터에서의 위치는 C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib !

 
모두 다운받고 프레젝트의  WebContent/WEB-INF/lib 폴더에 넣어주면 된다. 

 

프로젝트 구상

1. 게시판 목록보기
2. 게시판 글쓰기
3. 게시물 상세보기 (제목 클릭 시)
4. 파일 다운로드(파일 첨부 및 다운로드 기능 )
5. 게시물삭제하기
6. 게시물 수정하기

게시판의 프로세스

· MVP 패턴을 철저하게 지키는 비 회원제 게시판 구현
· 파일 다운로드 기능 및 비밀번호 검증 기능구현

 

MVC 패턴의 Model 부분


테이블 생성하기

--모델2 방식의 파일첨부형 게시판 테이블 생성
create table mvcboard (
	idx number primary key, -- 일련번호
	name varchar2(50) not null,  -- 작성자 이름
	title varchar2(200) not null, -- 제목
	content varchar2(2000) not null,  -- 내용
	postdate date default sysdate not null, -- 작성일
	ofile varchar2(200), -- 원본 파일명
	sfile varchar2(30),  -- 저장된 파일명 (파일명이 겹치면 에러가 날 수 있기때문에 이런식으로 처리)
	downcount number(5) default 0 not null, -- 다운로드 횟수 
	pass varchar2(50) not null,  -- 비밀번호
	visitcount number default 0 not null -- 조회수
);

 

더미 데이터 입력하기

--더미 데이터 입력 (디버깅을 위해)
insert into mvcboard (idx, name, title, content, pass)
    values (seq_board_num.nextval, '김유신', '자료실 제목1 입니다.','내용','1234');
insert into mvcboard (idx, name, title, content, pass)
    values (seq_board_num.nextval, '장보고', '자료실 제목2 입니다.','내용','1234');
insert into mvcboard (idx, name, title, content, pass)
    values (seq_board_num.nextval, '이순신', '자료실 제목3 입니다.','내용','1234');
insert into mvcboard (idx, name, title, content, pass)
    values (seq_board_num.nextval, '강감찬', '자료실 제목4 입니다.','내용','1234');
insert into mvcboard (idx, name, title, content, pass)
    values (seq_board_num.nextval, '대조영', '자료실 제목5 입니다.','내용','1234');

commit;

 

DTO(Data Transfer Object) 생성

· 계층 간 데이터 교환을 하기위해 사용하는 객체이다
· DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter) 만 가진 클래스이다.
· 유저가 입력한 데이터를 DB에 넣는 과정
 1 => 유저가 자신의 브라우저에서 데이터를 입력하여 form에 있는 데이터를 DTO에 넣어서 전송한다.
 2 => 해당 DTO를 받은 서버가 DAO를 이용하여 DB로 데이터를 집어넣는다.

package model2.mvcboard;

import java.sql.Date;

public class MVCBoardDTO {
	private String idx;
	private String name;
	private String title;
	private String content;
	private Date postdate;
	private String ofile;
	private String sfile;
	private int downcount;
	private String pass;
	private int visitcount;

	public String getIdx() {
		return idx;
	}
	public void setIdx(String idx) {
		this.idx = idx;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public Date getPostdate() {
		return postdate;
	}
	public void setPostdate(Date postdate) {
		this.postdate = postdate;
	}
	public String getOfile() {
		return ofile;
	}
	public void setOfile(String ofile) {
		this.ofile = ofile;
	}
	public String getSfile() {
		return sfile;
	}
	public void setSfile(String sfile) {
		this.sfile = sfile;
	}
	public int getDowncount() {
		return downcount;
	}
	public void setDowncount(int downcount) {
		this.downcount = downcount;
	}
	public String getPass() {
		return pass;
	}
	public void setPass(String pass) {
		this.pass = pass;
	}
	public int getVisitcount() {
		return visitcount;
	}
	public void setVisitcount(int visitcount) {
		this.visitcount = visitcount;
	}
	
}

Java Resources/src/model2/mvcboard/MVCBoardDTO.java

DAO(Data Access Object) 생성

· DAO는 DB에 data에 접근하기 위한 객체이다.
· DB에 접근하기 위한 Logic & Business Logic 을 분리하기 위해 사용한다.

package model2.mvcboard;

import java.util.List;
import java.util.Map;
import java.util.Vector;

import common.DBConnPool;

public class MVCBoardDAO extends DBConnPool{
	public MVCBoardDAO() {
		super();
	}
    // 검색 조건에 맞는 게시물의 개수를 반환합니다.
    public int selectCount(Map<String, Object> map) {
        int totalCount = 0;
        String query = "SELECT COUNT(*) FROM mvcboard";
        if (map.get("searchWord") != null) {
            query += " WHERE " + map.get("searchField") + " "
                   + " LIKE '%" + map.get("searchWord") + "%'";
        }
        try {
            stmt = con.createStatement();
            rs = stmt.executeQuery(query);
            rs.next();
            totalCount = rs.getInt(1);
        }
        catch (Exception e) {
            System.out.println("게시물 카운트 중 예외 발생");
            e.printStackTrace();
        }

        return totalCount;
    }

    // 검색 조건에 맞는 게시물 목록을 반환합니다(페이징 기능 지원).
    public List<MVCBoardDTO> selectListPage(Map<String,Object> map) {
        List<MVCBoardDTO> board = new Vector<MVCBoardDTO>();
        String query = " "
                     + "SELECT * FROM ( "
                     + "    SELECT Tb.*, ROWNUM rNum FROM ( "
                     + "        SELECT * FROM mvcboard ";

        if (map.get("searchWord") != null)
        {
            query += " WHERE " + map.get("searchField")
                   + " LIKE '%" + map.get("searchWord") + "%' ";
        }

        query += "        ORDER BY idx DESC "
               + "    ) Tb "
               + " ) "
               + " WHERE rNum BETWEEN ? AND ?";

        try {
            psmt = con.prepareStatement(query);
            psmt.setString(1, map.get("start").toString());
            psmt.setString(2, map.get("end").toString());
            rs = psmt.executeQuery();

            while (rs.next()) {
                MVCBoardDTO dto = new MVCBoardDTO();

                dto.setIdx(rs.getString(1));
                dto.setName(rs.getString(2));
                dto.setTitle(rs.getString(3));
                dto.setContent(rs.getString(4));
                dto.setPostdate(rs.getDate(5));
                dto.setOfile(rs.getString(6));
                dto.setSfile(rs.getString(7));
                dto.setDowncount(rs.getInt(8));
                dto.setPass(rs.getString(9));
                dto.setVisitcount(rs.getInt(10));

                board.add(dto);
            }
        }
        catch (Exception e) {
            System.out.println("게시물 조회 중 예외 발생");
            e.printStackTrace();
        }
        return board;
    }

Java Resources/src/model2/mvcboard/MVCBoardDAO.java 

MVC 패턴의 Controller 부분


Index.jsp 작성

· 서블릿은 JSP와 달리  이클립스에서바로 실행할 수 없다.
· 서블릿 파일이 아닌, 개발자가 정한 요청명으로 요청을 보내야 이에 매핑된 서블릿이 실행된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>파일 첨부형 게시판</h2>
	<a href="../mvcboard/list.do"> 게시판 목록 바로가기</a>  <!-- ../ 상위 디렉토리를 의미한다. -->
</body>
</html>

WebContent/Index.jsp

ListController 서블릿 클래스 작성 ( 컨트롤러)

package model2.mvcboard;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import utils.BoardPage;

@WebServlet("/mvcboard/view.do")  // 어노테이션 매핑
public class ListController extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//DAO 생성
		MVCBoardDAO dao = new MVCBoardDAO();
		
		//뷰에 전달할 매개변수 저장용 맵 생성
		Map<String, Object> map = new HashMap<String, Object>();
		
		String searchField = request.getParameter("searchField");
		String searchWord = request.getParameter("searchWord");
		if(searchWord != null) {
			// 쿼리스트링으로 전달받은 매개변수 중 검색어가 있다면 map에 저장
			map.put("searchField", searchField);
			map.put("searchWord", searchWord);
		}
		int totalCount = dao.selectCount(map); // 게시물 개수
		
		/* 페이지 처리 start */
		ServletContext application = getServletContext();
		int pageSize = Integer.parseInt(application.getInitParameter("POSTS_PER_PAGE"));
		int blockPage = Integer.parseInt(application.getInitParameter("PAGES_PER_BLOCK"));
			
		//현재 페이지 확인
		int pageNum = 1; // 기본값
		String pageTemp = request.getParameter("pageNum");
		if(pageTemp != null && !pageTemp.equals(""))
			pageNum = Integer.parseInt(pageTemp); //요청받은 페이지로 수정
		
		// 목록에 출력할 게시물 범위 계산
		int start = (pageNum -1 ) * pageSize + 1; // 첫 게시물 번호
		int end = pageNum * pageSize; // 마지막 게시물 번호
		map.put("start", start);
		map.put("end", end);
		/* 페이지처리 end */
		
		List<MVCBoardDTO> boardLists = dao.selectListPage(map);
		// 게시물 목록 받기
		dao.close(); 
		
		// 뷰에 전달할 매개변수 추가
		String pagingImg = BoardPage.pagingStr(totalCount, pageSize, blockPage, pageNum, "../mvcboard/list.do");
		map.put("pagingImg", pagingImg);
		map.put("totalCount", totalCount);
		map.put("pageSize", pageSize);
		map.put("pageNum", pageNum);
		
		// 전달할 데이터를 request 영역에 저장 후 List.jsp로 포워드
		request.setAttribute("boardLists",boardLists);
		request.setAttribute("map",map);
		request.getRequestDispatcher("/List.jsp").forward(request,response);
	}
}

Java Resources/src/model2/mvcboard/ListController.java

 

MVC 패턴의 View 부분


목록 보기 (View)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
  <link rel="stylesheet" href="/css/style.css">
  <script type="text/javascript">
  
  </script>
</head>
<body>
<div class="container">
<h2>파일 첨부형 게시판 - 목록보기(List)</h2>
	
<!-- 검색 폼 -->
<form method ="get">
<table width="100%">
	<tr align="center">
		<td>
			<select name="searchField" class="custom-select" style="width:10%; display:inline-block;">
				<option value="title">제목</option>
				<option value="content">내용</option>
			</select>
			<input type="text" name="searchWord"  class="form-control" style="width:30%; display:inline-block;"/>
			<input type="submit" value="검색하기" class="btn btn-primary" style="width:10%; display:inline-block;"/>
		</td>
	</tr>
</table>	
</form>
	
<!-- 목록 테이블 -->
<table class ="table">
	<tr class= "bg-color2">
		<th width ="10%">번호</th>
		<th width ="*"> 제목 </th>
		<th width ="15%"> 작성자 </th>
		<th width ="10%"> 조회수 </th>
		<th width ="15%"> 작성일 </th>
		<th width ="8%"> 첨부 </th>
	</tr>
<c:choose>
	<c:when test="${empty boardLists }"> <!-- 게시물이 없을때 -->
		<tr>
			<td colspan="6" align="center"> 등록된 게시물이 없습니다 ! </td>
		</tr>
	</c:when>
	
	<c:otherwise> <!-- 게시물이 있을때 -->
		<c:forEach items="${ boardLists }" var ="row" varStatus="loop">
		<tr align="center" class="bg-color4">
			<td> ${map.totalCount  - (((map.pageNum-1) * map.pageSize) + loop.index)} </td>
			<td align="left">
			 	<a href="../mvcboard/view.do?idx=${row.idx }">${row.title }</a> 
			</td>
			<td> ${row.name} </td>
			<td> ${row.visitcount} </td>	
			<td> ${row.postdate} </td>
			<td>
			<c:if test="${not empty row.ofile }">	
			<c:set var="fileNm" value="${row.ofile}" />
			<c:forTokens var="token" items="${fileNm }" delims="." varStatus="status">
				<c:if test="${status.last }">
					<c:choose> 
						<c:when test="${token eq 'html' }"> 
							<img src="/img/html.png" width="40px" height="40px"> 
						</c:when>
							
						<c:when test="${token eq 'txt'}"> 
							<img src="/img/txt.png"  width="40px" height="34px">
						</c:when> 
							 
						<c:when test="${token eq 'pdf'}"> 
							<img src="/img/pdf.png"  width="40px" height="40px"> 
						</c:when>
							 
						<c:when test="${token eq 'css'}"> 
							<img src="/img/css.png"  width="40px" height="40px"> 
						</c:when>
							 
						<c:when test="${token eq 'png'}"> 
							<img src="/img/png.png"  width="40px" height="40px"> 
						</c:when>
							 
						<c:when test="${token eq 'jpg'}"> 
							<img src="/img/jpg.png"  width="40px" height="40px"> 
						</c:when>
					</c:choose> 
				</c:if>
			</c:forTokens>			
			</c:if>
			</td>
		</tr>
		</c:forEach>
	</c:otherwise>
</c:choose>
	</table>	
	
	<!-- 하단메뉴(바로가기, 글쓰기) -->	
	<table>
		<tr align="center">
			<td>
				${map.pagingImg }
			</td>
			<td width="100"> <button type="button" onclick="location.href='../mvcboard/write.do';"> 글쓰기 </button> </td>
		</tr>	
	</table>
	</div>
</body>
</html>

WebContent/List.jsp