Spring05_MVC패턴실습

7 minute read



Spring MVC 실습 1

1. Maven Project생성

  • File - New - Maven Project
  • Create a simple project(skip archetype selection) 체크
  • Group Id(kr.ac.kopo) Artifact Id 등 설정 후, Packaging war로 설정!!

2. pom.xml 세팅

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>kr.ac.kopo</groupId>
  <artifactId>spring-mvc</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
  <dependencies>
  	<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>jsp-api</artifactId>
	    <version>2.2</version>
	    <scope>provided</scope>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>4.0.1</version>
	    <scope>provided</scope>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <version>5.3.9</version>
	</dependency>
  </dependencies>
</project>
  • spring-web말고 webmvc로 할것! xml에서는 4버전을 쓰지만 지금은 5버전사용가능

3. web.xml, spring-mvc.xml 파일 복붙

  • web.xml은 webapp/WEB-INF/에 복붙
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>spring-mvc</display-name>
  
  <servlet>
  	<servlet-name>dispatcher</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>
  			classpath:config/spring/spring-mvc.xml
  		</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>dispatcher</servlet-name>
	<url-pattern>*.do</url-pattern>  
  </servlet-mapping>
  
  <filter>
  	<filter-name>encodingFilter</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>

  <filter-mapping>
  	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>  
  </filter-mapping>
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • spring-mvc.xml 은 src/main/resources/config/spring/ 경로에 둔다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<context:component-scan base-package="kr.co.mlec" />
    <mvc:annotation-driven />
	<mvc:default-servlet-handler />
	<mvc:view-resolvers>
		<!-- forward시킬 주소 값을 자동으로 앞에 경로 붙이고 뒤에는 .jsp로 인식하도록 -->
		<mvc:jsp prefix="/WEB-INF/jsp/" suffix=".jsp" />
	</mvc:view-resolvers>

</beans>

4. 서버세팅

  • 다른 프로젝트에서 서버사용중이면 stop후 spring 서버 실행해야함 (충돌주의)
    1. Servers 에서 서버 tomcat 9버전 추가
    2. HTTP/1.1 포트번호 9999로 변경
    3. (선택사항) Use custom location 체크 후, 미리만들어둔 eclipse-webwork 경로로 설정
    4. 서버 Start

5. 소스파일

  • HelloController.java
package kr.co.mlec.hello;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {
	
	@RequestMapping("/hello/hello.do")
	public ModelAndView hello() {
		ModelAndView mav = new ModelAndView("hello/hello"); // forward시킬 jsp의 주소
		mav.addObject("msg","hi 스프링 mvc~~"); // request공유영역에 올림.
		return mav;
	}
}
  • hello.jsp : WEB-INF/jsp/hello 경로
    • WEB-INF폴더에 jsp 파일을 넣어 보안성 강화함
    • 이제는 css, img, js는 WEB-INF에 있으면 안됌!!!!!!!!!
    • css, img는 webapp 폴더아래에 서 관리

<%@ 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>
	서버에서 받은 메시지 : ${  msg  }
</body>
</html>
  
  • index.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>
	<a href="<%= request.getContextPath() %>/hello/hello.do">hello</a>
</body>
</html>
  




Spring MVC 실습 2

1. @RequestMapping

  • RequestMapping을 이용하여 method에 따라 다른 행동을 취하도록 할 수 있다.
  • 다른 동작을 하는 jsp를 하나의 url을 사용하도록 할 수 있음
  • @RequestMapping(value=” “, method= )
  • MethodController.java

1-1. @RequestMapping 분리1

package kr.co.mlec.method;

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

@Controller
public class MethodController {
	
	/* RequestMapping으로
	 * 같은 url을 method(get/post)에 따라 다른 행동을 취하도록 할 수 있다.
	 * 
	 */
	
	@RequestMapping(value="/method/method.do", method=RequestMethod.GET)
	public String callGet() {
		return "method/methodForm";
	}
	
	@RequestMapping(value="/method/method.do", method=RequestMethod.POST)
	public String callPost() {
		return "method/methodProcess";
	}
}
  • Dispatcher Servlet이 복잡한구조이므로, 우리가 유연하게 사용할 수 있음

★ Return타입 : ModelAndView, void, String

  • String : forward시킬 jsp주소라는 것을 알아서 인식함. ( 매우 유연 )
  • ModelAndView : forward, 공유영역 객체가 있다는 것을 인식
