본문 바로가기

JSP

MVC 모델 (6) - 최근 공지사항 메인페이지에 출력

MVC 모델을 위한 5개의 자바 패키지

▷ controller (Servlet 클래스)
      Model 과 View 의 연결 역할을 수행
     모든 요청의 시작점(진입점) 역할을 수행
     자바 코드로 이루어져 있지만, 웹으로부터의 요청도 처리되어야 하므로 Servlet 클래스로 정의

 

controller.BoardFrontController.java

package controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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 action.Action;
import action.BoardNoticeRecentListAction;
import vo.ActionForward;

// 서블릿 주소가 "...생략.../프로젝트명/xxx.bo" 일 경우 BoardFrontController 로 요청이 전달됨
@WebServlet("*.bo")
public class BoardFrontController extends HttpServlet {
	
	protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("BoardFrontController");
		
		request.setCharacterEncoding("UTF-8"); // POST 방식에 대한 한글 처리
		
		String command = request.getServletPath();
		System.out.println("command : " + command); // 받아온 주소값을 확인하기 위한 작업
		Action action = null; // Action 클래스의 인스턴스를 공통으로 다루기 위한 Action 타입 변수 선언
		
		ActionForward forward = null; // 포워딩 정보를 저장할 ActionForward 타입 변수 선언
		
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// GET/POST 방식 요청 구분 없이 공통으로 작업을 처리할 doProcess() 메서드 호출
		doProcess(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// GET/POST 방식 요청 구분 없이 공통으로 작업을 처리할 doProcess() 메서드 호출
		doProcess(request, response);		
	}
}

 

※참고 : 요청받은 주소(URL) 전체 추출

String requestURL = request.getRequestURL().toString();
System.out.println("requestURL : " + requestURL);
// requestURL : http://localhost:8080/MVC_Board/BoardList.bo 출력됨

// 1. 요청 주소 중 URI 부분(/프로젝트명/서블릿주소) 추출
String requestURI = request.getRequestURI();
System.out.println("requestURI : " + requestURI);
//requestURI : /MVC_Board/BoardList.bo 출력됨

// 2. 요청 주소 중 컨텍스트 경로(/프로젝트명) 추출
String contextPath = request.getContextPath();
System.out.println("contextPath : " + contextPath);
// contextPath : /MVC_Board 출력됨

// 3. 요청 주소 중 서블릿 주소 부분(프로젝트명 뒷부분) 추출
// => 1번 & 2번 과정에서 추출된 RequestURI 와 ContextPath 를 활용하여 서블릿 주소 추출 가능
//    (과거에 사용했던 방식이며 불편함)
// => RequestURI 에서 ContextPath 만큼 제외한 문자열 추출 시 서블릿 주소가 추출됨
//    (RequestURI 문자열의 substring() 메서드를 호출하여 ContextPath 문자열 길이 전달)
String command = requestURI.substring(contextPath.length());
System.out.println("command : " + command);

 

▷ /main/Main.bo가 요청되었을때 작업 처리(if문 사용)

 

controller.BoardFrontController.java

package controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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 action.Action;
import action.BoardNoticeRecentListAction;
import vo.ActionForward;

// 서블릿 주소가 "...생략.../프로젝트명/xxx.bo" 일 경우 BoardFrontController 로 요청이 전달됨
@WebServlet("*.bo")
public class BoardFrontController2 extends HttpServlet {
	
	protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("BoardFrontController");
		
		request.setCharacterEncoding("UTF-8");
		
		String command = request.getServletPath();
		System.out.println("command : " + command);
		Action action = null;
		
		ActionForward forward = null;
		
