https://sundaland.tistory.com/369
[ ▶ Content Types ]
Spring MVC (Model-View-Controller)은 요청에 따라 다양한 콘텐츠 타입을 처리할 수 있는 유연한 메커니즘을 제공한다. 이를 Content Negotiation(콘텐츠 협상)이라고 하며, 클라이언트의 요청에 따른 응답 형식을 결정하는 데 사용된다.
[ ▷ Content Negotiation이란? ]
Content negotiation은 클라이언트가 선호하는 응답 형식을 요청에 명시하면, 서버가 그에 따라 적절한 응답 형식을 선택하는 기능이다.
Spring MVC에서 content negotiation은 기본적으로 클라이언트가 보내는 Accept 헤더에 의해 주도됩니다. 하지만, URL 경로 확장자(/users.json), 쿼리 파라미터(/users?format=json), 또는 특정 요청 파라미터와 같은 다른 방법들도 사용할 수 있다.
[ ▷ Content Negotiation의 주요 개념 ]
△ Accept 헤더
응답 형식을 지정하는 주요 방법이다. 예를 들어, 클라이언트가 JSON 형식의 응답을 원할 경우, 다음과 같은 HTTP 요청을 보낼 수 있다. 이 경우, 서버는 가능한 경우 JSON 형식으로 응답을 보내려고 시도합니다.
- GET /users HTTP/1.1 Accept: application/json
△ Media Types(미디어 타입)
요청 또는 응답 데이터의 형식을 정의다. 예시로는 아래와 같은 미디어 타입이 있다.
- application/json: JSON 형식
- application/xml: XML 형식
- text/html: HTML 형식
- application/pdf: PDF 문서 등
△ Content Negotiation 전략
기본적으로 Spring MVC는 Accept 헤더를 확인하지만, 콘텐츠 타입을 결정하기 위해 사용할 수 있는 다른 방법도 있다.
- Path Extension (/users.json): 경로 확장자는 여전히 지원되지만, 보안 취약성 문제로 인해 Spring에서는 사용을 권장하지 않는다. (경로 탐색, 여러 콘텐츠 타입과의 모호성 문제)
- Query Parameter (/users?format=json): 경로 확장자 대신 명시적인 방법으로 추천되는 대안이다.
[ ▷ Spring MVC에서 Content Negotiation 설정 ]
ContentNegotiationConfigurer를 사용하여 콘텐츠 협상 동작을 Java 구성에서 커스터마이즈할 수 있다.
△ 기본 Content Negotiation 구성
▼ ContentNegotiationConfigurer를 사용하여 JSON 및 XML 콘텐츠 타입을 등록하는 방법
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 지원되는 미디어 타입 정의
configurer.mediaType("json", MediaType.APPLICATION_JSON);
configurer.mediaType("xml", MediaType.APPLICATION_XML);
}
}
이 구성은 JSON 및 XML 콘텐츠 타입을 지원하며, 이를 통해 협상이 가능하다. 하지만 기본적으로 여전히 Accept 헤더를 사용하여 콘텐츠 타입을 결정한다.
△ 더 상세한 Content Negotiation 구성
▼ 여러 가지 전략을 포함한 더욱 포괄적인 content negotiation 설정
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 다양한 콘텐츠 협상 전략 활성화
configurer
// 특정 미디어 타입이 없을 때 기본 미디어 타입 설정
.defaultContentType(MediaType.APPLICATION_JSON)
// /users.json과 같은 경로 확장자 허용 (보안상 위험 때문에 비권장)
.favorPathExtension(false) // 보안 이유로 기본적으로 경로 확장자는 비활성화
// /users?format=json과 같은 쿼리 파라미터를 통한 콘텐츠 협상 허용
.favorParameter(true)
.parameterName("format") // 쿼리 파라미터 키 정의
// Accept 헤더 기반 콘텐츠 협상
.ignoreAcceptHeader(false) // Accept 헤더가 제공된 경우 이를 사용
// 특정 미디어 타입이 없을 때 기본 미디어 타입 설정
.defaultContentType(MediaType.APPLICATION_JSON)
// 지원되는 미디어 타입 등록
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("html", MediaType.TEXT_HTML);
}
}
[ ▷ 주요 속성 설명 ]
△ favorPathExtension(boolean)
- false: 경로 확장자를 통한 콘텐츠 타입 결정을 비활성화한다. (/users.json)
- true: 활성화할 경우 파일 확장자를 통해 콘텐츠 타입을 결정할 수 있다. (/users.xml)
△ favorParameter(boolean)
- true: 쿼리 파라미터를 통해 콘텐츠 타입을 결정할 수 있다. (/users?format=json)
- **parameterName(String)는 쿼리 파라미터 이름을 지정한다. (/users?format=json에서format)
△ ignoreAcceptHeader(boolean)
- false: 콘텐츠 타입을 결정할 때 Accept 헤더를 사용한다.
- true: Accept 헤더를 무시합니다. 쿼리 파라미터나 다른 방법을 선호할 때 유용한다.
△ defaultContentType(MediaType)
- 특정 콘텐츠 타입이 요청되지 않은 경우 사용할 기본 미디어 타입을 설정한다. 위 예시에서는 application/json이 기본 콘텐츠 타입으로 설정되어 있다.
[ ▷ Path Extension의 보안 문제 ]
과거에는 Spring MVC에서 파일 확장자(/users.json)를 통해 콘텐츠 타입을 결정하는 방식이 사용되었다. 그러나 다음과 같은 보안 문제들 때문에 이 방식은 점차 권장되지 않게 되었다.
- 모호성: 파일을 제공하는 애플리케이션과 REST 엔드포인트가 공존할 때 경로 확장자는 혼동을 야기할 수 있다. (/files와 /users 엔드포인트가 동시에 있을 경우)
- RFD(Reflected File Download): 공격자가 파일 확장자를 조작해 사용자가 신뢰할 수 있는 소스에서 파일을 다운로드하도록 유도할 수 있는 보안 취약성이다.
- Content-Type Sniffing: 경로 확장자는 프록시나 브라우저에서 잘못 해석될 수 있어 MIME 타입을 잘못 해석할 가능성이 있다.
[ ▷ Reactive Stack에서의 Content Negotiation ]
Spring WebFlux(reactive 스택)에서도 content negotiation은 유사한 방식으로 작동하지만, Mono와 Flux와 같은 비동기, reactive 타입을 처리하도록 설계되었다. WebFlux에서도 ContentNegotiationConfigurer를 사용하여 콘텐츠 타입을 설정할 수 있지만, 비차단 및 비동기 응답을 처리하는 데 중점을 둔다.
예를 들어, WebFlux에서의 reactive 설정은 아래과 같이 구성될 수 있다.
@Configuration
public class WebFluxConfig implements WebFluxConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.defaultContentType(MediaType.APPLICATION_JSON)
.favorParameter(true)
.parameterName("format")
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
}
Spring WebFlux는 동일한 원칙을 따르지만, 높은 동시성 및 non-blocking I/O에 최적화되어 있다.
Spring의 콘텐츠 협상 메커니즘은 유연하고 강력하여 다양한 전략을 통해 응답 형식을 결정할 수 있다. 기본적으로는 Accept 헤더를 사용하지만, 특정 애플리케이션 요구 사항에 따라 쿼리 파라미터 등 다른 방법을 사용할 수 있으며, 경로 확장자는 보안 문제로 인해 권장되지 않는다.
이 구성 가능성은 개발자가 클라이언트에게 기대하는 포맷으로 효율적이고 안전하게 응답을 제공할 수 있도록 해준다.
'Web on Servlet Stack' 카테고리의 다른 글
View Resolvers (0) | 2024.10.15 |
---|---|
View Controllers (0) | 2024.10.15 |
Validation (0) | 2024.10.15 |
Type Conversion (0) | 2024.10.15 |
Interceptors (0) | 2024.10.15 |