오늘은 Spring 에서 지원해주는 Controller 리턴타입에 대해서 알아 보도록 하자. 저번에 Spring Controller 파라미터 타입을 작성했는데 나름 인기가 좋아서 리턴타입도 해보려고 한다. 이건 인기가 별로 없을 듯 한데..
어쨌든.. Spring 에서는 많은 리턴타입들을 제공해준다. 대부분 거의 다 알아볼텐데 (필자가 아는 것들은..) 빠진게 있거나 설명이 부족하다면 Spring 문서를 보는 것을 추천한다. 그럼 시작해보자.

String

필자가 가장 자주 사용하며 (뷰템플릿을 사용할 때) 가장 간단한 리턴 타입이다. String 에는 뷰네임을 지정해주면 된다.

@GetMapping("/string")
public String str(Model model) {
  model.addAttribute("user", DATA);
  return "index";
}

Model 안에 데이터를 담고 뷰명을 string 타입으로 작성하면 된다. 그럼 뷰에서는 다음과 같이 해당 코드를 작성하면 된다.

<!DOCTYPE html>
<html>
<body>

<h2>name</h2>
<h3>{{user.name}}</h3>
<h2>email</h2>
<h3>{{user.email}}</h3>

</body>
</html>

뷰템플릿은 mustache를 이용했다. 문법자체는 해당 뷰템플릿을 알아야 한다. 하지만 모델 자체를 받을 때는 비슷하니 다른 뷰템플릿도 동일할 듯 싶다.

ModelAndView

가장 대표적이며 필자도 예전에는 자주 사용한 클래스이다. (왜냐면 옛날 내 사수가 이걸 좋아했기에) 하지만 요즘은 거의 사용하지 않는다. 필자도 바로 위에 작성한 string 타입으로 작성한다.

@GetMapping("/modelview")
public ModelAndView modelview() {
  return new ModelAndView("index").addObject("user", DATA);
}

Model도 파라미터로 받을 필요 없이 ModelAndView 클래스에 addObject 메서드를 이용해서 해당하는 데이터를 담기만 하면 된다. String 타입과 비슷하다. 이것 역시 뷰에 작성할 부분은 동일하다.

//...
<h2>name</h2>
<h3>{{user.name}}</h3>
<h2>email</h2>
<h3>{{user.email}}</h3>
//...

void

Spring 에서는 void 타입도 리턴이 가능하다. void 타입인데 어떻게 view 명을 지정해줄 수 있을까? 딱히 view명을 지정해줄 방법이 보이지 않는데 말이다. Spring은 뷰명을 입력하지 않아도 기본적으로 해당 url을 이용해서 뷰네임을 결정한다. 실제 인터페이스는 RequestToViewNameTranslator 인터페이스이며 DefaultRequestToViewNameTranslator 구현체 하나만을 갖고 있다. 만약 기본적인 뷰명을 변경하고 싶다면 커스텀하게 구현하면 되지 않을까 싶다.

@GetMapping("/void")
public void void(Model model) {
  model.addAttribute("user", DATA);
}

void.html 혹은 그에 따른 뷰템플릿 양식으로 해당파일을 만들면 된다.

//...
<h2>name</h2>
<h3>{{user.name}}</h3>
<h2>email</h2>
<h3>{{user.email}}</h3>
//...

Model Object

여기서 말하는 Model은 Spring에서 제공해주는 Model 클래스가 아니다. 일반 우리가 자주 사용하는 모델 오브젝트를 이야기하는 것이다. Spring 에서는 모델 자체를 리턴해도 된다. 다음과 같이 말이다.

