본문 바로가기
☁︎KOSTA/☂︎KOSTA-WEB

[KOSTA] Spring 기반 Cloud 서비스 구현 개발자 양성 (Day 47) - Front Controller Design Pattern(실습위주), Git

by 하_센세 2022. 10. 12.

2022.10.11 [TUE]

- Day 47- 

 

수업 주요 목차

  • Front Controller Design Pattern
  • Git

🤖Review

 

 


 

Front Controller Design Pattern : 모든 클라이언트의 요청을 하나의 진입점으로 통합하여 처리하는 설계 패턴

  •  웹 어플리케이션 서비스의 공통 정책을 일관성 있고 효과적으로 처리할 수 있다.(예: 인코딩 , 인증,  예외처리, 데이터수집 등)

 

 👾 FrontController 적용 전 Web MVC 구조 👾

 

client 1 -- FindMemberByIdServlet -- Model

client 3 -- FindMemberByIdServlet -- Model

 

client 2 -- RegisterMemberServlet -- Model

client 4 -- RegisterMemberServlet -- Model

 

 

👾 FrontController 적용 후 Web MVC 구조 Ver 1 👾

 

client 1 -- FrontControllerServlet -- Model

client 2 -- FrontControllerServlet -- Model

client 3 -- FrontControllerServlet -- Model

client 4 -- FrontControllerServlet -- Model

client 5 -- FrontControllerServlet -- Model

 

 

 FrontControllerServlet : command 정보를 이용해 요청을 구분한다.

  String command=request.getParameter("command");

     if(command.equals("~~~"){

 

      } else if(command.equals("~~~~")){

 

      }

package org.kosta.myproject.controller;

import java.io.IOException;

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 org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;

@WebServlet("/FrontControllerServlet")
public class FrontControllerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//client의 요청을 분석한다.
		//request.setCharacterEncoding("utf-8"); 애초에 받아서 실행되기 전에 setting을 해줘야 함 여기 써도 되지만 그냥 post방식에 다 사용하는 거니까 doPost에 써주는게 제일 깔끔
		String command=request.getParameter("command");
		//System.out.println(command);
		if(command.equals("findCustomerById")) {
			String id=request.getParameter("customerId");
			CustomerVO cvo=MockDAO.getInstance().findCustomerById(id);
			if(cvo!=null) {
				request.setAttribute("cvo", cvo);
				request.getRequestDispatcher("findbyid-ok.jsp").forward(request, response);
			} else {
				request.getRequestDispatcher("findbyid-fail.jsp").forward(request, response);
			}
		} else if(command.equals("registerCustomer")) {
			CustomerVO cvo=new CustomerVO(request.getParameter("customerId"), request.getParameter("customerName"), request.getParameter("customerAddress"));
			MockDAO.getInstance().registerCustomer(cvo);
			response.sendRedirect("register.jsp");
		}
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doDispatch(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		doDispatch(request, response);
	}
}

 

  doGet()과 doPost()에서는 doDispatch()를 호출하고 하나의 front controller servlet이 여러 요청을 구분하기 위해 hidden 방식을 이용한다.

 

  클라이언트(index.jsp)

  <input type="hidden" name="command" value="~~">

하나의 FrontControllerServlet으로 여러 폼에서 요청이 전달될 경우 이를 구분하기 위한 방법 중 하나는 hidden tag를 이용한 방법이 있다.
<input type="hidden" name="command" value="findMemberById">
or
<input type="hidden" name="command" value="registerMember">
<%@ 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>WEB MVC + Front Controller Design Pattern ver 1</title>
<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>
</head>
<body>
<div class="container pt-3">
<h4>WEB MVC + Front Controller Design Pattern</h4>
<form action="FrontControllerServletVer3" method="get">
	<input type="hidden" name="command" value="findCustomerById">
	<input type="text" name="customerId" placeholder="고객아이디" required="required">
	
	<button type="submit">검색</button>
</form>
<br><br>
<form action="FrontControllerServletVer3" method="post">
	<input type="hidden" name="command" value="registerCustomer"> <%-- 2011~12년도까지 사용하던 방식 --%>
	<input type="text" name="customerId" placeholder="고객아이디" required="required"><br>
	<input type="text" name="customerName" placeholder="고객이름" required="required"><br>
	<input type="text" name="customerAddress" placeholder="고객주소" required="required"><br>
	<button type="submit">검색</button>
