첨부파일을 서버에 전송하는 방식은 크게 2가지가 있습니다.

 

1. <form> 태그를 이용하는 방식

- 일반적으로 페이지 이동과 동시에 첨부파일을 업로드하는 방식

- <iframe>을 이용해서 화면의 이동 없이 첨부파을일 처리하는 방식

2. Ajax를 이용하는 방식

- <input type="file">을 이용하고 Ajax를 처리하는 방식

- HTML5의 Drag And Drop 기능이나 JQuery 라이브러리를 이용해서 처리하는 방식

 

⇒ 화면 단에서 첨부파일 처리하는 방식은 다양하지만, 서버쪽에서 처리하는 바식은 대부분 비슷합니다.


첨부파일 처리를 위한 라이브러리 혹은 API

1. cos.jar : 2002년 이후에 개발이 종료되었으므로, 더 이상 사용을 권장하지 않음.

2. common-fileupload : 가장 일반적으로 많이 활용되고, 서블릿 스펙 3.0 이전에도 사용 가능

3. 서블릿 3.0 이상 : 3.0 이상부터는 자체적인 파일 업로드 처리가 API 상에서 지원 (Tomcat 7.0 버전 이상에서 지원)

 

이 게시물에서 3번 API 방식과 <form> 태그를 이용해 파일 업로드를 처리하겠습니다.

 

pom.xml 에 서블릿 버전을 수정합니다.

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

web.xml에 첨부파일 처리에 대한 설정을 합니다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	id="WebApp_ID" version="3.1">


	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- 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>
		<load-on-startup>1</load-on-startup>

		<multipart-config>
			<location>C:\\upload\\temp</location>
			<max-file-size>20971520</max-file-size> <!--1MB * 20 각 파일의 최대크기-->
			<max-request-size>41943040</max-request-size><!-- 40MB 한번에 요창할 수 있는 최대크기 -->
			<file-size-threshold>20971520</file-size-threshold> <!-- 20MB -->
		</multipart-config>

	</servlet>

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

  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


</web-app>

 

위 web.xml의 설정은 WAS(Tomcat) 자체의 설정이며, 스프링에서 업로드 처리는 MultipartResolver라는 타입의 객체를 빈으로 등록해야합니다.

 

Spring Legacy Project를 통해 MVC 프로젝트를 생성했다면 Web과 관련된 설정을 입력하는 servlet-context.xml에 객체를 등록합니다.

<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</beans:bean>

 

파일업로드 테스트를 위해 JSP 파일을 생성합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>


<form action="fileUpload" method="post" enctype="multipart/form-data">

<input type='file' name='uploadFile' multiple>

<button>UPLOAD</button>

</form>

</body>
</html>

파일 업로드 시 form 태그에 enctype의 속성값을 'multipart/form-data'로 지정해야합니다.

<input type='file'>에 multiple 속성을 지정해 여러 개의 파일을 업로드할 수 있습니다.

 

fileUpload 경로를 처리할 수 있는 Controller 파일을 생성하고 RequestMapping을 처리해줍니다.

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

@Controller
@Log4j
public class FileUploadController {

	@GetMapping("/fileUpload")
	public void uploadForm() {
    
	}

	@PostMapping("/fileUpload")
	public void uploadFormPost(MultipartFile[] uploadFile, Model model) {

		String uploadFolder = "D:\\upload";

		for (MultipartFile multipartFile : uploadFile) {
			log.info("File Name : " + multipartFile.getOriginalFilename());
			log.info("File Size: " + multipartFile.getSize());

			File saveFile = new File(uploadFolder, multipartFile.getOriginalFilename());

			try {
				multipartFile.transferTo(saveFile);
			} catch (Exception e) {
				log.error(e.getMessage());
			}
		}
	}
}

여기까지 처리시 화면에서 첨부한 실제 파일이 업로드 처리됩니다.


이외에도 파일업로드는 신경써야할 부분이 많습니다.

 

1. 파일업로드 정보를 데이터베이스에 별도로 저장해야합니다.

2. 이미지 파일의 경우 썸네일 파일을 별도로 생성합니다.

3. 파일의 확장자를 확인해 유효성 체크를 해야합니다.

4. 한 폴더에 모든 파일을 업로드하는 경우, 파일을 관리하기 어려우므로 년/월/일 형태의 폴더 등을 생성해 폴더를 분리해서 관리해야합니다.

5. 파일 업로드가 완료된 후 게시물을 조회하여 첨부파일을 클릭(혹은 더블 클릭) 하는 경우 파일 다운로드 혹은 미리보기 (이지미파일)을 처리해야 합니다.

