들어가며

  • 이번 포스트에서부터는 스프링 MVC의 기본적인 구조를 알아본다.
  • 이번 주제는 Dispatcher Servlet이라고 하는 스프링 MVC의 구조를 유도하며 왜 이렇게 되었는가에 대해 알아본다. 추가적으로 이 프론트 컨트롤러가 어떻게 발전하는지를 앞으로의 포스트를 보면서 알아보도록 한다.

 

 

 

프론트 컨트롤러의 도입

  • 기존 스프링을 사용하지 않고, 서블릿을 통하여 각 컨트롤러들을 사용한 시기에는 각 컨트롤러별로 공통적으로 처리해야 하는 로직이 존재하면 모든 서블릿 컨트롤러들에 대해 해당 로직을 넣어주어야 했다.

  • 만약 각 컨트롤러들이 호출될 때 마다 "Controller Called"라는 로그를 찍어야 한다고 생각해보자. 그렇다면 모든 컨트롤러에는 다음과 같은 코드가 붙어야 할 것이다.
@WebServlet(name = "myControllerA", urlPattern = "/myControllerA")
public class MyControllerA extends HttpServlet{
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response){
        System.out.println("Controller Called\n"); // create log in console
        
        ...
    }
}
  • 주목해야 하는것은 저 System.out.println을 통해 로그를 찍는 것이다.
  • 모든 컨트롤러에 저 로그를 출력해야하기 때문에 저 코드는 모든 컨트롤러에 포함되어야 한다. 문제는 이런 간단한 코드가 아니라 방대한 전처리가 필요해질 경우에 발생한다.
  • 복사 붙여넣기를 하면 되지만 만약 전처리 코드가 바뀌어야 한다면? 당연히 모든 부분에 대해 수정된 전처리 코드를 수정해야 하고 이는 곧 휴먼에러에 의한 문제와 귀찮음, 비효율적 생산성이 발생하게 된다.
  • 그리고 이를 해결하기 위해 프론트 컨트롤러(Front Controller)라는 구조를 도입하게 된다.

  • 이전까지는 각 컨트롤러에 공통 로직을 모두 달아야 했다면 이제는 하나의 컨트롤러에 모든 호출을 담당하게 한다. 그리고 공통 로직을 처리한 후에 이 호출을 담당할 컨트롤러로 호출을 이관해주면 된다.
  • 이때 모든 호출을 담당하는 하나의 컨트롤러를 프론트 컨트롤러(Front Controller)라고 한다.

 

 

 

프론트 컨트롤러 패턴의 특징

  • 프론트 컨트롤러가 모든 요청을 받도록 설계하기 때문에 말 그대로 입구가 단 하나가 된다. 즉 클라이언트가 웹에 요청을 보낸다면 가장 먼저 해당 요청을 받는곳은 프론트 컨트롤러가 된다.
  • 이 프론트 컨트롤러는 서블릿으로 설계되어야 한다. 왜냐면 클라이언트의 요청을 직접적으로 받기 때문이다. 프론트 컨트롤러는 해당 요청을 받은 뒤 URI를 보고, 해당 URI를 처리할 수 있는 컨트롤러를 찾아 그곳으로 요청을 위임한다.
  • 그런데 생각해보면 위 그림의 각 컨트롤러들은 사실 request, response등의 HttpServletRequest객체나 HttpServletResponse 객체가 필요하지 않다. 그냥 저 요청에 들어온 데이터값들이 필요한 것이다. 그렇다면 프론트 컨트롤러에서 데이터를 추출하는 로직을 추가한다면, 저 컨트롤러들은 그냥 데이터만 파라미터로 받으면 된다는 것이다.
  • 즉 다음과 같이 설계하면 된다는 것이다.
@WebServlet(name = "frontControllerServlet", urlPattern = "/front-controller/*")
public class FrontControllerServlet extends HttpServlet{
    ...
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response){
        String userName = request.getParameter("username");
        int userAge = Integer.parseInt(request.getParameter("age"));
        
        // call controller with data
        Controller.doSomething(userName, userAge);
    }
}
public class ControllerA{
    ...

    public void doSomething(String userName, int age){
        // do something
    }
}
  • Java 코드를 사용하였지만 대략적인 형태만 잡아둔 것이다. 구체적인 구현은 여러 단계를 거처 천천히 알아볼 것이다.
  • 중요한 것은 컨트롤러가 기존에 HttpServlet을 상속받아 설계하는 것에서 탈피하여 데이터만 받아 요청을 처리할 수 있다는 것이다.

 

 

 

마무리하며

  • 다음 포스트부터는 이 프론트 컨트롤러를 도입함으로서 어떻게 코드가 변경될 것인지에 대해 알아볼 것 이다.
  • 코드들은 github에 업로딩될 것이며 다음과 같은 환경에서 개발된다.
    • SpringBoot 2.7.4
      • Spring starter io로 들어가 빌드를 WAR로 꼭 설정해야 한다. 해당 코드는 JSP를 뷰로 사용하기 때문에 WAR로 하지 않을 시 정상적인 동작을 보장하기 어렵다.
      • Dependency는 Spring Web과 Lombok으로 해주면 된다.
      • SpringBoot 3.0.0 이상에서 할 경우 아래의 깃허브를 참고하지 않고 수행해야 한다. 많은 라이브러리가 바뀌었기 때문에 동일하게 하다가 오류가 발생할 수 있기 때문이다.
    • Lombok 사용
    • IntelliJ - Ultimate version 사용
    • Java 11
    • JSP 사용
  • 처음부터 세팅하기가 귀찮다면 https://github.com/ForteEscape/MVC-Study를 가져가도 된다.
 

GitHub - ForteEscape/MVC-Study

Contribute to ForteEscape/MVC-Study development by creating an account on GitHub.

github.com

  • Code -> Download ZIP을 한 뒤 IntelliJ나 Eclipse를 통해 사용하면 된다. 필자는 Eclipse 환경에서 테스트 해보지 않았기 때문에 IntelliJ에서 수행하는 것을 추천한다.
복사했습니다!