개발팀에서 지원요청이 왔다.

문제의 로직은 타 인터페이스 시스템에서 우리 시스템으로 파일업로드할 때, 컨트롤러 단에서 URI 중 특정 path를 @RequestMapping 어노테이션으로 정의하여 받아내는 부분이다.

현상은 로컬서버에서는 이상없는데 개발서버에서는 @RequestMapping 어노테이션으로 정의한 변수에 원하는 값과 다른 값으로 매핑된다는 것!

이 이슈는 개발팀에서 해결하였지만 나에게도 얻어 가는 부분은 꽤나 있었다. 데이터 전송, HTTP 헤더, 개발적인 요소 등등…

개발 환경

로컬서버는 tomcat 9버전을 사용하며, 개발/운영서버는 쿠버네티스 환경에서 jeus 8버전 서버를 사용하고 있다.

  • Spring 5.3.19 / Spring Boot 2.6.7
  • Sevlet 3.1 (개발서버) / Servlet 4.0 (로컬서버)
    • Jeus 8에서는 Servlet 3.1을 지원하고 Tomcat 9에서는 Servlet 4.0을 지원해서 Servlet 3.1을 사용하려면 Tomcat 8버전대를 사용해야 한다

문제 현상

타 인터페이스 시스템에서 우리 시스템으로 파일을 업로드하기 위해 /a/b/c path로 content-type은 multipart/form-data로 해서 GET 요청을 보냈을 때, getRequestURI();로 요청 url을 확인해 보면 /a/b/c path 대로 들어온 것은 확인 가능했다.

그러나 @RequestMapping 어노테이션으로 /a/{requestMapping}/** 값을 매핑시켰으나 requestMapping에는 엉뚱한 a가 입력됐다.

원인

파일을 전송하게 될 때는 바이너리 형태로 request를 던지게 된다. 바이너리 형태로 받으려면 요청 헤더의 content-type이 multipart/form-data여야 한다.

tomcat에서는 allowCasualMultiParsing=”true”로 설정하면 데이터를 파싱할 수 있는데

jeus에서는 해당 설정이 없어서 서버단에서 받아내지 못하니 데이터 파싱이 안됐다.

Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: The request's content-type is not multipart/form-data.

그래서 servlet 단에서 받아 내야 하는데 이상하게도 애플리케이션 설정에서 spring.servlet.multipart.enabled의 defualt 값이 true임에도 불구하고 jeus 서버는 데이터를 처리하지 못했다.

++) jeus에서 제공하는 servlet 라이브러리를 확인해 보니 Multipart.class에서 POST 메소드가 아니면 false로 반환하게끔 하드코딩되어 있었다!

public boolean isMultipart() {
	if (!this.requestImpl.getMethod().toLowerCase().equals("post"))
		return false; 
	String contentType = this.requestImpl.getContentType();
	return (contentType != null && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/form-data"));
}

해결 방법

  • 요청 메소드를 GET에서 POST 방식으로 변경
  • Header와 Body를 받아오는 부분 제거 (@RequestHeader, @RequestBody)

결과

바이너리 형태의 데이터 값을 받으려면 Content-type이 multipart/form-data 이어야 서버에서 요청을 제대로 처리할 수 있다.

그럼 요청 헤더의 Content-type을 지정해야 하는데, GET 방식은 URL을 이용하여 요청을 전송하기 때문에 적합하지 않으므로 POST 방식으로 변경하는게 적합하다.

추가로 알아본 내용으로는,

보통 multipart/form-data로 인코딩된 데이터는 파일 업로드 등 복잡한 형태의 데이터를 전송하는 데 적합한데, 이러한 경우 보통 POST 방식을 사용하는게 맞다고 한다.

POST 방식은 요청 본문에 데이터를 포함하기 때문에 데이터 크기에 대한 제약이 없어, multipart/form-data 형식으로 데이터를 전송하는 데 적합하기 때문이라고 한다!

참고

HttpServletRequest 함수를 사용한 요청 URL 취득

FileUpload Multipart 설정 (Tomcat)

[Spring Boot] 개발환경 구축 - File Upload

image

multipart/form-data란?[JSP]

image

제3장 웹 컨텍스트

Leave a comment