본문으로 바로가기

▶Controller의 파라미터 수집

Controller를 작성할 때 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능임.이 기능을 이용하면 매번 request.getParameter()를  이용하는 불편함을 없앨 수 있음.


예제를 위해 org.zerock.domain패키지를 만들고 ,SampleDTO클래스를 만들어줌.

SampleDTO클래스

 

1
2
3
4
5
6
7
8
9
10
package org.zerock.domain;
 
import lombok.Data;
 
@Data
public class SampleDTO {
 
    private String name;
    private int age;
}
cs


SampleDTO클래스는 Lombok의 @Data어노테이션을 이용해서 처리함.@Data를 이용하게 되면 getter/setter,equals(),toString 등의 메서드를 자동 생성하기 때문에 편함


SampleController의 메서드가 SampleDTO를 파라미터로 사용하게 되면 자동으로 setter메서드가 동적하면서 파라미터를 수집하게 됨.

(이를 확인하고 싶다면 직접set메서드를 제작하고 set메서드 내 간단한 로그 등을 출력해 조면 확인할 수 있음.)


SampleController클래스 일부


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 {
--생략--
    @GetMapping("/ex01")
    public String ex01(SampleDTO dto) {
        log.info(" "+dto);
        return "ex01";
    }
 
}
 
cs