        	// if문을 사용하여 추출된 서블릿 주소(command)를 판별하여
		// 각 주소에 따라 서로 다른 작업(액션) 요청
		if(command.equals("/main/Main.bo")) {
        	// 공통 모듈로 분리된 코드를 통해 포워딩 작업을 수행하므로
			// 현재 위치에서는 포워딩 정보만 설정하면 됨
			// 1. ActionForward 클래스 인스턴스를 직접 생성하여 정보 저장
//			forward = new ActionForward();
			// 2. ActionForward 객체를 통해 포워딩 주소를 저장 
//			forward.setPath("./main.jsp");
			// 3. ActionForward 객체를 통해 포워딩 방식 지정
			// => 뷰페이지로 이동할 경우에는 Dispatcher 방식 사용(URL 정보 숨김)
//            		forward.setRedirect(false); // false 값일 경우 메서드 호출 생략 가능
			// -----------------------------------------------------------------
			// 최근 공지사항 등 목록을 표시하려면 비즈니스 로직 필요
			// => 최근 목록 조회 후 조회 데이터 가지고 /main/main.jsp 페이지로 포워딩
            
			action = new BoardNoticeRecentListAction();
			try {
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// ActionForward 객체에 저장된 포워딩 방식에 따른 포워딩 작업 수행(공통 모듈로 분리)
		// 단, ActionForward 객체가 존재할 경우(= null 이 아닐 경우)에만 포워딩 작업 수행
		if(forward != null) {
        
        	// 포워딩 방식에 따라 Redirect 또는 Dispatcher 방식으로 포워딩
			// => ActionForward 객체의 isRedirect() 메서드 리턴값이 true 이면 Redirect 방식
			//    아니면(= false 이면) Dispatcher 방식으로 포워딩 수행
			//    => 이 때, 포워딩에 필요한 요청 URL 정보는 getPath() 메서드로 리턴받기
            
			if(forward.isRedirect()) { // Redirect 방식일 경우
            
				// Redirect 방식으로 포워딩 => response 객체의 sendRedirect() 메서드 호출
               			 response.sendRedirect(forward.getPath());
                
			} else { // Dispatcher 방식일 경우
            
				// RequestDispatcher 객체의 forward() 메서드 호출
               			RequestDispatcher dispatcher = request.getRequestDispatcher(forward.getPath());
				dispatcher.forward(request, response);
                
			}
		}
		
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}

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

}

 

▷ action 패키지에 통일성을 위해서 Action 인터페이스 생성

 

Action.java

package action;

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

import vo.ActionForward;

/*
 * FrontController 로 요청이 전달되면 요청에 해당하는 Action 클래스로 이동해야하는데
 * 이 때, Action 클래스의 execute() 메서드를 호출하여 작업을 진행하고
 * 작업 결과를 공통 항목인 ActionForward 객체 형태로 리턴받아야 함
 * => 위의 과정을 여러 Action 클래스에서 동일한 형태로 수행해야하므로 코드의 통일성 필요
 * => 따라서, 각 Action 클래스에서 수행할 메서드를 하나의 추상메서드로 제공하기 위해
 *    Action 인터페이스를 설계하고, 공통 항목인 execute() 메서드를 정의하여
 *    각 Action 클래스에서 Action 인터페이스를 상속받아 execute() 메서드를 구현하면
 *    코드의 통일성이 향상되고, 차후에 다형성 활용도 가능해짐 
 */
public interface Action {
	// 각 요청을 받아들일 execute() 메서드를 추상메서드로 정의하고
	// => 파라미터로 HttpServletRequest 객체와 HttpServletResponse 객체를 전달받음
	// => 포워딩 정보(URL 과 포워딩 방식)를 갖는 ActionForward 타입 객체를 리턴함
	// => 내부에서 발생하는 모든 예외를 외부로 위임
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

 

▷ BoardNoticeRecentListAction.java 파일로 이동(execute() 메서드 호출)

 

action.BoardNoticeRecentListAction.java

package action;

import java.util.ArrayList;

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

import svc.BoardNoticeListService;
import svc.BoardNoticeRecentListService;
import vo.ActionForward;
import vo.NoticeBoardDTO;
import vo.PageInfo;

public class BoardNoticeRecentListAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardNoticeRecentListAction");
		
		ActionForward forward = null;
		
		// ------------------------------------------------------------------------------------
		// 게시물 목록을 가져오기 전 최근 게시물 목록 5개 조회를 위한 getListCount() 메서드 호출
		// BoardNoticeRecentListService 클래스의 인스턴스 생성
		BoardNoticeRecentListService boardNoticeRecentListService = new BoardNoticeRecentListService();
		int listLimit = 3; // 페이지 당 보여줄 게시물 수
		// 게시물 목록 가져오기 위해 BoardNoticeRecentListService 인스턴스의 getNoticeRecentList() 메서드 호출
		// => 파라미터 : 조회할 목록 갯수(listLimit)
		//    리턴타입 : ArrayList<NoticeBoardDTO>(recentList)
		ArrayList<NoticeBoardDTO> recentList = 
				boardNoticeRecentListService.getNoticeRecentList(listLimit);
		// -------------------------------------------------------------------------------------
		// 뷰페이지에서 사용할 데이터(최근 게시물 목록 정보)를 request 객체에 저장
		request.setAttribute("recentList", recentList);
		// -------------------------------------------------------------------------------------
		// 게시물 목록 조회 작업 완료 후 ActionForward 객체를 통해 뷰페이지(notice_list.jsp)로 포워딩
		// => 요청 서블릿 주소(BoardNoticeList.bo)가 유지되어야 하고,
		//    request 객체가 유지되어야 하므로 Dispatcher 방식 포워딩 필요
		forward = new ActionForward();
		forward.setPath("./main.jsp");
		forward.setRedirect(false); // 생략 가능
		