</form>
</div>
</body>
</html>

 

 

  위와 같은 구조로 여러 클라이언트의 요청을 하나의 진입점으로 모아보았다. 이 경우 FrontControllerServlet의 doDispatch method의 코드량이 요청이 늘어날 수록 비대해질 수 밖에 없는 구조로 refactoring(내부적 구조개선)이 필요하다.

 

 🧚‍♂️ refactoring : 입,출력은 동일하나 시스템의 내부 구조를 개선하는 활동 (가독성, 재사용성,, 유지보수성 향상이 목적)

  • refactoring 1 안 : 요청에 대한 처리를 메서드 별로 분화
  • refactoring 2 안 : 메서드 별로 분화해서 FrontControllerServlet class자체가 커지는 것은 피할 수 없으므로 요청 처리를 전담할 Controller class를 별도로 생성&관리

 

👾 FrontController 적용 후 Web MVC 구조 Ver 2 👾

 

  1안 : 메서드별로 분화 FrontControllerServletVer2

package org.kosta.myproject.controller;

import java.io.IOException;

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 org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;
/**
 * 
 * Front Controller Design Pattern Version 2
 * 모든 클라이언트의 요청을 FrontControllerServlet에서 처리해본다.
 * doDispatch method의 코드량이 비대 -> refactoring -> method별로 분화
 * 
 * @author hasense
 *
 */
@WebServlet("/FrontControllerServletVer2")
public class FrontControllerServletVer2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void findCustomerById(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
			String id=request.getParameter("customerId");
			CustomerVO cvo=MockDAO.getInstance().findCustomerById(id);
			if(cvo!=null) {
				request.setAttribute("cvo", cvo);
				request.getRequestDispatcher("findbyid-ok.jsp").forward(request, response);
			} else {
				request.getRequestDispatcher("findbyid-fail.jsp").forward(request, response);
			}
	}
	
	protected void registerCustomer(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		CustomerVO cvo=new CustomerVO(request.getParameter("customerId"), request.getParameter("customerName"), request.getParameter("customerAddress"));
		MockDAO.getInstance().registerCustomer(cvo);
		response.sendRedirect("register.jsp");
	}
	
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//client의 요청을 분석한다.
		String command=request.getParameter("command");
		System.out.println(getServletName()+" "+command);
		if(command.equals("findCustomerById")) {
			findCustomerById(request, response);
		} else if(command.equals("registerCustomer")) {
			registerCustomer(request, response);
		}
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doDispatch(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		doDispatch(request, response);
	}
}

 

👾 Front Controller Design Pattern Version 3 👾

 

2안 : 클래스별로 분화 FrontControllerServletVer3

package org.kosta.myproject.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 
 * Front Controller Design Pattern Version 3
 * 모든 클라이언트의 요청을 하나의 진입점으로 모은다. 공통 적책 수행(인코딩, 인증, 예외처리, ....)
 * 클라이언트 요청을 처리할 별도의 Controller 클래스로 분화하여 서비스한다.
 * 
 * 
 * 토론점 : doDispatch 내부에서 개별 컨트롤러 객체 생성 후 실행시 각 컨트롤러 객체의 내부 구현부를 확인해서 각각 다르게 실행해야 하는 단점이 존재함
 * 					-> 인터페이스를 통한 표준화, 다형성 적용 환경을 마련하면 됨
 * 					Controller interface의 handleRequest(request,response) : String
 * 
 * @author hasense
 *
 */
@WebServlet("/FrontControllerServletVer3")
public class FrontControllerServletVer3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
		protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//client의 요청을 분석한다.
		String command=request.getParameter("command");
		System.out.println(getServletName()+" "+command);
		if(command.equals("findCustomerById")) {
			FindCustomerByIdController controller=new FindCustomerByIdController();
			controller.findCustomerById(request, response);
		} else if(command.equals("registerCustomer")) {
			RegisterCustomerContrrollerr controller=new RegisterCustomerContrrollerr();
			controller.registerCustomer(request, response);
		}
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doDispatch(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		doDispatch(request, response);
	}
}

 

- FindCustomerByIdController

package org.kosta.myproject.controller;

import java.io.IOException;

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

import org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;

public class FindCustomerByIdController {
	protected void findCustomerById(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		String id=request.getParameter("customerId");
		CustomerVO cvo=MockDAO.getInstance().findCustomerById(id);
		if(cvo!=null) {
			request.setAttribute("cvo", cvo);
			request.getRequestDispatcher("findbyid-ok.jsp").forward(request, response);
		} else {
			request.getRequestDispatcher("findbyid-fail.jsp").forward(request, response);
		}
	}
}

 

- RegisterCustomerContrroller

package org.kosta.myproject.controller;

import java.io.IOException;

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

import org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;

public class RegisterCustomerContrroller {
	protected void registerCustomer(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		CustomerVO cvo=new CustomerVO(request.getParameter("customerId"), request.getParameter("customerName"), request.getParameter("customerAddress"));
		MockDAO.getInstance().registerCustomer(cvo);
		response.sendRedirect("register.jsp");
	}
}

 

  모든 클라이언트의 요청을 하나의 진입점으로 모은다. 공통 적책 수행(인코딩, 인증, 예외처리, ....)

  클라이언트 요청을 처리할 별도의 Controller 클래스로 분화하여 서비스한다.

 

