BoostCource/Back-end

#03. BE - Spring MVC 실습

칸타탓 2018. 8. 3. 00:34

<3. Spring MVC 실습>

http://www.edwith.org/boostcourse-web



전에 했던 실습 순서를 참고하여, Maven 프로젝트 생성 후 pom.xml에 라이브러리를 추가하고, Servlet 버전을 3.1로 올려준다.

Spring MVC를 사용하기 위해 아래와 같은 순서로 설정을 추가한 후 실습을 진행한다.



DispatcherServlet을 FrontController로 설정해 주기.

세 가지 방법이 존재한다.

  1. web.xml 파일에 설정 (가장 많이 사용되는 방법이다.)

  2. javax.servlet.ServletContainerInitializer 사용
    - 서블릿 3.0 스펙 이상에서 web.xml파일을 대신해서 사용할 수 있다. => 이번 실습에서는 사용하지 않는다.

  3. org.springframework.web.WebApplicationInitializer 인터페이스를 구현해서 사용



- web.xml파일에서 DispatcherServlet 설정하기


- xml을 이용하는 방법

servlet-name은 servlet mapping이 가지고 있는 servlet-name과 일치하면 된다.

DispatcherServlet을 이용할 것이므로 반드시 패키지 명을 포함해서 클래스명을 아래와 같이 적어준다.

WebMVCContextConfig.xml 파일 안에 어떤 일을 할 것인지 알려줄 것이다.

- 자바 config를 이용하는 방법. 이 방법으로 실습을 진행한다.
init-param 부분에 xml이 아니라 자바 config 파일을 넣어준 것을 알 수 있다.

여기서 알아야 할 것은, 요청이 들어올 경우 Servlet이었을 경우에 url-pattern에 원하는 URL 주소를 넣고 이 부분에 요청이 들어오면 실제 servlet-name과 같은 name으로 매핑되어있는 servlet-class를 실행한다는 것!
/ 라고만 되어있으면 모든 요청을 다 받겠다는 뜻이다.





- WebApplicationInitializer를 구현해서 설정하는 방법

  • Spring MVC는 ServletContainerInitializer를 구현하고 있는 SpringServletContainerInitializer를 제공한다.

  • SpringServletContainerInitializer는 WebApplicationInitializer 구현체를 찾아 인스턴스를 만들고 해당 인스턴스의 onStartup 메소드를 호출하여 초기화한다.

아래와 같이 설정하지만, 이번 실습에서는 다루지 않는다.



- Spring MVC 설정

  • kr.or.connect.webmvc.config.WebMvcContextConfiguration

DispatcherServlet에 대한 설정은 Web.xml에서 하고 읽어들여야 할 설정은 별도로 자바 Config에 설정한다.

위 어노테이션에 관한 설명이다.

@Configuration
  • 이 어노테이션을 이용하여 자바 config 파일이라는 것을 알려준다.
  • org.springframework.context.annotation의 Configuration 애노테이션과 Bean 애노테이션 코드를 이용하여 스프링 컨테이너에 새 로운 빈 객체를 제공할 수 있다. 

@EnableWebMvc

  • DispatcherServlet의 RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver, MessageConverter 등 Web에 필요한 빈들을 대부분 자동으로 설정해준다.

  • xml로 절정했을 경우 <mvc:annotation-driven/> 와 동일하다.

  • 기본 설정 이외의 설정이 필요하다면 WebMvcConfigurerAdapter 를 상속받도록 Java config class를 작성한 후, 필요한 메소드를 오버라이딩 하도록 한다.


@ComponentScan

  • ComponentScan 애노테이션을 이용하면 Controller, Service, Repository, Component 애노테이션이 붙은 클래스를 찾아 스프링 컨테이너가 관리하게 된다.

  • DefaultAnnotationHandlerMapping과 RequestMappingHandlerMapping구현체는 다른 핸드러 매핑보다 훨씬 더 정교한 작업을 수행한다. 이 두 개의 구현체는 애노테이션을 사용해 매핑 관계를 찾는 매우 강력한 기능을 가지고 있다. 이들 구현체는 스프링 컨테이너 즉 애플리케이션 컨텍스트에 있는 요청 처리 빈에서 RequestMapping 애노테이션을 클래스나 메소드에서 찾아 HandlerMapping객체를 생성하게 된다.

  • HandlerMapping은 서버로 들어온 요청을 어느 핸들러로 전달할지 결정하는 역할을 수행한다.

  • DefaultAnnotationHandlerMappingDispatcherServlet이 기본으로 등록하는 기본 핸들러 맵핑 객체

  • RequestMappingHandlerMapping더 강력하고 유연하지만 사용하려면 명시적으로 설정해야 한다.

 

