본문 바로가기

개발자노트/Spring

Spring - 디스패쳐서블릿 (DispatcherServlet) / 핸들러 매핑 (HandlerMapping) / 뷰 리졸버 (ViewResolver)

DispatcherServlet이란??

- DS는 스프링MVC 프로젝트의 핵심이라고 할 수 있다.

 

- FC 역할을 하는 서블릿을 DispatcherServlet 이라한다.

 

- DS는 FC역할로써 클라이언트로부터 요청이 들어오게 되면 모든 요청을 먼저 받게 된다.

 

- 이 때, DS는 자신이 직접 Controller들을 호출하지 않고, HandlerMapping을 통해서 호출하게 된다.

 

- Controller의 수행 결과로 View의 경로(String타입)를 반환하는데,

이 경로는 ViewResolver(어떻게 가야하는지 처리해줄 수 있는 객체) 에게 전달되어 사용자에게 view 화면을 제공해준다.

 

- 수행 후 HttpServletResponse에 있는 최정 결과를 서블릿 컨테이너에게 돌려주고,

 컨테이너는 클라이언트(브라우저)에 전송하여 응답한다.

 

 

다음과 같이 Servlet 파일을 만들게 되면

 

 

 *.do 요청에 대해 DispatcherServlet을 참조하라는 설정이 만들어지게 된다.

 

 

어떠한 요청이 들어와도 doAction이 실행되게끔 설정을 해주고,

 

 

1. URI에 있는 요청정보를 substring을 이용해 요청정보만을 command에 담아준다.

2. hadlerMapping을 통해 해당 commamd 키값에 맞는 value 값을 가지고 있는 객체를 생성한다.

3. ctrl에 있는 request정보와 response 정보를 viewName에 담아준다.

4. viewName에 .do라는 요청이 있다면 요청사항.do를 실행하도록 하고

5. 만약 .do 요청이 없다면 viewResolver를 이용해 .jsp를 보낼 수 있도록 한다.

6. 요청정보에 맞는 view를 보내게 된다.

 

 

 

 

대략적인 흐름
사용자의 요청 OO*.do → (web.xml을 보고) DS → HM → C호출

→ 경로정보를 반환(어디로 가야하는지) → VR → 사용자에게 화면을 제공


기대효과
※ 메모리를 불필요하게 사용하던 객체들이 현저히 줄어듦
※ 하드코딩이 줄어들고, 코드의 불필요한 반복패턴이 줄어듦 → 코드가 간결해짐
※ 결합도가 낮아짐 ( new 가 줄어듦 ) / 응집도가 높아짐  → 유지보수 용이

 

 

 

추가사항

핸들러 매핑에 대해서

핸들러 매핑은 HashMap과 command에 맞는 Controller를 반환해주는 것으로 구성되어있다.

mappings라는 HashMap 덩어리를 만들어놓고,

mappings에 각 ("커맨드" , "그에맞는 Controller객체==value값" ); 들을 put 해놓는다.

그리고 DispatcherServlet에서 command 값을 ctrl를 통해 주게 되면,

만들어져있던 mappings에서 해당 command에 대한 controller수행 값을 반환해주게 된다.

 

 

 

사용코드

 

컨트롤러 인터페이스

package com.kim.biz.controller;

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

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

 

로그인 컨트롤러.java

package com.kim.biz.controller;

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

import com.kim.biz.member.MemberVO;
import com.kim.biz.member.impl.MemberDAO;

public class LoginController implements Controller {

	@Override
	public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
		String mid=request.getParameter("mid");
		String mpw=request.getParameter("mpw");
		MemberVO mVO=new MemberVO();
		mVO.setMid(mid);
		mVO.setMpw(mpw);
		
		MemberDAO mDAO=new MemberDAO();
		mVO=mDAO.selectOneMember(mVO);
		
		if(mVO==null) {
			return "login"; // VR가 .jsp를 추가해주기 때문에 생략해서 반환
		}
		else {
			return "main.do";
		}
	}
	
}

 

뷰리졸버.java

package com.kim.biz.controller;

public class ViewResolver {
	public String prefix; // 의존관계 -> DI => setter 주입
	public String suffix; // 의존관계 -> DI => setter 주입
	public String getPrefix() {
		return prefix;
	}
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}
	public String getSuffix() {
		return suffix;
	}
	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}
	
	public String getView(String viewName) {
		return prefix+viewName+suffix;
	}
}

 

 

핸들러매핑.Java

package com.kim.biz.controller;

import java.util.HashMap;
import java.util.Map;

public class HandlerMapping {  // 싱글톤 패턴이 유지됨
							   // 객체의 무분별한 생성을 막기 위해, 사용될 때마다 새로운 객체가 생성되는 것이 아님
	// input: 어떤 요청에 대해 == String
	// output: 무슨 컨트롤러를 반환해야하는지(객체를 제공해야하는지) == Controller 
	private Map<String,Controller> mappings; // 의존관계 -> DI => 생성자 주입
	
	public HandlerMapping() { // 생성자로 new 하는 과정
		mappings=new HashMap<String,Controller>();
		mappings.put("/login.do",new LoginController());
		mappings.put("/main.do",new MainController());
	}
	
	public Controller getController(String command) {
		return mappings.get(command);
	}
}

 

 

디스패쳐 서블릿

package com.kim.biz.controller;

import java.io.IOException;

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



/**
 * Servlet implementation class DispatcherServlet
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
	private HandlerMapping handlerMapping;
	private ViewResolver viewResolver;
	public void init() throws ServletException {
		handlerMapping=new HandlerMapping();
		viewResolver = new ViewResolver();
		viewResolver.setPrefix("./");
		viewResolver.setSuffix(".jsp");
	}
	
    public DispatcherServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request,response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		doAction(request,response);
	}
	
	private void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String command=request.getRequestURI();
		command=command.substring(command.lastIndexOf("/"));
		System.out.println(command);
		
		Controller ctrl=handlerMapping.getController(command);
		String viewName=ctrl.handleRequest(request, response);
		
		String view=null;
		if(viewName.contains(".do")) {
			view=viewName;
		}
		else {
			view=viewResolver.getView(viewName);
		}
		response.sendRedirect(view);
		
	}

}