HttpMessageConverter : 자바 객체와 Http요청 / 응답 Body를 변환하는 역할

interface
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) /*..*/;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) /*..*/;
}
@EnableWebMvc - WebMvcConfigurationSupport가 default HttpMessageConverter를 제공한다
public class WebMvcConfigurationSupport /*..*/ {
// ...
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
// ...
}
}

@ResponseBody
ResponseEntity
@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok() // http status (200 OK)
.eTag(etag) // response header (ETAG)
.build(body); // response body
}
@RequestBody : request body를 읽어와서 HttpMessageConverter를 통해 deSerialize 해서 객체로 전달받기 위한 용도
@PostMapping("/api/members")
@ResponseStatus(HttpStatus.CREATED)
public void createMember(@RequestBody Member member) {
// ...
}
Spring MVC Components
overview
Components
Handler?
쉽게 생각해서 Handler는 Controller다 라고 생각하면 된다
HandlerMapping
서버로 들어온 요청을 어떤 핸들러로 전달할지 결정하는 역할
HandlerAdapter
DispatcherServlet과 실제 핸들러 구현을 이어주는 Object Adapter 역할을 해준다
ViewController / RedirectViewController
@addViewController를 @Override 하면 된다
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addRedirectViewController("/this-is-home", "/");
}
HandlerMapping / HandlerAdapter
ViewResolver : 문자열 기반의 View 이름을 토대로 실제 view 구현을 결정하는 역할
관련 Components
LocaleResolver / LocaleContextResolver : view rendering 시 국제화 지원(locale,timezone) 결정
ThemeResolver : view rendering 시 어떤 테마를 사용할 지 결정하는 역할
RequestToViewNameTranslator : 핸들러가 아무것도 리턴하지 않았을 때 view 이름을 결정하는 역할
HandlerExceptionResolver : 요청처리 과정에서 발생하는 예외 제어할때 사용
MultipartResolver : 멀티파트 요청 처리 구현하는 역할
FlashMapManager : redirect 와 같이 하나의 요청에서 다른 요청으로 속성값을 전달하는데 FlashMap을 사용하는 Mechanism 제공
HandlerInterceptor : ServletFilter와 비슷하게 DispatcherServlet이 Controller를 수행하기 전/후 요청 응답을 참조, 가공할 수 있는 필터 역할을 한다(interface)
DispatcherServlet의 HandlerExcutionChain 실행

ServletFilter와 HandlerInterceptor의 차이?
ServletFilter는 DispatcherServlet 실행 전,후에 실행되지만 HandlerInterceptor는 DispatcherServlet이 실행되고, Controller 실행 전,후로 실행된다.
Applicationcontext 범위도 다르다.
Filter: Root ApplicationContext에 등록하고 관리된다. Filter는 Servlet ApplicationContext(MVC의 View나 @ExceptionHandler)를 이용할 수 없다 하지만HandlerInterceptor는 Servlet ApplicationContext에 등록, 관리된다.
HandlerInterceptor 설정
public class WebConfig implements WebMvcConfigurer {
// ...
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
}
FileUpload
Servlet 3.0 API 정보
@MultipartConfig(
location = "/tmp/",
maxFileSize = -1L,
maxRequestSize = -1L,
fileSizeThreshold = 1024
)
@Slf4j
public class FileUploadServlet extends HttpServlet {
private static final String CONTENT_DISPOSITION = "Content-Disposition";
private static final String UPLOAD_DIR = "/Users/user/Downloads";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
for (Part part : req.getParts()) {
String contentDisposition = part.getHeader(CONTENT_DISPOSITION);
if (contentDisposition.contains("filename=")) {
String fileName = extractFileName(contentDisposition);
if (part.getSize() > 0) {
part.write(UPLOAD_DIR + File.separator + fileName);
part.delete();
}
} else {
String formValue = req.getParameter(part.getName());
log.error("{}={}", part.getName(), formValue);
}
}
}
private String extractFileName(String contentDisposition) {
for (String token : contentDisposition.split(";")) {
if (token.trim().startsWith("filename")) {
String fileName = token.substring(token.indexOf("=") + 1).trim().replace("\"", "");
int index = fileName.lastIndexOf(File.separator);
return fileName.substring(index + 1);
}
}
return null;
}
}
Spring MVC에서 File Upload
View
ViewResolver
종류
Thymeleaf 문법 (변수 vs 메세지)
${variable}
<span th:text="${greeting}" />
#{messageKey}
<span th:text="#{greeting}" />
Thymeleaf 문법은 다음과 같다 (html5) 속성 이용
th:text - 텍스트 출력(HTML escape 됨)
th:utext - 텍스트 출력(HTML 태그 그대로 적용)
th:each - 반복문
th:if, th:unless - 조건문
th:swifth, th:case
th:with - 로컬변수 선언
Expression Basic Objects
Expression Utility Objects
${#strings.isEmpty(name)}
${#strings.contains(name,'ez')}
${#strings.startsWith(name,'Don')}
${#lists.size(list)}
${#lists.isEmpty(list)}