본문 바로가기

BackEnd/Spring

[코드로 배우는 스프링 웹 프로젝트] ch06 스프링 MVC Controller 1

728x90

1. 스프링 MVC 이용시 Controller의 특징

   ▷ HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 필요한 기능 구현

   ▷ 다양한 타입의 파라미터 처리, 다양한 타입의 리턴 타입 사용 가능

   ▷ GET, POST 방식 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능

   ▷ 상속/인터페이스 방식 대신에 어노테이션만으로도 필요한 설정 기능

 

 

◎ @Controller, @RequestMapping

 

◎ SampleController 클래스

package org.codehows.controller;

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

@Controller
@RequestMapping("/sample/*")
public class SampleController {
	
}


위의 클래스는 servlet-context.xml의 <context:component-scan base-package="org.codehows.controller" /> 코드를 통해 스프링의 객체에 자동으로 등록됩니다.

클래스 선언부에는 @Controller, @RequestMapping을 많이 사용함

@RequestMapping은 현재 클래스의 모든 메서드들의 기본적인 URL 경로

 

 

◎ SampleController 수정

package org.codehows.controller;

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

import lombok.extern.log4j.Log4j;

@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	@RequestMapping("")
	public void basic() {
		log.info("basic...............");
	}
}


여기서 Log4j, log가 오류가 나타납니다

 

◎ 오류 해결 방법(pom.xml 내용 수정)

	<!-- Logging -->
	...(생략)...
	<dependency>
	...(생략)...
		<!-- 아래의 scope 주석 처리 -->
		<!-- <scope>runtime</scope> -->
	</dependency>


runtime이 있어서 Log4j에 문제가 발생합니다.
따라서 주석처리하면 오류가 해결됩니다.

 

 

2. @RequestMapping의 변화

   ▷ @Controller 어노테이션은 추가적인 속성을 지정할 수 없지만, @RequestMapping의 경우 몇 가지 속성을 추가할 수 있음

   ▷ Method 속성은 GET 방식, POST 방식을 구분해서 사용할 때 이용

...(생략)...

	@RequestMapping(value="/basic", method= {RequestMethod.GET, RequestMethod.POST})
	public void basicGet() {
		log.info("basic get...............");
	}

	@GetMapping(value="/basicOnlyGet")
	public void basicGet2() {
		log.info("basic get only get...............");
	}
}


스프링 4.3버전부터는 @RequestMapping을 줄여서 사용할 수 있는 @GetMapping, @PostMapping 등의 축약형 표현을 사용하기도 함

   ▷ @RequestMapping은 GET, POST 방식 모두 지원해야 하는 경우 배열로 처리해서 지정 가능

   ▷ 최근에는 PUT, DELETE 방식 등 많이 사용하고 있음

 

 

3. Controller의 파라미터 수집

   ▷ Controller를 작성할 때 파라미터가 자동으로 수집되는 기능

   ▷ 매번 request.getParameter()를 이용하는 불편함을 없앨 수 있음

 

 

◎ SampleDTO 클래스

package src.codehows.domain;

import lombok.Data;

@Data
public class SampleDTO{
	private String name;
	private int age;
}

   ▷ SampleDTO 클래스는 Lombok의 @Data 어노테이션을 이용해 처리

   ▷ @Data를 이용하게 되면 getter/setter, equals(), toString() 등 메소드를 자동 생성하기 때문에 편리

 

 

(1) SampleController 수정 1

   ▷ SampleDTO 클래스를 파라미터로 사용하게 되면 자동으로 setter 메소드가 동작하면서 파라미터 수집

package org.codehows.controller;

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

import lombok.extern.log4j.Log4j;
import src.codehows.domain.SampleDTO;

@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {

...(생략)...
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);
		
		return "ex01";
	}
}


SampleController 경로가 '/sample/*' 이므로 ex01() 메소드를 호출하는 경로는 '/sample/ex01'이 됨

http://localhost:8080/sample/ex01?name=AAA&age=10

위의 경로로 설정하면 아래와 같이 오류가 나타납니다.


대신 로그창에는 아래와 같이 나타납니다.

INFO : org.codehows.controller.SampleController - SampleDTO(name=AAA, age=10)

 

 

◎ SampleController 수정 2

   ▷ Controller가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식 이용

   ▷ SampleDTO에는 int 타입으로 선언된 age가 자동으로 숫자로 변환되는 것을 볼 수 있음

package org.codehows.controller;

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

import lombok.extern.log4j.Log4j;
import src.codehows.domain.SampleDTO;

@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {

	...(생략)...	
    
	@GetMapping("/ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
		log.info("name" + name);
		log.info("age" + age);
		
		return "ex02";
	}
}


http://localhost:8080/sample/ex01?name=AAA&age=10

위의 경로로 설정하면 여전히 404 오류가 나타나지만 console창에 아래와 같은 데이터가 수집된 것을 확인할 수 있습니다.

 

 

