Spring Study

[스프링 MVC] #4

yeon.___.jin 2025. 5. 3. 01:08
반응형

김영한님의 스프링 MVC 1편 강의를 듣고 복습 겸 정리하는 포스팅입니다!!

아직 공부하는 중인 학생이라 부족한 부분이 있을 수 있습니다. 

혹시라도 틀린 부분이 있다면 언제든지 댓글로 남겨주세요. 감사합니다 🙇‍♂️


섹션 5. 프론트 컨트롤러 도입 - V1

프론트 컨트롤러 도입 전

  • 공통 로직을 각각의 컨트롤러에 직접 삽입해야 한다.
  • 공통으로 처리해야 하는 작업이 많아질수록 중복 코드가 증가한다.
  • 유지보수가 어렵다.

 

프론트 컨트롤러 도입 후 

 

프론트 컨트롤러 (FrontController)

  • 프론트 컨트롤러도 하나의 서블릿이다.  이 서블릿이 클라이언트의 모든 요청을 받아 처리한다.
  • 요청에 맞는 컨트롤러를 찾아 호출하는 역할이다. 
  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 된다.

프론트 컨트롤러 도입 효과

  • 공통 로직을 중앙에서 관리해 코드 중복이 사라진다. 
  • 나머지 컨트롤러는 비즈니스의 흐름만 신경쓰면 된다. 

스프링 웹 MVC와 프론트 컨트롤러 

  • 스프링 웹 MVC의 핵심도 "FrontController"이다. 
  • 스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있다. 

프론트 컨트롤러 도입 - v1

기존의 코드를 최대한 유지하면서, 프론트 컨트롤러를 도입

 

  1. 클라이언트가 HTTP 요청을 전송한다.
  2. FrontController(Servlet)가 요청을 받는다.
  3. 요청 URL 매핑 정보에서 해당 컨트롤러를 조회한다.
  4. 프론트 컨트롤러가 조회된 컨트롤러를 호출한다.
  5. 컨트롤러는 자신의 로직을 수행 후, JSP 경로를 반환한다.
  6. 프론트 컨트롤러는 jsp에 forword해서 HTML로 응답이 가게된다.

ControllerV1

package hello.servlet.web.frontcontroller.v1;

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

import java.io.IOException;

public interface ControllerV1 {
    void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다. 각 컨트롤러들은 이 인터페이스를 구현한다. 

일단 지금 단계에서는 기존 로직을 최대한 유지하는 것이 핵심이다. 

 

MemberFormControllerV1 - 회원 등록 폼 컨트롤러 

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberFormControllerV1 implements ControllerV1 {

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath = "/WEB-INF/views/new-form.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

 

MemberSaveControllerV1 - 회원 정보 저장 컨트롤러

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberSaveControllerV1 implements ControllerV1 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        System.out.println("member = " + member);
        memberRepository.save(member);

        //model에 데이터 보관
        request.setAttribute("member", member);

        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response);
    }
}

 

MemberListControllerV1 - 회원 목록 조회 컨트롤러

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

public class MemberListControllerV1 implements ControllerV1 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();

        request.setAttribute("members", members);

        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response);
    }
}

 

FrontControllerServletV1

package hello.servlet.web.frontcontroller.v1;

import hello.servlet.web.frontcontroller.v1.controller.MemberFormControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberListControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberSaveControllerV1;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")  // v1 하위에 있는 것들은 모두 이 컨트롤러와 맵핑이 된다.
public class FrontControllerServletV1 extends HttpServlet {

    private Map<String, ControllerV1> controllerMap = new HashMap<>();

    public FrontControllerServletV1() {
        controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
        controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
        controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV1.service");

        String requestURI = request.getRequestURI();

        ControllerV1 controller = controllerMap.get(requestURI);
        if(controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        controller.process(request, response);
    }
}

 

urlPatterns

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")

urlPatterns = "/front-controller/v1/*"로 설정하면,  /front-controller/v1를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다.

 

controllerMap

private Map<String, ControllerV1> controllerMap = new HashMap<>();
  • key : 요청 URL
  • value : 실행할 컨트롤러 인스턴스 

매핑정보

public FrontControllerServletV1() {
    controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
    controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
    controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
}
  • /front-controller/v1/members/new-form → MemberFormControllerV1
  • /front-controller/v1/members/save → MemberSaveControllerV1
  • /front-controller/v1/members → MemberListControllerV1

 

실행한 후 회원가입 창에 들어간 모습이다. 

 

1. 요청 URI 추출

String requestURI = request.getRequestURI();

 Service의 requestURI를 통해 사진에 있는 상단의 url 주소 "/front-controller/v1/members/new-form"를 그대로 받을 수 있다.

 

2. 매핑 정보에서 컨트롤러 조회

ControllerV1 controller = controllerMap.get(requestURI);

controller.Map.get(requestURI)를 통해 상단의 주소를 찾을 수 있다. 

 "/front-controller/v1/members/new-form" -> MemberFormControllerV1() 반환

 

3. 컨트롤러가 없으면 404 응답

if(controller == null){
    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    return;
}

페이지가 없거나 못찾는 경우에는 404 에러를 띄우게 된다. 

 

4. 컨트롤러 로직 실행

controller.process(request, response);

조회에 성공했다면 ControllerV1의 controller.process()를 호출하게 된다. 

반응형

'Spring Study' 카테고리의 다른 글

[스프링 MVC] #6  (1) 2025.05.04
[스프링 MVC] #5  (1) 2025.05.03
[스프링 MVC] #3  (2) 2025.04.27
[스프링 MVC] #2  (0) 2025.04.13
[스프링 MVC] #1  (0) 2025.04.10