들어가며
- 이번 포스트에서부터는 스프링 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 사용
- SpringBoot 2.7.4
- 처음부터 세팅하기가 귀찮다면 https://github.com/ForteEscape/MVC-Study를 가져가도 된다.
- Code -> Download ZIP을 한 뒤 IntelliJ나 Eclipse를 통해 사용하면 된다. 필자는 Eclipse 환경에서 테스트 해보지 않았기 때문에 IntelliJ에서 수행하는 것을 추천한다.
'Spring & JPA > SpringMVC' 카테고리의 다른 글
Spring MVC - MVC 구조 - 3. 프론트 컨트롤러 구현(2) (0) | 2023.05.30 |
---|---|
Spring MVC - MVC 구조 - 2. 프론트 컨트롤러 구현(1) (0) | 2023.05.30 |
Spring MVC - 기반 지식 - 4. WAS와 리소스 반환, CSR, SSR (0) | 2023.05.26 |
Spring MVC - 기반 지식 - 3. WAS와 멀티 쓰레드 (0) | 2023.05.26 |
Spring MVC - 기반 지식 - 2. WAS와 서블릿 (0) | 2023.05.26 |