SampleController의 경로가 /sample/* 이므로 ex01()메서드를 호출하는 경로는 /sample/ex01이 됨. 메서드에는 @GetMapping이 사용되었으므로 , 필요한 파라미터를 URL 뒤에 '?name=AAA&age=10'과 같은 형태로 추가해서 호출할 수 있음.info()함수 사용이 안되시는 분은system.out.print()로.... 저도 갑자기 안되요 ..... 이거 때문에...(아니다 여기다 하소연 해봤자.....)


실행된 결과를 보면 SampleDTO 객체 안에 name과 age속성이 제대로 수집된 것을 볼수 있습니다 (특히 주목할 점은 자동으로 타입을 변환해서 처리한다는 점입니다.

프로젝트를 실행할 때 기본 경로는 '/controller/'라는 경로로 동작하므로 , 이를 앞의 예제와 같이 '/'로 동작하도록 변경해서 실행해야 합니다.)




▶파라미터의 수집과 변환

Controller가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용함.예를 들어,SampleDTO에는 int 타입으로 선언된 age가 자동으로 숫자로 변환되는 것을 볼수 있음.


만일 기본 자료형이나 문자열 등을 이용한다면 파라미터의 타입만을 맞게 선언해주는 방식을  사용할 수 있음.


SampleController클래스 일부


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.springframework.web.bind.annotation.RequestParam;
import org.zerock.domain.SampleDTO;
 
 
@Controller
@RequestMapping("/sample/*")
 
public class SampleController {
 
    --추가 코드--
    @GetMapping("/ex02")
    public String ex02(@RequestParam("name"String name,@RequestParam("age"int age) {
        System.out.println("name: "+ name);
        System.out.println("age: "+ age);
 
        return "ex02";
    }
}
cs


ex02( )메서드 파라미터에 @ReauestParam어노테이션을 사용해서 작성되었는데 ,@ReauestParam은 파라미터로 사용된 변수의 이름과 전달되는 파라미터의 이름이 다른

경우에 유용하게 사용됩니다 (지금 예제의 경우 변수명과 파라미터의 이름이 동일하기 때문에 사용할 필요는 없었지만 @ReauestParam의 소개 차원에서 사용해 보았음.)


브라우저에서 http://localhost:8080/sample/ex01?name=AAA&age=10과 같이 호출하면 이전과 동일하게 데이터가 수집 된 것을 볼 수 있음.




 ▶리스트,배열 처리 

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


SampleController클래스 일부 추가 


1
2
3
4
5
6
    @GetMapping("/ex02List")
    public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
        System.out.println("ids: "+ ids);
    
        return "ex02List";
    }
cs


스프링은 파라미터의 타입을 보고 객체를 생성하므로 파라미터의 타입은 List<>와 같이 인터페이스 타입이 아닌 실제적인 클래스 타입으로 지정함.위 코드의 경우'ids'라는 이름의 파라미터가 여러 개 전달 되더라도 ArrayList<String>이 생성되어 자동으로 수집됨.브라우저 등을 이용해서 '프로젝트 경로/sample/ex02List?ids=111&ids=222&ids=333'을 호출하면 아래와 같은 결과를 볼 수 있다  .


배열의 경우도 동일하게 처리할 수 있음.결과는 같음.


SampleController클래스 일부 추가 

1
2
3
4
5
    @GetMapping("/ex02Array")    public String ex02Array(@RequestParam("ids"String[]ids) {
    System.out.println("array ids: "+ Arrays.toString(ids));
        return "ex02Array";    
    }
 
cs


▶객체 리스트 

민일 전달하는 데이터가 SampleDTO와 같이 객체 타입이고 여러 개를 처리해야 한다면 약간의 작업을 통해서 한 번에 처리를 할 수 있음.예를 들어 SampleDTO를 여러 개 전달받아서 처리하고 싶다면 다음과 같이 SampleDTO의 리스트를 포함하는 SampleDTOList클래스를 설계함.



SampleDTOList클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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<>(); 
    }
}
cs


SampleController에서는 SampleDTOList타입을 파라미터로 사용하는 메서드를 작성함.


SampleController 추가 


1
2
3
4
5
    @GetMapping("/ex02Baen")
    public String ex02Baen(SampleDTOList list) {
        System.out.println("list dtos: "+ list);
        return "ex02Baen";    
    }
cs


파라미터는 '[인덱스,]'와 같은 형식으로 전달해서 처리할 수 있음.


전송하려고 하는 URL

http://localhost:8080/sample/ex02Baen?list[0].name=aaa&list[2].name=bbb


Tomcat은 버전에 따라서 위와 같은 문자열에서 '[ ]'문자를 특수문자로 허용하지 않을 수 있음.


JavaScript를 이용하는 경우에는 encodeURIComponent()와 같은 방법으로 해결할 수 있으나 현재 예제의 경우에는 '['는 '%5B'로']'는 '%5D'로 변경하도록 함.


http://localhost:8080/sample/ex02Baen?list%5B0%5D.name=aaa&list%5B1%5D.name=bbb&list%5B2%5D.name=ccc


위의  URL을 호출하면 다음과 같이 여러 개의 SampleDTO 객체를 생성하는 것을 볼 수 있음.



출력된 결과를 보면 3개의 SampleDTO 객체가 생성된 것을 볼 수 있고,'[ ]'안에 인덱스 번호에 맞게 객체의 속성값이 세팅 된 것을 확인 할 수 있음.


▶@InitBinder

파라미터의 수집을 다른 용어로는 'binding(바인딩)'이 라고 함.변환이 가능한 데이터는 자동으로 변환되지만 경우에 따라서는 파라미터를 변환해서 처리해야 하는 경우도 존재함.예를 들어 ,화면에서 '2018-01-01'과 같이 문자열로 전달된 데이터를 java.util.Date타입으로 변환하는 작업이 그러함.스프링Controller에서는 파라미터를 바인딩할 때 자동으로 호출되는@InitBinder를 이용해서 이러한 변환을 처리할 수 있음.


org.zerock.domain 패키지에 TodoDTO라는 클래스를 작성.



TodoDTO클래스

1
2
3
4
5
6
7
8
9
10
11
12
package org.zerock.domain;
 
import java.util.Date;
 
import lombok.Data;
 
@Data
public class TodoDTO {
 
    private String title;
    private Date dueDate;
}
cs


TodoDTO에는 특별하게 dueDate 변수의 타입이 java.util.Date타입임.만일 사용자가'2018-01-01'과 같이 들어오는  데이터를 변환하고자 할 때 문제가 발생하게 됨.

이러한 문제의 간단한 해결책은 @InitBinder를 이용하는 것임.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package org.zerock.controller;
 
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
 
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 org.zerock.domain.SampleDTO;
import org.zerock.domain.SampleDTOList;
import org.zerock.domain.TodoDTO;
 
 
@Controller
@RequestMapping("/sample/*")
 
public class SampleController {
 
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-mm-dd");
        binder.registerCustomEditor(java.util.Date.classnew CustomDateEditor(dateFormat, false));
    } 
 
    @GetMapping("/ex03")
    public String ex03(TodoDTO todo) {
        System.out.println("todo: "+todo);
        return "ex03";
    }
}
 
cs


만일 브라우저에 'http://localhost:8080/sample/ex03?title=test&dueDate=2018-01-01'과 같이 입력했다면 서버에서는 정상적으로 파라미터를 수집해서  처리함.


결과

반면에 @InitBinder처리가 되지 않는다면 브라우저에서는 400에러가 발생하는 것을 볼수 있음.(400에러는 요청구문(syntax)이 잘못되었다는 의미임.)날짜가 정상적으로 처리되어도 아직 jsp페이지는 없으므로 다음과 같은 결과를 확인할 수 있음.




▶@DateTimeFormat

@InitBinder를 이용해서 날짜를 변환할 수도 있지만 ,파라미터로 사용되는 인스턴스변수에 @DateTimeFormat을 적용해도 변환이 가능함.

(@DateTimeFormat을 이용하는 경우에는 @InitBinder는 필요하지 않음.)


TodoDTO클래스 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
cs


문자열로 'yyyy/mm/dd'의 형식에 맞다면 자동으로 날짜 타입으로 변환이 됨.

주석 잡을 것 


1
2
3
4
5
6
7
8
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)); }
     */
cs


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


결과


1
2
todo: TodoDTO(title=test, dueDate=Mon Jan 01 00:01:00 KST 2018)
 
cs