Deep Dive /Java Web /16

16. Service → Controller: 결과 반환

서비스가 JPA 또는 MyBatis 경로를 끝내고 나면, 결과는 다시 컨트롤러 메서드로 돌아옵니다. Spring 오픈소스 기준으로 보면, 이 반환값은 결국 InvocableHandlerMethod.invokeForRequest()ServletInvocableHandlerMethod.invokeAndHandle()가 잡아 다음 응답 단계로 넘깁니다.


이번 단계의 역할

데이터 접근 결과를 컨트롤러가 응답 형태로 정리할 수 있도록 되돌려주는 단계입니다. 즉 비즈니스 로직 결과가 다시 웹 응답 파이프라인으로 복귀하는 지점입니다.


호출 흐름 요약

  1. 서비스가 엔티티, DTO, 상태값 등을 컨트롤러에 반환합니다.
  2. 컨트롤러 메서드가 최종 반환값을 결정합니다.
  3. InvocableHandlerMethod.invokeForRequest()가 그 반환값을 받습니다.
  4. ServletInvocableHandlerMethod.invokeAndHandle()가 다음 응답 처리 단계로 넘깁니다.

호출 흐름 다이어그램

sequenceDiagram participant Service participant Controller participant IHM as InvocableHandlerMethod participant SIHM as ServletInvocableHandlerMethod Service-->>Controller: result Controller-->>IHM: returnValue IHM-->>SIHM: returnValue

핵심 코드

// InvocableHandlerMethod.java
Object returnValue = doInvoke(args);
return returnValue;
// ServletInvocableHandlerMethod.java
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
this.returnValueHandlers.handleReturnValue(
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

코드 해설

서비스 반환 자체는 애플리케이션 코드이지만, 프레임워크가 관찰하는 다음 지점은 “컨트롤러 메서드 반환값”입니다. 그래서 16번은 서비스 계층이 끝나고 응답 계층이 다시 시작되는 연결 구간으로 보는 것이 맞습니다.


설계 의도

비즈니스 로직과 응답 렌더링을 분리하기 위해, Spring은 먼저 컨트롤러 반환값을 하나로 수집하고 그 뒤에 응답 전략을 선택합니다. 덕분에 서비스는 HTML/JSON 응답 형식을 몰라도 됩니다.


다음 단계 연결

다음 문서 17번에서는 컨트롤러 반환값이 ModelAndView 흐름인지, @ResponseBody / ResponseEntity 흐름인지로 갈라지는 응답 분기를 봅니다.

← 이전: 15-M4. Mapper → Service | 다음: 17. Controller → DispatcherServlet: 응답 분기