		return forward;
	}
}

 

▷ BoardNoticeRecentListService.java 파일로 이동(getNoticeRecentList() 메서드 호출)

 

service.BoardNoticeRecentListService.java

package svc;

import java.sql.Connection;
import java.util.ArrayList;

import dao.NoticeBoardDAO;
import dao.NoticeBoardDAO;
import vo.NoticeBoardDTO;

//import db.JdbcUtil;
// static import 기능을 사용하면 특정 클래스 내의 static 메서드들을
// 클래스명 없이 메서드명만으로 호출 가능해짐
// 기본 문법 : import static 패키지명.클래스명.메서드명; 또는 import static 패키지명.클래스명.*;
// => 주의! 메서드명 지정 시 소괄호() 는 명시하지 않음
//import static db.JdbcUtil.getConnection; // getConnection() 메서드만 import
import static db.JdbcUtil.*; // JdbcUtil 클래스의 모든 static 메서드를 import

public class BoardNoticeRecentListService {
	// 최근 게시물 목록 5개 조회 작업 요청을 위한 getNoticeRecentList() 메서드 정의
	// => 파라미터 : 조회할 목록 갯수(listLimit)
	//    리턴타입 : ArrayList<NoticeBoardDTO>(articleList)
	public ArrayList<NoticeBoardDTO> getNoticeRecentList(int listLimit) {
		System.out.println("BoardNoticeRecentListService - getNoticeRecentList()");
		ArrayList<NoticeBoardDTO> recentList = null;
		
		// 공통작업-1 - JdbcUtil 클래스로부터 Connection 객체 가져오기
		Connection con = getConnection();
		
		// 공통작업-2 - NoticeBoardDAO 클래스로부터 NoticeBoardDAO 객체 가져오기
		NoticeBoardDAO noticeBoardDAO = NoticeBoardDAO.getInstance();		
		
		// 공통작업-3 - Connection 객체를 NoticeBoardDAO 객체에 전달
		noticeBoardDAO.setConnection(con);
		
		// 게시물 목록 조회 작업 요청을 위해 Service 클래스의 selectArticleList() 메서드 호출
		// => 파라미터 : 게시물 수(listLimit)
		// => 리턴타입 : ArrayList<NoticeBoardDTO>(recentList)
		recentList = noticeBoardDAO.selectRecentList(listLimit);
		
		// 공통작업-4 - Connection 객체 반환
		close(con);
		
		return recentList;
	}
}

 

▷ dao클래스

 

dao.NoticeBoardDAO.java

package dao;

//import db.JdbcUtil;
import static db.JdbcUtil.close; // JdbcUtil 클래스의 close() 메서드 3개만 static import

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import vo.NoticeBoardDTO;

// 실제 비즈니스 로직을 수행할 NoticeBoardDAO 클래스 정의
// => 외부로부터 인스턴스마다 저장할 데이터를 각각 구별해야할 필요가 없으므로
//    싱글톤 패턴을 활용하여 인스턴스를 직접 생성하고 외부로 공유하도록 정의
public class NoticeBoardDAO {
	// -------------------- 싱글톤 패턴 -----------------------
	private static NoticeBoardDAO instance = new NoticeBoardDAO();
	
	private NoticeBoardDAO() {}

	public static NoticeBoardDAO getInstance() { // 리턴 전용이므로 Getter 만 필요
		return instance;
	}
	// --------------------------------------------------------
	// Service 클래스로부터 Connection 객체를 전달받아 멤버변수에 저장
	Connection con;

	public void setConnection(Connection con) { // 저장 전용이므로 Setter 만 필요
		this.con = con;
	}
    
