스프링 - Part 2 - 스프링 MVC 설정 02
https://rogi221.tistory.com/144
스프링 - Part 2 - 스프링 MVC 설정 01
MVC(Model-View-Controller) 대부분의 서블릿 기반 프레임워크들이 사용하는 방식 데이터와 처리, 화면을 분리하는 방식 웹에서는 Model 2 방식으로 표현 스프링과 스프링 MVC 스프링 프레임워크 Core + 여
rogi221.tistory.com
Model이라는 데이터 전달자
Controller의 메서드를 작성할 때는 특별하게 Model이라는 타입을 파라미터로 지정할 수 있다. Model 객체는 JSP에 컨트롤러에 생성된 데이터를 담아서 전달하는 역할을 하는 존재이다.
이를 이용해서 JSP와 같은 뷰(View)로 전달해야 하는 데이터를 담아서 보낼 수 있다. 메서드의 파라미터에 Model타입이 지정된 경우에는 스프링은 특별하게 Model 타입의 객체를 만들어서 메서드에 주입하게 된다.
@ModelAttribute 어노테이션
SampleController에 추가
// SampleController.java
... (생략) ...
@GetMapping("/ex04")
public String ex04(SampleDTO dto, int page) {
log.info("dtd: " + dto);
log.info("page: "+ page);
return "/sample/ex04";
}
}
ex04.jsp
// ex04.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>SAMPLEDTO ${ sampleDTO }</h2>
<h2>PAGE ${page }</h2>
</body>
</html>
http://localhost:8080/sample/ex04?name=aaa&age=11&page=9
@ModelAttribute 강제로 전달받은 파라미터를 Model에 담아서 전달하도록 할 때 필요한 어노테이션
//SampleController.java
... (생략) ...
@GetMapping("/ex04")
public String ex04(SampleDTO dto,@ModelAttribute("page") int page) {
log.info("dtd: " + dto);
log.info("page: "+ page);
return "/sample/ex04";
}
}
Controller 의 리턴 타입
스프링 MVC의 구조가 기존의 상속과 인터페이스에서 어노테이션을 사용하는 방식으로 변한 이후에 가장 큰 변화 중 하나는 리턴 타입이 자유로워 졌다는 점이다.
Controller의 메서드가 사용할 수 있는 리턴 타입
- String : jsp를 이용하는 경우에는 jsp 파일의 경로와 파일 이름을 나타내기 위해서 사용한다
- void : 호출하는 URL과 동일한 이름의 jsp를 의미한다
- VO, DTO 타입 : 주로 JSON 타입의 데이터를 만들어서 만환하는 용도로 사용한다.
- ResponseEntity 타입 : response 할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용한다
- Model, ModelAndView : Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용한다 (최근에는 많이 사용하지 않는다)
- HttpHeaders : 응답에 내용없이 Http 헤더 메세지만 전달하는 용도로 사용한다.
Void 타입
// SampleController.java
... (생략) ...
@GetMapping("/ex05")
public void ex05() {
log.info("/ex05........");
}
views/sample/ex05.jsp 파일을 넣으면 에러가 뜨지 않
String 타입
void 타입과 더불어 가장 많이 사용하는 것은 String 타입이다. String 타입은 상황에 따라 다른 화면을 보여줄 필요가 있을 경우에 유용하게 사용한다.
일반적으로 String 타입은 현재 프로젝트의 경우 JSP 파일의 이름을 의미한다.
객체 타입
Controller의 메서드 리턴 타입을 VO(Value Object)나 DTO(Data Transfer Object) 타입 등 복합적인 데이터가 들어간 객체 타입으로 지정할 수 있는데, 이 경우는 주로 JSON 데이터를 만들어 내는 용도로 사용한다.
pom.xml에 추가되는 Jackson -databind 라이브러리
// pom.xml
... (생략) ...
// 추가
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
SampleController의 일부
// SampleController.java
... (생략) ...
// 추가
@GetMapping("/ex06")
public @ResponseBody SampleDTO ex06() {
log.info("/ex06.........");
SampleDTO dto = new SampleDTO();
dto.setAge(10);
dto.setName("홍길동");
return dto;
}
http://localhost:8080/sample/ex06
ResponseEntity 타입
// SampleController.java
... (생략) ...
// 추가
@GetMapping("/ex07")
public ResponseEntity<String> ex07(){
log.info("/ex07............");
// {"name" : "홍길동"}
String msg = "{\"name\": \"홍길동\"}";
HttpHeaders header = new HttpHeaders();
header.add("Content-Type", "application/json;charset=UTF-8");
return new ResponseEntity<>(msg, header, HttpStatus.OK);
}
http://localhost:8080/sample/ex07
파일 업로드 처리
pom.xml에 추가되는 commons-fileupload 라이브러리
// pom.xml
... (생략) ...
// 추가
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
라이브러리를 추가한 후 파일이 임시로 업로드될 폴더를 C 드라이브 아래 upload/tmp로 작
servlet-context.xml의 일부
// servlet-context.xml
... (생략) ...
<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>
<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>
SampleController.java 에 추
// SampleController.java
... (생략) ...
@GetMapping("/exUpload")
public void exUpload() {
log.info("/exUpload........");
}
}
exUpload.jsp
// exUpload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; 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>
SampleController 의 일부 추가
// SampleController.java
... (생략) ...
@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이므로 그보다 작은 크기의 파일을 지정해서 업로드를 테스트 한다.
Controller의 Exception 처리
Controller를 작성할 때 예외 상황을 고려하면 처리해야 하는 작업이 엄청나게 늘어날 수 밖에 없다. 스프링 MVC에서는 이러한 작업을 다음과 같은 방식으로 처리할 수 있다.
- @ExceptionHandler와 @ControllerAdvice를 이용한 처리
- @ResponseEntity를 이용하는 예외 메세지 구성
@ControllerAdvice
// CommonExceptionAdvice.java
package org.codehows.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 CommonExceptionAdvie {
@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";
}
}
// servlet-context.xml
... (생략) ...
<context:component-scan base-package="org.codehows.controller" />
<context:component-scan base-package="org.codehows.exception" />
</beans:beans>
// 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>
http://localhost:8080/sample/ex04?name=aaa&age=bbb&page=9
404 에러 페이지
WAS의 구동중 가장 흔한 에러와 관련된 HTTP 상태 코드는 404와 500 에러 코드이다.
500 메세지는 'Internal Server Error' 이므로 @ExceptionHandler를 이용해서 처리되지만, 잘못 URL을 호출할 때 보이는 404 에러 메세지의 경우 조금 다르게 처리하는 것이 좋다.
// 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>
// CommonExceptionAdvice.java
... (생략) ...
@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>