Spring | 06 스프링 MVC의 Controller 4/4 - 파일 업로드 처리, 예외처리

파과·2022년 9월 20일

파일 업로드 처리

Servlet 3.0 전까지는 commons의 파일 업로드를 이용하거나 cos.jar 등을 이용해서 파일 업로드를 처리해왔지만, Servlet 3.0 이후로는 기본적으로 업로드되는 파일 처리 기능이 추가되어 있다.

Spring Legacy Project의 경우 Servlet 2.5를 기준으로 생성되기 때문에 3.0 이후 지원 설정을 사용하기 어렵다. 3.0이상의 파일 업로드 방식은 후반부에 다루도록 하고 여기서는 commons-fileupload 라이브러리를 이용하기로 하겠다.

pom.xml

<!-- 파일 업로드 라이브러리 -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.3</version>
</dependency>

파일이 임시로 업로드될 폴더 upload를 C 드라이브 아래 upload/tmp로 만든다.

servlet-context.xml 파일에 다음 코드를 추가한다 - id속성 값이 정확히 multipartResolver가 되도록 주의한다.

<!-- 파일 업로드 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <beans:property name="defaultEncoding" value="utf-8"></beans:property>
  <!-- 1024 * 1024 * 10 bytes 10MB -->
  <beans:property name="maxUploadSize" value="104857560"></beans:property>
  <!-- 1024 * 1024 * 2 bytes 2MB -->
  <beans:property name="maxUploadSizePerFile" value="2097152"></beans:property>
  <beans:property name="uploadTempDir" value="file:/C:/upload/tmp"></beans:property>
  <beans:property name="maxInMemorySize" value="10485756"></beans:property>
</beans:bean>
  • maxUploadSize : 한 번의 Request로 전달될 수 있는 최대 크기
  • maxUploadSizePerFile : 하나의 파일 최대 크기
  • maxInMemorySize : 메모리상에서 유지하는 최대 크기 - 이 이상의 데이터는 uploadTempDir에 임시 파일 형태로 보관된다. 절대 경로는 URI를 이용해야 하기 때문에 'file:/'로 시작한다.
  • defaultEncoding : 파일 이름이 한글일 경우 깨지는 문제를 처리한다.

SampleController에서는 get방식으로 파일 업로드 화면을 처리한다.

	@GetMapping("/exUpload")
	public void exUpload() {
		log.info("/exUpload..............");
	}

파일을 업로드할 exUpload.jsp를 views/sample에 작성한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/sample/exUploadPost" method="post" enctype="multipart/form-data">
	
	<div>
		<input type='file' name='files'>
	</div>
	<div>
		<input type='file' name='files'>
	</div>
	<div>
		<input type='file' name='files'>
	</div>
	<div>
		<input type='file' name='files'>
	</div>
	<div>
		<input type='file' name='files'>
	</div>
	<div>
		<input type='submit'>
	</div>
	
</form>
</body>
</html>

acion속성값에 맞게 SampleController 코드를 추가한다.

@PostMapping("/exUploadPost")
	public void exUploadPost(ArrayList<MultipartFile> files) {
		files.forEach(file -> {
			log.info(".....................");
			log.info("name:" + file.getOriginalFilename());
			log.info("size:" + file.getSize());
		});
	}

2MB 아래 크기의 파일을 넣어 테스트해본다.

예외처리

  • @ExceptionHandler와 @ControllerAdvice를 이용한 처리
  • @ResponseEntity를 이용하는 예외 메시지 구성

@ControllerAdvice

AOP(관점지향 프로그래밍)를 이용하는 방식. 컨트롤러를 작성할 때는 메서드의 모든 예외사항을 전부 핸들링해야 한다면 중복적이고 많은 양의 코드를 작성해야 하지만, AOP 방식을 이용하면 공통적인 예외사항에 대해서는 별도로 @ControllerAdvice를 이용해서 분리하게 된다.

org.zerock.exception 패키지에 CommonExceptionAdvice 클래스를 생성하자.

package org.zerock.exception;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import lombok.extern.log4j.Log4j;

@ControllerAdvice
@Log4j
public class CommonExceptionAdvice {

	@ExceptionHandler(Exception.class)
	public String except(Exception ex, Model model) {
		
		log.error("Exception......." + ex.getMessage());
		model.addAttribute("exception", ex);
		log.error(model);
		return "error_page";
	}
}

@ControllerAdvice는 해당 객체가 컨트롤러에서 발생하는 예외를 처리하는 존재임을 명시하는 용도.
@ExceptionHandler는 해당 메서드가 ()에 들어가는 예외 타입을 처리한다는 것을 의미.

Exception.class를 지정하였으므로 여기서는 모든 예외에 대한 처리가 except()만을 이용해 이루어진다.

org.zerock.exception패키지는 servlet-context.xml에서 인식하지 않기 때문에 <component-scan>을 이용해서 해당 패키지 내용을 조사하도록 해야 한다.

servlet-context.xml일부

<!-- 예외 처리 -->
<context:component-scan base-package="org.zerock.exception" />

error_page.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" import="java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h4><c:out value="${exception.getMessage()}"></c:out></h4>

<ul>

	<c:forEach items="${exception.getStackTrace() }" var="stack">
		<li><c:out value="${stack }"></c:out></li>
	</c:forEach>

</ul>

</body>
</html>

404 에러 페이지

DispatcherServlet을 이용해 처리하도록 web.xml을 수정한다.

<!-- Processes application requests -->
<servlet>
  <servlet-name>appServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
  </init-param>
  <init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>appServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

CommonExceptionAdvice에 추가

@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handle404(NoHandlerFoundException ex) {
	return "custom404";
}

custom404.jsp 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>해당 URL은 존재하지 않습니다.</h1>
</body>
</html>

/sample/로 시작하는 URL은 샘플컨트롤러가 무조건 동작하므로 이를 제외한 경로로 테스트해야 한다.

localhost:8080/nopage

0개의 댓글