스프링 MVC model-view-controller (모델-뷰-컨트롤러)는 스프링 프레임워크에서 가장 중요한 모듈입니다. 강력한 스프링 IoC 컨테이너를 기반으로 만들어졌으며 간단한 구성으로 스프링 IoC 컨테이너의 기능을 폭넓게 사용할 수 있습니다.
MVC는 일반적인 UI 디자인 패턴입니다. 이 패턴은 애플리케이션에서 역할을 기준으로 모델, 뷰, 컨트롤러를 나눔으로써 UI와 비즈니스 로직을 분리합니다. 모델은 뷰가 화면에 보여 줄 애플리케이션 데이터를 캡슐화하고, 뷰는 비즈니스 로직 없이 오로지 데이터를 보여 주며, 컨트롤러는 사용자에게서 요청을 받고 백엔드 서비스를 호출해 비즈니스를 처리하는 역할을 합니다.
백엔드 서비스가 비즈니스 처리를 끝내고 화면에 보여 줄 데이터를 반환하면 컨트롤러는 이 데이터를 모아 뷰에 출력할 모델을 만듭니다. MVC 패턴의 핵심은 UI와 비즈니스 로직을 분리해서 서로 영향을 주지 않고 독립적으로 변경할 수 있게 한다는 점입니다.
스프링 MVC 애플리케이션에서 모델은 보통 서비스 계층에서 처리되고 퍼시스턴스 계층에서 저장되는 객체입니다. 뷰는 JSP Java Server Pages , 타임리프 Thymeleaf, 프리마커 FreeMarker 등으로 작성한 템플릿이지만 엑셀이나 PDF 파일, RESTful 웹 서비스로도 정의할 수 있습니다.
우선 스프링 MVC의 핵심 동작 과정을 이해하기 위해, 가장 기본적인 웹 애플리케이션 구성부터 차근차근 따라가 보겠습니다.
프런트 컨트롤러 front controller 는 스프링 MVC에서 가장 중요한 컴포넌트입니다. 아주 간단한 스프링 MVC 애플리케이션이라면 웹 배포 서술자에 프런트 컨트롤러의 서블릿만 구성하면 됩니다. 보통 디스패처 서블릿(DispatcherServlet)이라는 스프링 MVC 컨트롤러가 스프링 MVC 프레임워크의 프런트 컨트롤러로 작동하며, 모든 웹 요청이 디스패처 서블릿을 거쳐 처리됩니다.
스프링 MVC 애플리케이션에 들어온 웹 요청은 제일 먼저 컨트롤러가 받으며, 스프링 웹 애플리케이션 컨텍스트에 구성된 다양한 컴포넌트나 컨트롤러에 적용된 애너테이션을 구성해 요청을 처리하는 데 필요한 작업을 수행합니다.
스프링에서 컨트롤러는 @Controller나 @RestController 애너테이션으로 정의합니다.
@Controller가 HTML 뷰를 반환하는 전통적인 웹 애플리케이션에 주로 사용된다면, @RestController는 @Controller와 @ResponseBody를 합쳐 객체를 JSON 또는 XML 형태로 반환하는 데 사용됩니다. 이는 주로 RESTful API를 개발할 때 유용합니다.
@Controller 애너테이션이 적용된 클래스(컨트롤러 클래스)가 요청을 받으면 요청을 처리하는 적절한 핸들러 메서드를 찾습니다. 컨트롤러 클래스는 각 요청을 하나 이상의 핸들러 메서드에 매핑하며 컨트롤러 클래스의 메서드에 @RequestMapping 애너테이션을 적용해 핸들러 메서드를 지정할 수 있습니다. @RequestMapping 외에도 HTTP 메서드에 따라 @GetMapping, @PostMapping 등 더 구체적인 애너테이션을 사용할 수 있습니다.
핸들러 메서드의 시그니처는 일반적인 자바 클래스와 마찬가지로 특별한 제약이 없습니다. 메서드 이름을 임의로 지정하고, 메서드 인수를 다양하게 정의하며, 애플리케이션 로직에 따라 다양한 타입(예: String, void)의 값을 반환할 수 있습니다.
앞으로 @RequestMapping 애너테이션이 적용된 핸들러 메서드에서 사용하는 다양한 메서드 인수를 만날 것입니다. 다음은 미리 알아 두면 좋을 몇 가지 유용한 인수 타입입니다.
컨트롤러는 먼저 적절한 핸들러 메서드를 선택하고 해당 핸들러 메서드에 요청을 전달해 로직을 실행합니다. 보통 컨트롤러는 백엔드 서비스를 호출해 요청을 처리하며 핸들러 메서드는 다양한 입력 인수에 정보를 추가하거나 삭제해 스프링 MVC 흐름을 이어갈 수 있도록 구성합니다.
핸들러 메서드는 요청을 처리한 후 반환값으로 지정된 뷰에 제어권을 위임합니다. 핸들러 메서드의 반환값으로는 실제 뷰 구현체보다 파일 확장자를 명시하지 않은 논리 뷰 이름을 지정하는 편이 더 유연해서 좋습니다.
핸들러 메서드의 반환값은 논리 뷰 이름을 나타내는 String이거나 void입니다. void일 때는 논리 뷰 이름이 핸들러 메서드나 컨트롤러 이름 기준으로 결정됩니다.
뷰에서는 얼마든지 핸들러 메서드의 인수에 접근할 수 있으므로 컨트롤러에서 뷰로 데이터를 전달하는 데 문제가 없습니다. 예를 들어 핸들러 메서드가 Map과 SessionStatus 타입 객체를 인수로 받고 핸들러 메서드에서 값을 수정하더라도 반환하는 뷰에서 수정된 동일한 객체에 접근할 수 있습니다.
컨트롤러 클래스는 뷰 리졸버 view resolver 를 이용해 전달받은 논리 뷰 이름을 실제 뷰 구현체(예: user.jsp, todos.html, report.pdf )로 해석합니다. 뷰 리졸버는 인터페이스의 구현체로써 웹 애플리케이션 컨텍스트에 빈으로 구성하며, 논리 뷰 이름에 해당하는 실제 구현체(예: HTML, JSP, PDF)를 반환합니다.
컨트롤러 클래스가 논리 뷰 이름으로 실제 뷰 구현체를 해석하면, 해당 뷰 구현체는 로직에 따라 핸들러 메서드가 전달한 객체를 렌더링 합니다. 뷰의 역할은 어디까지나 핸들러 메서드의 로직에 추가된 객체를 사용 자에게 정확하게 보여 주는 것입니다.
요약하자면, 스프링 MVC의 요청 처리 과정은 요청 → DispatcherServlet → 컨트롤러 → 뷰 리졸버 → 뷰 순서로 정리할 수 있습니다.
위 콘텐츠는 『스프링 6 레시피(5판)』의 내용을 기반으로 작성하였습니다.
댓글