[Spring] FrontController (0801)

왕감자·2024년 8월 1일

KB IT's Your Life

목록 보기
111/177

FrontController

✅ Spring이 채택하는 기본 구조


1) FrontController

  • 모든 요청을 앞에서 다 받아줌
    • 그 전에는 요청별로 서블릿이 받았음 ➞ 서블릿多
      ⇨ 서블릿(FrontController)을 하나만 운영~!
  • 흐름 제어 : url 분석 ➔ Controller (forward / redirect)
  • ~.jsp 다이렉트로 접근 X : 모든 요청은 FrontController가 처리해야 함
    • jsp파일을 WEB-INF 폴더에 배치해야함

🔸 사용자 요청 식별 방법

  • URL : http://서버IP:포트/context/경로(식별값)
    • getRequestURI()context/경로(식별값)
    • getContextPath()context
      getRequestURI().substring(getContextPath().length) = 경로(식별값)
private String getCommandName(HttpServletRequest request) {
	String requestURI = request.getRequestURI();
    String contextPath = request.getContextPath();
    return requestURI.substring(contextPath.length());

🔸 요청별 처리 코드 찾기

✅ Command 패턴 적용

  • Map<요청 식별값(String), Command 인터페이스 구현체>
    • Map에 없으면 404 에러
    • 요청별로 Map을 가짐

//FrontControllerServlet 틀

@WebServlet("/") // "/": 모든 요청을 다 받겠다
public class FrontControllerServlet extends HttpServlet {
    //요청별로 Map을 가짐
    Map<String, Command> getMap;
    Map<String, Command> postMap;

    public void init() {
        getMap = new HashMap<>();
        postMap = new HashMap<>();
    }
    
    //식별자 추출
    private String getCommandName(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        return requestURI.substring(contextPath.length());
    }

    private Command getCommand(HttpServletRequest request) {
        String commandName = getCommandName(request);

        Command command;
        if(request.getMethod().equalsIgnoreCase("GET")) { //대소문자 구분없이 비교해라
            command = getMap.get(commandName);
        } else {
            command = postMap.get(commandName);
        } //Map에 없으면 404에러

        return command;
    }
    
    public void execute(Command command, HttpServletRequest request, HttpServletResponse response) 
                throws  IOException, ServletException {
        String viewName = command.execute(request, response)
    }
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Command command = getCommand(request);
        if(command != null) {
            execute(command, request, response);
        } else { //404에러 처리
            
        }

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
    
}

2) 컨트롤러

실제 요청을 처리하고 흐름을 제어
✅ 각 메서드는 Command 인터페이스와 일치하게 작성 (매개변수/리턴타입)
✅ 상속X, POJO
✅ 메서드 참조로 연결할 것

🔸 GET 요청 (Forward)

  • 리턴값 : 뷰의 이름 (ex. todo/create)
    • [WEB-INF] - [views] : jsp 배치
    • "/WEB-INF/views/" + 뷰 이름 + ".jsp"
      • "/WEB-INF/views/" & ".jsp" ➡ 공통 부분 - 뷰 이름만 알면 됨~!
      • FrontController에서 합쳐줌 ⇨ forward 처리

🔸 POST 요청 (Redirect)

  • 리턴값 : 리다이렉트 url (ex. redirect:/todo/list)
    • redirect:redirect url 형식
      ⇨ FrontController에서 redirect 처리

🔸 404에러 처리

  • URL 맵핑 맵에 없는 경우
    • /WEB-INF/views/404.jsp로 forward
// localhost:8080/
@WebServlet("/") // "/": 모든 요청을 다 받겠다
public class FrontControllerServlet extends HttpServlet {
    //요청별로 Map을 가짐
    Map<String, Command> getMap;
    Map<String, Command> postMap;

    //접두어,접미어
    String prefix = "/WEB-INF/views/";
    String suffix = ".jsp";

    //HomeController
    HomeController homeController = new HomeController();

    public void init() {
        getMap = new HashMap<>();
        postMap = new HashMap<>();

        getMap.put("/", homeController::getIndex);
    }

    //식별자 추출
    private String getCommandName(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        return requestURI.substring(contextPath.length());
    }

    private Command getCommand(HttpServletRequest request) {
        String commandName = getCommandName(request);

        Command command;
        if(request.getMethod().equalsIgnoreCase("GET")) { //대소문자 구분없이 비교해라
            command = getMap.get(commandName);
        } else {
            command = postMap.get(commandName);
        } //Map에 없으면 404에러

        return command;
    }

    public void execute(Command command, HttpServletRequest request, HttpServletResponse response)
                throws  IOException, ServletException {
        String viewName = command.execute(request, response);

        if(viewName.startsWith("redirect:")) {
            response.sendRedirect(viewName.substring("redirect:".length()));
        } else {
            String view = prefix + viewName + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Command command = getCommand(request);
        if(command != null) {
            execute(command, request, response);
        } else { //404에러 처리
            String view = prefix + "404" + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }
    }

}


Todo 만들기~ 얍 ☜(゚ヮ゚☜)

TodoController

public class TodoController {
    public String getList(HttpServletRequest request, HttpServletResponse response) throws IOException {
        List<String> list = Arrays.asList("Todo 1", "Todo 2", "Todo 3");
        request.setAttribute("todoList", list);
        System.out.println("GET /todo/list");
        return "todo/list";
    }

