스프링 MVC
- 자바 웹 애플리케이션을 개발할 때 가장 많이 쓰이는 웹 프레임워크이다.
- MVC 는 Model-View-Controller 의 약자로, 기본 시스템 모듈을 MVC 로 나누어 구현되어있다.
- Model : 뷰를 생성하는 데 사용할 데이터
- View : 모델을 사용해 화면을 렌더링
- Controller : 흐름을 제어함. 브라우저 요청을 받아 모델을 만들고 뷰로 리다이렉션한다.
- Web 서버에 특화되어 만들어진 모듈로서 다음과 같은 주요 기능을 포함한다.
- 다양한 범위의 파라미터와 반환값을 가지는 유연한 컨트롤러 메서드 정의.
- 도메인 POJO 객체의 폼 백엔드 객체로의 재사용.
- 모델은 키-값 쌍과 함께 해시 맵을 사용. 또한 여러 뷰 기술과의 통합을 허용함.
- 바인딩이 유연하다. 타입 불일치 발생시 런타임 에러 이전에 밸리데이션 에러(400)로 해결 가능.
- 단위 테스트 컨트롤러에 MockMvc 프레임워크 포함.
- 스프링 부트를 사용하면 자바 애플리케이션이 내장 톰캣을 만들고 그 안에 DispatcherServlet을 등록한다.
스프링 MVC 아키텍처
- 모델 2 프론트 컨트롤러 아키텍처를 기반으로 설계되었다.
- 프론트 컨트롤러(DispatcherServlet)은 어떤 컨트롤러가 요청을 수행할지 결정하고, 랜더링할 뷰를 결정하는 결할을 한다.
- 스프링에 DispatcherServelt 의존성을 추가하고 스프링 컨텍스트를 시작하면,
요청 매핑
, 예외 처리
, 데이터 바인딩 및 벨리데이션
, @RequestBody를 통한 JSON변환
등 웹 MVC에 필요한 여러 기능들이 초기화 된다.
DispatcherServlet 동작 방식
스프링 부트와 구조가 다르다.
servlet context안에 spring이 들어가는 구조인 반면에 spring boot 경우에는 spring java application안에 tomcat이 들어가는 구조이다.
- 브라우저는 특정 URL에 요청을 보내고, DispatcherServlet은 가장 먼저 요청을 받아들인다.
- DispatcherServlet은 URI를 보고, 핸들러 매핑과 통신하여 요청을 전달할 컨트롤러를 찾는다.
- 핸들러 어댑터는 반환된 컨트롤러에서 어떤 메서드를 호출할지 결정한다.
- 핸들러 어댑터는 결정된 핸들러 메서드를 호출한다.
- 핸들러 메서드는 모델과 뷰를 반환한다.
- DispatcherServlet에서 반환된 뷰 이름을 가지고 뷰리졸버를 호출해 물리적 뷰를 찾는다.
- 뷰리졸버는 해당하는 뷰를 찾아 뷰로 반환한다.
- DispatcherServlet은 응답을 브라우저로 보낸다.
핸들러 매핑 vs 핸들러 어댑터
- 핸들러 매핑 : 요청을 처리할 핸들러를 찾는 인터페이스
RequestMappingHandlerMapping : 컨트롤러에 작성한 url을 기반으로 핸들러를 찾음
BeanNameUrlHandlerMapping : method 자체가 핸들러가 됨
- 핸들러 어댑터 : 핸들러 매핑이 찾아낸 핸들러를 처리하는 인터페이스
SimpleControllerAdapter
RequestMappingHandlerAdapter (가장 많이 쓰임)
View Resolver (뷰리졸버)
- 뷰리졸버의 역할은 핸들러에 명시된 논리적 뷰의 이름을 가지고 물리적 뷰를 찾는 인터페이스이다.
핸들러 매핑과 인터셉터
RequestMapping
- URI를 특정 컨트롤러 또는 컨트롤러 메서드에 매핑하는데 사용한다.
- 클래스 또는 메서드 레벨에서 사용할 수 있다.
- 선택옵션의 요청 메서드로 GET, POST, PUT, DELETE 등을 사용할 수 있다. 아무것도 적지않으면 GET으로 인식한다.
@GetMapping
, @PostMapping
도 가능하다.
HandlerInterceptor
- 인터셉터는 말 그대로 핸들러에 대한 요청을 가로채는 용도로 사용된다.
- 정확한 URI를 인터셉트할 수 있도록 따로 지정할 수 있다.
preHandle(HttpServletRequest request, HttpServletResponse response)
, postHandle(HttpServletRequest request, HttpServletResponse response)
, afterCompletion(HttpServletRequest request, HttpServletResponse response
메서드를 구현함으로써 요청 전후에 일부 처리를 지정할 수 있다.
- 여러 개의 인터셉터를 체이닝할 수 있다.
- 필터와 인터셉터에 대한 자세한 특징과 차이는 이 글을 참고하자.
핸들러 메소드
Http 메시지 컨버터
- 요청 본문에서 메시지를 읽어들이거나(@RequestBody), 응답 본문에 메시지를 작성할 때(@ResponseBody) 사용한다.
지원메소드 아규먼트와 리턴 타입
- WebRequest, NativeWebRequest, ServletRequest, HttpServletRequest : 요청 또는 응답 자체에 접근 가능한 API, 거의 사용할 일 x
- InputStream, Reader, OutputStream, Writer : 요청 본문을 읽어오거나, 응답 본문을 쓸 때 사용할 수 있는 API
- PushBuilder : 스프링5, Http/2 리소스 푸쉬에 사용
- HttpMethod : GET, POST 등에 대한 정보
- Locale, TimeZone, ZoneId : LocaleResolver가 분석한 요청의 Locale정보
- @PathVariable : URI 템플릿 변수를 읽을 때 사용
- @MatrixVariable : URI 경로 중에 키/값 쌍을 읽어 올 때 사용
- @RequestParam : 서블릿 요청 매개변수 값을 선언한 메소드 아규먼트 타입으로 변환해준다.
단순한 타입의 경우 이 애노테이션을 생략가능하다.
- @RequestHeader : 요청 헤더 값을 선언한 메소드 아규먼트 타입으로 변환해준다.
- @RequestBody : 리턴 값을 HttpMessageConverter를 사용해 응답 본문으로 사용한다.
(@Valid를 사용해서 값을 검증할 수 있다.)
- HttpEntity, ResponseEntity : 응답 본문 뿐 아니라헤더 정보까지, 전체 응답을 만들 때 사용한다.
- String : ViewResolver를 사용해서 뷰를 찾을 때 사용할 뷰 이름
- View : 암묵적인 모델 정보를 렌더링할 뷰 인스턴스
- Map, Model : 암묵적으로 판단한 뷰 렌더링 시 사용할 모델 정보
- @ModelAttribute : 암묵적으로 판단한 뷰 렌더링할 때 사용할 모델 정보 추가, 보통 생략 가능.
예외처리
- 컨트롤러 레벨에서의 예외처리는 @ControllerAdvice를 활용하면 일관된 예외처리가 가능하다.
- 컨트롤러 어드바이스는 기본적으로 모든 요청 매핑에서 공통된 기능을 제공한다. (예외처리 뿐만 아니라 바인딩 설정 모델 객체 등 활용 가능)
@ControllerAdvice
public class ExcetpionController {
@ExceptionHandler(value = RuntimeException.class)
public ResponseEntity<ExceptionResponse> handleRuntimeException(
HttpServletRequest request,
Exception ex
) {
return ResponseEntity.badrequest(ExceptionResponse.from(ex)).build();
}
}
결론
Spring MVC는 웹 애플리케이션을 개발할 때, HTTP 요청과 응답을 쉽게 제어할 수 있고, 요청에 알맞는 로직을 수행하도록 분리하기 위한 핸들러 매핑, 뷰리졸버의 기능을 제공한다.
요청 값의 벨리데이션과 인증, 인가에 대한 검사는 컨트롤러 어드바이스나 인터셉터를 통해서 그 오류를 일찍 발견할 수 있으므로 Spring MVC 에서 제공하는 API나 기능들을 잘 알아놓으면 효율적인(또는 REST 한) 웹 애플리케이션을 작성할 수 있을 것 같다.