관리 메뉴

제뉴어리의 모든것

[Spring] @Controller와 @RestController 차이 본문

Spring Boot

[Spring] @Controller와 @RestController 차이

제뉴어리맨 2021. 2. 1. 16:29

Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있습니다. 전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차이점은 HTTP Response Body가 생성되는 방식입니다. 이번에는 2가지 어노테이션의 차이와 사용법에 대해 알아보도록 하겠습니다.

 

1. @Controller(Spring MVC Controller)


[ Controller - View ]

전통적인 Spring MVC의 컨트롤러인 @Controller는 주로 View를 반환하기 위해 사용합니다. 아래와 같은 과정을 통해 Spring MVC Container는 Client의 요청으로부터 View를 반환합니다.

 

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.

  2. Mapping되는 Handler와 그 Type을 찾는 DispatcherServlet이 요청을 인터셉트한다.

  3. Controller가 요청을 처리한 후에 응답을 DispatcherServlet으로 반환하고, DispatcherServlet은 View를 사용자에게 반환한다.

@Controller가 View를 반환하기 위해서는 ViewResolver가 사용되며, ViewResolver 설정에 맞게 View를 찾아 렌더링합니다.

 

 

[ Controller - Data ]

하지만 Spring MVC의 컨트롤러에서도 Data를 반환해야 하는 경우도 있습니다. Spring MVC의 컨트롤러에서는 데이터를 반환하기 위해 @ResponseBody 어노테이션을 활용해주어야 합니다. 이를 통해 Controller도 Json 형태로 데이터를 반환할 수 있습니다.

 

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.

  2. Mapping되는 Handler와 그 Type을 찾는 DispatcherServlet이 요청을 인터셉트한다.

  3. @ResponseBody를 사용하여 Client에게 Json 형태로 데이터를 반환한다.

@RestController가 Data를 반환하기 위해서는 viewResolver 대신에 HttpMessageConverter가 동작합니다. HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 따라 사용되는 Converter가 달라집니다. 단순 문자열인 경우에는 StringHttpMessageConverter가 사용되고, 객체인 경우에는 MappingJackson2HttpMessageConverter가 사용되며, 데이터 종류에 따라 서로 다른 MessageConverter가 작동하게 됩니다. Spring은 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해 적합한 HttpMessageConverter를 선택하여 이를 처리합니다.

 

[ @Controller 예제 코드 ]

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 = "/retrieveUserInfo")
    public @ResponseBody UserVO retrieveUserInfo(@RequestBody UserVO userVO){
        return userService.retrieveUserInfo(userVO);
    }
    
    @GetMapping(value = "/userInfoView")
    public String userInfoView(Model model, @RequestParam(value = "userName", required = true) String userName){
        UserVO userVO = userService.retrieveUserInfo(userName);
        model.addAttribute("userInfo", userVO);
        return "/user/userInfoView";
    }

}

위 예제의 retrieveUserInfo는 UserVO라는 데이터를 반환하고자 하고 있고, UserVO를 json으로 반환하기 위해 @ResponseBody라는 어노테이션을 붙여주고 있다. userInfoView 함수에서는 View를 전달해주고 있기 때문에 String을 반환값으로 설정해주었다.

 

 

 

2. @RestController(Spring Restful Controller)


[ RestController ]

@RestController는 Spring MVC Controlle에 @ResponseBody가 추가된 것입니다. 당연하게도 RestController의 주용도는 Json 형태로 객체 데이터를 반환하는 것입니다. 개인적으로는 VueJS + Spring boot 프로젝트를 진행하며 Spring boot를 API 서버로 활용할 때 또는 Android 앱 개발을 하면서 데이터를 반환할 때 사용하였습니다.

 

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  2. Mapping되는 Handler와 그 Type을 찾는 DispatcherServlet이 요청을 인터셉트한다.
  3. RestController는 해당 요청을 처리하고 데이터를 반환한다.

 

 

[ @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 = "/retrieveUserInfo1")
    public UserVO retrieveUserInfo1(@RequestBody UserVO userVO){
        return userService.retrieveUserInfo(userVO);
    }

    @PostMapping(value = "/retrieveUserInfo2")
    public ResponseEntity<UserVO> retrieveUserInfo2(@RequestParam(value = "userName", required = true) String userName){
        userVO = userService.retrieveUserInfo(userName);

        if(userVO == null){
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        return new ResponseEntity<>(userVO, HttpStatus.OK);
    }

    @PostMapping(value = "/retrieveUserInfo3")
    public ResponseEntity<UserVO> retrieveUserInfo3(@RequestParam(value = "userName", required = true) String userName){
        return Optional.ofNullable(userService.retrieveUserInfo(userName))
                .map(userVO -> new ResponseEntity<>(userVO, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

 

retrieveUserInfo1의 메소드는 UserVO를 그대로 반환하고 있습니다. 하지만 이렇게 처리하는 것 보다 retrieveUserInfo2처럼 결과 데이터와 상태코드를 함께 제어하여 반환하는 것이 좋습니다. 그리고 만약 userService에서 반환하는 형태가 Optional이라면 retrieveUserInfo3 처럼 깔끔하게 처리를 해줄 수 있습니다.

(Optional에 대해서 잘 모른다면 여기를 참고해주세요!)

 

 

출처 : [Spring] @Controller와 @RestController 차이 - MangKyu's Diary (tistory.com)