Spring

[SpringBoot] @Controller와 @RestController의 차이점과 요청 처리 흐름

leejunkim 2025. 7. 3. 14:26

WeeklyPaper: Spring MVC에서 클라이언트의 요청 처리 흐름을 @Controller와 @RestController의 차이점을 중심으로 각각의 처리 과정과 특징을 포함하여 설명하세오.


웹 어플리케이션을 개발할때 클라이언트로부터 온 요청을 받아서 처리하고 응답하는 로직을 구현하는건 기본이다.

 

Spring MVC에서 공통된 순서는 이렇게 된다:

  • Client가 URI 형식으로 웹서비스 (어플리케이션)로 요청을 보낸다
    • URL 예시: https://.../v1/members/1
  • DispatcherServlet이 요청을 받음 (프론트 컨트롤러 역할)
  • DispatcherServlet가 요청받은 URI를 처리할 수 있는 핸들러(컨트롤러의 메서드)를 찾기 위해 HandlerMapping에게 요청 정보를 넘기고, HandlerMapping은 URL에 매핑되는 컨트롤러 메서드 정보를 찾아서 DispatcherServlet에게 다시 보내준다
  • DispatcherServlet은 찾은 핸들러를 실행할 수 있는 HandlerAdapter에게 요청 처리를 위임한다
  • HandlerAdapter가 실제 컨트롤러의 메서드를 실행(invoke)한다.
    • 만약 클라이언트가 보낸 요청의 본문(body)에 JSON 데이터가 있고, 컨트롤러 메서드가 @RequestBody/@RestController를 사용해 이 데이터를 자바 객체로 받아야 한다면, HandlerAdapter는 HttpMessageConverter를 사용해 HTTP 요청 메시지를 자바 객체로 변환한 뒤 메서드에 넘겨준다

여기서 HandlerAdapter이 메서드를 실행할때, 반환 객체를 어떻게 돌려줄지에 따라 선택지가 다르다.

@Controller - SSR

일단 전통적으로 Spring MVC는 Server-side Rendering(SSR)을 사용하는데, 이 뜻은 서버에서 클라이언트 보여질 화면을 미리 생성해서 보내주는 것을 뜻한다.

  • @Controller가 붙은 컨트롤러의 메서드가 String을 반환하면, Spring MVC는 이것을 View 이름(Logical View Name)으로 해석한다.
  • HandlerAdapter은 추후에 메서드의 리턴값인 View이름과 값들이 채워진 Model 객체를 DispatcherServlet에게 전달을 해준다.
  • DispatcherServlet은 이 View이름을 ViewResolver에세 전달해서 실제 View객체를 찾도록 요청한다.
  • ViewResolver는 application.properties나 application.yml에 설정된 prefix와 suffix(예: templates/, .html)를 View 이름과 조합하여 실제 템플릿 파일(예: templates/memberDetail.html)을 찾아 View 객체를 생성한다.
  • DispatcherServlet은 View 객체에 Model 데이터를 넘겨주어 화면 렌더링을 View에게 요청하고, 최종적으로 렌더링된 HTML 문서가 HTTP 응답 본문에 담겨 클라이언트에게 전달된다.
package com.springboot.member;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/v1/members")
public class MemberController {
    @GetMapping("/{member-id}")
    public String getMember(@PathVariable("member-id") long memberId, Model model) {
        // 1. 비즈니스 로직 처리 (DB에서 멤버 정보 조회 등)
        // Member member = memberService.findMember(memberId);
        
        // 2. Model에 View에 전달할 데이터를 담는다.
        // model.addAttribute("member", member); 
        model.addAttribute("memberId", memberId); // 예시 단순화

        // 3. View 이름을 반환한다.
        return "memberDetail"; // templates/memberDetail.html
    }
}

 

 

@RestController

View가 아닌 데이터를 반환하기 위해 사용이 된다. SSR이 아닌 Client-side rendering (CSR)을 하고 싶을 때 사용하면 된다.  

이때 ResponseEntity도 감싸서 반환을 하고, 이 객체는 JSON으로 직렬화 되어서 사용자에게 반환이 된다.

  • HandlerAdapter는 컨트롤러 메서드에 @ResponseBody가 있는 것을 확인하고, 반환된 객체(POJO, ResponseEntity 등)를 DispatcherServlet에 전달한다.
  • DispatcherServlet은 View를 찾는 ViewResolver를 거치지 않고, HttpMessageConverter를 바로 호출한다.
  • HttpMessageConverter는 반환된 자바 객체를 HTTP 응답에 맞는 형식(예: application/json)으로 직렬화(Serialization)한다
    • 이 직렬화된 데이터(JSON 문자열 등)는 HTTP 응답 본문에 담겨 클라이언트에게 직접 전달된다.
package com.springboot.member.v2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController // @Controller + @ResponseBody
@RequestMapping("/v2/members")
public class MemberControllerV2 {
    
    @GetMapping("/{member-id}")
    public ResponseEntity<MemberResponseDto> getMember(@PathVariable("member-id") long memberId) {
        // 비즈니스 로직 처리...
        
        // DTO 객체 생성
        MemberResponseDto response = new MemberResponseDto(memberId, 
                                                            "hgd@gmail.com", 
                                                            "홍길동");
                                                            
        // ResponseEntity로 감싸서 상태 코드와 함께 데이터 반환
        return new ResponseEntity<>(response, HttpStatus.OK);
    }
}

 

  • ResponseEntity
      • ResponseEntity를 사용하면 HTTP 상태 코드(Status Code), 헤더(Header) 등을 자유롭게 커스터마이징할 수 있어 더 정교한 응답이 가능하게 한다
      • ResponseEntity는 ViewResolver가 아닌 HttpMessageConterter을 통해 핸들러 메서드의 리턴 객체를 JSON으로 직렬화한다.
      • HttpMessageConverter가 ResponseEntity객체를 JSON으로 변환해준 것을 그대로 DispatcherServlet이 client에게 보내준다.
  • @RestController는 사실상 그냥 @Controller + @ResponseBody이다.
    • @ResponseBody = "View 찾지 말고, HttpMessageConverter 불러줘" (이렇게 이해하면 편하다).

결론

과거에는 JSP, HTML과 같은 View를 전달해 주었기에 주로 @Controller를 사용했다. 하지만 최근에는 프론트/백엔드를 나누어 개발하는 경우가 더 많아지고 있고, 따라서 백엔드는 프론트와 소통하기 위해 REST API를 사용해서 JSON으로 데이터만 전달해야 하므로 @RestController가 더 많이 사용된다고 한다.