Spring3 - forward
dispatch
@Controller
public class TestController {
@GetMapping("main2")
public String main() {
return "test2/main";
}
}
경로 매핑 시 “/” 기호도 생략이 가능하다.
스프링에서 다른 곳으로 Dispatcher 방식 포워딩 시 데이터를 전달하는 방법은 2가지가 있다.
매핑 메서드 파라미터를 통해 HttpServletRequest 객체 주입받기
기존에 사용하던 HttpServletRequest 객체의 setAttribute()를 사용하는 방법이다.
JSP 파일이 아니므로 내장 객체 request 가 존재하지도 않고 컨트롤러 파라미터에 HttpServletRequest 객체가 명시되어 있지도 않으므로 외부로부터 request 객체를 전달받아야 한다. 스프링에서는 의존주입(DI = Dependency Injection) 을 통해 전달받을 수 있다. 메서드 내의 파라미터 타입으로 HttpServletRequest 타입을 선언하면 스프링에 의해 해당 객체가 자동으로 전달(= 주입)된다.
@Controller
public class TestController {
@GetMapping("push")
public String push(HttpServletRequest request) {
request.setAttribute("msg", "Hello, World! - request");
request.setAttribute("test", new TestVO("제목 - request", "내용 - request"));
return "test2/push";
}
}
매핑 과정에서 메서드 호출 시점에 자동으로 HttpServletRequest 객체가 전달(주입)된다. JSP 의 MVC 패턴에서 서블릿 클래스(XXXFrontController) 정의 시 doGet(), doPost() 메서드가 request, response 객체를 자동으로 전달받는 것과 동일한 개념이다.
Dispatch 방식 포워딩을 통해 “WEB-INF/views/test2/push.jsp” 페이지로 포워딩되며 URL과 request 객체가 유지된다.
메서드 파라미터를 통해 Model 객체 주입받기
스프링 전용 Model 객체의 addAttribute()를 사용하는 방법이다. org.springframework.ui.Model 타입을 파라미터로 지정 시 데이터 저장이 가능한 Model 객체를 자동으로 주입받을 수 있다. HttpServletRequest 객체와 성격이 유사하며, java.util.Map 객체 기반으로 만든 스프링에서 제공하는 데이터 공유 객체다.
@Controller
public class TestController {
@GetMapping("push")
public String push(Model model) {
model.addAttribute("msg", "Hello, World! - Model 객체");
model.addAttribute("test", new TestVO("제목 - Model 객체", "내용 - Model 객체"));
// Dispatch 방식 포워딩을 통해 "WEB-INF/views/test2/push.jsp" 페이지로 포워딩
// => URL 유지, request 객체 유지
return "test2/push";
}
}
기존의 request 객체와 마찬가지로 데이터를 담아 전달하는 용도로 사용 가능한 데이터 전달자다. 스프링 전용 객체(라이브러리)이며, java.util.Map 을 기반으로 정의되어 있다. request.setAttribute() 와 마찬가지로 Model 객체의 addAttribute() 메서드로 저장하며 파라미터가 완벽하게 동일하다. request 객체와 범위(Scope)가 동일(하나의 요청에 대한 응답 발생 지점까지)하며 request 객체와 동시 사용이 불가능하다.(일반적인 데이터 저장 시 request 객체보다 Model 객체를 사용한다)
redirect
@Controller
public class TestController {
@GetMapping("redirect")
public String redirect(HttpServletRequest request) {
// 새로운 요청 발생 시 접근이 불가능해진다.
request.setAttribute("msg", "Hello, World! - request 객체");
return "redirect:/redirectServlet";
}
@GetMapping("redirectServlet")
public String redirectServlet(HttpServletRequest request) {
// 접근 불가(새로운 request 객체가 생성된다.)
System.out.println(request.getAttribute("msg"));
return "test2/redirect";
}
}
스프링에서 리다이렉트 방식의 포워딩을 수행하려면 return 문에 “redirect:/리다이렉트할주소” 형식으로 지정하면 된다. 주의할 점은 리다이렉트 방식은 request 객체를 통해 데이터 전달 불가능하며 URL 파라미터를 활용하여 전달해야한다. 새로운 서블릿 주소로 요청이 발생해야하는 경우에 사용하면 된다.
리다이렉트 방식의 데이터 전송 방법
request 객체는 사용 불가능하며, URL을 통한 파라미터로 전달해야 한다. 정확히는 데이터 저장 후 request.getAttribute() 메서드 등으로 데이터를 가져올 수가 없다. 단, Model 객체에 데이터 저장 후 리다이렉트 수행 시 자동 변환되어 URL 파라미터를 통해 전달된다.
@Controller
public class TestController {
@GetMapping("redirect")
public String redirect(Model model) {
String name = "홍길동";
int age = 20;
// 리다이렉트 과정에서 직접 URL 에 데이터를 연결할 경우
return "redirect:/redirectServlet?name=" + name + "&age=" + age;
}
}
기존의 리다이렉트 과정에서 직접 URL에 데이터를 연결하는 방법이다.
@Controller
public class TestController {
@GetMapping("redirect")
public String redirect(Model model) {
// model 객체에 "name" 이라는 속성명으로 name 변수값 저장
model.addAttribute("name", name);
// model 객체에 "age" 라는 속성명으로 age 변수값 저장
model.addAttribute("age", age);
return "redirect:/redirectServlet";
}
}
Model 객체를 사용하면 URL에 데이터를 직접 연결하지 않아도 자동으로 URL파라미터에 데이터를 연결시켜 준다. request대신 model을 사용하는 이유다.
파라미터 데이터를 컨트롤러 메서드 내에서 접근하는 방법
HttpServletRequest 객체를 활용한 기존 방법(스프링에서 사용하지 않음)
@Controller
public class TestController {
@GetMapping("redirectServlet")
public String redirectServlet(HttpServletRequest request) {
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
System.out.println("이름 : " + name + ", 나이 : " + age);
return "test2/redirect";
}
}
컨트롤러 내의 매핑 메서드에서 HttpServletRequest 객체 타입 파라미터 선언이 필요하다. 파라미터로 전달받은 데이터이므로 request.getAttribute() 사용이 불가능 하며 request.getAttribute("name")
를 시도할 시 null이 반환된다. URL 파라미터(폼 파라미터)로 전달받은 데이터는 request.getParameter() 메서드 사용이 필수다.
전달받은 파라미터명과 동일한 이름의 파라미터를 메서드 선언부에 명시
전달받은 파라미터명과 동일한 이름의 파라미터를 메서드 선언부에 명시하면 이름이 일치하는 파라미터 데이터를 자동으로 해당 변수에 저장해 준다. GET / POST 방식은 무관하다.
@Controller
public class TestController {
@GetMapping("redirectServlet")
public String redirectServlet(String name, int age) {
System.out.println("이름2 : " + name + ", 나이2 : " + age);
return "test2/redirect";
}
}
“name” 파라미터값이 name 변수에 저장되고 “age” 파라미터값이 age 변수에 저장되며 이 때 자동으로 형변환까지 처리된다. 주의할 점은 메서드 파라미터명과 URL 파라미터명이 일치하지 않으면 매핑이 불가능하므로 선언된 파라미터 변수에 데이터가 전달되지 못하여 null값이 저장된다. 그래서 null 값에 접근하게 되므로 다음과 같은 예외가 발생한다. java.lang.IllegalStateException: Optional int parameter 'age2' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.
VO(DTO) 클래스 사용
전달받은 파라미터명과 동일한 이름의 멤버변수를 갖는 VO(DTO)클래스를 선언하고 매핑 메서드 파라미터로 해당 VO클래스 타입 파라미터를 지정할 경우 해당 VO 클래스의 인스턴스 생성 및 Setter 호출을 통한 데이터 저장까지 자동으로 수행한다. 주의할 점은 VO 클래스에 멤버변수, 기본 생성자, Getter/Setter를 필수로 작성해야 한다.
@Controller
public class TestController {
@GetMapping("redirectServlet")
public String redirectServlet(PersonVO person) {
System.out.println("이름(Person) : " + person.getName() + ", 나이(Person) : " + person.getAge());
return "test2/redirect";
}
}
PersonVO객체 생성 및 데이터 저장까지 자동으로 수행되므로 즉시 PersonVO객체에 접근이 가능하다.(데이터 저장 과정에서 setXXX() 메서드가 자동으로 호출된다.) 단, 멤버변수명과 파라미터명이 일치하지 않으면 저장이 불가능하다.
@Controller
public class TestController {
@GetMapping("redirectServlet")
public String redirectServlet(PersonVO person, char gender) {
System.out.println("이름(Person) : " + person.getName() + ", 나이(Person) : " + person.getAge() + ", 성별(gender) : " + gender);
return "test2/redirect";
}
}
또한, VO객체에 일치하지 않는 파라미터는 별도로 변수 또는 다른 VO객체 타입을 통해 처리도 가능하다.
java.util.Map 타입을 파라미터로 선언하는 방법
@Controller
public class TestController {
@GetMapping("redirectServlet")
public String redirectServlet(@RequestParam Map<String, String> map) {
// Map 객체의 get() 메서드를 호출하여 파라미터로 key 전달 시 해당 value 값 리턴
System.out.println("이름(Map) : " + map.get("name") + ", 나이(Map) : " + map.get("age"));
return "test2/redirect";
}
}
파라미터명을 key, 파라미터값을 value 한 쌍으로 관리하는 Map 객체를 자동으로 생성 후 각 파라미터 데이터를 자동으로 Map 객체에 추가하는 방법이다. 단, Map 타입 변수만 파라미터로 선언했을 경우 스프링이 Map객체의 용도를 정확히 파악할 수 없기 때문에 단순히 Map 객체 생성만 되고 파라미터 데이터가 자동으로 추가되지 않는다. Map 객체 내에는 파라미터와 일치하는 속성이 없는 상태가 된다. 따라서, Map 타입 파라미터 선언 시 파라미터 앞에 @RequestParam 어노테이션을 지정하여 해당 Map 객체가 파라미터 저장용 객체라는 용도를 명시해야한다.
형변환 문제 해결 방법
파라미터로 전달받을 데이터가 저장될 변수를 직접 선언했을 경우 문자열 데이터의 경우 String타입 저장 시 파라미터가 없으면 null값으로 자동 처리되지만 문자열 외의 데이터(정수, 실수, boolean, char 등)가 파라미터로 전달되고 이 데이터들을 해당 데이터타입 변수로 전달받을 경우 자동 형변환 과정이 일어나므로 null 값에 대한 형변환 수행이 불가능하여 다음과 같은 예외가 발생하게 된다. HTTP 500 오류(java.lang.IllegalStateException: Optional int parameter 'age' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.)
해결 방법 1
모든 파라미터를 String 타입으로 선언하고 필요 시 메서드에서 직접 형변환 작업을 추가하는 방법이다.
해결 방법 2
@RequestParam 어노테이션을 사용하여 해당 변수가 파라미터 데이터 저장용이라는 표시를 달고 어노테이션 뒤에 소괄호() 기술 후 내부에 defaultValue = "기본값"
형식으로 기본값을 설정한다. 단, 모든 기본값은 문자열 형식으로 지정하며, 파라미터 전달 시 기본값은 무시된다.
@Controller
public class TestController {
@GetMapping("redirect")
public String redirect(Model model) {
String name = "홍길동";
int age = 20;
model.addAttribute("name", name);
// model.addAttribute("age", age);
return "redirect:/redirectServlet";
}
@GetMapping("redirectServlet")
public String redirectServlet(
@RequestParam(defaultValue = "") String name,
@RequestParam(defaultValue = "0") int age) {
// name 과 age 파라미터 중 전달되지 않은 파라미터가 있을 경우 지정된 기본값이 사용된다.
System.out.println("이름 : " + name + ", 나이 : " + age);
return "test2/redirect";
}
}
age파라미터는 전달되지 않았으므로 0이 저장된다.
Leave a comment