Front-End/JSP

[JSP] MVC 모델2 게시판 만들기-1 (서블릿, 자바 빈즈, JDBC, JSP 활용)

현기 2022. 10. 13. 20:08

지금까지 자바 웹 개발에 필요한

서블릿, 자바 빈즈, JSP, JDBC를 학습했습니다. 

(이전 글들을 참고해 주세요.)

 

웹 개발자들 사이에서 우스갯소리로 혼자서 게시판을 만들 줄 알면

웹을 다 만들 수 있다는 말이 있다고 합니다. 😂

 

그동안 배운 것들을 활용해서

웹 게시판을 MVC 모델 2로 구현해 봅시다.

 


📝MVC 모델 2란?

 

✔예전 포스팅 참고

https://hyunki99.tistory.com/46?category=1099323 

 

[JSP] 서블릿(Servlet)과 JSP 정리, MVC 패턴

서블릿은 자바 기반의 웹 프로그래밍 기술로 자바 언어의 모든 기능을 사용할 수 있다. (확장자도 .java) 스레드 기반의 빠른 처리 속도를 자랑하지만, 프로그램 내에서 HTML을 작성하는 것이 화면

hyunki99.tistory.com

 


 

⦁ MVC 모델 2

디자인 부분과 로직 부분을 나누어서 개발합니다.

따라서 유지보수가 좋고, 디자이너와 개발자의 분업이 편리합니다.

 

View(보여지는 화면)는 JSP로,

Model(DB와 연동, 비즈니스 로직 등)은 자바 빈즈로 개발합니다. 

 

서로 독립적으로 코딩하기 때문에 둘 사이에 연결이 필요합니다.

따라서 서블릿을 Controller로 사용합니다.

MVC 패턴에서 중심 역할을 하게 됩니다.

 

 


📝 1. View ( JSP )

 

 

 

⦁ View

WebContent/board/qna_borad_list.jsp
WebContent/board/qna_borad_write.jsp

✔ 뷰를 담당하는 JSP 파일입니다. 위 경로에 넣어줍니다.

 

더보기

📑 qna_borad_list.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="java.util.*"%>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="net.board.db.*" %>

<%
	List boardList=(List)request.getAttribute("boardlist");
	int listcount=((Integer)request.getAttribute("listcount")).intValue();
	int nowpage=((Integer)request.getAttribute("page")).intValue();
	int maxpage=((Integer)request.getAttribute("maxpage")).intValue();
	int startpage=((Integer)request.getAttribute("startpage")).intValue();
	int endpage=((Integer)request.getAttribute("endpage")).intValue();
%>

<html>
<head>
	<title>MVC 게시판</title>
</head>

