기술문서 ID KB000004
HowTo Spring Framework MVC와 DEXTUploadJ
에러코드
태그 DEXTUploadJ 사용방법 제품관련지식
작성일 2014-11-11
문제

Spring Framework(또는 전자정부프레임워크)와 DEXTUploadJ를 사용하여 웹 애플리케이션을 구축할 때 유의할 사항에 대해 설명합니다.

해결

DEXTUploadJ의 FileUpload 클래스는 파일 업로드를 처리하기 위해서 멀티파트(multipart) 데이터를 분석하고 저장하는 작업을 합니다.

일반적으로 순수 Servlet/JSP 기반 프로젝트에서는 클라이언트(브라우저)에서 전송된 멀티파트 데이터를 그대로 갖는 HtttpServletRequest 객체가 Servlet 혹은 JSP로 전달됩니다. FileUpload 객체가 생성될 때, 이 request를 받아 UploadStart 메소드가 시작될 때 처리가 됩니다.


그러나 Spring Framework(또는 전자정부프레임워크) 기반 웹 애플리케이션을 구동하고 파일 업로드를 할 때, DEXTUploadJ의 FileUpload 객체에서  "데이터를 읽어오는 데 실패했습니다. 사용자의 연결이 끊어졌거나 업로드를 중지했습니다."라는 오류가 발생하는 일이 있습니다.

파일 업로드를 처리하기 위해 멀티파트 데이터를 분석하고, 파일을 저장해야 하는데, 요청 헤더와 달리 본문의 내용이 부족하거나 비어 있는 경우에 해당합니다. 이런 현상이 발생하는 이유는 DEXTUploadJ의 FileUpload 객체가 작업을 하기 전에 다른 어떤 기능 때문에 요청 데이터가 이미 소비되었기 때문입니다. (데이터를 소비하는 필터나 개발 상 먼저 request 데이터를 읽어버리는 경우)

이런 현상이 가장 흔하게 발생하는 이유는 Spring Framework에서 파일 업로드를 처리 하기 위한 CommonMultipartResolver 빈(bean)을 DispatcherServlet 서블릿이 읽는 컨텍스트 xml 파일에 선언하는 경우입니다.

CommonMultipartResolver 또는 이를 상속하여 구현한 모든 빈은 HttpServletRequest로 전달된 클라이언트 멀티 파트 데이터를 먼저 소비해버립니다. 즉 DEXTUploadJ의 FileUpload 객체가 동작하기 전에, 업로드 요청을 가로채어 내용을 모두 소비해버리기 때문에 오류가 발생하는 것입니다.


그러므로 DEXTUploadJ를 사용하여 업로드를 처리하기 위해서는 업로드가 처리되는 컨트롤러의 대상 메소드가 실행되기 까지 일련의 요청 흐름에 CommonMultipartResolver와 같이 요청 데이터를 가로채어 소비하는 설정이 없어야 합니다.


웹 프로젝트의 구성이 단순하다면 DispatcherServlet 서블릿이 읽는 컨텍스트 xml 파일에 CommonMultipartResolver 빈 설정을 제거하는 것 만으로도 충분합니다. 간혹 공통(전역)으로 적용되는 컨텍스트 xml 파일이 있는 경우에, 해당 xml에도 CommonMultipartResolver 빈이 설정되어 있는지 확인이 필요합니다.


때론 CommonMultipartResolver와 DEXTUploadJ를 함께 사용해야하는 구성이 있을 수 있습니다.

CommonMultipartResolver와 DEXTUploadJ는 하나의 요청 흐름에 함께 사용할 수 없으므로 URL 매핑을 구분하도록 구성을 하면 둘 다 사용이 가능합니다.

다음은 web.xml에 분기 처리된 예제입니다.

<!-- *.up 요청은 DEXTUploadJ를 사용합니다. -->
<servlet>
<servlet-name>dispatcher_dextj</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
<!-- dispatcher-dextj.xml에는 CommonMultipartResolver가 선언되어 있지 않습니다. -->
    <param-value>/WEB-INF/dispatcher-dextj.xml</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcher_dextj</servlet-name>
  <url-pattern>*.up</url-pattern>
</servlet-mapping>

<!-- *.do 요청은 CommonMultipartResolver를 사용합니다. -->
<servlet>
<servlet-name>dispatcher_spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
<!-- dispatcher-spring.xml에는 CommonMultipartResolver가 선언되어 있습니다. -->
    <param-value>/WEB-INF/dispatcher-spring.xml</param-value>
  </init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher_spring</servlet-name>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>