(2) 리스트, 배열 처리

   ▷ 동일한 이름의 파라미터가 여러 개 전달되는 경우 ArrayList<> 등을 이용해서 처리가 가능

...(생략)...	

	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids")ArrayList<String> ids) {
		log.info("ids" + ids);
		
		return "ex02List";
	}
}


스프링은 파라미터 타입을 보고 객체를 생서하므로 파라미터 타입은 List<>와 같이 인터페이스가 아닌 실제 클래스 타입으로 지정
위 코드의 경우 ids라는 파라미터가 여러개 전달되더라도 ArrayList<String>이 생성되어 자동으로 수정됨


http://localhost:8080/sample/ex02List?ids=111&ids=222&ids=333

위의 경로로 지정했을 경우 콘솔창에 아래와 같이 나타나게 됩니다.
   → INFO : org.codehows.controller.SampleController - ids[111, 222, 333]


◎ 배열을 이용한 예제

...(생략)...
	@GetMapping("/ex02Array")
	public String ex02Array(@RequestParam("ids") String[] ids) {
		log.info("array ids" + Arrays.toString(ids));
		
		return "ex02Array";
	}
}

 

 

(3) 객체 리스트

   ▷ 객체 타입이고 여러 개를 처리해야 한다면 SampleDTOList 클래스를 통해 처리할 수 있음

 

◎ SampleDTOList 클래스 생성


◎ SampleDTOList.java 생성
package org.codehows.domain;

import java.util.ArrayList;
import java.util.List;

import lombok.Data;

@Data
public class SampleDTOList{
	
	private List<SampleDTO> list;
	
	public SampleDTOList() {
		list = new ArrayList<>();
	}
}​


SampleController.java 내용 추가

...(생략)...
	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
		log.info("list dtos: " + list);
		
		return "ex02Bean";
	}
}


http://localhost:8080/sample/ex02Bean?list%5B0%5D.name=aaa&list%5B2%5D.name=bbb
(= http://localhost:8080/sample/ex02Bean?list[0].name=aaa&list[2].name=bbb)

문법이 허용되지 않아서 '[' 대신 '%5B' ']' 대신 '%5D'가 나타납니다

콘솔창은 아래와 같이 나타납니다.

INFO : org.codehows.controller.SampleController - list dtos: SampleDTOList(list=[SampleDTO(name=aaa, age=0), SampleDTO(name=null, age=0), SampleDTO(name=bbb, age=0)])


3개의 SampleDTO 객체가 생성되었고 '[ ]' 안에 인덱스 번호에 맞게 객체의 속성값이 세팅됨

 

 

(4) @lnitBinder

   ▷ 파라미터의 수집, 'binding(바인딩)'이라고 함

   ▷ 변환 가능 데이터는 자동 변환되지만 경우에 따라서 파라미터를 변환해서 처리하는 경우도 존재

 

◎ TodoDTO 생성 경로 및 코드 작성


◎ 코드

package org.codehows.domain;

import java.util.Date;

import lombok.Data;

@Data
public class TodoDTO{
	private String title;
	private Date dueDate;
}

 

◎ SampleController 클래스 추가

package org.codehows.controller;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;

import org.codehows.domain.SampleDTO;
import org.codehows.domain.SampleDTOList;
import org.codehows.domain.TodoDTO;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import lombok.extern.log4j.Log4j;

@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(java.util.Date.class, 
				new CustomDateEditor(dateFormat, false));
	}
	
    ...(생략)...
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo) {
		log.info("todo: " + todo);
		return "ex03";
	}
}


http://localhost:8080/sample/ex03?title=test&dueDate=2018-01-01

위의 경로 연결하면 아래와 같은 결과가 출력됩니다.

INFO : org.codehows.controller.SampleController - todo: TodoDTO(title=test, dueDate=Mon Jan 01 00:00:00 KST 2018)

 

 

(5) @DateTimeFormat

   ▷ @InitBinder 이용해서 날짜 변환 가능하지만 파라미터로 사용되는 인스턴스 변수에 @DateTimeFormat을 적용해도 변환 가능

 

◎ TodoDTO 클래스 수정

package org.codehows.domain;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.Data;

@Data
public class TodoDTO{
	private String title;
	
	@DateTimeFormat(pattern = "yyyy/MM/dd")
	private Date dueDate;
}


http://localhost:8080/sample/ex03?title=test&dueDate=2018/01/01

위의 경로로 연결하면 아래와 같은 결과가 출력됩니다.

INFO : org.codehows.controller.SampleController - todo: TodoDTO(title=test, dueDate=Mon Jan 01 00:00:00 KST 2018)

initbinder를 사용했을 때와 동일한 결과가 나타납니다!!

 

 

