'전체 글'에 해당되는 글 508건

반응형

WAS의 구동 중 가장 흔한 에러와 관련된 HTTP 상태 코드는 '404'와 '500' 에러코드입니다. 500 메시지는 'Internal Server Error'이므로 @ExceptionHandler를 이용해서 처리되지만, 잘못된 URL을 호출할 때 보이는 404 에러 메시지의 경우 조금 다르게 처리하는 것이 좋습니다.

 

  서블릿이나 JSP를 이용했던 개발 시에는 web.xml을 이용해서 별도의 에러 페이지를 지정할 수 있습니다. 에러 발생 시 추가적인 작업을 하기는 어렵기 때문에 스프링을 이용해서 404와 같이 WAS 내부에서 발생하는 에러를 처리하는 방식을 알아두는 것이 좋습니다.

 

  스프링 MVC의 모든 요청은 DispatcherServlet을 이용해서 처리되므로 404에러도 같이 처리할 수 있도록 web.xml을 수정합니다.

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- 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>
		<!-- 404 error page -->
		<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>

</web-app>

web.xml의 일부를 수정해주었습니다.

 

org.zerock.exception.CommonExceptionAdvice에는 다음과 같이 메서드를 추가합니다.

package org.zerock.exception;

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
@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";
	}
	
	@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>

  브라우저에서 존재하지 않는 URL을 호출하면 custom404.jsp 페이지가 보이는 거슬 확인할 수 있습니다.

  (책에 나와 있는 예제는 'sample/.. 로 시작하는 URL의 경우에는 SampleController가 무조건 동작하므로 이를 제외한 경로 테스트합니다.)

 

출처: 코드로 배우는 스프링 웹 프로젝트.

반응형
블로그 이미지

꽃꽂이하는개발자

,
반응형

Controller를 작성할 때 예외 상황을 고려하면 처리해야 하는 작업이 엄청나게 늘어날 수 밖에 없습니다. 스프링 MVC에서는 이러한 작업을 다음과 같은 방식으로 처리할 수 있습니다.

 

- @ExceptionHandler와 @ControllerAdvice를 이용한 처리

- @ResponseEntity를 이용하는 예외 메시지 구성

 

@ControllerAdvice

@ControllerAdvice는 나중에 알게되는 AOP(Aspect-Oriented-Programming)를 이용하는 방식입니다. AOP에 대해서는 별도의 파트에서 설명하겠지만, 간단히 언급하자면 핵심적인 로직은 아니지만 프로그램에서 필요한 '공통적인 관심사(cross-concern)는 분리'하자는 개념입니다. Controller를 작성할 때는 메서드의 모든 예외사항을 전부 핸들링 해야 한다면 중복적이고 많은 양의 코드를 작성해야 하지만, AOP방식을 이용하면 공통적인 예외사항에 대해서는 별도로 @ControllerAdvice를 이용해서 분리하는 방식입니다.

 

예제를 위해 프로젝트에 패키지를 생성하고, CommonExceptionAdvice 클래스를 생성하겠습니다.

CommonExceptionAdvice는 @ControllerAdvice 어노테이션을 적용하지만 예외 처리를 목적으로 생성하는 클래스이므로 별도의 로직을 처리하지는 않습니다.

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";
	}
}

   CommonExceptionAdvice 클래스에는 @ControllerAdvice라는 어노테이션과 @ExceptionHandler라는 어노테이션을 사용하고 있습니다. @ControllerAdvice는 해당 객체가 스프링의 컨트롤러에서 발생하는 예외를 처리하는 존재임을 명시하는 용도로 사용하고, @ExceptionHandler는 해당 메서드가 () 들어가는 예외 타입을 처리한다는 것을 의미합니다. @ExceptionHandler 어노테이션의 속성으로는 Exception 클래스 타입을 지정할 수 있습니다. 위와 같은 경우 Exception.class를 지정하였으므로 모든 예외에 대한 처리가 except()만을 이용해서 처리할 수 있습니다.

 

  만일 특정한 타입의 예외를 다루고 싶다면 Exception.class 대신에 구체적인 예외의 클래스를 지정해야 합니다. JSP화면에서도 구체적인 메시지를 보고 싶다면 Model을 이용해서 전달하는 것이 좋습니다. org.zerock.exception 패키지는 servlet-context.xml에 인식하지 않기 때문에 <component-scan>을 이용해서 해당패키지의 내용을 조사하도록 해야 합니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<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>
	
	<context:component-scan base-package="org.zerock.controller" />
	<context:component-scan base-package="org.zerock.exception" />
	
	
	
	