<body>
<!-- 게시판 리스트 -->
<table width=50% border="0" cellpadding="0" cellspacing="0">
	<tr align="center" valign="middle">
		<td colspan="4">MVC 게시판</td>
		<td align=right>
			<font size=2>글 개수 : ${listcount }</font>
		</td>
	</tr>
	
	<tr align="center" valign="middle" bordercolor="#333333">
		<td style="font-family:Tahoma;font-size:8pt;" width="8%" height="26">
			<div align="center">번호</div>
		</td>
		<td style="font-family:Tahoma;font-size:8pt;" width="50%">
			<div align="center">제목</div>
		</td>
		<td style="font-family:Tahoma;font-size:8pt;" width="14%">
			<div align="center">작성자</div>
		</td>
		<td style="font-family:Tahoma;font-size:8pt;" width="17%">
			<div align="center">날짜</div>
		</td>
		<td style="font-family:Tahoma;font-size:8pt;" width="11%">
			<div align="center">조회수</div>
		</td>
	</tr>
	
	<%
		for(int i=0;i<boardList.size();i++){
			BoardBean bl=(BoardBean)boardList.get(i);
	%>
	<tr align="center" valign="middle" bordercolor="#333333"
		onmouseover="this.style.backgroundColor='F8F8F8'"
		onmouseout="this.style.backgroundColor=''">
		<td height="23" style="font-family:Tahoma;font-size:10pt;">
			<%=bl.getBOARD_NUM()%>
		</td>
		
		<td style="font-family:Tahoma;font-size:10pt;">
			<div align="left">
			<%if(bl.getBOARD_RE_LEV()!=0){ %>
				<%for(int a=0;a<=bl.getBOARD_RE_LEV()*2;a++){ %>
				&nbsp;
				<%} %>
				▶
			<%}else{ %>
				▶
			<%} %>
			<a href="./BoardDetailAction.bo?num=<%=bl.getBOARD_NUM()%>">
				<%=bl.getBOARD_SUBJECT()%>
			</a>
			</div>
		</td>
		
		<td style="font-family:Tahoma;font-size:10pt;">
			<div align="center"><%=bl.getBOARD_NAME() %></div>
		</td>
		<td style="font-family:Tahoma;font-size:10pt;">
			<div align="center"><%=bl.getBOARD_DATE() %></div>
		</td>	
		<td style="font-family:Tahoma;font-size:10pt;">
			<div align="center"><%=bl.getBOARD_READCOUNT() %></div>
		</td>
	</tr>
	<%} %>
	<tr align=center height=20>
		<td colspan=7 style=font-family:Tahoma;font-size:10pt;>
			<%if(nowpage<=1){ %>
			[이전]&nbsp;
			<%}else{ %>
			<a href="./BoardList.bo?page=<%=nowpage-1 %>">[이전]</a>&nbsp;
			<%} %>
			
			<%for(int a=startpage;a<=endpage;a++){
				if(a==nowpage){%>
				[<%=a %>]
				<%}else{ %>
				<a href="./BoardList.bo?page=<%=a %>">[<%=a %>]</a>&nbsp;
				<%} %>
			<%} %>
			
			<%if(nowpage>=maxpage){ %>
			[다음]
			<%}else{ %>
			<a href="./BoardList.bo?page=<%=nowpage+1 %>">[다음]</a>
			<%} %>
		</td>
	</tr>
	<tr align="right">
		<td colspan="5">
	   		<a href="./BoardWrite.bo">[글쓰기]</a>
		</td>
	</tr>
</table>
</body>
</html>

 

📑 qna_borad_write.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"%>

<html>
<head>
	<title>MVC 게시판</title>
	<script language="javascript">
	function addboard(){
		boardform.submit();
	}
	</script>
</head>
<body>
<!-- 게시판 등록 -->
<form action="./BoardAddAction.bo" method="post" 
	enctype="multipart/form-data" name="boardform">
<table cellpadding="0" cellspacing="0">
	<tr align="center" valign="middle">
		<td colspan="5">MVC 게시판</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="BOARD_NAME" type="text" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="BOARD_PASS" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="BOARD_SUBJECT" type="text" size="50" maxlength="100" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="BOARD_CONTENT" cols="67" rows="15"></textarea>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">파일 첨부</div>
		</td>
		<td>
			<input name="BOARD_FILE" type="file"/>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">
			<a href="javascript:addboard()">[등록]</a>&nbsp;&nbsp;
			<a href="javascript:history.go(-1)">[뒤로]</a>
		</td>
	</tr>
</table>
</form>
<!-- 게시판 등록 -->
</body>
</html>

 


 


📝 2. Model (자바 빈즈)

 

⦁ CREATE TABLE

✔ DB를 사용할 것이기 때문에 테이블을 생성해 줍니다.

CREATE TABLE BOARD(
	BOARD_NUM INT,
	BOARD_NAME VARCHAR2(20),
	BOARD_PASS VARCHAR2(15),
	BOARD_SUBJECT VARCHAR2(50),
	BOARD_CONTENT VARCHAR2(2000),
	BOARD_FILE VARCHAR2(50),
	BOARD_RE_REF INT,
	BOARD_RE_LEV INT,
	BOARD_RE_SEQ INT,
	BOARD_READCOUNT INT,
	BOARD_DATE DATE,
	PRIMARY KEY(BOARD_NUM)
);

 