@RequestMapping("/join.do")
	public ModelAndView join(MemberVO member) {
		//ModelAndView로 forward할 주소입력하고, 공유영역에 공유할 수 있음
		ModelAndView mav = new ModelAndView();
		mav.setViewName("form/memberInfo"); // forward
		mav.addObject("member",member); // 공유영역 추가
		
		return mav;
	}


1-2. @RequestMapping 분리2

  • value와 method를 분리해서 사용가능 (value는 class위에, method는 메소드위에)
package kr.co.mlec.method;

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

@RequestMapping(value="/method/method.do")
@Controller
public class MethodController {
	
	//@RequestMapping(value="/method/method.do", method=RequestMethod.GET)
	@RequestMapping(method=RequestMethod.GET)
	public String callGet() {
		return "method/methodForm";
	}
	
	//@RequestMapping(value="/method/method.do", method=RequestMethod.POST)
	@RequestMapping(method=RequestMethod.POST)
	public String callPost() {
		return "method/methodProcess";
	}
	
}

1-3. @RequestMapping 분리3

  • 겹치는 부분(“/form”)은 class위에 다른부분은 메소드 위에 정의해둘 수 있다.
package kr.co.mlec.form;

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

@Controller
@RequestMapping("/form")
public class MemberController {
	
	// "/form/joinForm.do"
	@RequestMapping("/joinForm.do")
	public String joinForm() {
		return "form/joinForm";
	}
  
  @RequestMapping("/join.do")
	public String join(HttpServletRequest request) {
		
		//원래 인코딩해주어야하지만 web.xml에서 이미 처리되도록 설정해주었음
		String id = request.getParameter("id");
		String password = request.getParameter("password");
		String name = request.getParameter("name");
		
		MemberVO member = new MemberVO();
		member.setId(id);
		member.setPassword(password);
		member.setName(name);
		
		//System.out.println(member.toString());
		request.setAttribute("member", member);
		
		
		return "form/memberInfo";
	}
  
  
}




Spring MVC 실습 3

1. @RequestParam

  • RequestParam으로 HttpServletRequest대신 사용할 수 있다.
  • java가 제공하는 것이 아니라 Spring이 제공함.
@RequestMapping("/join.do")
	public String join(HttpServletRequest request) {
		
		//원래 인코딩해주어야하지만 web.xml에서 이미 처리되도롱 설정해주었음
		String id = request.getParameter("id");
		String password = request.getParameter("password");
		String name = request.getParameter("name");
		
		MemberVO member = new MemberVO();
		member.setId(id);
		member.setPassword(password);
		member.setName(name);
		
		//System.out.println(member.toString());
		request.setAttribute("member", member);
		
		
		return "form/memberInfo";
	}
  • 위의 코드는 아래와 같다.
  • 물론 HttpServletRequest request도 함께사용가능.
    • 공유영역에 올리려면 request필요
@RequestMapping("/join.do")
	public String join(@RequestParam("id") String id,
						@RequestParam("password") String password,
						@RequestParam("name") String name,
            HttpServletRequest request) {
		
		
		MemberVO member = new MemberVO();
		member.setId(id);
		member.setPassword(password);
		member.setName(name);
		
		System.out.println(member.toString());
		//request.setAttribute("member", member);

		return "form/memberInfo";
	}

2. @ModelAttribute

  • 모든 변수를 하나하나 받아오는 것은 비효율적이다.
  • @ModelAttribute를 써서 VO객체에 바로 set해주고, 공유영역에 올려줄 수 있다.
  • 공유영역 객체의 이름을 내가 원하는 것으로 지정해줄 수 있다.
    • MemberVO -> member
@RequestMapping("/join.do")
	public String join(@ModelAttribute("member") MemberVO member) {
		
		// 모든 변수를 하나하나 다 써주는건 비효율적
		/*System.out.println(member.toString());
		 * @ModerAttribute안쓰면 request.setAttribute("memberVO", member); 로 알아서 등록시킴.
		 * 쓰면 ("member")로 지정가능
		 */
     
		return "form/memberInfo";
	}
  • 안쓰면 VO객체의 첫글자를 소문자로 자동으로 바꿔서 들어감.
    • MemberVO -> memberVO
@RequestMapping("/join.do")
	public String join(MemberVO member) {
		
		// 모든 변수를 하나하나 다 써주는건 비효율적
		System.out.println(member.toString());
		//request.setAttribute("memberVO", member); 로 알아서 등록시킴.

		return "form/memberInfo";
	}

Request영역에 객체 등록하는 방법

1.내가직접 파라미터 받아서 등록. 2.ModelAndView로 하는 방법(VO로 알아서 받아옴) 3.Model을 메소드 파라미터로 받아와서 등록하는방법.

