본문 바로가기
Spring

[Spring] Spring Controller 파라미터 타입 (2)

by Real Iron 2019. 2. 8.

Spring Controller 파라미터 타입 (2)


Posted on 2018년 11월 18일


출처 : http://wonwoo.ml/index.php/post/2092


오늘은 예전에 작성했던 Spring Controller 파라미터 타입 에 이어서 두번째 시간을 가져보도록 하자. Spring webmvc 뿐만아니라 다른 프로젝트에서도 다양한 파라미터 타입을 지원주고 있으니 web 뿐아니라 다른 프로젝트에서도 어떤 파라미터를 지원하고 있는지 알아보도록 하자.

webmvc

일단 먼저 Spring mvc 부터 시작하자. 저번에 대부분 spring webmvc에 관련해서 이야기했으나 그래도 많은 부분은 이야기 하지 못했다. 많지는 않지만 몇가지 추가적으로 sprig mvc 파라미터 타입을 살펴보자.

@Value

@Value 어노테이션을 파라미터 타입에 작성할 수 있다. 흠 글쎄다.  Value 어노테이션을 굳이 파라미터에 작성할 이유가 있나 싶기도 하지만 지원은 해주고 있으니..  사용은 하겠지? 

@GetMapping("/value")
public String value(@Value("${name}") String name) {
    //
}

위와 같이 작성후에 properties 에 name이라는 프로퍼티를 작성해주면 된다.

name=wonwoo

아주 간단하다.  이렇게 하면 value를 호출 할때 name이라는 파라미터에 wonwoo  가 자동으로 들어가게 된다.

RedirectAttributes

리다이랙션 할 때 유용한 인터페이스이다. 해당 인터페이스를 사용하면 손쉽게 파라미터등을 전달 할 수 있다. 

@GetMapping("/hi")
public String redirectAttributes(RedirectAttributes redirectAttributes) {
    redirectAttributes.addAttribute("id", 1);
    redirectAttributes.addAttribute("foo", "bar");
    return "redirect:redirectAttributes/{id}";
}

@GetMapping("/redirectAttributes/{id}")
public String redirectAttributesHi(@PathVariable Long id, String foo) {
   //

}

위와 같이 리다이랙션하는 부분에 RedirectAttributes  인터페이스를 파라미터 타입으로 받은 후 파라미터 등을 해당 attribute에  작성하면 된다. 그럼 실제 리다이랙트 부분의 uri는 아래와 같이 만들어진다.

/redirectAttributes/1?foo=bar

리다이랙트 할 때 유용한 파라미터 타입이다.

@RequestAttribute

RequestAttribute 어노테이션은 request attribute 를 가져올 수 있는 어노테이션이다. 미리 정의된 속성을 가져올 수도 있고 지정한 속성을 가져올 수 도 있다. 코드를 보면 더 이해하기 쉬울 것이다.

@ModelAttribute
public void foo(HttpServletRequest request) {
    request.setAttribute("foo", "bar");
}

@GetMapping("/requestAttribute")
@ResponseBody
public String requestAttribute(@RequestAttribute String foo) {
    return foo;
}

위와 같이 필자가 정의한 foo라는 속성을 @RequestAttribute 어노테이션을 이용해서 foo라는 속성을 가져왔다. 굳이 controller가 아닌 interceptor에서 해당 속성을 넣어서 컨트롤러에 파라미터로 넣을 수도 있다. 다양한 방법으로 사용할 수 있으니 참고하면 되겠다.

@MatrixVariable

@MatrixVariable 어노테이션은 조금 생소한 기능을 가지고 있다. 매트릭 형태의 파라미터라고 할까? 잘 쓰면 유연한 url이 되겠지만 필자는 아직 잘 모르겠다. 잘 쓰지 않아서 그런건지도.. 


@GetMapping("/matrix/{id}")
public String matrix(@PathVariable String id, @MatrixVariable int q) {
    
}

만약 위와 같은 코드가 있다면 우리는 아래와 같이 호출하면 된다.

