Tiny Bunny [WEB] FrontController - 솜님의 블로그
솜님의 블로그
카테고리
작성일
2024. 8. 26. 00:56
작성자
겨울솜사탕

FrontController는 모든 요청을 받는 방식을 말한다.

 

controller.jsp는 무조건 필요한데 HTML을 사용하지 않기 때문에 java라고 볼 수 있다.

그런데 .jsp는 컴파일하면 Servlet이 됨. 이것을 FrontController

 

프론트단에서 사용자들이 하는 모든 요청이 이곳으로 와줬으면 좋겠다~ 하는 관리를

톰캣(Server) 설정을 해주어야 한다.

 

톰캣(Server)은 서버도 제공해 주지만, 이러한 설정 역할을 해준다.

 

톰캣(Server)에게 설정을 제공하는 방법

1) xxx.xml (설정파일) 등록

 : 톰캣의 경우 web.xml 파일.

 

2) @ (어노테이션, 애너테이션) 등록

 

1)번의 경우 .xml이 다루기 어렵고, 스키마가 무겁다는 단점이 있기 때문에,

간략하고 가독성이 좋은 2) 번의 방식을 사용한다.

 

사용법은 Servlet 위쪽에 애너테이션을 작성해 준다.

 위와 같이 @WebServlet("*.do") 을 작성해 주면

xxxx.do로 끝나는 규칙이 있으면 이 페이지로 보내준다!

 

이 @어노테이션의 위치는 반드시 해당 클래스의 위쪽에 달아줘야 한다.

 

▼ 사용 예시

@WebServlet("*.do")
// 톰캣(server)이 구동될때, xxx.do로 끝나는 요청에 대하여 FC를 호출하게됨
public class FrontController extends HttpServlet {
	private static final long serialVersionUID = 1L;
   
    public FrontController() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	private void doAction(HttpServletRequest request, HttpServletResponse response) {
		// 1. 사용자가 무슨 요청을 했는지 추출 및 확인
		String uri=request.getRequestURI();
		String cp=request.getContextPath();
		String command=uri.substring(cp.length());
		System.out.println("command : "+command);
		
		// 2. 요청을 수행
		if(command.equals("/main.do")) {
			BoardDAO boardDAO=new BoardDAO();
			BoardDTO baodrDTO=new BoardDTO();
			ArrayList<BoardDTO> datas=boardDAO.selectAll(boardDTO);
			request.setAttribute("datas", datas);
		}
		else if(command.equals("/login.do")) {
			MemberDAO memberDAO = new MemberDAO();
			MebmerDTO memberDTO = new MemberDTO();
			memberDTO.setMid(request.getParameter("mid"));
			memberDTO.setPassword(request.getParameter("passowrd"));
			memberDTO = mebmerDAO.selectOne(memberDTO);
        	if(memberDTO != null){
           		session.setAttrubute("loginInfo", memberDTO.getMid());
       		}
		}
		else if(command.equals("/join.do")) {
			
		}
		else if(command.equals("/logout.do")) {
		
		}
		else {
			// 등록되지않은 요청입니다.
		}
		
		// 3. 응답(페이지 이동 등)
		//  1) 전달할 데이터가 있니? 없니? == 포워드? 리다이렉트?
		//  2) 어디로 갈까? == 경로
        
		if(forward == null) {
			// command 요청이 없는 경우
		}
		else {
			if(forward.isRedirect()) {
				try {
					response.sendRedirect(forward.getPath());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			else {
				RequestDispatcher dispatcher=request.getRequestDispatcher(forward.getPath());
				try {
					dispatcher.forward(request, response);
				} catch (ServletException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

doGet / doPost 어느 방식으로 오던 다 무언가를 수행해야 하기 때문에,

doGet과 doPost에 도달하면 doAction 을 수행할 수 있도록 코드를 작성해 준다.

 

🍀doAction의 3단계
1. 사용자가 무슨 요청을 했는지 추출 및 확인
2. 요청을 수행
3. 응답 (페이지 이동 등)

 

 

String uri=request.getRequestURI(); >> 무슨 요청을 했는지 URI로 확인을 해주는 역할을 한다.

 (ex. /day034/main.do)
String cp=request.getContextPath(); >> 현재 웹 애플리케이션의 기본 경로를 나타낸다.

(ex. /day034)
String command=uri.substring(cp.length());

>> uri의 길이를 subgstring를 사용하여 cp.length는 길이부터 자른다는 것을 의미한다.

>> 해당 코드를 사용할 경우 command에는 /main.do 가 들어가게 된다.

이때 변수를 저장하는 곳을 command 또는 action 이라고 한다.

(command? == action No. - 메뉴 번호를 의미한다)

 

 

이 Servlet을 사용하여 모든 요청을 받아주고. 응답을 수행한다.

따라서 기존에 사용하던 controller.jsp 파일은 필요하지 않고,

처음 페이지인 index.jsp 도 요청을 controller.jsp action 파라미터 값으로 보내지 않는다.

 

▼ index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 기존에 사용하던 방식
	// response.sendRedirect("controller.jsp?action=main");

	// FrontController로 보내주는 방식 xxx.do
	response.sendRedirect("main.do");
%>

기존에는 파라미터 방식으로 들고 다녀야 하니 무겁고 가독성이 떨어진다.

현재의 방식(xxx.do로 보내주는 방식)으로 바꿔주면 가독성이 올라가고, 파라미터가 0개라 가벼워진다!

 

또한 기존의 controller.jsp 에서는 DAO DTO 수만큼 <jsp:useBean> 선언해서 사용했는데,

이렇게 되면 필요 없는 기능에서도 new가 사용된다.

 

FrontController에서는 기능별로 요청을 따로 수행하기 때문에 훨씬 더 경제적이다.

(필요 없는 곳에서는 new를 사용하지 않는다 == 힙 메모리 영역을 사용하지 않는다.)

 

 


 

위의 FrontController 코드는 응집도가 낮은 상태.

응집도가 낮다 == 서로 관련 없는 코드들이 한 곳에 저장되어 있다

 

이러한 것의 단점은

1) 공동 작업이 어렵다.

2) 코드 작업 중에는 해당 페이지 내용 전부 접근 불가

따라서 한 가지 기능을 수정하면 다른 기능도 모두 사용이 불가하다.

 --> 가용성이 낮아져서 사용자가 이용을 못하는 경우에는, 반드시 그 사실을 고지해야 한다.

 

이러한 단점들 때문에 응집도를 높여야 한다.

응집도를 높이기 위해 xxxAction.java 클래스 만들어서 관리해 준다.

 

xxxAction.java == POJO (가벼움!)

상대적으로 무거운 NOT POJO인 Servlet은 적으면 좋기 때문에, FrontController만 있으면 된다.

xxxAction.java 로 클래스를 나눠주면 응집도를 높여주고, 가용성도 좋아진다!

 

▼ mainAction.java

	public void execute() {
		BoardDAO boardDAO=new BoardDAO();
		BoardDTO boardDTO=new BoardDTO();
		ArrayList<BoardDTO> datas=boardDAO.selectAll(boardDTO);
		request.setAttribute("datas", datas);
	}

 

자바 파일에서 기능을 수행하는 코드를 작성해 준다.

execute 메서드는, 외부의 요청정보를 받아와서 수행해야 하기 때문에 인자값으로 request와 response를 넣어준다.

 

xxxAction 파일이 여러 개 이기 때문에, 동일한 메서드명과 인자를 모두 같은 이름으로 사용하고 싶다.

==> 인터페이스를 이용한다!

 

▼ Action.java (인터페이스)

package controller.common;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

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

 

 

그리고 xxxAction.java에서의 값들은 결국 반환이 필요한데,

1. 어떻게 갈지? (포워드 or 리다이렉트)

2. 어디로 갈지? (경로)

의 반환이 필요하다.

 

java에서 반환 시 2개 이상의 반환이 필요할 때 DTO 파일에 담아서 반환한다.

따라서 DTO 같은 클래스 파일을 하나 만들어준다.

 

▼ ActionForward.java 

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;
	}
}

 

 

▼ 수정한 mainAction.java

package controller.board;

import java.util.ArrayList;

import controller.common.Action;
import controller.common.ActionForward;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.dao.BoardDAO;
import model.dto.BoardDTO;

public class MainAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) {
		BoardDAO boardDAO=new BoardDAO();
		BoardDTO boardDTO=new BoardDTO();
		ArrayList<BoardDTO> datas=boardDAO.selectAll(boardDTO);
		request.setAttribute("datas", datas);
		
		ActionForward forward=new ActionForward();
		forward.setRedirect(false); // 포워드 방식
		forward.setPath("main.jsp");
		return forward;
	}
	
}

인터페이스를 활용하므로, implements Action을 사용하고,

ActionForward 타입으로 반환해 준다.

 

forward.setRedirect에는 

- 반환할 값이 있다면 : 포워드 방식 false

- 반환할 값이 없다면 : 리다이렉트 방식 true를 입력한다.

 

 

▼ FrontController 수정

ActionForward forward=null;
		//모든 ACtion들이 forward를 받아서 쓸 예정이라, 위쪽에 선언
		
