코딩항해기
[JSP] DB에 있는 게시글 화면에 보여주기 본문
MVC패턴에 따른 게시글 화면 출력을 정리해보려고 한다.
게시글 출력은 고정된 정보를 보여주는 정적 페이지가 아닌 동적 페이지이므로, DB를 거쳐야한다.
먼저 사용자가 게시글을 보겠다는 요청을 보내게 되면, Controller는 요청에 따라 Model에게 데이터를 요청하게 된다.
(요청에 따라 Action을 실행한다.)
Model은 Controller의 요청에 따라 해당하는 데이터를 DB에서 가져와 Controller에게 전달한다.
(이 때 컨디션 등의 분기가 사용되기도 한다.)
Controller는 Model로부터 받은 데이터를 정제하거나, 데이터를 이용하여 분기를 나눠 View에게 전달한다.
View는 전달받은 데이터를 화면에 출력하여 사용자의 요청에 응답한다.
1. View
먼저 요청을 보낼 페이지를 만들어보겠다. 일반적인 사이트라면 헤더(header)의 역할을 해줄 페이지다.
해당 페이지는 간단하게 게시글로 이동하겠다는 요청(board.do)을 보낼 하이퍼링크만 만들었다.
<%@ 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>
<a href="board.do">게시판</a>
</body>
</html>
2. Controller
이제 이 요청(board.do)를 받아줄 컨트롤러 servlet파일이 필요하다.
do로 끝나는 모든 요청을 받는 Front Controller를 만들어 board.do에 대한 액션을 만들 것이다.
해당 액션에서는 model에게 게시글 리스트에 대한 데이터를 요청할 것이다.
이 때 new 연산자를 절약하기 위해 싱글톤 패턴의 핸들러 맵퍼를 사용할 것이다.
[JSP] Controller - 심화 (싱글톤패턴 : 핸들러맵핑 적용)
[JSP] Controller Servlet기존 실습들에서는 Controller가 분할되었다 다시 하나로 통합되는 과정을 거쳤다.하지만 여전히 응집도가 낮고 하나의 기능을 수정하기 위해서는 전체 기능이 사용 불가하다는
minibcake.tistory.com
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
response.sendRedirect("main.do");
%>
프론트 컨트롤러
package controller.common;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
//.do로 끝나는 모든 요청은 해당 servlet으로 옴
@WebServlet("*.do")
public class frontController extends HttpServlet {
private static final long serialVersionUID = 1L;
private HandlerMapper mappings;//핸들러맵퍼
//프론트 컨트롤러 생성자
public frontController() {
super();
this.mappings = new HandlerMapper();//한 번만 핸들러 맵퍼 객체를 만듦
}
//get 요청이 오면 실행
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
execute(request, response);
}
//Post 요청이 오면 실행
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
execute(request, response);
}
//.do로 끝나는 모든 요청을 처리할 메서드
private void execute(HttpServletRequest request, HttpServletResponse response) {
// 1. 사용자가 무슨 요청을 했는지 추출 및 확인
String cp = request.getContextPath(); //고정된 주소 추출
String command = request.getRequestURI().substring(cp.length()); //고정된 주소를 제외한 요청
System.out.println("Controller log : command : "+command);
// 2. 요청을 수행
ActionForward forward = mappings.getMapper(command).execute(request, response);
// 3. 응답(페이지 이동 등)
if(forward == null) {
//없는 요청을 한 경우, 에러페이지로 이동하기 위해 null값을 반환한 경우
//에러페이지로 리다이렉트함
System.err.println("Controller log : error forward null");
forward = new ActionForward();
forward.setRedirect(true);
forward.setPath("error/error.jsp");
}
//응답 실행
if(forward.isRedirect()) {
//리다이렉트인 경우
try {
response.sendRedirect(forward.getPath());
} catch (Exception e) {
System.out.println("Controller log : redirect exception "+e.getMessage());
}
}
else {
//포워드인 경우
try {
request.getRequestDispatcher(forward.getPath()).forward(request, response);
} catch (Exception e) {
System.out.println("Controller log : forward exception "+e.getMessage());
}
}
}
}
핸들러 맵퍼
package controller.common;
import java.util.HashMap;
import java.util.Map;
import controller.action.Action;
import controller.action.BoardAction;
import controller.action.MainAction;
public class HandlerMapper {
private Map<String, Action> mapper; //요청에 대해 기능을 반환
public HandlerMapper() {
this.mapper = new HashMap<>();
//요청값과 그에 따른 Action
this.mapper.put("/main.do", new MainAction()); //메인페이지
this.mapper.put("/board.do", new BoardAction()); //게시글페이지
}
//바로 Map(this.mapper)의 value값을 반환
public Action getMapper(String command) {
return this.mapper.get(command);
}
}
Action interface (핸들러 맵퍼에서 업캐스팅을 가능하게 해주고, 메서드의 선언부를 통일시켜준다.)
package controller.action;
import controller.common.ActionForward;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public interface Action {
ActionForward execute(HttpServletRequest request, HttpServletResponse response);
}
ActionForward (반환값으로 방식과 이동 경로 두 가지를 한 번에 반환하기 위한 클래스이다.)
package controller.common;
public class ActionForward {
private boolean redirect; // 방식(포워드, 리다이렉트)
private String path; // 이동 경로
public boolean isRedirect() {
return redirect;
}
public void setRedirect(boolean redirect) {
this.redirect = redirect;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
BoardAction (board.do 요청이 들어왔을 때 수행할 기능이 있다.)
package controller.action;
import controller.common.ActionForward;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.jiyoon.BoardDAO;
import model.jiyoon.BoardDTO;
public class BoardAction implements Action {
@Override
public ActionForward execute(HttpServletRequest request, HttpServletResponse response) {
ActionForward forward = new ActionForward();
//model에게 게시글 데이터 받아오기
BoardDAO boardDAO = new BoardDAO();
BoardDTO boardDTO = new BoardDTO();
boardDTO.setCondition("ALL");//전체목록 요청
boardDTO.setCategoryName("일반");//일반카테고리 게시글 리스트 요청
//기존 model를 재활용하며 들어간 페이지네이션 범위지정
//사용하지 않을 예정이므로 0~999999로 임의 지정
boardDTO.setStartNum(0);
boardDTO.setEndNum(999999);
request.setAttribute("datas", boardDAO.selectAll(boardDTO)); //View에게 데이터 전달
forward.setPath("board.jsp"); //게시글목록을 보는 페이지로 이동
forward.setRedirect(false); //포워드 방식으로 이동
return forward;
}
}
MainAction (메인페이지로 이동시켜준다.)
package controller.action;
import controller.common.ActionForward;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class MainAction implements Action {
@Override
public ActionForward execute(HttpServletRequest request, HttpServletResponse response) {
ActionForward forward = new ActionForward();
forward.setPath("main.jsp"); //메인 페이지로 이동
forward.setRedirect(true); //리다이렉트 방식으로 이동
return forward;
}
}
web.xml (에러발생 시 자동으로 에러 페이지로 이동할 수 있도록 설정한다.)
(*주석 작성 시 정상작동 안할 수 있으므로 주석은 자제한다.)
[JSP] xml 파일로 error 페이지 설정하기
.xml ( eXtensible Markup Language) 웹에서 데이터를 전송하기 위해 미리 약속해둔 방식으로 만들어진 문서이다. 톰캣에서는 WEB-INF 하위에 web.xml 파일이 있다면, 해당 파일을 참고하여 설정한다.web.xml 파
minibcake.tistory.com
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0">
<servlet>
<description></description>
<display-name>FrontController</display-name>
<servlet-name>FrontController</servlet-name>
<servlet-class>controller.common.FrontController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/FrontController</url-pattern>
</servlet-mapping>
<display-name>0831board</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/error/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error/error.jsp</location>
</error-page>
</web-app>
3. Model
Model파트는 직전 프로젝트에서 담당하여 자세히 정리했으므로, 해당 Model을 재활용하도록 하겠다.
(테이블, DTO, DAO)
[Team/붕어빵원정대(중중프)] 중중프 완료 Model 담당 파트 정리
팀명 : 붕어빵 원정대 (6인)(Model 파트 2인 / View 파트 2인 / Controller 파트 2인 ) 담당 : Model 파트장(게시글, 이미지, 카테고리 DTO DAO 진행) [붕어빵 원정대 - 갈빵질빵 JSP기반 웹페이지 프로젝
minibcake.tistory.com
4.Controller
View에게 응답하는 (데이터 전달하는) 부분을 2번 단계에서 함께 수행했다.
5. View
사용자에게 응답을 보여줄 페이지가 필요하다.
오류가 발생하는 경우의 수도 있으므로, 에러 페이지(error.jsp)와 게시글 목록 페이지(board.jsp)가 필요하다.
에러 페이지는 404 500 등 여러 종류가 있지만, 에러 페이지를 구분하면 보안에 문제가 생길 수 있다고 하여 하나로 통일하도록 하겠다. 나중에는 다른 방식으로 에러 페이지를 띄우지 않는 방식을 연구해보는 것도 좋을 것 같다.
먼저, 에러 페이지에서는 간단한 안내와 메인으로 돌아갈 수 있는 버튼을 넣고자 한다.
에러 페이지에 대해 찾아보니 안내 목적 외에도 페이지 이탈률을 낮추기 위해 간단한 액션을 넣거나 게임을 넣거나, 관련된 재밌는 스토리 이미지를 넣는 경우도 많은 것으로 보였다. 지금은 게시글 연습이므로 간단한 안내와 돌아갈 수 있는 버튼으로 마무리 짓겠다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>찾을 수 없는 페이지 입니다.</title>
<link rel="stylesheet" type="text/css" href="error.css">
</head>
<body>
<main>
<section>
<div>죄송합니다. 현재 페이지를 찾을 수 없습니다.</div>
<button type="button" onclick="location.href='main.do'">메인으로 돌아가기</button>
</section>
</main>
</body>
</html>
css는 이 흐름에서 중요한 부분은 아니라서 시간절약을 위해 chatGPT에게 만들어 달라고 요청했다.
@charset "UTF-8";
/* body 스타일링 */
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* main 섹션 스타일링 */
main {
background-color: #fff;
padding: 40px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
/* div 스타일링 */
main section div {
font-size: 18px;
margin-bottom: 20px;
color: #333;
}
/* 버튼 스타일링 */
button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
이제 데이터를 받아와 출력할 게시글 페이지를 만든다.
게시글 페이지에서는 받아온 데이터에서 공개, 비공개를 구분하여 비공개 글일 경우 따로 멘트를 띄울 예정이다.
현재는 게시글 상세보기 기능이 아직 없으므로, 클릭이 가능하지 않도록 만들 예정이다. (if문)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>일반 게시판</title>
<link rel="stylesheet" type="text/css" href="css/board.css">
</head>
<body>
<main>
<h1>일반 게시판</h1>
<section>
<article class="title">
<span>번호</span>
<span>제목</span>
<span>작성자</span>
<span>작성일자</span>
</article>
<%--Controller에서 넘겨줄때 K값이 datas이므로 datas로 받는다. (사전에 약속된 값)--%>
<%--varstatue은 인덱스번호이다. --%>
<c:forEach var="data" items="${datas}" varStatus="status">
<article>
<span>${status.index + 1}</span>
<span>${data.title}
<c:if test="${data.visibility == '비공개'}"><b>[비공개글]</b></c:if>
</span>
<span>${data.memberNickname}</span>
<span>${data.writeDay}</span>
</article>
</c:forEach>
</section>
<a href="main.do">메인으로 돌아가기</a>
</main>
</body>
</html>
마찬가지로 css는 중요한 부분이 아니므로 chatGPT에게 만들어달라고 요청한 다음 일부만 수정해 사용했다.
@charset "UTF-8";
/* 기본 스타일 */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* 메인 섹션 스타일 */
main {
background-color: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 800px;
}
/* 제목 스타일 */
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
/* 게시글 제목 스타일 */
.title {
display: flex;
justify-content: space-between;
font-weight: bold;
background-color: #8f8f8f;
color: white;
padding: 10px;
border-radius: 5px;
margin: 7px 0;
}
/* 각 게시글의 스타일 */
article {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #ddd;
}
/* 마지막 게시글 하단에 선 없애기 */
article:last-of-type {
border-bottom: none;
}
/* 게시글 호버 효과 */
article:hover {
background-color: #f9f9f9;
}
/* 게시글 항목 스타일 */
span {
flex: 1;
text-align: center;
}
span:first-child {
flex: 0.5;
}
/*a 태그*/
a {
text-decoration: none;
color: #333;
font-size: 14px;
margin: 10px 0;
float: right;
}
실행하면 위처럼 전달된 데이터가 잘 뜨는 것을 확인할 수 있다.
또한 비공개글과 공개글을 구분한 것을 알 수 있다.
위 아래로 구분된 것은,, 샘플데이터가 보여주기 애매하게 들어가 있어서 일 뿐 비공개 공개를 구분하며 순서가 뒤섞인 것은 아니다.
(해당 데이터를 조회하는데 사용된 쿼리 실행 결과)
'JSP (+JAVA Web)' 카테고리의 다른 글
[JSP] alert창 띄우고 페이지 전환하기 (0) | 2024.09.04 |
---|---|
[JSP] Servlet - 리스너 Listener (0) | 2024.09.02 |
[JSP] JDBC - DAO 템플릿 정리 (0) | 2024.08.29 |
[JSP] header, footer 넣기 2탄 (JSTL.ver) (0) | 2024.08.29 |
[JSP] 입력받은 이미지 파일 저장하기 (0) | 2024.08.27 |