본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Document

[SpringBoot] 공식 문서 요약(2) - Spring Web MVC

by 김코더 김주역 2021. 10. 4.
반응형

SpringBoot Document Review 2

https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/

 

Spring Web MVC Framework

https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-developing-web-applications.html


1) Spring MVC 기본 설정

(1) ViewResolver
- ContentNegotiatingViewResolver : 최종적으로 View의 Return 타입을 판단한다.
- BeanNameViewResolver : View를 Bean의 이름으로 찾는다.
- 등등..
ViewResolver들은 후보가 될만한 View들을 찾고, ContentNegotiatingViewResolver이 최종 판단을 한다.
(2) static resources(WebJars 포함) :
(3) Converter, GenericConverter, Formatter
(4) HttpMessageConverters
(5) index.html
(6) MessageCodesResolver : 에러 코드를 생성하는 인터페이스
(7) Custom Favicon : 창 아이콘
(8) ConfigurableWebBindingInitializer

 

 

2) Spring MVC 추가 설정

- 기본적으로 WebMvcConfigurer 인터페이스를 @Configuration 클래스에 상속 받아서 설정한다.
- 추가로 RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver을 설정할 때는 WebMvcRegistrations 인터페이스를 @Configuraion 클래스에 상속 받아서 설정한다.
- @EnableWebMvc 어노테이션을 이용하는 방법도 있는데, 이 방법은 SpringBoot의 기본 설정을 사용하지 않고 일일이 설정을 모두 작성해야 된다.

 

 

3) HttpMessageConverters

- HTTP 요청 및 응답 메세지를 String, JSON, XML 등의 리턴 타입으로 변환해주는 인터페이스.
- 사용되는 Converter의 종류는 요청 헤더에 따라 달라질 수 있다. 예) "Accept:application/xml"
- RestController에서 객체를 반환하면 기본적으로 JSON이 반환되며, XML이 반환되게 하려면 객체 클래스에 @XmlRootElement를 붙이고, 요청 측에서는 Accept:application/xml 헤더로 보내야 한다. RestController는 @ResponseBody가 기본적으로 탑재되어있어서, 따로 어노테이션을 명시해주지 않아도 HTTP 응답데이터(body)에 자바 객체가 매핑되어 전달 된다. Controller의 경우에 body를 자바 객체로 받기 위해서는 @ResponseBody 어노테이션을 반드시 명시해주어야한다.
- 커스터마이징(Converters 추가 포함) 방법으로는 이 인터페이스 타입을 반환하는 메소드를 Bean으로 등록하는 방법, 오버라이딩하는 방법이 있다.
- HttpMessageConverters 리스트를 확인하기 위해서는, 해당 클래스를 @Autowired로 가져와서 getConverters() 메소드를 이용하면 된다.

 

4) Custom JSON Serializers and Deserializers

- 상황에 따라 특정 필드를 제외하거나 특정 필드의 이름을 바꿔야할 때 커스터마이징이 필요하다.

- JSON을 직렬화 또는 역직렬화하기 위해 각각 JsonSerializer, JsonDeserializer 클래스를 이용할 수 있다.

- 커스터마이징된 클래스들은 보통 모듈을 통해 Jackson에 등록되는데, Spring Boot에서는 @JsonComponent 어노테이션을 제공하여 간단하게 Spring Bean으로 등록할 수 있다.

- @JsonComponent는 클래스에 붙여도 되고, 메소드에 붙여도 된다.

※ 참고 예제

https://homoefficio.github.io/2016/11/18/%EC%95%8C%EA%B3%A0%EB%B3%B4%EB%A9%B4-%EB%A7%8C%EB%A7%8C%ED%95%9C-Jackson-Custom-Serialization/

https://multifrontgarden.tistory.com/172

 

 

5) Static Content

(1) static path
- Spring Boot에서는 기본적으로 정적 파일을 /resources/static, /resources/public, /resources/resources, /resources/META-INF/resources에서 찾는다. 그리고 요청 URL에서는 저 경로를 명시하지 않고 root로 요청하면 된다.
참고로 이러한 처리는 ResourceHttpRequestHandler가 수행해주는데, 정적 파일을 찾는 경로를 수정하고 싶다면 WebMvcConfigurer 인터페이스를 상속한 @Configuration 클래스를 생성하고 addResourceHandlers() 메소드를 오버라이딩해서 수행하면 된다.
- Spring MVC에서는 모든 요청을 DispatcherServlet에서 받는데, 여기서 처리하지 못하는 요청은 ResourceHttpRequestHandler가 default servlet에게 위임해주는 것이다.
- 만약, 요청 URL를 바꾸고 싶거나 정적 파일을 찾는 경로를 바꾸고 싶다면 설정 파일에 다음과 같이 작성한다.