    public String getView(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("GET /todo/view");
        return "todo/view";
    }

    public String getCreate(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("GET /todo/create");
        return "todo/create";
    }

    public String postCreate(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("POST /todo/create");
        return "redirect:/todo/list";
    }

    public String getUpdate(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("GET /todo/update");
        return "todo/update";
    }

    public String postUpdate(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("POST /todo/update");
        return "redirect:/todo/list";
    }

    public String postDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("POST /todo/delete");
        return "redirect:/todo/list";
    }
}

FrontControllerServlet

@WebServlet("/") // "/": 모든 요청을 다 받겠다
public class FrontControllerServlet extends HttpServlet {
    //요청별로 Map을 가짐
    Map<String, Command> getMap;
    Map<String, Command> postMap;

    //접두어,접미어
    String prefix = "/WEB-INF/views/";
    String suffix = ".jsp";

    //HomeController
    HomeController homeController = new HomeController();
    //TodoController
    TodoController todoController = new TodoController();

    public void init() {
        getMap = new HashMap<>();
        postMap = new HashMap<>();

        getMap.put("/", homeController::getIndex);

        getMap.put("/todo/list", todoController::getList);
        getMap.put("/todo/view", todoController::getView);
        getMap.put("/todo/create", todoController::getCreate);
        getMap.put("/todo/update", todoController::getUpdate);

        postMap.put("/todo/create", todoController::postCreate);
        postMap.put("/todo/update", todoController::postUpdate);
        postMap.put("/todo/delete", todoController::postDelete);
    }

    //식별자 추출
    private String getCommandName(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        return requestURI.substring(contextPath.length());
    }

    private Command getCommand(HttpServletRequest request) {
        String commandName = getCommandName(request);

        Command command;
        if(request.getMethod().equalsIgnoreCase("GET")) { //대소문자 구분없이 비교해라
            command = getMap.get(commandName);
        } else {
            command = postMap.get(commandName);
        } //Map에 없으면 404에러

        return command;
    }

    public void execute(Command command, HttpServletRequest request, HttpServletResponse response)
                throws  IOException, ServletException {
        String viewName = command.execute(request, response);

        if(viewName.startsWith("redirect:")) {
            response.sendRedirect(viewName.substring("redirect:".length()));
        } else {
            String view = prefix + viewName + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Command command = getCommand(request);
        if(command != null) {
            execute(command, request, response);
        } else { //404에러 처리
            String view = prefix + "404" + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

🤔 또 다른 애플리케이션 제작 시 FrontController에서 수정되야 하는 부분은 어디인가?

  • 수정되지 않는 부분(공통)은 부모 클래스로 올리자~! (DispatcherServlet)
    • @WebServlet 어노테이션 X

DispatcherServlet (부모 클래스)

//@WebServlet 어노테이션 X - 부모라서~!
//매번 이 클래스를 상속 받으면 됨니도~
public class DispatcherServlet extends HttpServlet {
    //요청별로 Map을 가짐
    Map<String, Command> getMap;
    Map<String, Command> postMap;

    //접두어,접미어
    String prefix = "/WEB-INF/views/";
    String suffix = ".jsp";

    public void init() {
        getMap = new HashMap<>();
        postMap = new HashMap<>();
        createMap(getMap, postMap);
    }

    protected void createMap(Map<String, Command> getMap, Map<String, Command> postMap) {

    }

    //식별자 추출
    private String getCommandName(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        return requestURI.substring(contextPath.length());
    }

    private Command getCommand(HttpServletRequest request) {
        String commandName = getCommandName(request);

        Command command;
        if(request.getMethod().equalsIgnoreCase("GET")) { //대소문자 구분없이 비교해라
            command = getMap.get(commandName);
        } else {
            command = postMap.get(commandName);
        } //Map에 없으면 404에러

        return command;
    }

    public void execute(Command command, HttpServletRequest request, HttpServletResponse response)
            throws  IOException, ServletException {
        String viewName = command.execute(request, response);

        if(viewName.startsWith("redirect:")) {
            response.sendRedirect(viewName.substring("redirect:".length()));
        } else {
            String view = prefix + viewName + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Command command = getCommand(request);
        if(command != null) {
            execute(command, request, response);
        } else { //404에러 처리
            String view = prefix + "404" + suffix;
            request.getRequestDispatcher(view).forward(request, response);
        }

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

FrontControllerServlet (DispatcherServlet 상속 받음)

@WebServlet("/")
public class FrontControllerServlet extends DispatcherServlet {
    //HomeController
    HomeController homeController = new HomeController();
    //TodoController
    TodoController todoController = new TodoController();

    @Override
    protected void createMap(Map<String, Command> getMap, Map<String, Command> postMap) {
        getMap.put("/", homeController::getIndex);

        getMap.put("/todo/list", todoController::getList);
        getMap.put("/todo/view", todoController::getView);
        getMap.put("/todo/create", todoController::getCreate);
        getMap.put("/todo/update", todoController::getUpdate);

        postMap.put("/todo/create", todoController::postCreate);
        postMap.put("/todo/update", todoController::postUpdate);
        postMap.put("/todo/delete", todoController::postDelete);
    }
}

0개의 댓글