IT/SpringMVC

9. Spring MVC를 이용한 웹 페이지 작성 실습

인러너 2018. 8. 27. 14:49

@ 아래 내용은 https://www.edwith.org/boostcourse-web/ 사이트의 강의를 정리한 글 입니다. 



핵심 개념

  • DispacherServlet
  • WebApplicationInitializer
  • @RequestMapping = @GetMapping = @PostMapping




Controller작성 실습 1/3

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

// 이클립스 maven  아키타입은 webapp로 설정이다. 
//프로젝트 네비게이터 들어가서 메인 폴더안에 java폴더를 하나 만들어주자 .
//pom.xml 추가 할 부분은 저번 처럼 추가 해주면 되겠다. 
// 잘추가 했다면 maven project update 항상 수행 해주자 .
// 그리고 네비게이터에서 facet 부분에서 jst.web version= 3.1 잘 설정 되있는지 항상 확인 해주자
//그리고 프로젝트에서 우클릭 poroperties 에서 maven -> projectfacet 에서 다이나믹웹모듈도 3.1로 
 잘 바뀌어있는지 확인하자

DispatcherServlet을 FrontController로 설정하기
  • web.xml 파일에 설정
  • javax.servlet.ServletContainerInitializer 사용
    - 서블릿 3.0 스펙 이상에서 web.xml파일을 대신해서 사용할 수 있다.
  • org.springframework.web.WebApplicationInitializer 인터페이스를 구현해서 사용

// 이중에서 가장많이 사용되는방법이  1번과 3번이다. 이번강의는 이 두가지 방법을 보자


web.xml파일에서 DispatcherServlet 설정하기 1/2

// servletname은 매핑 네임과 일치 만하면된다. servlet-class 는 스프링이 제공하고 있는 클래스 명을 제대로 넣어주자 
// initparam 부분을 xml 파일로 설정 
xml spring 설정 읽어들이도록 DispathcerServlet설정
// initparam 부분을 javaconfig로 설정 
// 요청부분에 / 가있다.  그래서 모든 요청을 디스패쳐서블릿이 다 받아들이 는 것.
Java config spring 설정 읽어들이도록 DispathcerServlet설정

WebApplicationInitializer를 구현해서 설정하기 1/2

  • Spring MVC는 ServletContainerInitializer를 구현하고 있는 SpringServletContainerInitializer를 제공한다.
  • SpringServletContainerInitializer는 WebApplicationInitializer 구현체를 찾아 인스턴스를 만들고 해당 인스턴스의 onStartup 메소드를 호출하여 초기화한다.
//이방법의 단점은 초기 시간이 오래 걸릴 수 있다. 
//우리는 이방법 사용안한다 
WebApplicationInitializer를 구현해서 설정하기 2/2

Spring MVC 설정

  • kr.or.connect.webmvc.config.WebMvcContextConfiguration
Spring MVC 설정

@Configuration

  • org.springframework.context.annotation의 Configuration 애노테이션과 Bean 애노테이션 코드를 이용하여 스프링 컨테이너에 새 로운 빈 객체를 제공할 수 있다.

    //설정파일이다 라는 뜻

@EnableWebMvc

  • DispatcherServlet의 RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver, MessageConverter 등 Web에 필요한 빈들을 대부분 자동으로 설정해준다.
  • xml로 설정의 <mvc:annotation-driven/> 와 동일하다. //우리 수업에서는 사용 안할 것이다 
  • 기본 설정 이외의 설정이 필요하다면 WebMvcConfigurerAdapter 를 상속받도록 Java config class를 작성한 후, 필요한 메소드를 오버라이딩 하도록 한다.

// 웹에 필요한 빈들을 자동으로 설정 해준다는 것 .
@EnableWebMVC

WebMvcConfigurationSupport

 // 관심있으면 위 url 찾아보시면 된다. 


@ComponentScan

  • ComponentScan애노테이션을 이용하면 Controller, Service, Repository, Component애노테이션이 붙은 클래스를 찾아 스프링 컨테이너가 관리하게 된다. //xml파일에다가 bean으로 등록했던 것 과 비슷 
  • DefaultAnnotationHandlerMapping과 RequestMappingHandlerMapping구현체는 다른 핸들러 매핑보다 훨씬 더 정교한 작업을 수행한다. 이 두 개의 구현체는 애노테이션을 사용해 매핑 관계를 찾는 매우 강력한 기능을 가지고 있다. 이들 구현체는 스프링 컨테이너 즉 애플리케이션 컨텍스트에 있는 요청 처리 빈에서 RequestMapping애노테이션을 클래스나 메소드에서 찾아 HandlerMapping객체를 생성하게 된다.
  • HandlerMapping은 서버로 들어온 요청을 어느 핸들러로 전달할지 결정하는 역할을 수행한다.
  • DefaultAnnotationHandlerMapping은 DispatcherServlet이 기본으로 등록하는 기본 핸들러 맵핑 객체이고, RequestMappingHandlerMapping은 더 강력하고 유연하지만 사용하려면 명시적으로 설정해야 한다.

 

WebMvcConfigurerAdapter

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

 

Controller(Handler) 클래스 작성하기

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

 

@RequestMapping

  • Http 요청과 이를 다루기 위한 Controller 의 메소드를 연결하는 어노테이션
  • Http Method 와 연결하는 방법
     - @RequestMapping(value="/users", method=RequestMethod.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설정 파일 사용할때 매핑에서 / 사용했었다.  그런데 모든 / 는 다가져가서 컨트롤러에서 찾을려고 하기때문에  css, img, 등등은 저쪽에서 찾으라는 의미 

//매핑정보가 없는 url매핑은 스프링에 디폴트 서블릿 ... 기능 사용 하면 WAS로 넘기고 WAS가 알아서 한다. 

// setViewName("main"); 은 밑에 있는 ViewResolver 를 사용해서 넘어가게 되는 것. 

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>

 // 디스패쳐 서블릿을 프론트 컨트롤러로 사용하도록 추가하는 것. 

// WebMvcContextConfiguration 아까 만들었던 자바 설정파일 읽어들이도록 initparam 에 추가 했다. 항상 패키지명 추가하는 것 잊지말자

// contextClass 빈공장 만드는 거 기억하자 

//디스패쳐 서블릿이 실행될때 init param 부분을 불러들어서 읽어들이게 된다.  


main.jsp //views 안에다가 만들자 .

<%@ 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>


//그리고 테스트 할때는 기본으로 설정되어있는 index파일을 삭제 시켜주자 ~ 그러면 잘 나온다. 아니면 자동으로 index

먼저 찾아 간다. 


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

// public String plusform() 에서 리턴 타입이 String 인데 modelandview로해도 된다. 그러면 리턴 값도 당연히 modelandview 형식으로 줘야 겠다. 


// Post 형식으로 받기때문에 @PostMapping 쓴다. 

// 메소드의 인자 부분을 보게되면 requestparam 이 나오는데 세부내용은 위에 설명이 있다. 파라미터 값을 

다음 나오는 int value1 (변수) 에 넣어준다는 의미 

// ModelMap 객체를 사용하게되면 키 밸류 형태로 값이 들어가게되고 리턴 페이지 에서 modelMap에 있는 값을 사용할 수 있다. 


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

//@ModelAttribute 를 사용하면 스프링이 자동으로 채워준다. 

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

// @Getmapping("/ggods/{id}") Pathvariable 로 값을 받는것.. 



생각해보기

  1. SpringMVC를 이용해서 웹 어플리케이션을 개발하는 것과 서블릿을 이용해 개발하는 것과 비교해보면 어떤 장단점이 있다고 생각하세요?