@RequestMapping("/join.do")
	public String join(MemberVO member, Model model) {
		
		// request공유영역에 등록하는 방법은 여러개이다.
		// 1.내가직접 setAtt
		// 2.ModelAndView
		// 3.Model을 파라미터로 받아와서 등록하는방법
		model.addAttribute("member",member);
		
		return "form/memberInfo";
	}




Spring MVC 실습 4

1. @ResponseBody

  • 어떤 Client가 요청시 요청받은 메소드가 jsp를 거치지 않고 바로 데이터를 응답한다.
  • 4버전부터 @RestController가 등장.
  • 메소드에 붙는 어노테이션
  • 사용
    • 단순문장, json형태로 리턴할 때 사용.
    • ajax쓸때 사용.
    • 메시지를 forward시키지않고 바로 응답하게 하고 싶을 때 사용.
  • 바로 넘기면 한글안나옴. message-converters가 필요하다.
  • spring-mvc.xml수정
  • <mvc:annotation-driven>부분 수정
<mvc:annotation-driven>
  <mvc:message-converters>
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>text/html; charset=utf-8</value>
        </list>
      </property>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

string msg보내기

package kr.co.mlec.body;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/ajax")
public class ResBodyController {
	
	@RequestMapping("/resBody.do")
	@ResponseBody
	public String resStringBody(HttpServletRequest request) {
		// 메시지를 forward시키지않고 바로 응답하게 하고싶다.
		return "ok, 성공!";
	}
}

json 보내기

  • .json uri 처리할 수 있도록 web.xml 수정(아래 추가)
<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
<url-pattern>*.json</url-pattern>  
</servlet-mapping>
  • 406에러의 이유 : .json은 불러왔지만 응답의 해석안되고있음
  • message-converter가 또 필요함. 아래의 value추가해준다.
<value>application/json; charset=utf-8</value>
  • jackson (Mave Repository에서 검색) 추가.(Databind, Core, Annotations)
    • jackson도 컨버터가 빌요하지만 5버전부터는 필요없어짐.
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>text/html; charset=utf-8</value>
          <value>application/json; charset=utf-8</value>
        </list>
      </property>
    </bean>
	<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
	<dependency>
	    <groupId>com.fasterxml.jackson.core</groupId>
	    <artifactId>jackson-databind</artifactId>
	    <version>2.12.4</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
	<dependency>
	    <groupId>com.fasterxml.jackson.core</groupId>
	    <artifactId>jackson-core</artifactId>
	    <version>2.12.4</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
	<dependency>
	    <groupId>com.fasterxml.jackson.core</groupId>
	    <artifactId>jackson-annotations</artifactId>
	    <version>2.12.4</version>
	</dependency>
	@RequestMapping("/resBody.json")
	@ResponseBody
	public Map<String, String> resJsonBody(){
		Map<String, String> result = new HashMap<String, String>();
		result.put("id", "hong");
		result.put("name", "홍길동");
		result.put("addr", "서울");
		
		return result;

	}
  • VO
@RequestMapping("/resVOBody.json")
@ResponseBody
public MemberVO resJsonVOBody(){
  MemberVO vo = new MemberVO();
  vo.setId("hong");
  vo.setName("홍길동");
  vo.setPassword("1234");
  return vo;
}
  • List<String>
@RequestMapping("/resStringListBody.json")
@ResponseBody
public List<String> resJsonStringListBody(){
  List<String> list = new ArrayList<String>();
  for(int i = 1; i<4 ;i++) {
    list.add(String.valueOf(i));
  }
  return list;
}
  • List<MemberVO>
@RequestMapping("/resVOListBody.json")
@ResponseBody
public List<MemberVO> resVOListBody(){

  List<MemberVO> list = new ArrayList<MemberVO>();
  for(int i = 1; i<4 ;i++) {
    MemberVO vo = new MemberVO();
    vo.setId("hong");
    vo.setName("홍길동");
    vo.setPassword("1234");
    list.add(vo);
  }
  return list;
}

Tip ( ResponseBody <-> RequestBody )

  • ResponseBody: java가 가진 객체를 json형태로 바꿔주는 것
  • RequestBody : json형태로 날아오는 것을 java객체로 바꿔줌.


2. @RestController

  • ResponseBody대신 class에 @RestController붙이면 같은 기능을 할 수 있음
package kr.co.mlec.body;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import kr.co.mlec.form.MemberVO;

@RestController
@RequestMapping("/ajax")
public class RestBodyController {
	
	// 메세지 기반의 컨트롤러 생성시 RestController
	
