[Spring] 8.Controller




스프링 MVC 컨트롤러



스프링 MVC를 공부하는 데 있어서 필수적인 내용은 컨트롤러를 어떻게 만들고, 처리하는가입니다!

우선 MVC 컨트롤러가 무엇을 처리하는지부터 알아보겠습니다~!

  • 파라미터 수집 : 웹에서 가장 많이하는 작업은 사용자의 요청에 필요한 데이터를 추출하고, 이를 VO(Value Object) 혹은 DTO(Data Transfer Object)로 변환하는 파라미터의 수집작업입니다.



  • 애노테이션을 통한 간편 설정 : 개발자는 클래스나 메소드의 선언에 필요한 애노테이션을 추가함으로써 요청이나 응답에 필요한 모든 처리를 완료할 수 있습니다.



  • 로직의 집중 : 기존의 모델 2는 특정한 URI마다 컨트롤러를 개발하는 경우가 많았지만 스프링 MVC 컨트롤러의 경우 각 메소드마다 필요한 애노테이션을 설정할 수 있기 떄문에 여러 메소드를 하나의 컨트롤러에 집중해서 작업할 수 있습니다.



  • 테스트의 편리함 : 스프링은 테스트 모듈을 사용해서 스프링 MVC로 작성된 코드를 WAS의 실행없이도 테스트할 수 있습니다.




스프링 MVC 에서 주로 사용하는 애노테이션의 종류




스프링 MVC에서는 컨트롤러가 상속 등의 전통적인 기법을 사용하지 않는 대신에 애노테이션으로 많은 일을 처리합니다!


image




Spring Project의 servlet-context.xml 파일



컨트롤러의 개발과 관련해서 가장 먼저 알아야하는 내용은 스프링 MVC 컨트롤러가 어떤 설정을 통해서 동작하는지에 대한 이해입니다!

servlet-context는 웹과 관련된 자원을 관리하는 파일이죠~! 스프링 MVC 관련 설정만을 분리하기 위해서 만들어진 파일입니다!

servlet-context를 보면 controller가 어떻게 동작하는지 알 수 있습니다.


<!-- servlet-context.xml 주요부분 -->

	<!-- 클래스 선언에 애노테이션을 이용해서 컨트롤러를 작성할 수 있다는 선언 -->
	<annotation-driven />

	<!-- 웹에서 이미지나 CSS,JavaScript 파일과 같이 고정된 자원들의 위치를 의미 -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- 뷰를 어떻게 처리하는가에 대한 설정 -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<!-- WEB-INF 는 절대로 브라우저에서 직접 접근할 수 없는 경로이기에 컨트롤러에 의해 접근-->
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
    <!-- base-package 속성 값에 해당하는 패키지 내부의 클래스들을 조사한다는 뜻-->
	<context:component-scan base-package="io.github.kookyungmin" />




컨트롤러 실습 - void 리턴 타입의 경우



이제부터 컨트롤러를 간단하게 작성하고 URI에 따른 결과를 보겠습니다!

그 전에 src/main/java 에 controller 폴더를 추가해줍니다!


image



먼저 컨트롤러의 메소드 리턴 타입이 void인 경우를 봐보겠습니다!


//SampleContoller.java

package io.github.kookyungmin.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

//컨트롤러
@Controller
public class SampleController {
	
	//로그를 남겨줌
	private static final Logger logger = 
		LoggerFactory.getLogger(SampleController.class);
	
	//void return 타입
	@RequestMapping("doA") //URI에 doA 가 입력되면 doA()가 호출됨
	public void doA() {
		logger.info("doA 호출됨");
	}
}



Run on Server를 해보겠습니다!


image



Spring 로그를 보면, URI 에 “/”만 입력되면 HomeController가 “/doA”가 입력되면 SampleController가 호출되는 것을 볼 수 있습니다!

한 번 접속해보겠습니다!


image




image



현재 /kookyungmin/WEB-INF/views/ 경로에 doA.jsp 파일이 없기에 페이지 오류가 떴지만,


image



접속로그를 보면 SampleController의 doA가 제대로 호출함을 알 수 있습니다!


String 리턴 타입인 경우



만일 컨트롤러에서 메소드의 리턴 타입이 문자열인 경우라면 결과는 ‘문자열+.jsp’ 파일을 찾아서 실행하게 됩니다~! (servlet-context에서 prefix, subfix 와 연관)


//SampleContoller.java


//컨트롤러
@Controller
public class SampleController {
	
	//로그를 남겨줌
	private static final Logger logger = 
		LoggerFactory.getLogger(SampleController.class);
	
	//String return 타입
	@RequestMapping("doB") //URI에 /doB 가 입력되면 doB()가 호출됨
	public String doB(@ModelAttribute("msg") String msg) {
		logger.info("doB 호출됨");
		return "result";
	}
}



doB() 메소드 내의 파라미터에 사용된 @ModelAttibute(“msg”)는 요청 시 msg 이름의 파라미터를 문자열로 처리해주고, 뷰에 전달되도록 합니다.

쉽게 말해서 자동으로 모델 객체를 생성해서 String 타입의 msg 속성을 추가해서 뷰에 전달하는 것입니다!


// /WEB-INF/views/result.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>result.jsp</title>
</head>
<body>
    <!-- 전달받은 model의 속성 msg에 접근 -->
    <span>Hello ${msg}</span>