6. 게시물을 삭제하는 경우 데이터베이스에서 해당 게시물의 파일 정보를 조회해 관련 파일도 함께 삭제해줘야합니다.

Controller의 메서드가 사용할 수 있는 리턴 타입

 

* String : jsp를 이용하는 경우에는 jsp 파일의 경로와 파일이름을 나타내기 위해서 사용

* void : 호출하는 URL과 동일한 이름의 jsp를 의미합니다.

* VO, DTO 타입 : 주로 JSON 타입의 데이터를 만들어서 반환하는 용도로 사용합니다.

* ResponseEntity 타입 : response 할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용합니다.

* Model, ModelAndView : Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용합니다.

* HttpHeaders : 응답에 내용 없이 Http 헤더 메시지만 전달하는 용도로 사용합니다.


VO, DTO 와 같은 객체 타입

* 메이븐을 사용하는 경우 pom.xml에 의존을 추가합니다.

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.4</version>
</dependency>

 

메서드 반환형 앞에 @ResponseBody 어노테이션을 사용합니다. 스프링 MVC가 객체를 JSON 타입으로 변환해서 전달합니다.

public class Sample {

	@GetMapping("/ex01")
	public @ResponseBody SampleDTO ex01() {
		SampleDTO dto = new SampleDTO();
		dto.setName("홍길동");
		return dto;
	}
}

ResponseEntity 타입

원하는 헤더 정보나 데이터를 전달할 수 있습니다. ResponseEntity는 HttpHeaders 객체를 같이 전달할 수 있고 이를 통해서 원하는 HTTP 헤더 메시지를 가공하는 것이 가능합니다.

public class Sample {

	@GetMapping("/ex02")
	public ResponseEntity<String> ex02() {
		String msg = "{\"name\":\"홍길동\"}";
		
		HttpHeaders header = new HttpHeaders();
		header.add("Content-Type", "application/json;charset=UTF-8");
		
		return new ResponseEntity<>(msg, header, HttpStatus.OK);
	}
}

 

@DateTimeFormat

파라미터를 변환해서 처리해야하는 경우에 사용

public class Sample {
	
	@DateTimeFormat(pattern="yyyy/MM/dd")
	private Date date;
}

 

문자열로 yyyy/MM/dd 형식이 맞다면 자동으로 날짜 타입으로 변환


@ModelAttribute

스프링 MVC의 Controller는 기본적으로 자바 빈즈 큐칙에 맞는 객체는 다시 화면으로 객체를 전달합니다. 반면 기본 자료형의 경우는 파라미터로 선언하더라도 기본적으로 화면까지 전달되지 않습니다. 

 

@ModelAttribute 어노테이션은 강제로 전달받은 파라미터를 Model에 담아서 전달하도록 할 때 사용합니다. 타입에 관계없이 무조건 Model에 담아서 전달되므로, 파라미터로 전달된 데이터를 다시 화면에서 사용해야 할 경우에 유용하게 사용됩니다.

public class sample {

	@GetMapping("/ex01")
	String ex01(SampleVO vo, @ModelAttribute("var1") int var1) {
		return "test";
	}
}

@ControllerAdvice

코딩을 할 때 각 소스파일에 모든 예외 사항을 고려하면 처리해야하는 작업이 엄청나게 늘어날 수 밖에 없습니다. 클래스를 생성하고 @ControllerAdvice 어노테이션을 이용해 예외사항을 공통적으로 처리할 수 있습니다.

import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import lombok.extern.log4j.Log4j;

@ControllerAdvice
public class CommonException {
	
	@ExceptionHandler(Exception.class)
	public String except(Exception e, Model model) {
		model.addAttribute("exception", e);
		return "exception_page";
	}
	
	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public String handle404(NoHandlerFoundException ex) {
		return "404error_page";
	}
}

@ControllerAdvice : 해당 객체가 스프링의 컨트롤러에서 발생하는 예외를 처리하는 존재임을 명시하는 용도

@ExceptionHandler : 해당 메서드가 ( ) 들어가는 예외 타입을 처리한다는 것을 의미

@ResponseStatus : HTTP 상태에 따른 예외 처리

 

* 404 에러를 @ControllerAdvice 어노테이션이 적용된 객체에서 처리하기 위해서는 web.xml 을 수정해야합니다.

→ <init-param> 태그에 throwExceptionIfNoHandlerFound 파라미터 추가

<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>

 

'개발 > spring' 카테고리의 다른 글

[Spring] 스프링 첨부 파일 업로드  (0) 2022.08.30
[Spring] 스프링(Spring) Controller의 리턴 타입  (0) 2022.08.16

+ Recent posts