</beans:beans>

CommonExceptionAdvice의 except()의 리턴값은 문자열이므로 JSP파일의 경로가 됩니다. JSP는 error_page.jsp이므로 /WEB-INF/views 폴더 내에 작성해야 합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!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>

예외의 메시지가 정상적으로 출력되는지 확인해 보려면 고의로 숫자나 날짜 등의 파라미터 값을 변환에 문제 있게 만들어서 호출해 볼 수 있습니다. 예를 들어, '/sample/ex-94?name=aaa&age=11&page=9'와 호출해야 하는 URL에서 고의로 age 값을 숫자로 변환할 수 없게 다른 값을 전달해 보거나 page와 같은 파라미터를 생략하는 등의 작업을 통해서 확인할 수 있습니다. 

 

 

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

스프링 MVC 프로젝트의 기본 구성  (0) 2020.05.11
404 에러 페이지  (0) 2020.05.11
파일 업로드 처리  (0) 2020.05.08
Controller의 리턴 타입  (0) 2020.05.06
RedirectAttributes  (0) 2020.05.06
블로그 이미지

꽃꽂이하는개발자

,
반응형

  Controller의 많은 작업은 스프링 MVC를 통해서 처리하기 때문에 개발자는 자신이 해야 하는 역할에만 집중해서 코드를 작성할 수 있지만, 조금 신경써야 하는 부분이 있다면 파일을 업로드하는 부분에 대한 처리힐 것입니다. 파일 업로드를 하기 위해서는 전달되는 파일 데이터를 분석해야 하는데, 이를 위해서 Servlet 3.0 전까지는 commons의 파일 업로드를 이용하거나 cos.jar 등을 이용해서 처리를 해 왔습니다. Servlet 3.0 이후(Tomcat 7.0)에는 기본적으로 업로드 되는 파일을 처리할 수 있는 기능이 추가되어 있으므로 더 이상 추가적인 라이브러리가 필요로 하지 않습니다.

 

  조금 아쉬운 점은 'Spring Legacy Project'로 생성되는 프로젝트의 경우 Servlet 2.5를 기준으로 생성되기 때문에 3.0 이후에 지원되는 설정을 사용하기 어렵다는 점입니다. 3.0이상의 파일 업로드 방식은 후에 다루도록 하고 예제는 일반적으로 많이 사용하는 commons-fileupload를 이용하겠습니다.

 

pom.xml에 추가되는 commons-fileupload 라이브러리

		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.3</version>
		</dependency>

라이브 러리를 추가한 후 파일이 임시로 업로드될 폴더를 C:\upload\tmp 로 작성합니다.

 

servlet-context.xml 설정

servlet-context.xml은 스프링 MVC의 특정한 객체(빈)을 설정해서 파이릉ㄹ 처리합니다. 다른 객체(Bean)를 설정하는 것과 달리 파일 업로드의 경우에는 반드시 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에 임시 파일의 형태로 보관됩니다. uploadempDir에서 절대 경로를 이용하려면 URI형태로 제공해야 하기 때문에 'file:/'로 시작하도록 합니다.

  defaultEncoding은 업로드 하는 파일의 이름이 한글일 경우 깨지는 문제를 처리합니다.

 

SampleController에서는 다음과 같이 get방식으로 파일을 업로드할 화면을 처리합니다.

package org.zerock.controller;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.zerock.domain.SampleDTO;
import org.zerock.domain.SampleDTOList;
import org.zerock.domain.TodoDTO;

import lombok.extern.log4j.Log4j;