⦁ 파일 위치 및 설명

net.board.db 패키지를 만들고 다음과 같이 2개의 자바 파일을 만들어 주세요.

 

BoardBean.java 파일은 테이블의 컬럼을 필드로 가지고 있는 클래스입니다.

 

BoardDAO.java는 커넥션 풀을 이용해서 DB에 접속하고 메서드를 통해

쿼리문을 실행합니다.

 

 

JDBC에 대해 잘 모른다면 이 글을 참고하세요.

  몇 가지 설정이 필요합니다.😀

https://hyunki99.tistory.com/55

 

[JSP] JSP에서 DB연동하기 (JDBC, 오라클, 커넥션 풀)

JDBC(Java DataBase Connectivity)는 자바 언어로 DB 프로그래밍을 하기 위한 라이브러리입니다. DBMS에 종속되지 않는 관련 API를 JDK에서 제공합니다. 사용하기 위해서는 각 DBMS 회사에서 제공하는 라이브

hyunki99.tistory.com

 

더보기

📑 BoardBean.java

package net.board.db;

import java.sql.Date;

public class BoardBean {
	private int BOARD_NUM;
	private String BOARD_NAME;
	private String BOARD_PASS;
	private String BOARD_SUBJECT;
	private String BOARD_CONTENT;
	private String BOARD_FILE;
	private int BOARD_RE_REF;
	private int BOARD_RE_LEV;
	private int BOARD_RE_SEQ;
	private int BOARD_READCOUNT;
	private Date BOARD_DATE;
	
	public int getBOARD_NUM() {
		return BOARD_NUM;
	}
	public void setBOARD_NUM(int bOARD_NUM) {
		BOARD_NUM = bOARD_NUM;
	}
	public String getBOARD_NAME() {
		return BOARD_NAME;
	}
	public void setBOARD_NAME(String bOARD_NAME) {
		BOARD_NAME = bOARD_NAME;
	}
	public String getBOARD_PASS() {
		return BOARD_PASS;
	}
	public void setBOARD_PASS(String bOARD_PASS) {
		BOARD_PASS = bOARD_PASS;
	}
	public String getBOARD_SUBJECT() {
		return BOARD_SUBJECT;
	}
	public void setBOARD_SUBJECT(String bOARD_SUBJECT) {
		BOARD_SUBJECT = bOARD_SUBJECT;
	}
	public String getBOARD_CONTENT() {
		return BOARD_CONTENT;
	}
	public void setBOARD_CONTENT(String bOARD_CONTENT) {
		BOARD_CONTENT = bOARD_CONTENT;
	}
	public String getBOARD_FILE() {
		return BOARD_FILE;
	}
	public void setBOARD_FILE(String bOARD_FILE) {
		BOARD_FILE = bOARD_FILE;
	}
	public int getBOARD_RE_REF() {
		return BOARD_RE_REF;
	}
	public void setBOARD_RE_REF(int bOARD_RE_REF) {
		BOARD_RE_REF = bOARD_RE_REF;
	}
	public int getBOARD_RE_LEV() {
		return BOARD_RE_LEV;
	}
	public void setBOARD_RE_LEV(int bOARD_RE_LEV) {
		BOARD_RE_LEV = bOARD_RE_LEV;
	}
	public int getBOARD_RE_SEQ() {
		return BOARD_RE_SEQ;
	}
	public void setBOARD_RE_SEQ(int bOARD_RE_SEQ) {
		BOARD_RE_SEQ = bOARD_RE_SEQ;
	}
	public int getBOARD_READCOUNT() {
		return BOARD_READCOUNT;
	}
	public void setBOARD_READCOUNT(int bOARD_READCOUNT) {
		BOARD_READCOUNT = bOARD_READCOUNT;
	}
	public Date getBOARD_DATE() {
		return BOARD_DATE;
	}
	public void setBOARD_DATE(Date bOARD_DATE) {
		BOARD_DATE = bOARD_DATE;
	}
}

 

