
Spring Boot를 이용한 RESTful 웹 개발을 하다 보면 @RestController를 달아둔 메서드만 작성해도 간단하게 JSON 응답이 반환됩니다. 하지만 내부적으로는 수많은 컴포넌트가 유기적으로 동작하며 이 결과를 만들어냅니다.
이번 글에서는 Spring Boot에서 HTTP 요청이 들어와 @RestController가 이를 처리하고, 응답으로 변환되어 반환되기까지의 전체 과정을 이해하는 데 초점을 맞춰 설명하겠습니다.
[클라이언트 요청]
↓
DispatcherServlet
↓
HandlerMapping → Handler(Controller)
↓
HandlerAdapter 호출
↓
Controller 메서드 실행
↓
[응답 객체 반환]
↓
HttpMessageConverter가 객체 → JSON 등으로 변환
↓
HttpServletResponse에 응답 작성
↓
[클라이언트 응답 전달]
모든 HTTP 요청은 DispatcherServlet을 통해 시작됩니다. Spring Boot는 내부적으로 / 경로에 DispatcherServlet을 자동 등록하여 모든 요청을 이 객체가 받아들이도록 합니다.
@Configuration
public class WebConfig {
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}
DispatcherServlet의 역할:
DispatcherServlet은 내부적으로 등록된 여러 HandlerMapping 중 적절한 매핑을 통해 요청을 처리할 Handler(Controller 메서드)를 찾습니다.
대표적으로는 다음과 같은 매핑 전략이 있습니다:
예시: GET /users 요청 → UserController.getUsers()
찾아낸 핸들러는 객체일 뿐 실행 방법을 DispatcherServlet이 직접 알 수 없습니다. 그래서 필요한 것이 HandlerAdapter입니다.
RequestMappingHandlerAdapter: @RestController, @Controller 메서드를 실행해줌
인자 바인딩, 유효성 검사, @RequestBody 변환 등도 여기서 수행
핸들러 메서드를 호출한 결과로 Java 객체가 반환됩니다.
@RestController는 @Controller + @ResponseBody가 결합된 애노테이션입니다.
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping
public List<User> getUsers() {
return userService.findAll();
}
}
위처럼 반환된 List<User>는 View 이름이 아니라 객체 자체이므로, 이 객체를 HTTP 응답 본문으로 변환해주는 과정이 필요합니다.
@RestController와 @Controller의 차이점 자세히 보기
이 시점에 등장하는 핵심 컴포넌트가 바로 HttpMessageConverter입니다.
Spring은 Java 객체를 HTTP 응답 본문으로 직렬화(serialize)하거나, 반대로 요청 본문을 Java 객체로 역직렬화(deserialize)할 때 이 컴포넌트를 사용합니다.
요청 처리 시: @RequestBody → JSON을 객체로 변환
응답 처리 시: 컨트롤러가 반환한 객체 → JSON으로 변환 (여기서 사용!)
| 클래스 | 설명 |
|---|---|
MappingJackson2HttpMessageConverter | JSON 변환 (Jackson 기반) |
StringHttpMessageConverter | 문자열 처리 |
ByteArrayHttpMessageConverter | 바이너리 처리 |
FormHttpMessageConverter | x-www-form-urlencoded 처리 |
Spring Boot는 자동으로 MappingJackson2HttpMessageConverter를 등록하고, application/json으로 응답을 보냅니다.
변환된 JSON 문자열은 HttpServletResponse 객체에 작성되며, 상태 코드와 Content-Type 헤더도 함께 설정됩니다.
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": 1,
"name": "Juyong"
}
]
| 단계 | 설명 |
|---|---|
| 1 | DispatcherServlet이 요청 수신 |
| 2 | HandlerMapping이 적절한 Controller 메서드 탐색 |
| 3 | HandlerAdapter가 메서드 실행 |
| 4 | @RestController 메서드가 객체 반환 |
| 5 | HttpMessageConverter가 객체를 JSON 등으로 직렬화 |
| 6 | 응답 본문으로 작성되어 클라이언트에 전송 |
Spring에서는 @Controller와 @RestController를 모두 제공하는데, 두 애노테이션은 사용하는 상황과 목적이 다릅니다.
| 애노테이션 | 주로 쓰는 상황 | 반환 결과 | 설명 |
|---|---|---|---|
@Controller | 웹 페이지 렌더링 | View 이름 (HTML, JSP 등) | 서버에서 뷰 템플릿을 조합해 HTML을 반환하고 싶은 경우 |
@RestController | REST API 응답 | JSON, XML, 문자열 등 | 데이터를 API 형태로 전달하고 싶은 경우 (프론트엔드 분리 구조에 적합) |
HTML을 직접 렌더링하여 브라우저에 전달하고 싶을 때 사용.
@Controller
public class HomeController {
@GetMapping("/home")
public String home(Model model) {
model.addAttribute("username", "이주용");
return "home"; // templates/home.html 이 렌더링됨 (Thymeleaf 등)
}
}
데이터를 JSON으로 응답하여 클라이언트 애플리케이션에서 처리하도록 할 때
@RestController
@RequestMapping("/api/users")
public class UserApiController {
@GetMapping
public List<User> getAllUsers() {
return userService.findAll(); // 자동으로 JSON으로 변환됨
}
}
🔗 Spring 공식 문서: Web MVC DispatcherServlet
🔗 Velog: SOAP에서 REST로의 전환