Back-End/Spring Boot

[Spring Boot] @Controller와 @RestController

ch4njun 2021. 4. 5. 22:31
반응형

Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있다. Spring MVC Framwork에서 사용되는 컨트롤러는 @Controller이고, RESTful Service에서 사용되는 컨트롤러는 @RestController이다. 이러한 두 가지 컨트롤러의 차이점은 HTTP Response Body가 생성되는 방식이다.

 

그러면, 두 가지 종류의 컨트롤러가 어떻게 동작하는지 알아보자.

 

@Controller(Spring MVC Controller)


@Controller 어노테이션을 통해 생성한 컨트롤러는 전통적인 Spring MVC Framework에서 주로 사용하는 컨트롤러이다. 이 컨트롤러의 주요 목적은 View를 반환하기 위함이다. 즉, Client에 요청에 따라 그에 맞는 View를 찾아 반환해주는 역할을 수행한다.

 

 

  1. Client는 URI 형식으로 웹 서버에 Request를 전송한다.
  2. Mapping되는 Handler와 그 Type에 따라 Dispatcher Servlet이 해당 Request를 인터셉트한다.
  3. Controller가 Request를 처리한 후에 응답을 Dispatcher Servlet에 반환하고, Dispatcher Servlet은 View를 Client에게 반환한다. 이 때 Controller가 View를 반환하기 위해서는 ViewResolver가 사용되면 설정에 맞게 알맞는 View를 찾아 랜더링한다.

 

여기서 Dispatcher Servlet은 Client의 모든 Request를 한 곳에서 받아서 처리하기 위해 존재한다. 즉, 제일 앞에서 중앙집중식 요청 처리를 수행한다. (Front Controller라 부르기도 한다) Request를 받은 Dispatcher Servlet은 그에 맞는(Mapping 되는) Handler로 Request를 전달한다. 그리고 그에 대한 실행결과를 HTTP Response 형태로 만들어 Client에게 반환한다.

 

이러한 @Controller에도 View를 반환하는 것이 아닌 JSON와 같은 형태로 데이터를 반환하는 경우가 있다. Spring MVC Framework의 @Controller에서는 데이터를 반환하기 위해서 @ResponseBody 어노테이션을 활용해야 한다.

 

이렇게 @ResponseBody 어노테이션을 추가하면 데이터를 반환하기 위해 ViewResolver 대신 HttpMessageConverter가 동작한다. HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 어떤 Converter가 사용될지 결정된다.

 

예를 들면, 단순 문자열의 경우 StringHttpMessageConverter, 객체의 경우에는 MappingJackson2HttpMessageConverter가 사용된다. Spring은 Client의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보를 조합해 적합한 Converter를 선택해 이를 처리한다.

 

여기서 HTTP Accept 헤더는 application/xml 이나 application/json 와 같이 Client가 HTTP Request를 보낼때 지정할 수 있으며 이에 따라 반환되는 데이터의 형식이 달라진다.

 

이러한 컨트롤러를 사용한 예제코드를 살펴보자.

package com.mang.blog.application.user.controller;

import com.mang.blog.application.user.model.UserVO;
import com.mang.blog.application.user.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@Controller
@RequestMapping("/user")
public class UserController {

    @Resource(name = "userService")
    private UserService userService;

    @PostMapping(value = "/info")
    public @ResponseBody User info(@RequestBody User user){
        return userService.retrieveUserInfo(user);
    }
    
    @GetMapping(value = "/infoView")
    public String infoView(Model model, @RequestParam(value = "userName", required = true) String userName){
        User user = userService.retrieveUserInfo(userName);
        model.addAttribute("user", user);
        return "/user/userInfoView";
    }

}

 

심지어 과거 Spring MVC Framework에서는 이러한 컨트롤러를 생성하는 과정을 어노테이션이 아니라 XML 파일에 설정정보를 저장했었다. 아마 내가 Spring에 대해서 처음 배울때는 그렇게 했던거 같은데 정말 많이 편해진거같다.

 

@RestController(Spring RESTful Service Controller)


@RestController는 위에서 살펴본 @Controller에 @ResponseBody 어노테이션을 활용해 데이터를 반환하던 것과 같은 역할을 수행한다. 즉, @RestController는 View가 아니라 JSON와 같은 데이터를 반환하는 용도로 사용된다.

 

해당 Spring 서버를 REST API 서버로 활용할 때 사용할 수 있는 컨트롤러이다.

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  2. Mapping되는 Handler와 그 Type에 따라 Dispatcher Servlet이 해당 Request를 인터셉트한다.
  3. Mapping되는 RestController는 해당 Request를 처리하고 JSON과 같은 데이터를 Client에게 반환한다.

 

이러한 RestController를 사용하는 예제코드를 살펴보자.

package com.mang.blog.application.user.controller;

import com.mang.blog.application.user.model.UserVO;
import com.mang.blog.application.user.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource(name = "userService")
    private UserService userService;

    @PostMapping(value = "/info1")
    public User info1(@RequestBody User user){
        return userService.retrieveUserInfo(user);
    }

    @PostMapping(value = "/info2")
    public ResponseEntity<User> info2(@RequestParam(value = "userName", required = true) String userName){
        User user = userService.retrieveUserInfo(userName);

        if(user == null){
            return ResponseEntity.noContent().build()
        }

        return ResponseEntity.ok(user)
    }

    @PostMapping(value = "/info3")
    public ResponseEntity<User> info3(@RequestParam(value = "userName", required = true) String userName){
        return Optional.ofNullable(userService.retrieveUserInfo(userName))
                .map(user -> ResponseEntity.ok(user))
                .orElse(ResponseEntity.noContent().build());
    }
}

 

 

 

반응형