WebMvcConfigurerAdapter

  • org.springframework.web.servlet.config.annotation. WebMvcConfigurerAdapter

  • @EnableWebMvc 를 이용하면 기본적인 설정이 모두 자동으로 되지만, 기본 설정 이외의 설정이 필요할 경우 해당 클래스를 상속 받은 후, 메소드를 오버라이딩 하여 구현한다.




 

- Controller(Handler) 클래스 작성하기

  • @Controller 애노테이션을 클래스 위에 붙인다.

  • Controller 클래스를 작성할 때 클래스 위나 메소드에 맵핑을 위해 @RequestMapping 애노테이션을 클래스나 메소드에서 사용한다.

    • 요청이 들어왔을 때 어떤 URL로부터 들어온 요청인지를 알아내서 실제로 처리해야하는 컨트롤러가 무엇인지와 그 컨트롤러에서 구현하고있는 메서드가 무엇인지를 알아내야 하기 때문에.

 

- @RequestMapping


Http 요청과 이를 다루기 위한 Controller 의 메소드를 연결하는 어노테이션

Spring은 아래와 같은 방법으로 RequestMapping로 요청하는 것을 제공한다.


Http Method와 연결하는 방법

 - @RequestMapping(value="/users", method=RequestMethod.POST)

=>/users로 들어왔을 때 실행한다. POST 방식으로 들어왔을 때.

 - From Spring 4.3 version 부터는 아래와 같은 방법으로 사용한다.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping


Http 특정 해더와 연결하는 방법
 - @RequestMapping(method = RequestMethod.GET, headers = "content-type=application/json")


Http Parameter와 연결하는 방법
 - @RequestMapping(method = RequestMethod.GET, params = "type=raw")


Content-Type Header 와 연결하는 방법
 - @RequestMapping(method = RequestMethod.GET, consumes = "application/json")


Accept Header와 연결하는 방법
 - @RequestMapping(method = RequestMethod.GET, produces = "application/json")




 

실습


WebMvcContextConfiguration.java

package kr.or.connect.mvcexam.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "kr.or.connect.mvcexam.controller" })
public class WebMvcContextConfiguration extends WebMvcConfigurerAdapter {
	@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(31556926);
        registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
        registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
        registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
    }
 
    // default servlet handler를 사용하게 합니다.
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
   
    @Override
    public void addViewControllers(final ViewControllerRegistry registry) {
    		System.out.println("addViewControllers가 호출됩니다. ");
        registry.addViewController("/").setViewName("main");
    }
    
    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
}

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app>

  <display-name>Spring JavaConfig Sample</display-name>

  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>kr.or.connect.mvcexam.config.WebMvcContextConfiguration</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>


</web-app>

 

main.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>
<h1>main page~~~!!</h1>
</body>
</html>



- Controller작성 실습 1/3

  1. 웹 브라우저에서 http://localhost:8080/mvcexam/plusform 이라고 요청을 보 내면 서버는 웹 브라우저에게 2개의 값을 입력받을 수 있는 입력 창과 버튼이 있는 화면을 출력한다.

  2. 웹 브라우저에 2개의 값을 입력하고 버튼을 클릭하면 http://localhost:8080/mvcexam/plus URL로 2개의 입력값이 POST방식으로 서버에게 전달한다. 서버는 2개의 값을 더한 후, 그 결과 값을 JSP에게 request scope으로 전달하여 출력한다.

 


* Spring MVC가 지원하는 Controller메소드 인수 타입


  • javax.servlet.ServletRequest

  • javax.servlet.http.HttpServletRequest

  • org.springframework.web.multipart.MultipartRequest

  • org.springframework.web.multipart.MultipartHttpServletRequest

  • javax.servlet.ServletResponse

  • javax.servlet.http.HttpServletResponse

  • javax.servlet.http.HttpSession

  • org.springframework.web.context.request.WebRequest

  • org.springframework.web.context.request.NativeWebRequest

  • java.util.Locale

  • java.io.InputStream

  • java.io.Reader

  • java.io.OutputStream

  • java.io.Writer

  • javax.security.Principal

  • java.util.Map

  • org.springframework.ui.Model

  • org.springframework.ui.ModelMap

  • org.springframework.web.multipart.MultipartFile

  • javax.servlet.http.Part

  • org.springframework.web.servlet.mvc.support.RedirectAttributes

  • org.springframework.validation.Errors

  • org.springframework.validation.BindingResult

  • org.springframework.web.bind.support.SessionStatus

  • org.springframework.web.util.UriComponentsBuilder

  • org.springframework.http.HttpEntity<?>

  • Command 또는 Form 객체

 


* Spring MVC가 지원하는 메소드 인수 애노테이션


  • @RequestParam

  • @RequestHeader

  • @RequestBody

  • @RequestPart

  • @ModelAttribute

  • @PathVariable

  • @CookieValue

 

