스프링 MVC를 이용하는 경우 작성되는 Controller는 다음과 같은 특징이 있다.
- HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 필요한 기능 구현
- 다양한 타입의 파라미터 처리, 다양한 타입의 리턴 타입 사용 가능
- GET 방식, POST 방식 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능
- 상속/인터페이스 방식 대신에 어노테이션만으로도 필요한 설정 가능
다른 프레임 워크들과 달리 스프링 MVC는 어노테이션을 중심으로 구성되기 때문에 예제들을 작성할 때에도 각 어노테이션의 의미에 대해서 주의해가며 학습해야 한다.
1. @Controller, @RequestMapping
1-1. 프로젝트 내 org.zerock.controller 패키지 폴더에 SampleController라는 이름의 클래스를 작성한다.
- 작성된 SampleController 클래스는 자동으로 스프링의 객체(Bean)으로 등록되는데 servlet-context.xml에 그 이유가 있다.
- servlet-context.xml에는 <context:component-scan>이라는 태그를 이용해서 지정된 패키지를 조사하도록 설정되어 있다. 해당 패키지에 선언된 클래스들을 조사하면서 스프링에서 객체 설정에 사용되는 어노테이션들을 가진 클래스들을 파악하고 필요하다면 이를 객체로 생성해서 관리하게 된다.
- SampleController 클래스가 스프링에서 관리되면 화면상에는 클래스 옆에 작게 's'모양의 아이콘이 추가된다.
- 클래스 선언부에는 @Controller와 함께 @RequestMapping을 많이 사용한다.
@RequestMapping은 현재 클래스의 모든 메서드들의 기본적인 URL 경로가 된다. 예를 들어, SampleController 클래스를 다음과 같이 '/sample/*'이라는 경로로 지정했다면 다음과 같은 URL은 모두 SampleController에서 처리된다.
1-2. 추가적인 코드 작성
package org.zerock.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 관련 에러가 또 생겼는데 pom.xml에서 log4j관련 의존성에서 exclusion 부터 runtime 까지 주석을 치니까 해결이 되었고 그뒤로 또 알 수 없는 에러가 생겼는데 버전을 1.2.15에서 1.2.16으로 바꾸니 해결되었다. 중앙저장소에서의 종속성 누락으로 1.2.15의 메타데이터가 잘못되서 생긴일이라는데...... 나중에 더 자세히 알아보아야 겠다!
- 프로젝트를 WAS에서 실행해보면 스프링이 인식할 수 있는 정보가 출력되는 것을 볼 수 있는데, 위와 같은 경우에는 아래와 같이 로그가 보인다. src/resources 폴더 내에 log4j.xml의 모든 'info'를 'debug'로 수정하면 아래와 같은 로그를 볼 수 있다.
- 현재 프로젝트의 경우 '/' 와 '/sample/*'는 호출이 가능한 경로라는 것을 확인할 수 있다.
2. @RequstMapping의 변화
@Controller 어노테이션은 추가적인 속성을 지정할 수 없지만, @RequestMapping의 경우 몇 가지의 속성을 추가할 수 있다. 이 중에서도 가장 많이 사용하는 속성이 method 속성이다. Method 속성은 흔히 GET방식, POST 방식을 구분해서 사용할 때 이용한다.
스프링 4.3 버전부터는 이러한 @RequestMapping을 줄여서 사용할 수 있는 @GetMapping, @PostMapping이 등장하는데 축약형의 표현이므로, 아래와 같이 비교해서 학습하는 것이 좋다.
2-1. SampleController 소스 코드
package org.zerock.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;
@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
@RequestMapping("")
public void basic() {
log.info("basic...............................");
}
@RequestMapping(value = "/basic", method = {RequestMethod.GET, RequestMethod.POST})
public void basicGet() {
log.info("basig get...........................");
}
@GetMapping("/basicOnlyGet")
public void basicGet2() {
log.info("basig get only get........................");
}
}
- @RequestMapping은 GET, POST 방식 모두를 지원해야 하는 경우에 배열로 처리해서 지정할 수 있다. 일반적인 경우에는 GET, POST 방식만을 사용하지만 최근에는 PUT, DELETE 방식 등도 점점 많이 사용하고 있다. @GetMapping 의 경우 오직 GET 방식만 사용할 수 있으므로, 간편하기는 하지만 기능에 대한 제한은 많은 편이다.
3. Controller의 파라미터 수집
Controller를 작성할 때 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능이다! 이 기능을 이용하면 매변 request.getParameter()를 이용하는 불편함을 없앨 수 있다.
3-1. 예제를 위해 org.zerock.domain이라는 패키지를 작성하고, SampleDTO 클래스를 작성한다.
- SampleDTO 클래스는 Lomok의 @Data 어노테이션을 이용해서 처리한다. @Data를 이용하게 되면 getter/setter, equals(), toString() 등의 메서드를 자동 생성하기 때문에 편리하다.
3-2. SampleController 소스코드를 추가한다.
package org.zerock.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.zerock.domain.SampleDTO;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
@RequestMapping("")
public void basic() {
log.info("basic...............................");
}
@RequestMapping(value = "/basic", method = {RequestMethod.GET, RequestMethod.POST})
public void basicGet() {
log.info("basig get...........................");
}
@GetMapping("/basicOnlyGet")
public void basicGet2() {
log.info("basig get only get........................");
}
@GetMapping("/ex01")
public String ex01(SampleDTO dto) {
log.info("" + dto);
return "ex01";
}
}
- SampleController의 메서드가 SampleDTO를 파라미터로 사용하게 되면 자동으로 setter 메서드가 동작하면서 파라미터를 수집하게 된다. (이를 확인하고 싶다면 직접 set 메서드를 제작하고 set 메서드 내 간단한 로그 등을 출력해 보면 확인할 수 있다.)
- SampleController의 경로가 '/sample/*' 이므로 ex01() 메서드를 호출하는 경로는 '/sample/ex01'이 된다. 메서드에는 @GetMapping이 사용되었으므로, 필요한 파라미터를 URL 뒤에 '?name=AAA&age=10'과 같은 형태로 추가해서 호출할 수 있다.
3-3. 호출해보기
3-4. 파라미터의 수집과 변환
Controllor가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용한다. 예를 들어, SampleDTO에는 int 타입으로 선언된 age가 자동으로 숫자로 변환되는 것을 볼 수 있다.
만일 기본 자료형이나 문자열 등을 이용한다면 파라미터의 타입만을 맞게 선언해주는 방식을 사용할 수 있다.
3-4-1. SampleController 코드 추가
@GetMapping("/ex02")
public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
log.info("name : " + name);
log.info("age : " + age);
return "ex02";
}
- ex02() 메서드는 파라미터에 @RequestParam 어노테이션을 사용해서 작성되었는데, @RequestParam은 파라미터로 사용된 변수의 이름과 전달되는 파라미터의 이름이 다른 경우에 유용하게 사용된다. (지금 예제의 경우 변수명과 파라미터의 이름이 동일하기 때문에 사용할 필요는 없었지만 소개차원에서 하는 얘기다)
브라우저에서 'http://localhost:8080/sample/ex02?name=AAA&age=10' 과 같이 호출하면 이전과 동일하게 데이터가 수집된 것을 볼 수 있다.
3-5. 리스트, 배열 처리
동일한 이름의 파라미터가 여러 개 전달되는 경우에는 ArrayList<> 등을 이용해서 처리가 가능하다.
@GetMapping("/ex02List")
public String ex02List(@RequestParam("ids")ArrayList<String> ids) {
log.info("ids : " + ids);
return "ex02List";
}
3-6. 객체 리스트
만일 전달하는 데이터가 SampleDTO와 같이 객체 타입이고 여러 개를 처리해야 한다면 약간의 작업을 통해서 한 번에 처리를 할 수 있다. 예를 들어, SampleDTO를 여러 개 전달 받아서 처리하고 싶다면 다음과 같이 SampleDTO의 리스트를 포함하는 SampleDTOList 클래스를 설계한다.
3-6-1. SampleDTOList 클래스를 생성하고 코드를 아래와 같이 작성한다.
package org.zerock.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<>();
}
}
3-6-2. SampleController 클래스에 코드를 아래와 같이 추가한다.
@GetMapping("/ex02Bean")
public String ex02Bean(SampleDTOList list) {
log.info("list dtos : " + list);
return "ex02Bean";
}
3-6-3. 파라미터는 '[인덱스]'와 같은 형식으로 전달해서 처리한다.
* Tomcat은 버전에 따라서 [] 이와 같은 특수문자를 허용하지 않을 수 있다. 따라서 [는 %5B, ]는 %5D로 치환하여 호출해보도록 한다.
3-7. @InitBinder
파라미터의 수집을 다른 용어로는 'Binding(바인딩)'이라고 한다. 변환이 가능한 데이터는 자동으로 변환되지만 경우에 따라서는 파라미터를 변환해서 처리해야 하는 경우도 존재한다. 예를 들어, 화면에서 '2018-01-01'과 같이 문자열로 전달된 데이터를 java.util.Date 타입으로 변환하는 작업이 그러하다. 스프링 Controller에서는 파라미터를 바인딩할 때 자동으로 호출되는 @InitBinder를 이용해서 이러한 변환을 처리할 수 있다.
3-7-1. org.zerock.domain 패키지에 TodoDTO 클래스를 생성하고 다음과 같이 작성한다.
package org.zerock.domain;
import java.util.Date;
import lombok.Data;
@Data
public class TodoDTO {
private String title;
private Date dueDate;
}
- TodoDTO에는 특별하게 dueDate 변수의 타입이 java.util.Date이다. 만일 사용자가 '2018-01-01'과 같은 데이터를 변환하고자 할 때 문제가 발생한다. 이럴때 @InitBinder를 이용한다!
3-7-2. 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";
}
3-7-3. 브라우저에 호출한 결과 화면
* 만약 @InitBinder 처리가 되지 않는다면 브라우저에서는 400 에러가 발생하는 것을 볼 수 있다. (400 에러는 요청 구문이 잘못되었다는 의미이다.)
'Spring > Spring' 카테고리의 다른 글
[10] 기본적인 웹 게시물 관리 - 스프링 MVC 프로젝트의 기본 구성 (0) | 2019.12.18 |
---|---|
[09] 스프링 MVC의 Controller 및 여러 예제 2 (0) | 2019.12.17 |
[07] 스프링 MVC의 기본 구조 및 예제 실습 (0) | 2019.12.17 |
[06] Mybatis와 스프링 연동 (0) | 2019.12.16 |
[05] 커넥션 풀 설정 (0) | 2019.12.13 |