@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(dateFormat, false));
	}
	
	@RequestMapping("")
	public void basic() {
		log.info("basic.....");
	}
	
	@RequestMapping(value="/basic", method = {RequestMethod.GET, RequestMethod.POST})
	public void basicGet() {
		log.info("basic get..........");
	}
	
	@GetMapping("/basicOnlyGet")
	public void basicGet2() {
		log.info("basic get only get..........");
	}
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);
		
		return "ex01";
	}
	@GetMapping("ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
		
		log.info("name:" + name);
		log.info("age:"+ age);
	
		return "ex02";
	}
	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
		
		log.info("ids: " + ids);
		
		return "ex02List";
	}
	
	@GetMapping("/ex02Array")
	public String ex02Array(@RequestParam("ids") String[] ids) {
		
		log.info("array ids:" + Arrays.toString(ids));
		
		return "ex02Array";
	}
	
	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
		log.info("list dtos:"+list);
		return "ex02Bean";
	}
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo) {
		log.info("todo:" + todo);
		
		return "ex03";
	}
	
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, @ModelAttribute("page") int page) {
		
		log.info("dto:" + dto);
		log.info("page:" + page);
		
		return "/sample/ex04";
	}
	
	@GetMapping("/ex05")
	public void ex05() {
		log.info("/ex05........");
	}
	
	@GetMapping("/ex06")
	public @ResponseBody SampleDTO ex06() {
		log.info("/ex06.....12345");
		
		SampleDTO dto = new SampleDTO();
		dto.setAge(10);
		dto.setName("뽀야미");
		
		return dto;
	}
	@GetMapping("/ex07")
	public ResponseEntity<String> ex07(){
		log.info("/ex07........");
		
		String msg ="{\"name\": \"홍길동\"}";
		
		HttpHeaders header = new HttpHeaders();
		header.add("Content-Type", "application/json;charset=UTF-8");
		
		return new ResponseEntity<>(msg, header, HttpStatus.OK);
	}
	@GetMapping("/exUpload")
	public void exUpload() {
		log.info("/exUpload..........");
	}
}

 업로드를 해 볼 /WEB-INF/views/sample/exUpload.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>
<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는 여러 개의 파일을 한꺼번에 업로드하는 예제로 작성해 봅니다. <form>태그의 action속성, method 속성, enctype 속성에 주의해서 작성해야 합니다. 브라우저는 아래와 같은 모습으로 보입니다.

 

  exUpload.jsp의 action 속성값은 '/sample/exUploadPost'로 작성되었으며, 이에 맞는 메서드를 SampleController에 추가합니다.

package org.zerock.controller;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.zerock.domain.SampleDTO;
import org.zerock.domain.SampleDTOList;
import org.zerock.domain.TodoDTO;

import lombok.extern.log4j.Log4j;



@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(dateFormat, false));
	}
	
	@RequestMapping("")
	public void basic() {
		log.info("basic.....");
	}
	
	@RequestMapping(value="/basic", method = {RequestMethod.GET, RequestMethod.POST})
	public void basicGet() {
		log.info("basic get..........");
	}
	
	@GetMapping("/basicOnlyGet")
	public void basicGet2() {
		log.info("basic get only get..........");
	}
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);
		
		return "ex01";
	}
	@GetMapping("ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
		
		log.info("name:" + name);
		log.info("age:"+ age);
	
		return "ex02";
	}
	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
		
		log.info("ids: " + ids);
		
		return "ex02List";
	}
	
	@GetMapping("/ex02Array")
	public String ex02Array(@RequestParam("ids") String[] ids) {
		
		log.info("array ids:" + Arrays.toString(ids));
		
		return "ex02Array";
	}
	
	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
		log.info("list dtos:"+list);
		return "ex02Bean";
	}
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo) {
		log.info("todo:" + todo);
		
		return "ex03";
	}
	
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, @ModelAttribute("page") int page) {
		
		log.info("dto:" + dto);
		log.info("page:" + page);
		
		return "/sample/ex04";
	}
	
	@GetMapping("/ex05")
	public void ex05() {
		log.info("/ex05........");
	}
	
	@GetMapping("/ex06")
	public @ResponseBody SampleDTO ex06() {
		log.info("/ex06.....12345");
		
		SampleDTO dto = new SampleDTO();
		dto.setAge(10);
		dto.setName("뽀야미");
		
		return dto;
	}
	@GetMapping("/ex07")
	public ResponseEntity<String> ex07(){
		log.info("/ex07........");
		
		String msg ="{\"name\": \"홍길동\"}";
		
		HttpHeaders header = new HttpHeaders();
		header.add("Content-Type", "application/json;charset=UTF-8");
		
		return new ResponseEntity<>(msg, header, HttpStatus.OK);
	}
	@GetMapping("/exUpload")
	public void exUpload() {
		log.info("/exUpload..........");
	}
	
	@PostMapping("/exUploadPost")
	public void exUploadPost(ArrayList<MultipartFile> files) {
		files.forEach(file -> {
			log.info("--------");
			log.info("name:" + file.getOriginalFilename());
			log.info("size:" + file.getSize());
		});
			
	
	}
}

 MVC는 전달되는 파라미터가 동일한 이름으로 여러 개 존재하면 배열로 처리가 가능하므로 파라미터를 MultipartFile의 배열 타입으로 작성합니다. 실제로 파일을 업로드해 보면 아래와 같은 결과를 볼 수 있습니다. 현재 설정은 한 파일의 최대 크기가 2MB이므로 그보다 작은 크기의 파일을 이렇게 업로드를 테스트합니다.

  위의 그림에서 중간에 보이는 로그는 SampleController에서 업로드 정보가 올바르게 처리되는 것을 보여주고 있습니다. 최종 업로드를 하려면 byte[]를 처리해야 하는데 예제는 아직 처리하지 않은 상태입니다(이에 대한 예제는 뒤쪽에서 예제로 작성하겠습니다)

 

 