@RequestParam

  • Mapping된 메소드의 Argument에 붙일 수 있는 어노테이션

  • @RequestParam의 name에는 http parameter의 name과 멥핑

  • @RequestParam의 required는 필수인지 아닌지 판단

 

@PathVariable

  • @RequestMapping의 path에 변수명을 입력받기 위한 place holder가 필요함

  • place holder의 이름과 PathVariable의 name 값과 같으면 mapping 됨

  • required 속성은 default true

 

@RequestHeader

  • 요청 정보의 헤더 정보를 읽어들 일 때 사용

  • @RequestHeader(name="헤더명") String 변수명

 

Spring MVC가 지원하는 메소드 리턴 값

  • org.springframework.web.servlet.ModelAndView

  • org.springframework.ui.Model

  • java.util.Map

  • org.springframework.ui.ModelMap

  • org.springframework.web.servlet.View

  • java.lang.String

  • java.lang.Void

  • org.springframework.http.HttpEntity<?>

  • org.springframework.http.ResponseEntity<?>

  • 기타 리턴 타입

 


 

* 실습 코드


plusForm.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 method="post" action="plus">  
value1 : <input type="text" name="value1"><br>
value2 : <input type="text" name="value2"><br>
<input type="submit" value="확인">  
</form>  
</body>
</html>

 

plusResult.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>
${value1} 더하기 ${value2} (은/는) ${result} 입니다.
</body>
</html>

 

PlusController.java

package kr.or.connect.mvcexam.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class PlusController {
	@GetMapping(path = "/plusform")
	public String plusform() {
		return "plusForm";
	}

	@PostMapping(path = "/plus")
	public String plus(@RequestParam(name = "value1", required = true) int value1,
			@RequestParam(name = "value2", required = true) int value2, ModelMap modelMap) {
		int result = value1 + value2;

		modelMap.addAttribute("value1", value1);
		modelMap.addAttribute("value2", value2);
		modelMap.addAttribute("result", result);
		return "plusResult";
	}
}


 - Controller작성 실습 2/3

  1. http://localhost:8080/mvcexam/userform 으로 요청을 보내면 이름, email, 나이를 물어보는 폼이 보여진다.

  2. 폼에서 값을 입력하고 확인을 누르면 post방식으로 http://localhost:8080/mvcexam/regist 에 정보를 전달하게 된다.

  3. regist에서는 입력받은 결과를 콘솔 화면에 출력한다.

 

- Controller작성 실습 3/3

  1. http://localhost:8080/mvcexam/goods/{id} 으로 요청을 보낸다.

  2. 서버는 id를 콘솔에 출력하고, 사용자의 브라우저 정보를 콘솔에 출력한다.

  3. 서버는 HttpServletRequest를 이용해서 사용자가 요청한 PATH정보를 콘솔에 출력한다.

 



* 실습 코드


userform.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 method="post" action="regist">  
name : <input type="text" name="name"><br>
email : <input type="text" name="email"><br>
age : <input type="text" name="age"><br>
<input type="submit" value="확인"> 
</body>
</html>


UserController.java

package kr.or.connect.mvcexam.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.connect.mvcexam.dto.User;

@Controller
public class UserController {
	@RequestMapping(path="/userform", method=RequestMethod.GET)
	public String userform() {
		return "userform";
	}
	
	@RequestMapping(path="/regist", method=RequestMethod.POST)
	public String regist(@ModelAttribute User user) {

		System.out.println("사용자가 입력한 user 정보입니다. 해당 정보를 이용하는 코드가 와야합니다.");
		System.out.println(user);
		return "regist";
	}
}

 

User.java

package kr.or.connect.mvcexam.dto;

public class User {
	private String name;
	private String email;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", email=" + email + ", age=" + age + "]";
	}	
}


regist.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>등록되었습니다.</h2>
</body>
</html>


goodsById.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>
id : ${id } <br>
user_agent : ${userAgent }<br>
path : ${path }<br>
</body>
</html>


GoodsController.java

package kr.or.connect.mvcexam.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;

@Controller
public class GoodsController {
	@GetMapping("/goods/{id}")
	public String getGoodsById(@PathVariable(name="id") int id,
							   @RequestHeader(value="User-Agent", defaultValue="myBrowser") String userAgent,
							  HttpServletRequest request,
							  ModelMap model
							  ) {
		
		String path = request.getServletPath();
		
		System.out.println("id : " + id);
		System.out.println("user_agent : " + userAgent);
		System.out.println("path : " + path);
		
		model.addAttribute("id", id);
		model.addAttribute("userAgent", userAgent);
		model.addAttribute("path", path);
		return "goodsById";
	}
}