Keep going

스프링 MVC의 Controller 본문

Records/Spring Framework

스프링 MVC의 Controller

코딩천재홍 2021. 2. 8. 17:17

스프링 MVC 를 이용하는 경우 작성되는 Controller의 특징

  • HttpServletRequest, HttpServeltResponse를 거의 사용할 필요 없이 필요한 기능 구현
  • 다양한 타입의 파라미터 처리, 다양한 타입의 리턴 타입 사용 가능
  • GET 방식, POST 방식 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능
  • 상속/인터페이스 방식 대신에 어노테이션만으로도 필요한 설정 가능

1. @Controller, @RequestMapping

프로젝트 내 org.zerock.controller 패키지 폴더에 SampleController라는 이름의 클래스 작성

 
SampleController 클래스
package org.zerock.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping
public class SampleController {
    
}
 
cs

 

- SampleController의 클래스 선언부에 @Controller라는 스프링 MVC에서 사용하는 어노테이션을 적용하고 있다.

- 작성된 SampleController 클래스는 위의 그림과 같이 자동으로 스프링의 객체(Bean)에 등록되는데 servlet-context.xml에 <context:component-scan base-package="org.zerock.controller" /> 태그를 이용하여 지정된 패키지를 조사하도록 설정되어 있기 때문이다.

- 해당 패키지에 선언된 클래스들을 조사하면서 스프링에서 객체 설정에 사용되는 어노테이션들을 가진 클래스들을 파악하고 필요하다면 이를 객체로 생성해서 관리하게 된다.

- 클래스 선언부에는 @Controller와 함께 @RequestMapping 을 많이 사용한다.

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