(예시)

spring.mvc.static-path-pattern=/img/** 
spring.resources.static-locations=classpath:/image/

static-path-pattern은 요청 경로의 패턴이고, static-locations은 실제 정적 파일의 저장 경로이다. 위와 같은 경우에는 정적 파일은 /resources/image/에 저장하고 URL 요청은 /img/파일명으로 하면 된다.

(2) template 경로
템플릿 엔진의 경우에는 /resources에 templates 디렉토리를 생성해서 해당 파일을 추가하면 된다.

(3) webjars
- Webjars는 jquery, bootstrap, npm 등과 같은 웹 라이브러리들을 묶어둔 패키지다. https://www.webjars.org/ 에서는 dependency 추가 코드와 포함된 파일들의 이름이 잘 명시되어있다. 그리고, webjars-locator-core 라이브러리를 추가함으로써 파일 경로 작성 시 버전 명시를 생략할 수도 있다.

※ ex) /webjars/jquery/3.3.1-1/jquery.min.js -> /webjars/jquery/jquery.min.js

- 또, Cache busting(부수기)도 사용할 수 있다. Cache busting은 unique file version identifier를 사용하여 브라우저에게 새로운 버전이 있음을 알려주고, 브라우저는 새로운 버전을 서버로부터 받아와 사이트를 업데이트 할 수 있게 된다.

정적 파일(static file)은 캐시에 저장되어 만료 될 때까지 오랜시간 동안 존재할 수 있다. 만약 사이트를 업데이트 하는 경우 이전 버전의 파일들이 캐시에 남아 변경사항을 알아차리지 못할 수 있는데, 이 때에는 사용자에게 변경되지 않은 문서를 보여주게 된다.


(4) 초기 페이지
루트 경로로 요청을 하면 기본적으로 /static/index.html 이 출력된다. 그 다음의 우선 순위는 index 템플릿 파일이다.

(5) favicon.ico
웹 페이지의 아이콘을 지정할 수 있다. /static 경로와 root classpath 경로에서 차례대로 찾아 적용시켜준다.

 

6) 경로 매칭과 콘텐츠 협상

Spring Boot는 기본적으로 요청 경로에 suffix pattern(.확장자)이 포함되면 컨트롤러로 매핑이 안되게 설정 되어있다.
suffix 매칭을 포함하기 위해 다음과 같이 설정하고 query parameter을 이용할 수 있다.

spring.mvc.contentnegotiation.favor-parameter=true

ex) /projects/spring-boot?format=json -> @GetMapping("/projects/spring-boot")

※ 파라미터 이름은 기본적으로 format이며, 따로 변경할 수도 있다. (문서 참고)
※ 이 방법으로 적용한 확장자는 Accept 헤더보다 우선 순위가 높다.


그래도 꼭 확장자 방식을 써야겠다면(비추천) 다음과 같이 설정한다.

spring.mvc.contentnegotiation.favor-path-extestion=true spring.mvc.pathmatch.use-registered-suffix-pattern=true

※ 확장자 패턴이 아니면 적용이 안됨
※ 이 방법으로 적용한 확장자는 파라미터 방식, Accept 헤더보다 우선 순위가 높다.

 

 

7) WebBindingInitializer

- WebDataBinder을 초기화하는 클래스로, 특정 request에 들어있는 웹 데이터를 바인딩해준다.
- 객체로 바인딩 해주는 것도 이 클래스가 담당한다.
- 커스터마이징은 Converter(혹은 ConfigurableWebBindingInitializer)에 대한 @Bean을 만들어서 수행 가능하다.
사용 예제 - https://leesoo7595.github.io/2018/10/21/Spring_ConfigurableWebBindingInitializer/

 

8) Template 엔진

(1) 종류 : FreeMarker, Groovy, Thymeleaf, Mustache 등
https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-template-engines

(2) JSP의 한계 : 내장된 servlet container(실행 가능한 packaged file)의 경우에 생기는 JSP의 한계
- Tomcat 혹은 Jetty 사용 시 배포 가능성 : war 가능, jar 불가능(Tomcat의 hard-coded file pattern 때문)
- Undertow는 아예 JSP를 지원하지 않는다.
- error.jsp를 따로 생성하는 것으로 에러 핸들링에 대한 default view를 override하지 않는다. Custom error pages를 사용해야 한다.

(3) 만약 jar로 실행했을 때 template 엔진이 인식이 안된다면, template 엔진에 대한 prefix를 classpath*:/templates/로 설정하면 된다. ex) spring.mustache.prefix=classpath*:/templates/

 

9) Error Handling

https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-error-handling
- Spring Boot는 에러가 발생했을 시 /error 디렉토리로 매핑해주는데, 이 페이지는 global 에러 페이지이며 servlet container에 등록되어있다.
- 기본적으로 Machine 클라이언트에게는 에러 내용이 포함된 JSON로 응답하고, Browser 클라이언트에게는 HTML 형태의 whitelabel 에러 View로 응답한다.
- content type 커스터마이징 : BasicErrorController 클래스를 상속 받아두고, produces 속성을 포함하는 매핑 어노테이션과 public 메소드를 함께 추가해서 Bean으로 등록하는 방법
- 에러 내용 커스터마이징 : ErrorAttributes에 대한 Bean을 추가하는 방법
- 모든 Controller 범위에서 발생하는 예외에 대한 예외 처리 메소드들을 한 클래스에 모아서 처리하고 싶다면 @ControllerAdvice를 붙인 클래스를 정의해서 사용한다. (문서 참고)
- status code에 대한 에러 페이지 생성 : html 페이지의 경우에는 정적 파일을 찾는 경로에 /error 디렉토리를 생성하고, 그 안에 [에러코드].html 파일을 저장하면 된다. template의 경우에는 /resources/templates/error 디렉토리에 [에러코드].[확장자] 파일을 저장하면 된다.

※ 예) resources/public/error/404.html
※ 예) resources/templates/error/5xx.ftl -> 시리즈 마스크를 적용하여 500번대의 에러를 모두 포함

또, template의 경우에는 ErrorViewResolver 인터페이스를 상속받아 ModelAndView 타입을 반환해주는 resolveErrorView 메소드를 오버라이딩해서 쓸 수 있다. 이 방법은 웹 파일에 attributes를 적용할 수 있도록 한다.
- @ExceptionHandler와 @ControllerAdvice가 잡지 못한 에러는 ErrorController에서 잡아준다.
- 특정 Exception 클래스를 상속 받아서 예외 클래스를 커스터마이징할 수 있다.

 

10) Spring HATEOAS

- HATEOAS는 Hypermedia As The Engine Of Application State의 약자로, 현재 리소스와 연관된 호출 가능한 자원 상태 정보를 제공하는 동시에 그 리소스에 대한 액션 링크도 같이 제공한다. 즉, 하이퍼미디어를 사용해서 구성되는 RESTAPI를 개발할 때 쓰인다.
- Spring에서는 @EnableHypermediaSupport를 사용하거나 하이퍼미디어 기반의 많은 Bean들을 등록할 필요 없이, HATEOAS에 대한 자동 설정이 어느정도 되어 있기 때문에 LinkDiscoverers 혹은 ObjectMapper을 사용하면 된다. @EnableHypermediaSupport를 직접 사용하게 되면 ObjectMapper가 비활성화 될 수 있다.


11) CORS 지원

https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-cors

- CORS는 Cross Origin Resource Sharing의 약자로, 추가 HTTP헤더를 사용하여 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. (보통 Cross Domain 문제에 의해 막혀있다)

※ Cross Domain 문제 : 동일 출처 정책(Same Origin Policy)에 의해 다른 출처에 요청하는 것을 보안 문제로 간주하고 이를 차단하는 것이다. 출처가 같아야 한다는 것은 프로토콜, 호스트, 포트 넘버가 모두 같아야 한다는 것이다.

- CORS는 자원 제공서버에 설정하면 되고, 자원을 return하는 매핑 메소드에 @CrossOrigin을 붙일 수 있다.

- WebMvcConfigurer 인터페이스를 상속받은 @configuration 클래스를 이용하여 글로벌하게 적용할 수도 있다.

 

 

 

 

반응형

댓글