http://localhost:8080/matrix/1;q=11

위와 같이 ; 세미콜론으로 구분이 된다. 조금 어색한 url이 된거 같다.  문서에서 다른 예제도 가져왔다. 어떤지 한번 보고 넘어가자.

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable(name="q", pathVar="ownerId") int q1,
        @MatrixVariable(name="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22
}

/owners/42;q=11/pets/21;q=22

흠.. 좋은건가? 괜찮은 건가? 관심이 있다면 한번 살펴보는 것도 나쁘지 않다. 

참고로 @MatrixVariable 어노테이션을 사용하려면 UrlPathHelper 의 removeSemicolonContent 속성을 false로 작성해야 된다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

UriComponentsBuilder

사용할 일이 있긴 하나? 아무튼 지원은 해주고 있으니 한번 살펴보자. 아니 뭐 살펴볼 것도 없다. 코드를 보자.

@GetMapping("/uriComponentsBuilder")
@ResponseBody
public String uri(UriComponentsBuilder uriComponentsBuilder) {

}

글쎄다. 딱히 유용한 정보들이 들어가는 것도 아니고. 언제 어디에 쓰일지는 잘..

Spring data

spring webmvc를 살펴봤는데 spring data 에서도 몇가지 파라미터를 제공해주고 있다. 한번 살펴보도록 하자.

Pageable 와 Sort

Spring data에는 Pageable 인터페이스와 Sort 클래스가 존재한다. Pageable은 페이지 사이즈, 페이지 번호 Sort등을 넣을 수 있다.  Sort만 사용할 경우에는 Sort 클래스만 파라미터 타입으로 작성하면 된다.

@GetMapping("/page")
public String page(Pageable pageable, Sort sort) {
    
}

위와 같이 작성하였을 경우에 우리는 다음과 같이 호출 할 수 있다.

/page?size=10&page=1&sort=id,desc

그럼 Spring data 에서 적절하게 파싱해 Pageable과 Sort에 값을 넣어 준다. 그럼 손쉽게 spring data api를 사용할 수 있다. 

@PageableDefault 어노테이션을 사용해서 해당 디폴트 값도 변경 가능하다.

@GetMapping("/page")
public String page(@PageableDefault(size = 100, page = 1, sort = "id", direction = Direction.ASC) Pageable pageable) {
 
}

Predicate

Querydsl에서 지원하는 Predicate도 사용할 수 있다. Spring data 와 querydsl을 사용하면 좀 더 손쉽게 사용할 수 있다. 

@GetMapping("/dsl")
public Iterable<Bar> dsl(@QuerydslPredicate(root = Bar.class) Predicate predicate) {
    return barRepository.findAll(predicate);
}

public interface BarRepository extends QuerydslBinderCustomizer<QBar>, JpaRepository<Bar, Long>, QuerydslPredicateExecutor<Bar> {

    @Override
    default void customize(QuerydslBindings bindings, QBar user) {
        bindings.bind(String.class)
            .first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
    }
}

사용하고 싶다면 다음과 같이 호출하면 된다.

/dsl?name=foo

그럼 name이 foo인것을 like검색 한다. 좀 더 자세한 내용은 문서를 통해 확인하면 되겠다.

@ProjectedPayload

Spring data 에서 지원해주는 @ProjectedPayload 어노테이션이다. 인터페이스로 작성하면 된다. 

@GetMapping("/payload")
public String payload(PayLoadExample payLoad) {

}

@ProjectedPayload
public interface PayLoadExample {
    String getName();
}

위와 같이 인터페이스에 @ProjectedPayload 어노테이션을 작성하면 된다. 간단하다. 그러고 나서 아래와 같이 호출하면 된다.

/payload?name=wonwoo

그럼 PayLoadExample 에 getName() 메서드를 호출해보면 wonwoo라는 값을 꺼낼 수 있다.

또한 파라미터 말고도 body도 가능하다. 

