본문 바로가기
코딩 공부/web & Java

[Spring] @Controller와 @RestController

by 현장 2023. 11. 4.

@Controller

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

  1. Client는 URI 형식으로 웹 서비스에 요청을 보냅니다.
  2. DispatcherServlet이 요청을 처리할 대상을 찾습 니다.
  3. HandlerAdapter을 통해 요청을 Controller로 위임합니다.
  4. Controller는 요청을 처리한 후에 ViewName을 반환합니다.
  5. DispatcherServlet은 ViewResolver를 통해 ViewName에 해당하는 View를 찾아 사용자에게 반환합니다.

Controller가 반환환 뷰의 이름으로부터 View를 렌더링하기 위해서 ViewResolver 설정에 맞게 View를 찾아 렌더링합니다.

 

- Controller로 Data 반환하기

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

  1. Client는 URI 형식으로 웹 서비스에 요청을 보냅니다.
  2. DispatcherServlet이 요청을 처리할 대상을 찾습니다.
  3. HandlerAdapter을 통해 요청을 Controller로 위임합니다.
  4. Controller는 요청을 처리한 후에 객체를 반환합니다.
  5. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환됩니다.

컨트롤러를 통해 객체를 반환할 때에는 일반적으로 ResponseEntity로 감싸서 반환을 합니다. 그리고 객체를 반환하기 위해서는 viewResolver 대신에 HttpMessageConverter가 동작합니다.

HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 따라 사용되는 Converter가 달라집니다.

객체인 경우 MappingJackson2HttpMessageConverter가 사용되는 등 데이터 종류에 따라 서로 다른 MessageConverter가 작동하게 됩니다.

Spring은 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해 적합한 HttpMessageConverter를 선택하여 이를 처리합니다.

MessageConverter가 동작하는 시점은 HandlerAdapter와 Controller가 요청을 주고 받는 시점입니다.

그림의 4번에서는 메세지를 객체로, 6번에서는 객체를 메세지로 변환하는데 메세지 컨버터가 사용됩니다.

-예시 코드

@Controller
public class UserController {

    private UserDaoService service;

    public UserResource(UserDaoService service) {
        this.service = service;
    }

    @GetMapping(value = "/users")
    @ResponseBody
    public ResponseEntity<User> findUser(@RequestParam("id") int id){
        return ResponseEntity.ok(userService.findById(id));
    }
    
}

 

위 예제의 findUser는 User 객체를 ResponseEntity로 감싸서 반환하고 있고, User를 json으로 반환하기 위해 @ResponseBody라는 어노테이션을 붙여주고 있습니다.

단, 이렇게 데이터를 반환하는 RestController와 View를 반환하는 Controller를 분리하여 작성하는 것이 좋습니다.

 

@RestController

@RestController는 데이터를 응답으로 제공하는 REST API를 개발할 때 주로 사용합니다. @Controller + @ResponseBody로 합쳐진 형태로 Json 형태로 객체 데이터를 반환니다. 

  1. Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  2. DispatcherServlet이 요청을 처리할 대상을 찾는다.
  3. HandlerAdapter을 통해 요청을 Controller로 위임한다.
  4. Controller는 요청을 처리한 후에 객체를 반환한다.
  5. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환된다.

- 예시코드

@RestController
public class UserResource {
    private UserDaoService service;

    public UserResource(UserDaoService service) {
        this.service = service;
    }

    // GET /users
    @GetMapping("/users/{id}")
    public User findUsers(@PathVariable int id) {
        User user = service.findOne(id);
        return user;
    }

    // POST /users
    @PostMapping("/users")
    public ResponseEntity<Object> createUser(@RequestBody User user) {
        User saveUser = service.save(user);

        // 새로 만들어진 유저(ex. /users/{id} <id = user.getId()>) 반환 -> location 헤더를 통해서 가능
        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(saveUser.getId())
                .toUri();

        return ResponseEntity.created(location).build();
    }

}

findUser는 User 객체를 그대로 반환하고 있습니다. 이러한 경우의 문제는 클라이언트가 예상하는 HttpStatus를 설정해줄 수 없다는 것입니다.

예를 들어 어떤 객체의 생성 요청이라면 201코드를 기대할 것이지만 객체를 그대로 반환하면 HttpStatus를 설정해줄 수 없습니다.

그래서 createUser와 같이 상황에 맞는 ResponseEntity로 감싸서 반환해주어야 합니다.

 

Reference

'코딩 공부 > web & Java' 카테고리의 다른 글

[Spring] ResponseEntity  (0) 2023.11.07
[Spring] 전역 예외 처리  (0) 2023.11.04
[Spring] Spring Security  (0) 2023.10.26
[Spring] 스프링 실행 후 웹에서 한글 ?? 깨짐 에러  (0) 2023.10.20
[Spring] JPA  (0) 2023.10.17