java설정에서 파일 업로드 처리

Java 설정을 이용하는 경우에는 @Bean을 이용해서 처리하기는 하지만, id 속성을 같이 부여합니다. servlet-context.xml과 관련된 설정이므로 ServletConfig 클래스를 이용해서 처리합니다.

package org.zerock.config;

import java.io.IOException;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

//servletConfig는 기존의 servlet-context.xml에 설정된 모든 내용을 담아야 하는데
// @EnableWebMvc 어노테이션과 WebMvcConfigurer 인터페이스를 구현하는 방식(과거에는 WebMcConfigurerAdapter 추상클래스를 사용하였으나, 스프링 5.0버전부터는 Deprecated됨)
// @Configuration과 WebConfigurationSupport 클래스를 상속하는 방식

// WebMvcConfigurer은 스프링 MVC와 관련된 설정은 메서드로 오버라이드 하는 형태를 이용할 때 사용합니다. 
// ServletConfig 클래스 역시 @ComponentScan을 이용해서 다른 패키지에 작성된 스프링의 객체를 인식가능
@EnableWebMvc
@ComponentScan(basePackages= {"org.zerock.controller"})
public class ServletConfig implements WebMvcConfigurer {
	
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		InternalResourceViewResolver bean = new InternalResourceViewResolver();
		bean.setViewClass(JstlView.class);
		bean.setPrefix("/WEB-INF/views/");
		bean.setSuffix(".jsp");
		registry.viewResolver(bean);
	}
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}

	@Bean(name = "multipartResolver")
	public CommonsMultipartResolver getResolver() throws IOException {
		CommonsMultipartResolver resolver = new CommonsMultipartResolver();
		
		//10MB
		resolver.setMaxUploadSize(1024 * 1024 * 10);
		
		//2MB
		resolver.setMaxUploadSizePerFile(1024 * 1024 * 2);
		
		//1MB
		resolver.setMaxInMemorySize(1024 * 1024);
		
		//temp upload
		resolver.setUploadTempDir(new FileSystemResource("C:\\upload\\tmp"));
		
		resolver.setDefaultEncoding("UTF-8");
		
		return resolver;
	}
}

 

 

출처: 코드로배우는 스프링 웹 프로젝트 구멍가게 코딩단 중..

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

404 에러 페이지  (0) 2020.05.11
Controller의 Exception 처리  (0) 2020.05.08
Controller의 리턴 타입  (0) 2020.05.06
RedirectAttributes  (0) 2020.05.06
@ModelAttribute 어노테이션  (0) 2020.05.06
블로그 이미지

꽃꽂이하는개발자