토론점 : doDispatch 내부에서 개별 컨트롤러 객체 생성 후 실행시 각 컨트롤러 객체의 내부 구현부를 확인해서 각각 다르게 실행해야 하는 단점이 존재함

  -> 인터페이스를 통한 표준화, 다형성 적용 환경을 마련하면 됨

  Controller interface의 handleRequest(request,response) : String

  

 

👾 Front Controller Design Pattern Version 4 👾

-Controller (Interface)

클라이언트 요청을 처리하는 개별 컨트롤러의 상위 인터페이스 -> 표준화, 캡슐화 
컨트롤러를 사용하는 측은 인터페이스의 추상 method handleRequest만 파악해서 사용하면 된다.
          -> 단일한 메세지 방식으로 표준화 
package org.kosta.myproject.controller;

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

public interface Controller {
	public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

 

- Front Controller Servlet

package org.kosta.myproject.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/FrontControllerServletVer4")
public class FrontControllerServletVer4 extends HttpServlet {
	private static final long serialVersionUID = 1L;

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

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		doDispatch(request, response);
	}

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			String command = request.getParameter("command");
			Controller controller = null;
			if (command.equals("findCustomerById")) {
				controller=new FindCustomerByIdController();
			} else if (command.equals("registerCustomer")) {
				controller=new RegisterCustomerController();
			}
			String path=controller.handleRequest(request, response);
			if(path.startsWith("redirect:")) {
				response.sendRedirect(path.substring(9));
			} else {
				request.getRequestDispatcher(path).forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	} // doDispatch
}

post방식에서 redirect를 이용할 경우 path 앞에 redirect:를 붙여주고 조건에 startsWith() method사용

path에서 redirect:를 뺀 뒷부분만 사용하고 싶다면 subString을 사용(몇번째부터인지 모르겠으면 test case하나 만들어서

"redirect:".trim().length()로 redirect:의 길이를 파악해주면 된다.)

 

 

- Find

package org.kosta.myproject.controller;

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

import org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;

public class FindCustomerByIdController implements Controller {
	@Override
	public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String path=null;
		String id=request.getParameter("customerId");
		CustomerVO cvo=MockDAO.getInstance().findCustomerById(id);
		if(cvo!=null) {
			request.setAttribute("cvo", cvo);
			path="findbyid-ok.jsp";
		} else {
			path="findbyid-fail.jsp";	
		}
		return path;
	}
}

- Register

package org.kosta.myproject.controller;

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

import org.kosta.myproject.model.CustomerVO;
import org.kosta.myproject.model.MockDAO;

public class RegisterCustomerController implements Controller {

	@Override
	public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		CustomerVO cvo=new CustomerVO(request.getParameter("customerId"), request.getParameter("customerName"), request.getParameter("customerAddress"));
		MockDAO.getInstance().registerCustomer(cvo);
		System.out.println("고객 등록 컨트롤러 : "+request.getParameter("customerName"));
		String path="redirect:register.jsp";
		return path;
	}
}

 

1. Find By Id

 

2. Register


 

🔎 Git 실습 내용

 

1.Create a new repository

  • Add a README file
  • Add .gitignore

 

 

2. Edit .gitignore 

▼참고 사이트▼

 

사용하는 프로그램에 맞춰서 .gitignore 파일을 만들어주는 사이트

 

https://www.toptal.com/developers/gitignore

 

gitignore.io

Create useful .gitignore files for your project

www.toptal.com

 

3. Clone Git repository in Eclipse

 

      STEP 1. copy the url

      STEP 2. Clone a Git Repository

 

GitHub에서 url copy하면 자동적으로 정보가 채워진다. 주의할 점은 Authentication User이름과 Password(발급받은 토큰)를 잘 넣어줘야 한다. next누르고 Finish누르면 clone 끝!

 

 

4. Share Project

 

 

5. Commit & Push

파일 수정 후 저장하면 Unstaged Changes에 올라가고 옆에 있는 + 표시를 눌러주거나 마우스로 드래그해서 Staged Changes로 내려준다.

 

Commit 전 message에 변경사항을 적어주고 아래 Commit을 먼저 눌러준다.

 

 

이렇게 Push해야 할 내용이 1개 있다고 뜨면 아래 Push Head...를 눌러준다.

 

 

Preview -> Push해주면 끝!

 

 

 

//오늘의 숙제

오늘은 블로그 정리만 하고 쉬어야 하는 날...🧟‍♀️