글에 앞서서
- 본문은 Spring Framework Version 5의 습득을 위한 글이다.
- 이 글을 상업적 목적으로 쓰지 않았다
Authors
Rod Johnson , Juergen Hoeller , Keith Donald , Colin Sampaleanu , Rob Harrop , Thomas Risberg , Alef Arendsen , Darren Davison , Dmitriy Kopylenko , Mark Pollack , Thierry Templier , Erwin Vervaet , Portia Tung , Ben Hale , Adrian Colyer , John Lewis , Costin Leau , Mark Fisher , Sam Brannen , Ramnivas Laddad , Arjen Poutsma , Chris Beams , Tareq Abedrabbo , Andy Clement , Dave Syer , Oliver Gierke , Rossen Stoyanchev , Phillip Webb , Rob Winch , Brian Clozel , Stephane Nicoll , Sebastien DeleuzeCopyright © 2004-2016
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
1. Spring Web MVC
1.2. DispatcherServlet
1.2. DispatcherServlet
1 | <web-app> |

serlvet-context.xml
1 | <context:component-scan base-package="org.example"> |
application-context.xml
1 | <context:component-scan base-package="org.example"> |
1.3. Filters
1.3. Filters
spring-web 모듈은 몇가지 유용한 필터를 제공한다
HttpPutFormContentFilter: browser는 form의 요청을GET,POST만 사용할 수 있으나, 해당 필터를 사용해서PUT,PATCH요청도 해석할 수 있음ShallowETagHeaderFilter: 응답값을 버퍼링하고 ETag값을 계산
1.4. Annotated Controllers
1.4.2. RequestMapping
RequestMapping의 shortcut
@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping
URI patterns
wildcards
?, *, ** 등의 와일드 카드 사용 가능
regx
/spring-web-3.0.5.jar 요청에 대한 매핑
1 | ("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") |
1.4.2. RequestMapping
Pattern comparison
요청이 여러 URL 패턴에 매칭되었을 때, AntPathMatcher.getPatternComparator(String path)에 의해 더 정확한 패턴에 매칭됨
Consumable media types
Request Header의 Content-Type에 매칭
1 | (path = "/pets", consumes = "application/json") |
Producible media types
RequestHeader의 Accept에 매칭
1 | (path = "/pets/{petId}", produces = "application/json;charset=UTF-8") |
Parameters, headers
1 | (path = "/pets/{petId}", params = "myParam=myValue") |
1.4.3. Handler Methods
Method Arguments
| Controller method arguments | Description |
|---|---|
javax.servlet.http.HttpSession |
현재 세션 정보(Thread-safe하지 않음) |
java.security.Principal |
인증된 유저 정보 |
java.util.Locale |
요청의 Locale 정보, LocaleResolver로 인해 계산 |
java6+: java.util.TimeZonejava8+: java.time.ZoneId |
LocaleContextResolver에 의해 계산 |
@PathVariable |
|
@MatrixVariable |
|
@RequestParam |
|
@RequestHeader |
|
@CookieValue |
|
@RequestBody |
|
HttpEntity<B> |
|
@RequestPart |
|
RedirectAttributes |
|
@ModelAttribute |
|
Errors, BindingResult |
|
@RequestAttribute |
1.4.3. Handler Methods
Return Values
| Controller method return value | Description |
|---|---|
@ResponseBody |
HttpMessageConverter에 의해 변환되어 응답에 쓰임 |
HttpEntity<B>, ResponseEntity<B> |
header와 body를 가지며, body는 HttpMessageConverter에 의해 변환됨 |
HttpHeaders |
body가 없이 header만 응답 |
String |
viewName |
View |
|
void |
ServletResponse, OuputStream을 파라미터로 받았거나, @ResponseStatus가 메서드에 등록된 경우에모든 처리가 완료된 것으로 간주 |
DeferredResult<V> |
위 반환값으로 다른 스레드(any thread)에서 비동기로 반환 |
Callable<V> |
위 반환값으로 Spring MVC가 관리하는 스레드에서 비동기로 반환 |
| Reactive types Reactor, RxJava, etc. |
|
| 그 외 | String의 경우 viewName으로 간주, 그 외에는 @ModelAttribute로 간주 |
1.4.3. Handler Methods
Jackson JSON
1 |
|
1.4.5. Binder Methods
custom editor
1 |
|
formatter
1 |
|
1.4.6. Exception Methods
1 |
|
@ControllerAdvice에서도 사용 가능
1.4.7. Controller Advice
@ExceptionHandler, @InitBinder, @ModelAttribute 등의 처리는 @ControllerAdvice에서 할 수 있음
1 | // Target all Controllers annotated with @RestController |
1.5. URI Links
1.5.1. UriComponents
1 | String uriTemplate = "http://example.com/hotels/{hotel}"; |
1 | URI uri = UriComponentsBuilder.fromUriString(uriTemplate) |
1.6. Async Requests
1.6. Async Requests
Spring MVC는 Servlet 3.0을 확장하여 아래와 같은 기능을 제공한다
- 하나의 비동기 결과값을 담는
DeferredResult,Callable을 반환할 수 있음 SSE,raw data를 stream할 수 있음- reactive 동작
1.6.1. DeferredResult
1 | ("/quotes") |
외부 이벤트, scheduled task 등의 다른 스레드에서 처리된 비동기 결과를 반환
1.6.2. Callable
1 |
|
미리 설정해둔
TaskExecutor에서 반환
1.6.3. Processing
비동기 처리 개요
ServletRequest의startAsync()를 호출하여 비동기 모드로 사용할 수 있다
이로 인해, Servlet과 Filter를 종료시키지만 response는 남아 따로 처리하고 완료시킬 수 있게 된다request.startAsync()를 호출하면 추가적인 제어를 할 수 있는AsyncContext가 반환된다(dispatch(String),setTimeout(long))ServletRequest는 현재 상태(초기화, dispatch, forward) 등을 파악할 수 있는DispatcherType에 대한 접근을 제공한다
1.6.3. Processing
DefferedResult
- Controller는
DeferredResult를 반환하고, 이를 in-memory queue에 저장한다 - Spring MVC가
request.startAsync()를 호출한다 - 그동안
DispatcherServlet과Filter는 요청을 처리하는 스레드를 종료시킨다. response만 남겨둔다 - 어느 스레드에서
DeferredResult에 값을 넣으면, Spring MVC는 ServletContainer에 요청을 되돌린다(dispatch) DispatcherServlet이 다시 실행되며, 비동기 반환 값에 대한 처리가 시작된다
1.6.3. Processing
Callable
- Controller가
Callable을 반환한다 - Spring MVC는
request.startAsync()를 호출하고,Callable가TaskExecutor에 의해 다른 스레드에서 실행되도록 한다 - 그동안
DispatcherServlet과Filter는 요청을 처리하는 스레드를 종료시킨다. response만 남겨둔다 Callable에 완료되어 값이 반환되면, Spring MVC는 Servlet Container에 값을 되돌린다(dispatch)DispatcherServlet이 다시 실행되며, 비동기 반환 값에 대한 처리가 시작된다
1.6.3. Processing
Exception handling
DeferredResult
DeferredResult를 사용하면 setResult나 setErrorResult를 사용한다.
두 경우 모두 Spring MVC에서 Servlet Container로 완료 처리를 dispatch하며, 기존의 예외 처리 방식을 따르게 된다(@ExceptionHandler 등)
Callable
대부분 비슷하다. 다만 Callable에는 setErrorResult 등의 메서드가 없으므로, 스스로 exception을 throw한다.
Interception
AsyncHandlerInterceptor, CallableProcessingInterceptor, DeferredResultProcessingInterceptor 등이 존재
1.6.3. Processing
Compare to WebFlux
Servlet 3.0에서 비동기를 하는 방법은 Filter-Serlvet chain은 종료시킨채, reponse만 남겨두어 다른 스레드에서 응답을 채우는 방식
개별 요청에 대한 응답의 쓰기 작업은 여전히 blocking I/O으로 이루어지며,
WebFlux는 이를 non-blocking I/O으로 처리하는 것이 큰 차이점
또 하나는, WebFlux는 Controller의 메서드에 비동기 혹은 반응형 타입을 지원함
1.6.4. HTTP Streaming
DeferredResult나 Callable은 비동기 처리 후, 한 번만 반환.Streaming은 여러번 반환
Objects
1 | ("/events") |
1.6.4. HTTP Streaming
SSE(Server Sent Events)
SseEmitter는 ResponseBodyEmitter의 서브 클래스로서 W3C SSE 스펙을 준수하여 Controller에서 Stream을 생성하도록 함
https://www.w3schools.com/html/html5_serversentevents.asp
1 | (path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE) |
하지만 SSE는 IE에서 돌아가지 않으므로(망할), WebSocket과 SockJS 등을 사용할 것을 권장
1.6.4. HTTP Streaming
Raw data
1 | ("/download") |
1.6.5. Reactive Types
- 단일 값(
Mono,Single)에 대해서는DeferredResult와 유사한 동작 - 여러 값(
Flux,Observable) 등,application/stream+json이나text/event-stream유형의 값은ResponseBodyEmitter,SseEmitter와 유사한 동작
1.6.6. Configuration
Servlet Container
Filter와 ServletContainer는 asyncSupported값이 있으며 이를 true로 해야한다
Java
AbstractAnnotationConfigDispatcherServletInitializer로 Servlet Container를 사용한다면 자동으로 설정되어 있다
web.xml
<async-supported>true</async-supported>를 DispatcherServlet 설정과 Filter 설정에 추가한다Filter Mapping에 <dispatcher>ASYNC</dispatcher>를 추가한다
Spring MVC
Java
WebMvcConfigurer의 configureAsyncSupport를 사용
xml
<mvc:annotation-driven> 아래에 <async-support> 선언
1.7. CORS
1.7.3. @CrossOrigin
1 |
|
default
- 모든 origin 허용
- 모든 헤더 허용
- 모든 HTTP method 허용
maxAge는 30분allowedCredentials는 기본적으로 비활성화됨.
1.7.3. @CrossOrigin
1 | (origins = "http://domain2.com", maxAge = 3600) |
1.7.4. Global Config
1 |
|
1.7.5. CORS Filter
1 | CorsConfiguration config = new CorsConfiguration(); |
1.9. HTTP Caching
1.9.1. Cache-Control
1 | // Cache for an hour - "Cache-Control: max-age=3600" |
1.9.2. Static resources
ResourceHttpRequestHandler를 설정해서 Last-Modified 헤더와 Cache-Control 헤더를 적절히 변경할 수 있다
1 |
|
1.9.3. @Controller caching
Controller는 Cache-Control, ETag, If-Modified-Since 등의 다양한 헤더를 다룰 수 있다
1 | ("/book/{id}") |
위 코드에서 응답에 ETag 및 Cache-Control 헤더가 포함되며
클라이언트가 보낸 조건부 헤더가 컨트롤러에서 설정한 캐시 정보와 일치하면 HTTP 304 Not Modified 응답의 빈 body를 내려보낸다
1.9.3. @Controller caching
1 |
|
request.checkNotModified(lastModified):true를 반환하는 경우, response status와 header를 설정return null:request.checkNotModified()와 결합하여 Spring MVC가 요청을 처리하지 않게 만듦
1.9.4. ETag Filter
ShallowEtagHeaderFilter가 응답의 내용을 캐싱하고 ETag 헤더로 내보낼 MD5 해시를 생성- 클라이언트가 동일 자원에 대한 요청을 보내면 해당 해시를
If-None-Match값으로 사용 - 동일한 요청일 경우 필터가 304 응답
- 네트워크 대역 트래픽을 낮추는 효과는 있지만, CPU 사용은 낮추지 못함
- 앞서 설명한
Controller수준의 다른 전략들은 연산을 줄일 수 있음
1.10. View Technologies
1.10. View Technologies
- Thymeleaf
- FreeMarker
- Groovy Markup
- Script Views
- Mustache
- React
- ext.
- JSP & JSTL
- Tiles
- RSS, Aom
- PDF, Excel
- Jackson
- XML
- XSLT
1.11. MVC Config
1.11.2. MVC Config API
1 |
|
보통은 WebMvcConfigurerAdapter를 상속받아서 쓰지 않을까…
1.11.3. Type conversion
1 |
|
1.11.4. Validation
global validator
1 |
|
local validator
1 |
|
1.11.5. Interceptors
1 |
|
1.11.6. Content Types
ContentNegotiationConfigurer를 통해 확장자로 MediaType을 결정할 수 있음
예) /a.json의 응답을 application/json으로 변경
1 |
|
1.11.7. Message Converters
아래와 같이 custom ObjectMapper를 주입할 수 있다
1 |
|
classpath 내에 아래 모듈이 발견되면 자동으로 설정된다
jackson-datatype-jdk7:java.nio.file.Path등의 Java 7에 정의된 타입jackson-datatype-joda: Joda-Time 타입 지원jackson-datatype-jsr310: Java 8에 정의된 Date, Time 타입 지원jackson-datatype-jdk8:Optional등 Java 8에 정의된 타입
그 외
jackson-datatype-money:javax.money타입 지원(unofficial module)jackson-datatupe-hibernate:lazy-loading등의 하이버네이트 지원
1.11.8. View Controllers
특정 URL을 바로 view로 매핑
1 |
|
1.11.10. Static Resources
/resources로 들어오는 요청을 /public혹은 classpath 내의 /static에서 찾도록 설정.
1 |
|