Joslynn의 하루

MSA Full-Stack 개발자 양성 과정 - JSP&Servlet MVC 구조 Refactoring_221014 본문

MSA Full-Stack 개발자 양성과정/JSP&Servlet

MSA Full-Stack 개발자 양성 과정 - JSP&Servlet MVC 구조 Refactoring_221014

Joslynn 2022. 10. 14. 18:02

MVC 구조

version1

 

1. View

< a href= "front?key=insert/update/delete/select">

 등록 / 수정 / 삭제 / 조회

</a>

: key를 보냄으로써 Sevlet에서 어떤 기능인지 구분할 수 있음 

 

2. DispatcherServlet: 진입점 서블릿(Controller 역할)

class DispatcherServlet extends HttpServlet{

    @webServlet("/front")

    Xxxservice(request, response){

           String key = request.getParameter("key");
           Controller con = null;
           
           if (key.equals("insert"){
          	 con = new InsertConroller();
           } else if (key.equals("update")){     
          	 con = new UpdateConroller();
           } else if....
          
          ModelAndView m = con.handleRequest(request, response);
          if(m.isRedirect(){
          	  //true이면 redirect 방식
              response.sendRedirect(m.getViewName());
          } else {
          	  //false이면 forward 방식
              request.getRequestDispatcher(m.getViewName()).forward(request, response);
          }
    }
}

 

: 수많은 등록 삭제 조회 수정 요청을 service에서만 담당하면 한 메소드가 너무 많은 역할을 담당하게됨

: 분산 필요 → Controller(interface)를 만듦

 

3. Interface Controller

public interface Controller{

	ModelAndView handleRequest(request, response); // 요청을 받고 응답하는 역할을 가장 많이 담당함
	//ModelAndView를 리턴
}

: Controller 인터페이스를 implements 하는 InsertController, SelectController, UpdateController, DeleteController.....

: 각각 메소드 재정의

: 확장된 모든 Impl 클래스들은 Controller라고 부를 수 있음 (다형성) → 유지보수에 유리

 

4. class ModelAndView

class ModelAndView{
    String viewName; //최종적으로 이동할 뷰의 page이름
    boolean isRedirect; //이동 방식 결정 (true이면 redirect 방식, false면 forward방식)
	
}

Version 2: Listener 사용

 

1. ServletContexLister

public class AppListener implements ServletContextListener{

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		//~.propertiest 파일 로딩
    
        while(){
            //1. key와 value를 분리 
            //2. String을 객체로 만든다 -> 생성 (Reflection 개념 적용)
            //3. Map에 저장(key: <String>, value: <Controller>) // map.put(key, value);
        }
        
        ServletContext application = sce.getServletContext();
    	application.setAttribute("map", map);
    }
    
}

 

**~.properties 파일

#key = value

insert = InsertController

delete = DeleteController

select = SelectController

update = UpdateController

 

**Reflection 개념

 : String의 문자열을 Controller라는 객체로 생성해야 함
 : Class<?>는 어떤 객체가 가지고 있는 필드, 생성자, 메소드의 정보를 동적으로 가져올 수 있도록 도와주는 객체
 : relection 개념은 동적으로, 즉, 실행도중에 필요한 객체를 적절하게 생성하고,  그 객체가 가지고 있는 생성자나 메소드를 동적으로 호출할 수 있도록 도와주는 개념
 : 자바에서 이러한 개념을 적용해놓은 API가 Class<?>이다.

 

2. DispatcherServlet 변경

class DispatcherServlet extends HttpServlet{
	Map<String, Controller> map;
	Xxxinit(){
	 	map = application.getAttribute("map");
	}
    
    Xxxservice(request, response){
    	String key = request.getParameter("key");
        Contoller con = map.get(key);
        
        ModelAndView m = con.handleRequest(request, response);
          if(m.isRedirect(){
          	  //true이면 redirect 방식
              response.sendRedirect(m.getViewName());
          } else {
          	  //false이면 forward 방식
              request.getRequestDispatcher(m.getViewName()).forward(request, response);
          }
    }
}

 


Version 3: 여러 기능 추가

1. 시나리오

1) Version2

    ㉮ 회원관리

        - 가입, 로그인, 로그아웃, 정보수정, 탈퇴 → 각각 XxxController 적용

    ㉯게시판 관리

        - 게시판 등록, 수정, 삭제 → 각각 XxxController 적용

    ㉰ 상품관리

       - 상품 등록, 수정 삭제 → 각각 XxxController 적용

2) Version3

    ㉮ 회원관리 → 기능별로 XxxController 적용

        - 가입, 로그인, 로그아웃, 정보수정, 탈퇴 -- 메소드

    ㉯게시판 관리

    ㉰ 상품관리

: 뷰에서 요청시 front?key=select&methodName=login

: parameter 정보 중 key는 어떤 XxxController를 실행해야 하는 객체를 찾아주는 정보, methodName은 찾은 XxxController의 어떤 메소드를 호출해야 하는지 찾아주는 정보이다.

 

HandlerMappingListener

import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

import kosta.mvc.controller.Controller;

/**
 * 서버가 시작될 때, 각 Controller의 구현 객체를 미리 생성해서 Map에 저장한 후, ServletContext영역에 map을 저장
 * 
 */
@WebListener
public class HandlerMappingListener implements ServletContextListener {

	public void contextDestroyed(ServletContextEvent sce) {
	}

	public void contextInitialized(ServletContextEvent sce) {

		ResourceBundle rb = ResourceBundle.getBundle("actionMapping"); // actionMapping.properties 로딩

		try {

			Map<String, Controller> map = new HashMap<String, Controller>();
			Map<String, Class<?>> clzMap = new HashMap<String, Class<?>>();

			for (String key : rb.keySet()) {
				String value = rb.getString(key);

				Class<?> className = Class.forName(value);
				Controller con = (Controller) className.getDeclaredConstructor().newInstance();

				map.put(key, con);
				clzMap.put(key, className);

			}
			// 현재 프로젝트의 모든 영역에서 map을 사용할 수 있도록 ServletContext영역에 저장한다.
			ServletContext applcation = sce.getServletContext();
			applcation.setAttribute("map", map);
			applcation.setAttribute("clzMap", clzMap);
			applcation.setAttribute("path", applcation.getContextPath());

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

DispatcherServlet

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 모든 사용자 요청을 처리할 진입점 Controller의 역할 : loadOnStartup -> 서버 올릴때,
 * DispatcherServlet을 사전 초기화(진입점이므로 사전 생성이 좋음)
 */
@WebServlet(urlPatterns = "/front", loadOnStartup = 1)
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private Map<String, Controller> map;
	private Map<String, Class<?>> clzMap;

	@Override
	public void init() throws ServletException {
		ServletContext application = super.getServletContext();
		map = (Map<String, Controller>) application.getAttribute("map");
		clzMap = (Map<String, Class<?>>) application.getAttribute("clzMap");
	}

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String key = request.getParameter("key"); // user, board, goods
		String methodName = request.getParameter("methodName");

		System.out.println("key = " + key);
		System.out.println("methodName = " + methodName);

		try {
			Controller con = map.get(key);
			Class<?> className = clzMap.get(key);

			Method method = className.getDeclaredMethod(methodName, HttpServletRequest.class,
					HttpServletResponse.class);

			// 호출
			ModelAndView mv = (ModelAndView) method.invoke(con, request, response);

			if (mv.isRedirect()) {
				response.sendRedirect(mv.getViewName());
			} else {
				request.getRequestDispatcher(mv.getViewName()).forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

 

Comments