@PostMapping("/payload")
public String payload(@RequestBody PayLoadExample payLoad) {
    return payLoad.getName();
}

위와 같이 @RequestBody 어노테이션을 작성하고 호출하면 body로도 받을 수 있다. 

또한 만약 json으로 body를 받는다면 @JsonPath 어노테이션을 이용해서 해당 필드도 변경할 수 있다. 

@ProjectedPayload
public interface PayLoadExample {
    @JsonPath("$..firstname")
    String getName();
}

그럼 firstname 으로 body를 작성해서 보내면 된다. xml을 이용한다면 @XBRead 을 이용해서 변경할 수 있다.

@ProjectedPayload
public interface PayLoadExample {

    @XBRead("//firstname")
    String getName();
}

@JsonPath, @XBRead 어노테이션을 사용하려면 적절한 디펜더시가 필요하다. 

<!-- JsonPath -->
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>${version}</version>
</dependency>
<!-- XBRead -->
<dependency>
    <groupId>org.xmlbeam</groupId>
    <artifactId>xmlprojector</artifactId>
    <version>${version}</version>
</dependency>

Spring cloud web gateway

spring cloud gateway mvc 에서도 ProxyExchange 클래스를 파라미터 타입으로 지정할 수 있다.

프록시 역할을 하는 클래스이다. 아주 간편하게 프록시 역할을 할 수 있다.

@GetMapping("/proxy")
public String proxy(ProxyExchange<String> exchange) {
    ResponseEntity<String> response =
        exchange.uri("https://start.spring.io")
            .get();
    return response.getBody();
}

적절하게 헤더 정보도 변경할 수 있고 파라미터, body도 정보도 변경 할 수도 있다. 

spring webflux

webflux에도 파라미터 타입이 몇가지 추가 되었다. 예를들어 ServerWebExchange 인터페이스나 ServerHttpRequest , ServerHttpResponse 인터페이스를 추가로 파라미터 타입으로 받을 수 있다. 

@GetMapping("/server")
public Mono<?> webflux(ServerWebExchange serverWebExchange) {
    ServerHttpRequest request = serverWebExchange.getRequest();
    ServerHttpResponse response = serverWebExchange.getResponse();
    return Mono.empty();
}

ServerWebExchange 파라미터를 받아서 getRequest 와 getRepsonse 를 꺼내서 사용할 수도 있고 ServerHttpRequest, ServerHttpResponse 를 각각 파라미터로 받아서 사용해도 된다.

@GetMapping("/server")
public Mono<?> webflux(ServerHttpRequest request, ServerHttpResponse response) {
    return Mono.empty();
}

또 한 Mono를 파라미터로 받을 수 도 있다.

@GetMapping("/webflux")
public Mono<?> webflux(Mono<Foo> foo) {
    return foo;
}

위와 같이 Mono를 사용해서 파라미터로 받을 수도 있고 @RequestBody 어노테이션을 이용해서  body로도  받을 수 있다. 여기서 주의할 점은 wrapper Type 은 지원하지 않는다.  model object만 지원하고 있으니 그것만 주의해서 사용하면 되겠다.

오늘은 이렇게 Spring Controller 파라미터 타입에 대해서 좀 더 알아봤다. 유용한 파라미터 타입이 있다면 적절하게 사용하면 좋을 것 같다. 

기존에 있던 web 과 새로운 webflux는 호환되지 않는다. 하지만 이미 기존의 webmvc에 존재 했던 예를들어 @RequestParam, @RequestBody , … 기타 등등 들 파라미터 타입들이  webflux 에서 다시 구현되어 있다. (왜냐면 인터페이스가 완전히 다르기 때문이다. ) 하지만 우리는 그것을 신경쓰지 않고 마치 webmvc 처럼 동일하게 사용할 수 있었던 것이다. 그래서 webflux에서도 @RequestParam, @RequestBody … 기타 등등 webmvc에서 사용했던 어노테이션, 클래스들을 사용할 수 있다. 물론 모두는 아니겠지만 대부분은 사용가능하다.