이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Controller 메서드의 파라미터는 누가 만들어 주는가?
- Controller 반환값은 어떻게 View 렌더링 또는 Response Body로 갈라지는가?
- Fat Controller를 피하려면 요청 처리 책임을 어떻게 나누어야 하는가?
개요
Spring MVC의 Controller는 HTTP 요청을 애플리케이션 로직으로 연결하는 handler다. HandlerAdapter는 Controller 메서드를 호출하기 전에 파라미터를 준비하고, 호출 후 반환값을 해석한다.
기존 MVC 문서의 Request Response Cycle은 이 흐름을 “요청 수신, 핸들러 결정, 핸들러 실행, 서비스 처리, 모델 바인딩, 뷰 렌더링”으로 나누어 설명한다. 새 문서에서는 이 단계를 하나의 실행 흐름으로 압축한다.
원리
Controller 메서드는 선언적이다.
@GetMapping("/members/{id}")
public String member(@PathVariable Long id, Model model) {
Member member = memberService.find(id);
model.addAttribute("member", member);
return "member/detail";
}여기서 id와 model을 개발자가 직접 만들지 않는다. HandlerAdapter가 적절한 ArgumentResolver를 통해 값을 준비한다. 일반 @Controller에서 반환값 "member/detail"은 view name으로 해석되고 ViewResolver가 실제 View를 찾는다.
REST API에서는 흐름이 다르다.
@GetMapping("/api/members/{id}")
public MemberResponse member(@PathVariable Long id) {
return memberService.findResponse(id);
}@RestController나 @ResponseBody 맥락에서는 반환 객체가 View 이름이 아니라 response body로 처리된다. 이때 HttpMessageConverter가 객체를 JSON 등으로 직렬화한다. 같은 String 반환이라도 일반 MVC Controller에서는 view name이 될 수 있고, REST 맥락에서는 body가 될 수 있으므로 애너테이션 맥락을 함께 봐야 한다.
Controller의 적절한 책임
Controller가 맡기 좋은 책임은 다음과 같다.
- 요청 경로와 method를 선언한다.
- 요청 데이터를 DTO나 파라미터로 받는다.
- 입력 검증 결과를 확인한다.
- Service를 호출한다.
- View model 또는 response DTO를 반환한다.
- HTTP 상태 코드, 헤더, body 모양을 결정한다.
Controller가 맡지 않는 편이 좋은 책임은 다음과 같다.
- 핵심 비즈니스 규칙
- 트랜잭션 경계의 복잡한 조정과 도메인 상태 변경 규칙
- 외부 시스템 호출 정책
- 긴 분기와 계산 로직
실전 팁
- Controller는 HTTP adapter로 보고, 업무 규칙은 Service와 Domain으로 밀어낸다.
- 요청 DTO와 응답 DTO를 분리하면 API 변경과 내부 모델 변경을 독립적으로 다루기 쉽다.
- Model에 Entity를 그대로 넣을지 DTO를 넣을지는 화면 요구사항과 lazy loading 위험, OSIV 설정, 템플릿에서 접근할 필드 범위를 함께 고려한다.
- REST API에서
String반환은 view name인지 body인지 애너테이션 맥락을 반드시 확인한다. - Controller 테스트는 “요청 조건, 바인딩, 상태 코드, 응답 구조”를 중심으로 잡는다.
위험 신호!
- Controller 메서드 하나가 수십 줄의 업무 분기를 가진다.
- Controller에서 repository를 직접 호출한다.
- Entity를 그대로 JSON 응답으로 노출한다.
- 요청 파라미터 파싱을 수동으로 반복한다.
- View 렌더링 Controller와 REST API Controller의 반환값 의미를 혼동한다.
확인 질문
확인 질문
- Controller 메서드 파라미터는 누가 준비하는가?
- HandlerAdapter가 ArgumentResolver를 사용해 준비한다.
- Controller의 적절한 책임은 무엇인가?
- HTTP 요청을 해석하고 Service를 호출한 뒤 View model이나 response DTO를 반환하는 것이다.
@RestController에서 객체를 반환하면 어떤 일이 일어나는가?
- 반환 객체가 View 이름이 아니라 response body로 처리되고 MessageConverter가 직렬화한다.