public class Hello {
  private String name;
  private String email;

//...

@GetMapping("/hello")
public Hello hello() {
  return new Hello("wonwoo", "wonwoo@test.com");
}

이 또한 역시 뷰네임을 지정해줄 수 없기에 void와 마찬가지로 RequestToViewNameTranslator 이용해서 뷰네임을 지정한다. 또한, 모델에 대한 명도 조금 다르다 앞에서는 모델명을 지정해주었지만 여기서는 해줄수 없다. (물론 해줄수 있는 방법은 있는데 여기서는 설명하지 않겠다.) 그래서 해당 클래스의 명으로 모델명이 지어진다.

//...
<h2>name</h2>
<h3>{{hello.name}}</h3>
<h2>email</h2>
<h3>{{hello.email}}</h3>
//...

앞에서는 user 라는 모델명을 명시했지만 여기서는 명시하지 않았기에 해당 클래스가 모델명이 된다. Hello으로 클래스로 작성했기에 hello란 모델명으로 작성하면 된다.

Map , 기타 Map(Spring)

모델 오브젝트와 동일하게 Map 형태도 리턴타입으로 가능하다. 하지만 모델 오브젝트와 조금 다른점은 개별로 모델이 등록된다는 것이다. Map이라는 클래스니까 map이라는 모델명이 나올 것 같지만 그렇지 않다.

@GetMapping("/map")
public Map<String, String> map() {
  return DATA;
}

만약 위와 같이 작성했다면 우리는 다음과 같이 뷰를 작성해야 된다.

//...
<h2>name</h2>
<h3>{{name}}</h3>
<h2>email</h2>
<h3>{{email}}</h3>
//...

하지만 그닥 추천하지 않는다고 한다. 그래서 더이상 설명은 생략한다.

@ResponseBody

이것 역시 자주 사용하기에 대부분 다 알 것이라고 생각한다. SPA로 개발을 하거나 ajax로 개발을 할 떄 유용한 어노테이션이다. Http 본문자체를 리턴하며 xml 혹은 json 등 여러 메시지 컨버터를 Spring에서 지원하니 그건 따로 살펴봐야 된다.

@GetMapping("/body")
@ResponseBody
public String body() {
  return "<h2>name</h2>\n" +
      "<h3>wonwoo</h3>\n" +
      "<h2>email</h2>\n" +
      "<h3>wonwoo@test.com</h3>";
}

하지만 요즘은 @ResponseBody 어노테이션을 직접사용하지 않고 @Controller 어노테이션과 @ResponseBody 어노테이션을 메타 어노테이션으로 작성해둔 @RestController를 많이 사용하니 참고하면 되겠다.

Spring5

Spring5에서 지원해주는 reactive streams 또한 리턴타입으로 가능하다. 기본적으로 spring5는 reactor를 지원한다. 왜냐하면 자기들이 만들었으니.. 어쨌든 reactor 에 있는 Mono, Flux으로 리턴타입으로 작성해도 된다.

@GetMapping("/spring5")
public Mono<String> hello() {
  return Mono.just("hello world");
}

여기서는 Mono만 했지만 Flux 또한 가능하다.
또한 reactive streams의 인터페이스인 Publisher을 리턴타입으로 명시해도 동작한다.

@GetMapping("/spring5")
public Publisher<String> hello() {
  return Mono.just("hello world");
}

이 외에도 rxjava1, rxjava2에 있는 Observable, Single, Completable, Flowable, Maybe 등 rxjava1과 rxjava2를 모두 지원하니 적당한 디펜더시를 작성한다면 사용해도 좋다. 또한 지금 현재 버전(5.0.7)에서는 java9에 들어간 reactive streams 인터페이스도 지원하니 자기에게 알맞는 구현체 또는 인터페이스를 사용해도 된다.

Rendering

spring5 에서는 Rendering 인터페이스가 추가되었다. reactive streams를 사용할 때 해당하는 뷰와 데이터를 넣을 수 있는 인터페이스이다.

@GetMapping("/")
public Rendering hi() {
  return Rendering
      .view("index")
      .modelAttribute("user", DATA)
      .build();
}

기존의 Model과 비슷한 생김새이다. 그래서 많이 거부감들지 않게 작성할 수 있다.

오늘은 이렇게 Spring에서 지원해주는 Controller 리턴타입에 대해서 알아봤다. 필자가 모르는 것이 있을 수 있기에 더욱 상세한 내용들은 해당문서를 참고하면 좋겠다. 알면 저도 알려주세요.