	// 최근 공지사항 5개를 조회하는 selectRecentList() 메서드 정의
	public ArrayList<NoticeBoardDTO> selectRecentList(int listLimit) {
		ArrayList<NoticeBoardDTO> noticeBoardList = null;
		
		// DB 작업에 필요한 공통 변수 선언(Connection 객체는 멤버변수로 존재하므로 제외)
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			// 전체 게시물 목록 조회(번호 기준 내림차순 정렬)
			// => LIMIT 절 뒤의 파라미터는 시작 행번호(0 고정 = 최신글), 레코드 수를 지정
			String sql = "SELECT * FROM board ORDER BY num DESC LIMIT 0,?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, listLimit); // 가져올 레코드 수
			rs = pstmt.executeQuery();
			
			noticeBoardList = new ArrayList<NoticeBoardDTO>();
			
			while(rs.next()) {
				// 1개 레코드 정보를 저장할 NoticeBoardDTO 객체 생성 후 데이터 저장
				// => 단, 파일명은 목록 출력 대상에 포함되지 않으므로 저장 생략 가능
				NoticeBoardDTO noticeBoard = new NoticeBoardDTO();
				noticeBoard.setNum(rs.getInt("num"));
				noticeBoard.setSubject(rs.getString("subject"));
				noticeBoard.setContent(rs.getString("content"));
				noticeBoard.setDate(rs.getDate("date"));
				
				// 모든 레코드를 저장할 ArrayList 객체에 NoticeBoardDTO 객체를 추가
				noticeBoardList.add(noticeBoard);
			}
			
		} catch (SQLException e) {
			System.out.println("SQL 구문 오류 발생!");
			e.printStackTrace();
		} finally {
			close(rs);
			close(pstmt);
		}
		
		return noticeBoardList;
	}
}

 

▷ DB에서 조회후 service, action, view 페이지로 리턴 및 데이터 이동

 

main.jsp

<%@page import="vo.NoticeBoardDTO"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
// 조회된 최근 공지사항 목록(ArrayList<NoticeBoardDTO> 객체 = recentList) 가져오기
ArrayList<NoticeBoardDTO> recentList = (ArrayList<NoticeBoardDTO>)request.getAttribute("recentList");
%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>main/main.jsp</title>
<link href="../css/default.css" rel="stylesheet" type="text/css">
<link href="../css/front.css" rel="stylesheet" type="text/css">
</head>
<body>
	<div id="wrap">
		<!-- 헤더 들어가는곳 -->
		<jsp:include page="../inc/top.jsp"/>
		<!-- 헤더 들어가는곳 -->
		  
		<div class="clear"></div>   
		<!-- 본문들어가는 곳 -->
		<!-- 메인 이미지 -->
		<div id="main_img"><img src="../images/main_img.jpg"></div>
		<!-- 본문 내용 -->
		<article id="front">
		  	<div id="solution">
		  		<div id="hosting">
		  			<h3>Web Hosting Solution</h3>
					<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
		  		</div>
		  		<div id="security">
		  		  	<h3>Web Security Solution</h3>
					<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
		  		</div>
		  		<div id="payment">
		  			<h3>Web Payment Solution</h3>
					<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
		  		</div>
		  	</div>
		  	<div class="clear"></div>
			<div id="sec_news">
				<h3><span class="orange">최근 공지사항</span></h3>
				<%-- recentList 객체 크기만큼 반복 --%>
				<c:forEach items="${recentList}" var="noticeBoard">
					<a href="../center/BoardNoticeContent.bo?num=${noticeBoard.num }&page=1">
					<dl>
						<dt>${noticeBoard.subject }</dt>
						<dd>${noticeBoard.content }</dd>
					</dl>
					</a>
				</c:forEach>
			</div>
		
			<div id="news_notice">
		  		<h3 class="brown">News &amp; Notice</h3>
				<dl>
					<dt>Vivamus id ligula....</dt>
					<dd>Proin quis ante Proin quis anteProin 
					quis anteProin quis anteProin quis anteProin 
					quis ante......</dd>
				</dl>
				<dl>
					<dt>Vivamus id ligula....</dt>
					<dd>Proin quis ante Proin quis anteProin 
					quis anteProin quis anteProin quis anteProin 
					quis ante......</dd>
				</dl>
		  	</div>
	  	</article>
		  
		<div class="clear"></div>  
		<!-- 푸터 들어가는곳 -->
		<jsp:include page="../inc/bottom.jsp"/>
		<!-- 푸터 들어가는곳 -->
	</div>
</body>
</html>

→ 메인페이지에서 request.getAttribute() 를 이용하여 데이터 받아 온 후 

     jdtl문을 사용하여 데이터 출력!

 

-------------------------------------------------------------------------------------------------------------------------------------------

메인 페이지에 최근 공지사항 3개 출력하기 완성!

'JSP' 카테고리의 다른 글

JQuery (선택자)  (0) 2022.03.04
JQuery (사용방법)  (0) 2022.03.04
MVC 모델 (5)  (0) 2022.02.24
MVC 모델 (4)  (0) 2022.02.24
MVC 모델 (3)  (0) 2022.02.24