개발/[Spring] 블로그 만들기

[코드로 배우는 스프링 웹 프로젝트] 6강. 스프링 MVC의 Controller 2 (Model, Controller의 Return / Exception 처리)

ee2ee2 2021. 12. 17. 00:48
728x90
반응형

해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.


 

 

[코드로 배우는 스프링 웹 프로젝트] 6강. 스프링 MVC의 Controller 1 (어노테이션, @RequestMapping 활용,

해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다. 스프링 MVC를 이용하는 경우 작성되는 Controller는 아래와 같은 특징이 있다. -. HttpServletRequest, HttpServletResponse

ee2ee2.tistory.com


위 글에 이어서 작성하는 글이다!

 

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>

 

실행 결과

page 의 경우 JAVA Beans에 정의되어있지 않으므로, 전달이 안되었음

스프링 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.......");
	}

실행 결과

호출하는&amp;nbsp; URL과 동일한 이름의 jsp 호출

이유?

servlet-context.xml에서 아래 설정과 같이 URL 경로를 View로 처리하기 때문에

servlet-context.xml 일부


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>

실행결과

에러 메세지를 출력하기 위해 고의로 파라미터 타입과 다른 값을 입력해본다.

except() 함수를 통해 error_page.jsp가 호출됨을 확인할 수 있다.

 

만약, 에러를 따로 처리하지 않았다면?


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

 

다음 시간부터는 본격적으로 웹 게시물 관리에 대해 진행해본다!