,
반응형
  • String: jsp를 이용하는 경우에는 jsp 파일의 경로와 파일이름을 나타내기 위해서 사용합니다.
  • void: 호출하는 URL과 동일한 이름의 jsp를 의미합니다.
  • VO, DTO 타입: 주로 JSON 타입의 데이터를 만들어서 반환하는 용도로 사용합니다.
  • ResponseEntity 타입: response 할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용합니다.
  • Model. ModelAndView: Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용합니다.
  • HttpHeaders: 응답에 없는 내용없이 Http 헤더 메시지만 전달하는 용도로 사용합니다.

 

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

 

void 타입..

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

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

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

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

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

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

 

 

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

 

 

 

--void 타입..

메서드의 리턴 타입을 void로 지정하는 경우 일반적인 경우에는 해당 URL의 경로를 그대로 JSP파일의 이름으로 사용하게 됩니다.

 

--String 타입

  상황에 따라 다른 화면을 보여줄 필요가 있을 경우 사용.(if ~ else)

  

-- 객체 타입

Controller의 메서드 리턴 타입을 VO나 DTO타입 등 복합적인 데이터가 들어간 객체 타입으로 지정할 수 있는데, 이 경우는 주로 JSON 데이터를 만들어 내는 용도로 사용합니다.

 

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

Controller의 Exception 처리  (0) 2020.05.08
파일 업로드 처리  (0) 2020.05.08
RedirectAttributes  (0) 2020.05.06
@ModelAttribute 어노테이션  (0) 2020.05.06
Model이라는 데이터 전달자  (0) 2020.05.06
블로그 이미지

꽃꽂이하는개발자

,
반응형

일회성으로 데이터를 전달하는 용도.

 

Servlet에서 redirect방식

response.sendRedirect("/home?name=aaa&age=10");

 

스프링 MVC를 이용하는 경우

rttr.addFlashAttribute("name", "AAA");
	rrtr.addFlashAttribute("age", 10);
    
    return "redirect:/";
    

 

RedirectAttributes는 Model과 같이 파라미터로 선언해서 사용.

addFlashAttribute(이름, 값) 메서드를 이용해서 화면에 한 번만 사용하고 다음에는 사용되지 않는 데이터를 전달하기 위해서 사용합니다.

 

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

파일 업로드 처리  (0) 2020.05.08
Controller의 리턴 타입  (0) 2020.05.06
@ModelAttribute 어노테이션  (0) 2020.05.06
Model이라는 데이터 전달자  (0) 2020.05.06
@DateTimeFormat  (0) 2020.05.06
블로그 이미지

꽃꽂이하는개발자

,
반응형
package org.zerock.controller;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.zerock.domain.SampleDTO;
import org.zerock.domain.SampleDTOList;
import org.zerock.domain.TodoDTO;

import lombok.extern.log4j.Log4j;



@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(dateFormat, false));
	}
	
	@RequestMapping("")
	public void basic() {
		log.info("basic.....");
	}
	
	@RequestMapping(value="/basic", method = {RequestMethod.GET, RequestMethod.POST})
	public void basicGet() {
		log.info("basic get..........");
	}
	
	@GetMapping("/basicOnlyGet")
	public void basicGet2() {
		log.info("basic get only get..........");
	}
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);
		
		return "ex01";
	}
	@GetMapping("ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
		
		log.info("name:" + name);
		log.info("age:"+ age);
	
		return "ex02";
	}
	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
		
		log.info("ids: " + ids);
		
		return "ex02List";
	}
	
	@GetMapping("/ex02Array")
	public String ex02Array(@RequestParam("ids") String[] ids) {
		
		log.info("array ids:" + Arrays.toString(ids));
		
		return "ex02Array";
	}
	
	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
		log.info("list dtos:"+list);
		return "ex02Bean";
	}
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo) {
		log.info("todo:" + todo);
		
		return "ex03";
	}
	
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, int page) {
		
		log.info("dto:" + dto);
		log.info("page:" + page);
		
		return "/sample/ex04";
	}
}

SampleController

package org.zerock.domain;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.Data;

@Data
public class TodoDTO {

	private String title;
	
	@DateTimeFormat(pattern = "yyyy/MM/dd")
	private Date dueDate;
}