	@RequestMapping("/restBody.do")
	public String resStringBody(HttpServletRequest request) {
		// 메시지를 forward시키지않고 바로 응답하게 하고싶다.
		// 바로 넘기면 한글안나옴. message 컨버터가 필요하다.
		return "ok, 성공!";
	}
	
	

	@RequestMapping("/restBody.json")
	public Map<String, String> resJsonBody(){
		Map<String, String> result = new HashMap<String, String>();
		result.put("id", "hong");
		result.put("name", "홍길동");
		result.put("addr", "서울");
		
		return result;

	}

	
	@RequestMapping("/restVOBody.json")
	public MemberVO resJsonVOBody(){
		MemberVO vo = new MemberVO();
		vo.setId("hong");
		vo.setName("홍길동");
		vo.setPassword("1234");
		
		return vo;

	}
	
	
	@RequestMapping("/restStringListBody.json")
	public List<String> resJsonStringListBody(){
		List<String> list = new ArrayList<String>();
		for(int i = 1; i<4 ;i++) {
			list.add(String.valueOf(i));
		}
		
		return list;
	}
	
	
	@RequestMapping("/restVOListBody.json")
	public List<MemberVO> resVOListBody(){
		
		List<MemberVO> list = new ArrayList<MemberVO>();
		for(int i = 1; i<4 ;i++) {
			MemberVO vo = new MemberVO();
			vo.setId("hong");
			vo.setName("홍길동");
			vo.setPassword("1234");
			list.add(vo);
		}

		return list;
	}

}




Spring MVC 실습 5

컨트롤러 없이 페이지 매핑

  • uploadForm.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>파일업로드 테스트</h2>
	<form method="post" enctype="multipart/form-data" action="<%= request.getContextPath() %>/file/upload.do">
	
		<input type="text" name="id" value="test"/><br>
		<input type="file" name="attachFile1" /><br>
		<input type="file" name="attachFile2" /><br>
		<input type="submit" value="업로드" />
	</form>
</body>
</html>
  
  • spring-mvc.xml
    • </mvc:annotation-driven>아래에 추가
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
  <!-- 최대 업로드 파일사이즈 : 10MB -->
  <property name="maxUploadSize" value="10485760"/>
</bean>
<mvc:view-controller path="/file/uploadForm.do" view-name="file/fileUploadForm"/>
  • pom.xml
    • 아래 내용 추가
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
  • UploadController.java
package kr.co.mlec.file;

import java.io.File;
import java.util.Iterator;
import java.util.UUID;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

@RequestMapping("/file")
@Controller
public class UploadController {

	@Autowired
	ServletContext servletContext;
	  
	@RequestMapping(value="/upload.do", method=RequestMethod.POST)
	public ModelAndView fileUpload(MultipartHttpServletRequest mRequest) throws Exception {
		
		// 실행되는 웹어플리케이션의 실제 경로 가져오기
		String uploadDir = servletContext.getRealPath("/upload/");
		System.out.println(uploadDir);

		
		
		ModelAndView mav = new ModelAndView("file/uploadResult");
		//forward주소 

		String id = mRequest.getParameter("id"); // 
		System.out.println("id : " + id);
		
		Iterator<String> iter = mRequest.getFileNames();
		while(iter.hasNext()) {
			
			String formFileName = iter.next();
			// 폼에서 파일을 선택하지 않아도 객체 생성됨
			MultipartFile mFile = mRequest.getFile(formFileName);
			
			// 원본 파일명
			String oriFileName = mFile.getOriginalFilename();
			System.out.println("원본 파일명 : " + oriFileName);
			
			if(oriFileName != null && !oriFileName.equals("")) {
			
				// 확장자 처리
				String ext = "";
				// 뒤쪽에 있는 . 의 위치 
				int index = oriFileName.lastIndexOf(".");
				if (index != -1) {
					// 파일명에서 확장자명(.포함)을 추출
					ext = oriFileName.substring(index);
				}
				
				// 파일 사이즈
				long fileSize = mFile.getSize();
				System.out.println("파일 사이즈 : " + fileSize);
				
				// 고유한 파일명 만들기	
				String saveFileName = "mlec-" + UUID.randomUUID().toString() + ext; // 확장자 붙이기
				System.out.println("저장할 파일명 : " + saveFileName);
			
				// 임시저장된 파일을 원하는 경로에 저장
				mFile.transferTo(new File(uploadDir + saveFileName));
			} 
		} 
		return mav;
	}
	/*
	@RequestMapping("/uploadForm.do")
	public String upload() {
		
		return "file/fileUploadForm";
	}
	*/
}