해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.
위 글에 이어서 작성하는 글이다!
4) Model이라는 데이터 전달자
: Model이라는 객체는 JSP 컨트롤러에서 생성된 데이터를 담아서 전달하는 역할을 하는 존재이다. 이를 이용해서, JSP와 같은 뷰(View)로 전달해야하는 데이터를 담아서 보낼 수 있다.
(Method의 파라미터를 Model 타입으로 선언하게 되면 자동으로 스피링 MVC에서 Model타입의 객체를 만들어 주므로, 개발자의 입장에서는 필요한 데이터를 담아주는 작업만 하면 됨)
1. @ModelAttribute 어노테이션
: 웹 페이지 구조는 Request에 전달된 데이터를 가지고, 피요하다면 추가적인 데이터를 생성해서 화면으로 전달하는 방식으로 동작한다. 이 때, Model의 경우 파라미터로 전달된 데이터는 존재하지 않지만 화면에서 필요한 데이터를 전달하기 위해 사용한다.
SampleController.java에 추가
@GetMapping("/ex04")
public String ex04(SampleDTO dto, int page) {
//http://localhost:8080/sample/ex04?name=AAA&age=20&page=3
log.info("dto : " + dto);
log.info("page : " + page);
return "/sample/ex04";
}
/WEB-INF/views/sample 위치에 ex04.jsp 생성
ex04.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>
<h2>SAMPLESTO ${sampleDTO }</h2>
<h2>PAGE ${page}</h2>
</body>
</html>
실행 결과
스프링 MVC의 Controller는 기본적으로 Java Beans 규칙에 맞는 객체는 다시 화면으로 객체를 전달함.
위 문제를 해결하기 위해, @ModelAttribute를 사용한다.
SampleController.java 추가
@GetMapping("/ex04_2")
public String ex04_2(SampleDTO dto, @ModelAttribute("page") int page) {
//http://localhost:8080/sample/ex04_2?name=AAA&age=20&page=3
log.info("dto : " + dto);
log.info("page : " + page);
return "/sample/ex04";
}
<@ModelAttribute("page")와 같이 항상 값(value)을 지정해줘야 JSP에서 ${page}와 같이 받을 수 있다!>
실행 결과
5) Controller의 리턴 타입
<주요 Return 타입>
-. String : JSP를 이용하는 경우에는 JSP 파일의 경로와 파일이름을 나타내기 위해 사용
-. void : 호출하는 URL과 동일한 이름의 jsp를 의미
-. VO(Value Object), DTO(Data Transfer Object) : 주로 JSON 타입의 데이터를 반환하는 용도로 사용
-. ResponseEntity 타입 : response할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용
-. Model, ModelAndView : Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용 (최근에는 사용안하는 추세)
-. HttpHeaders : 응답에 내용없이 Http 헤더 메세지만 전달하는 용도로 사용
1. void 타입
SampleController.java 추가
@GetMapping("/ex05")
public void ex05() {
log.info("/ex05.......");
}
실행 결과
이유?
servlet-context.xml에서 아래 설정과 같이 URL 경로를 View로 처리하기 때문에
2. String 타입
앞서 작성한 예제들이 모두 String 타입의 반환이었으므로, 예제 소스만 첨부한다.
SampleController.java 일부
@GetMapping("/ex04")
public String ex04(SampleDTO dto, int page) {
//http://localhost:8080/sample/ex04?name=AAA&age=20&page=3
log.info("dto : " + dto);
log.info("page : " + page);
return "/sample/ex04";
}
3. 객체 타입 : VO(Value Object), DTO(Data Transfer Object)
: 이 경우는 주로 JSON 데이터를 만들어내는 용도로 사용한다. 이를 위해서는 pom.xml에 라이브러리 하나를 추가해줘야한다.
pom.xml
<!-- Controller의 메소드 리턴 타입을 VO(Value Object)나 DTO(Data transfer Object) 타입 들 복합적 데이터가 들어간 객체타입으로 지정 가능
이를 위해, 아래 라이브러리 추가 필요 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
SampleController.java 일부
@GetMapping("/ex06")
public @ResponseBody SampleDTO ex06() {
log.info("/ex06.......");
SampleDTO dto = new SampleDTO();
dto.setAge(10);
dto.setName("홍길동");
return dto;
}
@ResponseBody 란?
메소드에서 리턴되는 값은 View를 통해 출력되는 것이 아닌 HTTP Response Body 에 쓰여지도록 한다. (JSON 타입)
실행결과
4. ResponseEntity 타입
: 원하는 헤더정보나 데이터 전달을 할 수 있음.
SampleController.java 일부
@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);
}
실행결과
6) Controller의 Exception 처리
Exception 처리 방법 2가지
-. @ExceptionHandler와 @ControllerAdvice를 이용한 처리
-. @ReponseEntity를 이용한 예외 메세지 구성
1. @ControllerAdvice
: @ControllerAdvice는 AOP(Aspect-Oriented-Programming)를 이용한 방식이다.
간단하게 말하면, 핵심 로직은 아니지만 프로그램에서 필요한 '공통 관심사'를 분리하여 관리하는 것이다. 이 방식은 중복적인 코드의 양을 줄이고 유지보수에 유용하다.
예시를 위해, 패키지 및 클래스를 생성해본다.
org.zerock.exception 패키지 내 CommonExceptionAdvice.java 생성
CommonExceptionAdvice.java
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) : 해당 메소드가 ()에 들어가는 예외타입을 처리한다는 것을 뜻함.
* -> 이 경우, Exception.class을 지정하였으므로. 모든 예외에 대한 처리가 except()만을 이용해서 처리할 수 있음.
*/
org.zerock.exception 패키지는 현재 servlet-context.xml에서 인식하지 않기 때문에 인식할 수 있도록 아래와 같이 추가해준다.
servlet-context.xml
...생략
<context:component-scan base-package="org.zerock.controller" />
<context:component-scan base-package="org.zerock.exception" />
</beans:beans>
위, CommonExceptionAdvice. 클래스의 except()함수의 Return값은 String이므로, WEB-INF/views 폴더 내에 error_page.jsp 파일을 생성해주자.
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 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>This is ERROR PAGE</title>
</head>
<body>
<h3>This is ERROR PAGE</h3> <br>
<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>
실행결과
: 에러 메세지를 출력하기 위해 고의로 파라미터 타입과 다른 값을 입력해본다.
만약, 에러를 따로 처리하지 않았다면?
JAVA 설정을 하는 경우
: 패키지 및 클래스 모두 동일하게 생성 후, ServletConfig.java 파일에 해당 패키지 조사 가능하도록 추가
ServletConfig.java 일부
@EnableWebMvc
@ComponentScan(basePackages = {"org.zerock.controller","org.zerock.exception"})
public class ServletConfig implements WebMvcConfigurer{
... 생략
}
2. 404 에러 페이지 생성하기
: 가장 흔한 에러는 주로 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>
<!-- 스프링 MVC의 모든 요청은 DispatcherServletd에서 처리되므로 404에러도 같이 처리할 수 있도록 함 -->
<!-- 기존 404에러 페이지 뜨기 전에 여기서 먼저 캐치해서 커스텀 에러페이지를 띄우도록 함. -->
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
CommonExceptionAdvice.java 일부
: 입력된 URL의 페이지를 찾을 수 없는 경우(404)에러 일 때, handler404()함수가 동작하도록 함.
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handler404(NoHandlerFoundException ex) {
return "error_404";
}
error_404.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 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>
<h1>해당 URL은 존재하지 않습니다.</h1>
</body>
</html>
실행결과
: 존재하지 않는 URL을 호출하면 error_404.jsp 페이지가 보이는 것을 확인할 수 있음
(/sample/* 로 시작하는 URL의 경우레는 SampleController가 무조건 동작하므로, 이를 제외한 임의의 URL로 테스트 하도록 하자.)
JAVA 설정을 하는 경우
: 위와 다른 내용은 모두 동일하게 생성 진행 후, web.xml 수정 부분을 아래와 같이 WebConfig.java에서 추가하도록 함.
WebConfig.java 일부
protected void customizingRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
다음 시간부터는 본격적으로 웹 게시물 관리에 대해 진행해본다!
'개발 > [Spring] 블로그 만들기' 카테고리의 다른 글
[코드로 배우는 스프링 웹 프로젝트] 8강. 영속(Peristence) / 비즈니스(Business) 계층의 CRUD 구현하기 (0) | 2021.12.22 |
---|---|
[코드로 배우는 스프링 웹 프로젝트] 7강. 스프링 MVC 프로젝트의 기본 구성 (0) | 2021.12.21 |
[코드로 배우는 스프링 웹 프로젝트] 6강. 스프링 MVC의 Controller 1 (어노테이션, @RequestMapping 활용, 파라미터 수집) (0) | 2021.12.11 |
[코드로 배우는 스프링 웹 프로젝트] 5강. 스프링 MVC의 기본 구조 (0) | 2021.12.09 |
[코드로 배우는 스프링 웹 프로젝트] 4강. MyBatis와 스프링 연동 (0) | 2021.11.30 |