TodoDTO

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>

	<h2>SAMPLEDTO ${sampleDTO }</h2>
	<h2>page ${page} </h2>
</body>
</html>

ex04.jsp

 

 

출처는 구멍가게코딩단의 코드로배우는 스프링 웹 프로젝트의 일부분입니다.

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

Controller의 리턴 타입  (0) 2020.05.06
RedirectAttributes  (0) 2020.05.06
Model이라는 데이터 전달자  (0) 2020.05.06
@DateTimeFormat  (0) 2020.05.06
@InitBinder  (0) 2020.04.29
블로그 이미지

꽃꽂이하는개발자

,
반응형

  Controller의 메서드를 작성할 때는 특별하게 Model이라는 타입을 파라미터로 저장할 수 있습니다. Model 객체는 JSP에 컨트롤러에서 생성된 데이터를 담아서 전달하는 역할을 하는 존재입니다. 이르르 이용해서 JSP와 같은 뷰로 전달해야 하는 데이터를 담아서 보낼 수 있습니다. 메서드의 파라미터에 Model 타입이 지정된 경우에는 스프링은 특별하게 Model 타입의 객체를 만들어서 메서드에 주입하게 됩니다.

 

  Model은 모델 2방식에서 사용하는 request.setAttribute()와 유사한 역할을 합니다. Servlet을 이용해 본 적이 있다면 다음과 같은 코드에 익숙할 것입니다.

 

request.setAttribute("serverTime", new java.util.Date());

requestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/jsp/home.jsp");

dispatcher.forward(request, response);

 

위의 코드를 스프링에서는 Model을 이용해서 다음과 같이 처리하게 됩니다.

 

public String home(Model model){

	model.addAttribute("servletTime", new java.util.Date());
    
    return "home";
    
   }

 

  메서드의 파라미터를 Model 타입으로 선언하게 되면 자동으로 스프링 MVC에서 Model 타입의 객체를 만들어 주기 때문에 개발자의 입장에서는 필요한 데이터를 담아주는 작업만으로 모든 작업이 완료 됩니다. Model을 사용해야 하는 경우는 주로 Controller에 전달된 데이터를 이용해서 추가적인 데이터를 가져와야 하는 상황입니다.

 

  예를 들어, ,다음과 같은 경우들을 생각해 볼 수 있습니다.

- 리스트 페이지 번호를 파라미터로 전달받고, 실제 데이터를 View로 전달해야 하는 경우

- 파라미터들에 대한 처리 후 결과를 전달해야 하는 경우.

 

 

출처는 구멍가게 코딩단의 코드로 배우는 스프링 웹 프로젝트입니다.

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

RedirectAttributes  (0) 2020.05.06
@ModelAttribute 어노테이션  (0) 2020.05.06
@DateTimeFormat  (0) 2020.05.06
@InitBinder  (0) 2020.04.29
낚서장.. 타이핑 연습?  (0) 2020.04.24
블로그 이미지

꽃꽂이하는개발자

,
반응형

@InitBinder를 이용해서 날짜를 변환할 수도 있지만, 파라미터로 사용되는 인스턴스 변수에 @DataTimeFormat을 적용해도 변환이 가능하다.(@DateTimeFormat을 이용하는 경우에는 @InitBinder는 필요하지 않습니다.)

 

package org.zerock.domain;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.Data;

@Data
public class TodoDTO {

	private String title;
	
	@DateTimeFormat(pattern = "yyyy/MM/dd")
	private Date dueDate;
}

 

 

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

 

주소창에 입력을 해 봅시다

http://localhost:8080/sample/ex03?title=test&dueDate=2018-01-01

불러오는 중입니다...

 

그러면 

아래와 같이 변환됩니다.

반응형

'2020 > 주저리 주저리 타이핑.. 낙서장.' 카테고리의 다른 글

@ModelAttribute 어노테이션  (0) 2020.05.06
Model이라는 데이터 전달자  (0) 2020.05.06
@InitBinder  (0) 2020.04.29
낚서장.. 타이핑 연습?  (0) 2020.04.24
스프링 MVC의 Controller  (0) 2020.04.24
블로그 이미지

꽃꽂이하는개발자

,