[spring] 스프링 MVC의 Controller - 1
업데이트:
Controller의 특징
- HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 필요한 기능 구현
- 다양한 타입의
파라미터
처리, 다양한 타입의리턴
타입 사용 가능 - GET, POST 방식 등 전송 방식에 대한 처리를
어노테이션
으로 처리 가능 - 상속/인터페이스 방식 대신에 어노테이션만으로 필요한 설정 가능
@Controller, @RequestMapping
@Controller
src/main/java > org.zerock.controller > SampleController 클래스 생성
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/sample/*")
public class SampleController {
}
작성된 SampleController 클래스는 servlet-context.xml에 의해 자동으로 스프링의 객체(Bean)에 등록이 된다.
<!-- servlet-context.xml 중 -->
<context:component-scan base-package="org.zerock.controller" />
<context:component-scan>
태그를 사용해서 지정된 패키지를 조사하도록 설정 되어 있다. 해당 패키지에 선언된 클래스들을 조사하면서 스프링에서 필요한 객체(Bean) 설정에 사용되는 어노테이션을 가진 클래스들을 파악하고, 이를 객체로 생성해서 관리한다.
클래스가 스프링에서 관리되면 클래스 이모티콘에 ‘S’ 모양의 아이콘이 추가된다.
@RequestMapping
@RequestMapping은 현재 클래스의 모든 메서드들의 기본적인 URL 경로
가 된다. 방금 만든 예제 클래스 기준으로 다음과 같은 URL은 모두 SampleController에서 처리된다.
- /sample/aaa
- /sample/bbb
- /sample/b12f
@RequestMapping 어노테이션은 클래스 선언
과 메서드 선언
에 사용할 수 있다.
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
에서 runtime 부분을 주석처리하면 된다.
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<!-- <scope>runtime</scope> -->
dependency는 각각의 scope 가진다. scope의 종류는 다음과 같다.
- compile : 기본 영역이며 컴파일 시점에 필요하고 배포할 때도 포함되어진다.
- Provided : 컴파일 시점에 필요하지만 배포 시점에는 불필요한 라이브러리로 JDK, Selvet API, Java EE API 등이 해당된다.
- runtime : 컴파일 시점에는 필요 없지만 runtime 시점에 필요한 라이브러리이다.
- system : Repository에서 검색을 하지 않고 명시된 위치에서 Jar를 이용한다.
- test : 테스트 컴파일과 실행 시점에만 사용된다.
- import : Maven 2.0.9 이후에서만 적용된ㅌ다. 다른 POM 설정 파일에 정의되어 있는 의존 관계 설정을 현재 프로젝트로 가져온다.
<dependencyManagement>
에서만 사용이 가능하다.
scope를 runtime을 제외한 1번과 2번은 에러가 발생하지 않는다. runtime을 주석처리하면 에러가 해결되는 이유는 기본 영역이 compile이라 그런 것 같다.
이제 Tomcat으로 실행해보면 아래와 같은 로그가 출력된다.
현재 프로젝트의 경우 ‘/’와 ‘/sample/*‘는 호출이 가능한 경로라는 것을 확인할 수 있다.
@RequestMapping의 변화
@RequestMapping은 몇 가지 속성을 추가할 수 있다. 대표적으로는 method
속성이다. method 속성은 전송 방식인 GET, POST 방식을 구분해서 사용할 때 이용한다.
스프링 4.3버전부터는 @RequestMapping을 줄여서 사용할 수 있는 @GetMapping
, @PostMapping
이 등장한다.
// 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(value = "/basic", method = {RequestMethod.GET,
RequestMethod.POST})
// get과 post를 모두 지원하는 경우 배열로 처리해서 지정
public void basic() {
log.info("basic!!!!!!!!!!!!!!!");
}
@GetMapping("/basicOnlyGet")
public void basicGet2() {
log.info("basic get only get!!!!!!!!!!!!!!!!!!!!!!");
}
}
@GetMapping의 경우 오직 GET 방식에만 사용할 수 있으므로, 편하기는 하지만 기능에 대한 제한은 많은 편이다.
Controller의 파라미터 수집
Controller를 작성할 때 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능이다.
org.zerock.domain 패키지를 작성 후 SampleDTO 클래스를 작성한다.
package org.zerock.domain;
import lombok.Data;
@Data
public class SampleDTO {
private String name;
private int age;
}
lombok의 @Data 어노테이션은 getter/setter, equals(), toString() 등의 메서드를 자동으로 생성한다.
// SampleController 일부
@GetMapping("/ex01")
public String ex01(SampleDTO dto) {
log.info("" + dto);
return "ex01";
}
ex01() 메서드를 호출하는 경로는 ‘/sample/ex01’이 된다. 메서드에는 @GetMapping이 사용되었으므로, 필요한 파라미터를 URL 뒤에 ‘?name=AAA&age=10’과 같은 형태로 추가해서 호출할 수 있다.
서버 실행 후 위와 같이 주소 창에 입력하면 로그에 다음과 같이 출력된다.
실행된 결과를 보면 SampleDTO 객체 안에 name과 age 속성이 제대로 수집된 것을 볼 수 있고, 자동으로 타입 변환을 해서 처리하는 걸 볼 수 있다.
파라미터의 수집과 변환
Controller가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용한다. 만일 기본 자료형이나 문자열 등을 이용한다면 파라미터의 타입만을 맞게 선언해주는 방식을 사용할 수 있다.
@GetMapping("/ex02")
public String ex02(@RequestParam("name") String name,
@RequestParam("age") int age) {
log.info("name : " + name);
log.info("age : " + age);
return "ex02";
}
@RequestParam은 파라미터로 사용된 변수의 이름과 전달되는 파라미터의 이름이 다른 경우에 유용하게 사용된다.
위 코드를 실행하면 아래와 같은 로그를 확인할 수 있다.
리스트, 배열 처리
동일한 이름의 파라미터가 여러 개 전달되는 경우에는 ArrayList<>
등을 이용해서 처리가 가능하다.
@GetMapping("/ex02List")
public String ex03(@RequestParam("ids")ArrayList<String> ids) {
log.info("ids : " + ids);
return "ex02List";
}
스프링은 파라미터의 타입을 보고 객체를 생성하므로 파라미터의 타입은 List<> 와 같이 인터페이스 타입이 아닌 실제적인 클래스 타입으로 지정한다.
주소창에 위와 같이 입력하면 다음과 같은 로그를 확인할 수 있다.
INFO : org.zerock.controller.SampleController - ids : [111, 222, 333]
배열의 경우도 동일하게 처리할 수 있다.
@GetMapping("/ex02Array")
public String ex02Array(@RequestParam("ids") String[] ids) {
log.info("array ids : " + Arrays.toString(ids));
return "ex02Array";
}
객체 리스트
만약 전달하는 데이터가 객체 타입이고 여러 개를 처리해야 한다면 따로 클래스를 설계하면 한 번에 처리할 수 있다.
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<>();
}
}
파라미터는 [인덱스]
와 같은 형식으로 전달해서 처리할 수 있다.
경로/sample/ex02Bean?list[0].name=aaa&list[2].name=bbb
위의 경로를 입력하면 에러가 발생한다. Tomcat의 버전에 따라서 []
를 특수문자로 허용하지 않을 수 있다. 그래서 ‘[]’를 ‘%5B’, ‘%5D’로 변경한다.
경로/sample/ex02Bean?list%5B0%5D.name=aaa&list%5B2%5D.name=bbb
그러면 로그에 여러 개의 객체를 생성하는 것을 볼 수 있다.
INFO : org.zerock.controller.SampleController - list dtos : SampleDTOList
(list=[SampleDTO(name=aaa, age=0), SampleDTO(name=null, age=0), SampleDTO
(name=bbb, age=0)])
@InitBinder
파라미터의 수집은 다른 용어로는 binding(바인딩)
이라고 한다. 변환이 가능한 데이터는 자동으로 변환되지만 경우에 따라서는 파라미터를 변환해서 처리해야 하는 경우가 존재하는데 이런 경우 ‘@InitBinder’로 변환을 처리할 수 있다.
TodoDTO 클래스를 생성한다.
package org.zerock.domain;
import java.util.Date;
import lombok.Data;
@Data
public class TodoDTO {
private String title;
private Date dueDate;
}
// SampleController.java
@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";
}
실행하면 로그에서 @InitBinder 처리가 된 걸 확인할 수 있다.
INFO : org.zerock.controller.SampleController - todo : TodoDTO
(title=test, dueDate=Mon Jan 01 00:00:00 KST 2018)
@DateTimeFormat
파라미터로 사용되는 인스턴스 변수에 @DateTimeFormat을 적용해도 변환이 가능하다.
package org.zerock.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;
}
//SampleController.java
// 주석 처리 후 실행
// @InitBinder
// public void initBinder(WebDataBinder binder) {
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(dateFormat, false));
// }
실행하면 아래와 같이 로그가 출력된다.
INFO : org.zerock.controller.SampleController - todo : TodoDTO
(title=test, dueDate=Sun May 23 00:00:00 KST 2021)
- 참고 : 코드로 배우는 스프링 웹 프로젝트
댓글남기기