📑BoardDAO.java

package net.board.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class BoardDAO {
	Connection con;
	PreparedStatement pstmt;
	ResultSet rs;
	
	public BoardDAO() {
		try {
			Context init = new InitialContext();
			DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/OracleDB");
			con = ds.getConnection();
		} catch(Exception ex) {
			System.out.println("DB : "+ex);
			return;
		}
	}
	
	public int getListCount() {
		int x = 0;
		
		try {
			pstmt = con.prepareStatement("select count(*) from board");
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				x=rs.getInt(1);
			}
		} catch(Exception ex) {
			System.out.println("getListCount : " + ex);
		} finally {
			if(rs!=null) try {rs.close();} catch(SQLException ex) {}
			if(pstmt!=null) try {pstmt.close();} catch(SQLException ex) {}
		}
		
		return x;
	}
	
	public List getBoardList(int page,int limit){
		String board_list_sql="select * from "+
		"(select rownum rnum,BOARD_NUM,BOARD_NAME,BOARD_SUBJECT,"+
		"BOARD_CONTENT,BOARD_FILE,BOARD_RE_REF,BOARD_RE_LEV,"+
		"BOARD_RE_SEQ,BOARD_READCOUNT,BOARD_DATE from "+
		"(select * from board order by BOARD_RE_REF desc,BOARD_RE_SEQ asc)) "+
		"where rnum>=? and rnum<=?";
		
		List list = new ArrayList();
		
		int startrow=(page-1)*10+1;
		int endrow=startrow+limit-1;	
		try{
			pstmt = con.prepareStatement(board_list_sql);
			pstmt.setInt(1, startrow);
			pstmt.setInt(2, endrow);
			rs = pstmt.executeQuery();
			
			while(rs.next()){
				BoardBean board = new BoardBean();
				board.setBOARD_NUM(rs.getInt("BOARD_NUM"));
				board.setBOARD_NAME(rs.getString("BOARD_NAME"));
				board.setBOARD_SUBJECT(rs.getString("BOARD_SUBJECT"));
				board.setBOARD_CONTENT(rs.getString("BOARD_CONTENT"));
				board.setBOARD_FILE(rs.getString("BOARD_FILE"));
				board.setBOARD_RE_REF(rs.getInt("BOARD_RE_REF"));
				board.setBOARD_RE_LEV(rs.getInt("BOARD_RE_LEV"));
				board.setBOARD_RE_SEQ(rs.getInt("BOARD_RE_SEQ"));
				board.setBOARD_READCOUNT(rs.getInt("BOARD_READCOUNT"));
				board.setBOARD_DATE(rs.getDate("BOARD_DATE"));
				list.add(board);
			}
			
			return list;
			
		}catch(Exception ex){
			System.out.println("getBoardList ���� : " + ex);
		}finally{
			if(rs!=null) try{rs.close();}catch(SQLException ex){}
			if(pstmt!=null) try{pstmt.close();}catch(SQLException ex){}
		}
		return null;
	}
	
	public Connection getCon() {
		return con;
	}

	public void setCon(Connection con) {
		this.con = con;
	}

	public PreparedStatement getPstmt() {
		return pstmt;
	}

	public void setPstmt(PreparedStatement pstmt) {
		this.pstmt = pstmt;
	}

	public ResultSet getRs() {
		return rs;
	}

	public void setRs(ResultSet rs) {
		this.rs = rs;
	}
}

 

 


 


📝 3. Controller (서블릿)

 

요청을 받아서 Model과 View를 연결시켜주는 통로 역할입니다.

 

⦁ 파일 위치 및 설명

 

BoardFrontController 파일은 컨트롤러의 가장 핵심이 되는 파일입니다.

꼭 서블릿으로 생성해 줍니다.

 

마치 리액트의 라우팅처럼 사용자가 어느 주소로 접속하면 보여줄 페이지를 지정합니다.

