이번 블로그에서는 스프링 MVC의 Controller Part1에 이어 설명을 이어 나가도록 하겠습니다.
이번 장에서는 크게 3가지르 살펴 보겠습니다.
-
파일 업로드 처리
-
Controller의 Exception 처리
-
404 페이지 처리
파일 업로드 처리
스프링에서 파일 업로드를 처리하기 위해서는 전달되는 파일 데이터를 분석을 해야합니다.( 파일을 전달할때 어떤 데이터가 전달 되어야 하는지 알아야합니다) Servlet 3.0 전까지는 commons 의 파일 업로드를 이용하거나 cos.jar등을 라이브러리를 이용하여 처리를 했습니다. 하지만 Servlet 3.0 이후 부터는 (Tomcat 7.0) 에는 기본적으로 업로드 되는 파일을 처리할수 있는 기능이 추가되어 있어 따로 라이브리가 필요하지 않습니다.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
파일 업로드 처리를 하기 위해서는 commons-fileupload를 pom.xml에 추가해야합니다.
그 다음으로 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>
<beans:property name="maxUploadSize" value="104857560"></beans:property>
<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>
servlet-context.xml
- maxUploadSize : 한 번의 Request로 전달 될수 있는 최대 크기
- maxUploadPerSize : 하나의 파일 최대 크기
- maxInMemorySize : 메모리 상에서 유지하는 최대의 크기 만일 이 크기 이상은 uploadTempDir에 임시 파일로 보관됩니다.
- uploadTempDir : 파일 업로드시 임시로 사용할 디렉토리를 설정해 줍니다. 절대 경로를 이용하면 URL 형태로 제공해야하기 때문에 "file:/" 형태로 설정해줍니다.
- defaultEncoding : 파일 업로드시 한글일 경우 깨지는 문제를 처리합니다.
가장 먼저 데이터를 저장할 임시 파일 디렉토리 를 생성해 줍니다.
그리고 SampleController에서 다음과 같이 Get 방식/Post 방식 으로 파일을 업로드할 화면을 처리 및 업로드를 처리할 메소드를 구현합니다.
@GetMapping("/exupload")
public void upload() {
log.info("/ex Upload ...............");
}
@PostMapping("/exuploadPost")
public void exUploadPost(ArrayList<MultipartFile> files) {
files.forEach(file -> {
log.info("-----------------------------------");
log.info("name : "+file.getOriginalFilename());
log.info("size : "+file.getSize());
});
}
그 다음으로 파일 업로드 할 화면을 생성해 줍니다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<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>
exupload.jsp
다음과 같이 jsp 파일을 생성한뒤 파일을 보내보도록 하겠습니다.
파일을 선택한뒤 제출 버튼을 누르면 404페이지가 출력이 됩니다. 이것은 Post 방식으로 보낼었을때 출력할 페이지를 찾지 못하여 출력되는 페이지로 에러가 출력이 되어야 정상입니다. 에러 페이지를 확인했다면 STS에서 console 에 출력된 로그를 확인해 봅시다
INFO : com.exe.controller.SampleController - /ex Upload ...............
INFO : com.exe.controller.SampleController - -----------------------------------
INFO : com.exe.controller.SampleController - name : exo5(2).png
INFO : com.exe.controller.SampleController - size : 9857
INFO : com.exe.controller.SampleController - -----------------------------------
INFO : com.exe.controller.SampleController - name : ex05(3).png
INFO : com.exe.controller.SampleController - size : 7391
INFO : com.exe.controller.SampleController - -----------------------------------
INFO : com.exe.controller.SampleController - name : SampleDTO.png
INFO : com.exe.controller.SampleController - size : 15745
INFO : com.exe.controller.SampleController - -----------------------------------
INFO : com.exe.controller.SampleController - name : jsson.png
INFO : com.exe.controller.SampleController - size : 7112
INFO : com.exe.controller.SampleController - -----------------------------------
INFO : com.exe.controller.SampleController - name : jacksonResponseEntity.png
INFO : com.exe.controller.SampleController - size : 48251
로그를 확인해 보았을때 각 파일별 이름과 파일 사이즈가 출력이 된것을 볼수 있습니다. 이것을 최종으로 업로드를 하려먼 byte[]를 사용하여 읽어드린뒤 파일을 저장하면 됩니다.
Controller의 Exception 처리
Controller를 작성할때 예외 상황을 고려하면 처리해야 하는 작업이 정말 많다고 느끼실수 있습니다. 스프림MVC에서는 이러한 작업을 다음과 같은 방식으로 처리를 합니다.
- @ExceptionJandler와 @ControllerAdvice를 이용한 처리
- @ResponseEntity를 이용하는 예외 메세지 구성
@ControllerAdvice는 AOP(Aspect-Oriented-Progreamming)을 이용하는 방식입니다. AOP에 대해서는 별토의 페이지에서 설명을 하겠습니다. 간단히 말해서 핵심적인 로직은 아니지만 프로그램에서 공통적인 관심사를 처리하자는 개념입니다. Controller에서 작성을 할때 모든 예외 상황을 핸들링 해야한다면 중복적이고 많은 양의 코드를 작성 해야하지만 AOP 방식을 이용하면 공통적인 예외적인 상황에 대해서는 @ControllerAdvice를 이용하여 분리를 할수가 있습니다.
com.exe.exception 패키지를 생성하고 예외처리를 위한 CommonExceptionAdvice 클래스 파일을 생성합니다.
package com.exe.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";
}
}
commonExceptionAdvice.java
@ExceptionHandler 어노테이션을 이용하여 Exception.class 을 호출합니다. 어떤 Error가 출력이 되는지 확인하기 위하여 lombok에서 제공하는 log.error() 메소드안에 message를 출력했습니다.
<context:component-scan base-package="com.exe.controller" />
<context:component-scan base-package="com.exe.exception"/>
root-context.xml 안에 예외 처리에 관련된 패키지를 객체로 인식할수 있도록 <context:component-scan>을 태그를 작성합니다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" import="java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<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>
그리고 View에 error_page를 작성합니다. 위 페이지 코드는 서버에 예외가 생겨 응답을 줄때 예외 메세지가 <li>테그에 맞게 출력 되도록 하는 JSTL 코드입니다.
http:localhost:8080/sample/ex04?name=aaa&age=abc&page=8 의 URL을 호출하였을때 age는 int 데이터타입을 가지고 있기 때문에 형식에 맞지 않는 데이터 타입이 선언이 되었다고 error 메세지가 출력이 됩니다. 단 이때 호출되는 페이지는 기존의 Tomcat이 가지고 있는 500페이지가 아닌 사용자 설정 에러페이지가 출력이 된다는 점입니다.(기존에 가지고 있는 Tomcat 에러페이지는 Tomcat 정보가 그대로 노출이 됩니다. 이것은 웹 보안 취약점을 가지기 때문에 예외 처리를 해주는것은 중요합니다.)
ERROR: com.exe.exception.CommonExceptionAdvice - {exception=org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'sampleDTO' on field 'age': rejected value [abc]; codes [typeMismatch.sampleDTO.age,typeMismatch.age,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sampleDTO.age,age]; arguments []; default message [age]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'age'; nested exception is java.lang.NumberFormatException: For input string: "abc"]}
그리고 콘솔에는 다음과 같은 error 가 출력이 됩니다. default message [age]} ; Fail to convert property of type 'java.lang.String' 에서 볼수 있듯이 age가 원래는 int인데 String 데이터 타입으로 변수를 선언하여 맞지 않는 데이터 타입에 Error가 출력되었다는것을 알수 있습니다.
404페이지 처리
웹 브라이저에서 서버에 요청을 줄때 서버가 페이지를 찾지 못하거나 없는 URL 주소를 요청하게 된다면 서버는 404 웹 코드를 응답을 하게 됩니다. 하지만 404 에러페이지는 출력을 하게 될때에는 에러페이지를 구성하지 않으면 Tomcat 의 버전 정보가 노출이 될수 있고 이것은 웹 보안 취약점으로 분류가 될수 있습니다. 따라서 위에있는 예외 처리 처럼 404페이지도 에러 페이지를 만들어 주어야 합니다.
<!-- 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>thorwExceptionIfNoHandlerFound</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>
404페이지 처리를 하기위해서는 web.xml 파일 안에 404에러 코드에 관련된 테그를 작성해야 합니다. <servlet>테그 안에 <init-param>테그를 생성하여 404페이지를 처리하기 위한 파라미터 테그를 작성합니다.
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handler404(NoHandlerFoundException ex) {
return "custom404";
}
위에 com.exe.exception 패키지 안에 있는 CommonExceptionAdvice 파일에 위 메소드를 추가합니다. @ResponseStatus 어노테이션에 작성된 404 에러 페이지 응답은 HttpStatus.NOT_FOUND 를 의미합니다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h1>해당 URL은 존재하지 않습니다. </h1>
</body>
</html>
CommonExceptionAdvice 에 메소드를 추가하였다면 custom404.jsp페이지를 생성하여 다음 HTML 코드를 작성합니다.
http://localhost:8080/nopage 로 웹 브라우저에 요청을 주게 된다면 custom404 페이지를 출력하게되어 Tomcat의 정보 노출을 막을수 있습니다.
지금까지 Spring MVC의 Controller 에 대해서 알아보았습니다. 긴글 읽어주셔서 감사합니다.
'개발이야기 > Spring' 카테고리의 다른 글
[Code Lean Spring Web]6. Spring MVC의 Controller_Part1 (0) | 2019.11.18 |
---|---|
[Code Lean Spring Web] 4. MyBatis와 Spring 연동 (0) | 2019.11.17 |
[Code Lean Spring Web] 3. 스프링과 Oracle DataBase 연동 (0) | 2019.11.17 |
[Code Lean Spring Web] 2. 스프링 특징 및 의존성 주입 테스트 (0) | 2019.11.16 |
[Code Lean Spring Web] 1. Spring 환경 세팅 (0) | 2019.11.16 |