</body>
</html>




image




만들어진 결과 데이터를 전달해야 하는 경우




컨트롤러를 제작하면서 가장 많이 하는 작업은 다른 객체의 도움을 받아 만들어진 데이터를 뷰로 전달하는 일을 하는 것입니다!

이 때는 스프링 MVC의 Model 객체를 사용해서 간편하게 처리할 수 있습니다!

간단하게 실습해보겠습니다!

먼저, 데이터를 담을 객체인 ProductVO를 만들겠습니다.


package io.github.kookyungmin.domain;

public class ProductVO {
	private String name;
	private double price;
	
	public ProductVO(String name, double price) {
		super();
		this.name = name;
		this.price = price;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "ProductVO [name=" + name + ", price=" + price + "]";
	}
}




//SampleContoller.java


//컨트롤러
@Controller
public class SampleController {
	
	//로그를 남겨줌
	private static final Logger logger = 
		LoggerFactory.getLogger(SampleController.class);
	
	//만들어진 결과 데이터를 전달
	@RequestMapping("doC")
	public String doC(Model model) {
		//sample date 만듬
		ProductVO product = new ProductVO("Sample Product", 10000);
		
		logger.info("doC 호출됨");
		
		model.addAttribute(product);
		return "productDetail";
	}
}



위의 코드를 보시면 doC() 메소드의 선언에 특이하게도 Model이라는 클래스를 파라미터로 사용한 것을 볼 수 있습니다. Model 클래스는 스프링 MVC에서 기본적으로 제공되는 클래스로,

이 클래스의 용도는 뷰에 원하는 데이터를 전달하는 일종의 컨테이너나 상자의 역할을 한다고 생각하면 됩니다!

위의 메소드에서는 ProductVO 클래스의 객체를 생성한 후 Model에 담아서 뷰로 전달했습니다~!

Model의 method인 addAttribute()를 이용했는데, addAttribute()는 크게 두가지의 형태입니다.


  • addAttribute(“이름”,객체) : 객체에 특별한 이름을 부여해 뷰에서 이름값을 이용해서 객체 처리



  • addAttribute(객체) : 자동으로 저장되는 객체의 클래스명 앞글자를 소문자로 처리한 클래스명을 이름으로 간주



예를 들어 위의경우 ProductVO를 이름 없이 저장했기에, 뷰에서의 이름은 productVO가 됩니다!


뷰의 코드는 다음과 같습니다!


<!-- productDetail.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>productDetail.jsp</title>
</head>
<body>
    <!-- 전달받은 model의 속성 productVO에 접근 -->
    <span>${productVO.name}</span>
    <span>${productVO.price}</span>
</body>
</html>




image




리다이렉트를 해야 하는 경우



가끔은 특정한 컨트롤러의 로직을 처리할 때 다른 경로를 호출해야 하는 경우가 있습니다~!

이 경우에는 스프링 MVC의 특별한 문자열인 redirect: 를 이용합니다!

리다이렉트를 하는 경우 RedirectAttributes 클래스를 파라미터로 같이 사용하게 되면 리타이렉트 시점에 원하는 데이터를 임시로 추가해서 넘기는 작업이 가능합니다!


//SampleContoller.java

//컨트롤러
@Controller
public class SampleController {
	
	//로그를 남겨줌
	private static final Logger logger = 
		LoggerFactory.getLogger(SampleController.class);
	
	
	
	@RequestMapping("/doE")
	public String doE(RedirectAttributes rttr) {
		//rttr 객체에 임시데이터 msg를 추가하여 넘김
		rttr.addFlashAttribute("msg","This is the Message!! with redirected");
		return "redirect:/doF";
	}
	
	@RequestMapping("/doF")
	public void doF(@ModelAttribute("msg") String msg) {
		logger.info("doF called..........."+msg);
	}
}




image



Run on Server하고 브라우저에 http:// localhost:8081/kookyungmin/doE라고 입력하면, 리다이렉트에 의해 http:// localhost:8081/kookyungmin/doF로 이동합니다.


JSON 데이터를 생성하는 경우



스프링 MVC의 장점 중 하나는 최근 프로그래밍에서 많이 사용되는 JSON 데이터에 대한 처리를 너무나 간단하게 처리할 수 있다는 것입니다!

이를 위해서 pom.xml에 jackson-databind 라이브러리를 추가해줍니다!


<!-- jackson -->


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




//SampleContoller.java

//컨트롤러
@Controller
public class SampleController {
	
	//로그를 남겨줌
	private static final Logger logger = 
		LoggerFactory.getLogger(SampleController.class);
	
	
	//ProductVO를 json 데이터 형태로 반환
	@RequestMapping("/doJSON")
	public @ResponseBody ProductVO doJSON() {
		ProductVO product = new ProductVO("샘플상품",30000);
		
		return product;
	}
}




image




product가 json 데이터 형태로 출력이 되었네요!!

다음과 같이 Chrome 확장 프로그램에 JSON Formatter 를 추가하면 좀더 보기 쉬운 형태로 변환해줍니다.


image




image




WAS 없이 컨트롤러 테스트하기



스프링 MVC에서는 WAS 구동 없이도 컨트롤러를 테스트 할 수 있습니다!

다음과 같이 pom.xml에서 Servlet을 변경해주면됩니다!


<!-- Servlet -->

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