예를 들어, SampleController 클래스를 /sample/* 이라는 경로로 지정했다면 /sample/aaa , /sample/bbb 와 같은 URL은 모두 SampleController에서 처리된다.

 

# SampleController클래스의 @log4j 어노테이션에 에러가 날때 (log4j cannot be resolved to a type)

→ 런타임부분을 주석처리해준다.

<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</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>
cs

 

 

@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...............");
    }
}
cs

- SampleController는 Lombok의 @Log4j를 사용한다. 

- @Log4j는 @Log가 java.util.Logging을 이용하는데 반해 Log4j 라이브러리를 활용한다. (Spring Legacy Project로 생성한 프로젝트는 기본적으로 Log4j 추가되있음)

 

 


2. @ReqeustMapping의 변화

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

- 가장 많이 사용하는 속성은 method 속성이다.

- Method 속성은 흔히 GET 방식, POST 방식을 구분해서 사용할 때 이용한다.

- 스프링 4.3버전부터는 이러한 @RequestMapping 을 줄여서 사용할 수 있는 @GetMapping, @PostMapping이 등장하는데 축약형의 표현이다.

 

SpringController 클래스의 일부
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("basic get...........");
    }
    
    @GetMapping("/basicOnlyGet")
    public void basicGet2() {
        
        log.info("basic get only get....................");
    }
}
 
cs

- @RequestMapping은 GET, POST 방식 모두를 지원해야 하는 경우에 배열로 처리해서 지정할 수 있다.

- 일반적인 경우에는 GET, POST 방식만을 사용하지만 최근에는 PUT, DELETE 방식 등도 점점 많이 사용하고 있다.

- @GetMapping의 경우 오직 GET 방식에만 사용할 수 있으므로, 간편하기는 하나 기능에 대한 제한은 많다.

 


3. Controller의 파라미터 수집

- Controller를 작성할 때 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능이다.

- 이 기능을 이용하면 매번 request.getParameter()를 이용하는 불편함을 없앨 수 있다.

- 실습을 위해 org.zerock.domain이라는 패키지를 작성하고, SampleDTO 클래스를 작성한다.

 

SampleDTO 클래스
package org.zerock.domain;
 
import lombok.Data;
 
@Data
public class SampleDTO {
    private String name;
    private int age;
}
cs

 

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

 

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'과 같은 형태로 추가해서 호출할 수 있다.

 

실행 결과

- 실행된 결과를 보면 SampleDTO 객체 안에 name과 age 속성이 제대로 수집된 것을 볼 수 있다.

- 특히 주목할 점은 자동으로 타입을 반환해서 처리한다는 점이다.

 

 

 

3.1 파라미터의 수집과 변환

 

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

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

 

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

 

SampleController에 추가
@GetMapping("/ex02")
    public String ex02(@RequestParam("name"String name, @RequestParam("age")int age) {
        
        log.info("name: "+name);
        log.info("age: "+age);
        
        return "ex02";
    }
cs

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

 

실행 결과

 

- 이전 처럼 'http://localhost:8080/sample/ex02?name=AAA&age=10' 과 같이 호출하면 이전과 동일하게 데이터가 수집된 것을 볼 수 있다.

 

 

3.2 리스트, 배열 처리

 

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

 

SampleController에 추가
@GetMapping("/ex02List")
    public String ex02List(@RequestParam("ids")ArrayList<String> ids) {
        
        log.info("ids: "+ ids);
        
        return "ex02List";
    }
cs

 

- 스프링은 파라미터의 타입을 보고 객체를 생성하므로 파라미터의 타입은 List<>와 같이 인터페이스 타입이 아닌 실제적인 클래스 타입으로 지정한다.

- 위 코드의 경우 'ids'라는 이름의 파라미터가 여러 개 전달되더라도 ArrayList<String>이 생성되어 자동으로 수집된다.

- 'http://localhost:8080/sample/ex02List?ids=111&ids=222&ids=333' 와 같이 호출한다면 아래와 같은 로그가 출력된다.

 

 

배열의 경우도 동일하게 처리할 수 있다.

 

SampleController에 추가
@GetMapping("/ex02Array")
    public String ex02Array(@RequestParam("ids"String[] ids) {
        
    log.info("array ids: "+ Arrays.toString(ids));
    return "ex02Array";
    }
}
cs

 

 

3.3 객체 리스트

 

- 만일 전달하는 데이터가 SampleDTO와 같이 객체 타입이고 여러 개를 처리해야 한다면 약간의 작업을 통해서 한 번 처리를 할 수 있다.

- 예를 들어 SampleDTO를 여러 개 전달받아서 처리하고 싶다면 SampleDTO의 리스트를 포함하는 SampleDTOList 클래스를 설계 한다.

 

SampleDTOList 클래스
package org.zerock.domain;
 
import java.util.ArrayList;
import java.util.List;
 
public class SampleDTOList {
 
    private List<SampleDTO> list;
    
    public SampleDTOList() {
        list= new ArrayList<>();
    }
}
 
cs

 

SampleController에 추가
@GetMapping("/ex02Bean")
    public String ex02Bean(SampleDTOList list) {
        
        log.info("list dtos: "+list);
        
        return "ex02Bean";
    }
cs

 

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

- Tomcat은 버전에 따라서 문자열에서 '[]' 문자를 특수문자로 허용하지 않을 수 있기 때문에 ' [ '는 '%5B' 로 ' ] '는 '5D' 로 변경한다.

 

- 전송하려고 하는 URL :

'프로젝트 경로/sample/ex02Bean?list%5B0%5D.name=aaa&list%5B1%5D.name=BBB&list%5B2%5D.name=CCC'

 

실행결과

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

 

 

3.4 @InitBinder

- 파라미터이 수집을 다른 용어로는 'binding(바인딩)' 이라고 한다. 

- 변환이 가능한 데이터는 자동으로 변환되지만 경우에 따라서는 파라미터를 변환해서 처리해야 하는 경우도 존재한다.

- 예를 들어, 화면에서 '2018-01-01'과 같이 문자열로 전달된 데이터를 java.util.Date 타입으로 변환하는 작업이 그렇다.

- 스프링 Controller 에서는 파라미터를 바인딩할 때 자동으로 호출되는 @InitBinder를 이용해서 이러한 변환을 처리할 수 있다.

 

TodoDTO 클래스
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를 이용하는 것이다.

 

SampleController의 일부
@InitBinder
    public void InitBinder(WebDataBinder binder) {
        SimpleDateFormat dateForamt = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(java.util.Date.classnew CustomDateEditor(dateForamt, false));
    }
 
 
@GetMapping("/ex03")
    public String ex03(TodoDTO todo) {
        log.info("todo: " + todo);
        return "ex03";
    }
cs

 

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

 

 

- 반면에 @InitBinder 처리가 되지 않는다면 브라우저에서는 400 에러가 발생하는 것을 볼 수 있다. (400 에러는 요청 구문이 잘못되었다는 의미이다.

 

 

3.5 @DateTimeFormat

 

-@InitBinder를 이용해서 날짜를 변환할 수도 있지만, 파라미터로 사용되는 인스턴스 변수에 @DateTimeFormate을 적용해도 변환이 가능하다. (@DateTimeFormat을 이용하는 경우에는 @InitBinder는 필요하지 않다.)

TodoDTO 클래스 (@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;
}
 
cs

 

SampleController - @InitBinder 주석처리를 하고 실행시킨다.

 

 

실행 결과

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

 

 


출처 : 코드로 배우는 스프링 웹 프로젝트 [구멍가게 코딩단]

'Records > Spring Framework' 카테고리의 다른 글

스프링 MVC 프로젝트의 기본 구성  (0) 2021.02.15
스프링 MVC 의 Controller(2)  (0) 2021.02.09
스프링 MVC의 기본 구조  (0) 2021.02.05
MyBatis와 스프링 연동  (0) 2021.02.03
스프링과 Oracle Database 연동  (0) 2021.02.03
Comments