그리고 redirect / forward 중 어떤 방식으로 접속했는지 확인하고 다른 동작을 실행합니다.

 

⦁ 서블릿 매핑

#WebContent/WEB-INF/web.xml 파일

  <servlet>
    <servlet-name>BoardFrontController</servlet-name>
    <servlet-class>net.board.action.BoardFrontController</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>BoardFrontController</servlet-name>
    <url-pattern>*.bo</url-pattern>
  </servlet-mapping>

✔ *.bo 로 설정하면 뒤에 .bo가 붙은 모든 url에 대해서 매핑됩니다.

 

⦁ 소스코드

더보기

📑 BoardFrontController 서블릿

package net.board.action;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class BoardFrontController extends HttpServlet implements Servlet{
    protected void doProcess(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        String RequestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        String command = RequestURI.substring(contextPath.length());
        ActionForward forward=null;
        Action action = null;

        if(command.equals("/BoardList.bo")){ // 게시판 리스트는 DB를 가지고온다.
            action = new BoardListAction();
            try{
                forward = action.execute(request, response);
            } catch(Exception e){
                e.printStackTrace();
            }
            
        } else if(command.equals("/BoardWrite.bo")){ // 글쓰기
        	forward = new ActionForward();
            forward.setRedirect(false);
            forward.setPath("./board/qna_board_write.jsp");
        }
        
        if(forward.isRedirect()){
            response.sendRedirect(forward.getPath());
        }else{
            RequestDispatcher dispatcher =
                    request.getRequestDispatcher(forward.getPath());
            dispatcher.forward(request, response);
        }
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doProcess(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doProcess(request, response);
    }
}

 

 

📑 ActionForward.java

✔ isRedirect, path 필드와 get/set 메서드를 가지고 있습니다.

package net.board.action;

public class ActionForward {
	private boolean isRedirect=false;
	private String path=null;
	
	public boolean isRedirect() {
		return isRedirect;
	}
	public void setRedirect(boolean isRedirect) {
		this.isRedirect = isRedirect;
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
}

 

📑 Action 인터페이스

✔ xxxxAction 클래스가 사용할 인터페이스입니다.

   요청과 응답 객체를 전달받고, 포워딩 정보를 저장한 ActionForward 객체를 리턴하도록 정의합니다.

public interface Action {
	
    public ActionForward execute(
            HttpServletRequest request,
            HttpServletResponse response) throws Exception;

}

 

📑 BoardListAction.java

✔ BoardDAO클래스를 생성하고 getBoardList 메서드를 통해 게시글 목록을 가져옵니다.

package net.board.action;

import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.board.db.BoardDAO;

 public class BoardListAction implements Action {
	 
	 public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception{
		BoardDAO boarddao = new BoardDAO();
		List boardlist = new ArrayList();
		
	  	int page=1;
		int limit=10;
		
		if(request.getParameter("page")!=null){
			page=Integer.parseInt(request.getParameter("page"));
		}
		
		int listcount = boarddao.getListCount(); 
		boardlist = boarddao.getBoardList(page,limit); 
		
   		int maxpage=(int)((double)listcount/limit+0.95);
   		int startpage = (((int) ((double)page / 10 + 0.9)) - 1) * 10 + 1;
   		int endpage = maxpage;
   		
   		if (endpage>startpage+10-1) endpage=startpage+10-1;
   		
   		request.setAttribute("page", page);	
   		request.setAttribute("maxpage", maxpage); 
   		request.setAttribute("startpage", startpage); 
   		request.setAttribute("endpage", endpage);    
		request.setAttribute("listcount",listcount); 
		request.setAttribute("boardlist", boardlist);
		
		ActionForward forward= new ActionForward();
	   	forward.setRedirect(false);
   		forward.setPath("./board/qna_board_list.jsp");
   		return forward;
	 }
 }

 

 

 

 

 


 


 

MVC 모델 2를 구현했습니다.

다음 포스팅에서는 게시판의 기능들을

구현해 보겠습니다.

 


참고 문헌 : 

 

반응형