		if(command.equals("/main.do")) {
			MainAction mainAction=new MainAction();
			forward = mainAction.execute(request, response);
		}
		else if(command.equals("/bodrd.do")) {
			BoardAction boardAction=new BoardAction();
			forward = boardAction.execute(request, response);
		}
		else if(command.equals("/login.do")) {
			LoginAction loginAction=new LoginAction();
			forward = loginAction.execute(request, response);
		}
		else if(command.equals("/joinPage.do")) {
			JoinPageAction joinPageAction=new JoinPageAction();
			forward = joinPageAction.execute(request, response);
		}
		else if(command.equals("/join.do")) {
			JoinAction joinAction=new JoinAction();
			forward = joinAction.execute(request, response);
		}
		else if(command.equals("/logout.do")) {
			LogoutAction logoutAction=new LogoutAction();
			forward = logoutAction.execute(request, response);
		}

 

 

 

🍀정리
- View는 이제 .jsp 페이지로 바로 보내는 것이 아니라, 무조건 Controller를 거쳐 액션명.do로 요청을 보낸다.
- 액션명.do로 요청을 보내면 Servlet을 타고 요청이 해당 페이지로 가는데, 이건 @ 어노테이션 덕분이다.
 : 그래서 서버가 늘어나거나 처리해야 할 요청이 많아지면 FrontController2를 만들 수 있음.
   이때 어노테이션을 @("*.mem") 식으로 처리할 수 있다.

 

'WEB > ' 카테고리의 다른 글

[WEB] 이미지(파일) 업로드  (5) 2024.09.02
[WEB] 핸들러맵핑  (0) 2024.08.26
[WEB] xml 파일  (0) 2024.08.25
[WEB] 커스텀 태그  (0) 2024.08.25
[WEB] JSTL  (0) 2024.08.25