4. Model이라는 데이터 전달자

   ▷ Controller의 메소드를 작성할 때 Model이라는 타입을 파라미터로 지정할 수 있음

   ▷ Model 객체는 JSP의 컨트롤러에서 생성된 데이터를 담아 전달하는 역할

   ▷ JSP와 같은 뷰(View)로 전달해야 하는 데이터를 담아서 보낼 수 있음

   ▷ 메서드의 파라미터에 Model 타입이 지정된 경우에는 스프링은 특별하게 Model 타입의 객체를 만들어 메서드에 주입

 

Model은 모델 2 방식에서 사용하는 request.setAttribute()와 유사한 역할

1. Servlet에서 모델 2방식으로 데이터를 전달하는 방식
request.setAttribute("serverTime", new java.util.Date());
RequestDispatcher dispatcher = requset.getRequestDispatcher("/WEB-INF/jsp/home.jsp);
dispatcher.forward(request, response);​


2. 스프링에서 Model을 이용해 데이터 전달하는 방식
public String home(Model model){
	model.addAttribue("serverTime", new java.util.Date());
    return "home";
}​


메소드의 파라미터를 Model 타입으로 선언하게 되면 자동으로 스프링 MVC에서 Model 타입의 객체를 만들어 주며 개발자의 입장에서 필요한 데이터를 담아주는 작업만으로 모든 작업이 완료됨.

 

◎ @ModelAttribute 어노테이션

   ▷ 웹페이지 구조는 Request에 전달된 데이터를 가지고 필요하다면 추가적인 데이터를 생성해서 화면으로 전달하는 방식

    Model의 경우 파라미터로 전달된 데이터는 없지만 화면에 필요한 데이터를 전달하기 위해 사용

   ▷ 페이지 번호는 파라미터로 전달되지만, 결과 데이터를 전달하려면 Model에 담아 전달함

 

◎ 기본 자료형 파라미터 선언(SampleController에 내용 추가)

...(생략)...
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, int page) {
		log.info("dto: " + dto);
		log.info("page: " + page);
		
		return "/sample/ex04";
	}
}

 

   ▷ ex04()는 SampleDTO 타입과 INT 타입의 데이터를 파라미터로 사용
   ▷ 결과 확인을 위해 'WEB-INF/views' 폴더 아래 sample 폴더 생성 및 리턴값에서 사용한 'ex04'에 해당하는 ex04.jsp 작성

 

◎ ex04.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>SAMPLEDTO ${sampleDTO}</h2>
	<h2>PAGE ${page}</h2>

</body>
</html>


http://localhost:8080/sample/ex04?name=aaa&age=11&page=9

위의 경로 입력시 아래의 화면 출력


int 타입으로 선언된 page는 전달되지 않습니다.

따라서 @ModelAttribute 사용(강제로 전달받은 파라미터를 Model에 담아서 전달하도록 할 때 필요한 어노테이션)

@ModelAttribute가 걸린 파라미터는 타입에 관계없이 무조건 Model에 담아서 전달되므로 파라미터로 전달된 데이터를 다시 화면에서 사용해야할 경우 유용하게 사용됨

 

 SampleController 내용 수정(ex04 내용 추가)

...(생략)...
	@GetMapping("/ex04")
	public String ex04(SampleDTO dto, @ModelAttribute("page") int page) {
		log.info("dto: " + dto);
		log.info("page: " + page);
		
		return "/sample/ex04";
	}
}


http://localhost:8080/sample/ex04?name=aaa&age=11&page=9

위의 경로 입력시 아래의 화면 출력

 

◎ RedirectAttributes

   ▷ Model 타입과 더불어 스프링 MVC가 자동으로 전달해 주는 타입

   ▷ RedirectAttributes는 일회성으로 데이터를 전달하는 용도로 사용

Model은 모델 2 방식에서 사용하는 request.setAttribute()와 유사한 역할

1. Servlet에서 redirect방식
respons.sendRedirect("/home?name=aaa&age=10");​

 

2. 스프링 MVC 이용하는 redirect 처리
rttr.addFlashAttribute("name", "AAA");
	rttr.addFlashAttribute("age", 10);
    
    return "redirect:/";​

 

RedirectAttributes는 Model 같이 파라미터로 선언해서 사용하고, addFlashAttribute(이름, 값) 메소드를 이용해서 화면에 한 번만 사용하고 다음에는 사용되지 않는 데이터를 전달하기 위해 사용

 

 

MVC Controller를 이용해서 SampleController.java 파일을 만들어 화면창과 콘솔창에 출력해보는 예제를 실시해보았습니다.

 

결과가 좀 나타나니 사용하는 방법을 조금씩 익혀가는 것 같아요!

 

controller가 내용이 많아서 다음 글에서도 다뤄볼게요!!

 

많은 분들의 피드백은 언제나 환영합니다!  많은 댓글 부탁드려요~~

 

 

728x90