public int sum(int a, int b) {
return a + b;
}
int result = sum(1, 2);
/plays POST
로 요청만 보냈을 뿐인데요?
@Controller
public class WebController {
private final RacingCarService racingCarService;
public WebController(final RacingCarService racingCarService) {
this.racingCarService = racingCarService;
}
@PostMapping("/plays")
public ResponseEntity<ResultResponse> play(@RequestBody final NamesAndCountRequest namesAndCount) {
ResultResponse resultResponse = racingCarService.playGame(namesAndCount);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.body(resultResponse);
}
}
호출을 Spring 내부
에서하게 된다.ResponseEntity<ResultResponse> result = ㅈwebController.play(namesAndCountRequest);
어떤 매개변수를 몇개 넣어서 어떤 리턴 타입
으로 구현할 것을 예상을 한것일까...이 글에서는 이 궁금증을 기반으로 Spring이 특정 요청을 처리하여 응답을 주는 과정을 가볍게 살펴볼 것이다.
요청
을 처리하여 그에 따른 동적인 응답
을 반환하는 역할을 하는 Servlet이 등장하게 된다.(사진 출처 : https://tecoble.techcourse.co.kr/post/2021-05-23-servlet-servletcontainer/)
javax.servlet.http.HttpServlet
을 상속하여 요청에 대해서 처리를 담당하는 Servlet 클래스이다.import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
// ...
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
// GET 요청을 처리
}
// ...
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
// POST 요청을 처리
}
// ...
@Override
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
// GET, POST, DELETE, PUT, ... 모든 요청을 처리
}
// ...
}
HTTP 요청
을 자바 객체로서 HttpServletRequest
로 매개변수로 받아서 사용하면되고HTTP 응답
의 내용을 자바 객체(매개변수)로서 받아온 HttpServletResponse
이 객체에 정보를 담으면 된다.[spring.io 설명(링크)]
HTTP 요청 컨트롤러(핸들러)를 위한 중앙 디스패처이다.
웹 요청을 처리하기 위해 등록된 핸들러를 디스패치하고,
편리하게 매핑하고, 예외 핸들링을 지원한다.
HttpServlet
을 상속한다.모든 요청
을 받아서 요청에 맞는 컨트롤러(핸들러)
를 연결해주는 역할을 한다.그래서 그건 누가 결정할까?
(사진 출처: https://mangkyu.tistory.com/49)
Handler Mapping(spring.io)
요청
와컨트롤러(핸들러)
를 매핑을 정의하는 객체에 대한 인터페이스다.
사용자가 정의 가능하다.
사용자가 Handler Mapping을 빈으로 등록하지 않는다면 기본적으로BeanNameUrlHandlerMapping
과RequestMappingHandlerMapping
이 기본 값이다.
@RequestMapping
이 붙어있는 정보와 URL 요청을 매핑한다.@GetMapping
과 @PostMapping
도 @RequestMapping
을 포함한다.@Controller
public class WebController {
private final RacingCarService racingCarService;
public WebController(final RacingCarService racingCarService) {
this.racingCarService = racingCarService;
}
@PostMapping("/plays")
public ResponseEntity<ResultResponse> play(@RequestBody final NamesAndCountRequest namesAndCount) {
ResultResponse resultResponse = racingCarService.playGame(namesAndCount);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.body(resultResponse);
}
}
@GetMapping("/plays")
라는 어노테이션을 읽어 /plays GET
요청과 매핑한다./
로 시작하는 이름을 가진 빈과 요청 URL을 매핑한다.import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component("/plays")
public class PlayController implements Controller {
@Override
public ModelAndView handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return new ModelAndView();
}
}
/plays
라는 이름을 가진 빈을 /plays
요청에 매핑하는 것이다.컨트롤러(핸들러) 메서드를 실행시키는건 핸들러 어뎁터가 알아서 하고,
DispatcerServlet이 Handler Adapter에게 바라는 형태는 ModelAndView다.
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component("/plays")
public class PlayController implements HttpRequestHandler {
@Override
public void handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
// ...
}
}
org.springframework.web.servlet.mvc.Controller
을 구현한 핸들러 메서드를 실행시킨다.org.springframework.stereotype.Controller
아님 주의import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component("/plays")
public class PlayController implements Controller {
@Override
public ModelAndView handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return new ModelAndView();
}
}
@RequestMapping
어노테이션으로 등록된 핸들러 메서드를 실행시킨다.@Controller
public class WebController {
private final RacingCarService racingCarService;
public WebController(final RacingCarService racingCarService) {
this.racingCarService = racingCarService;
}
@PostMapping("/plays")
public ResponseEntity<ResultResponse> play(@RequestBody final NamesAndCountRequest namesAndCount) {
ResultResponse resultResponse = racingCarService.playGame(namesAndCount);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.body(resultResponse);
}
}
HttpRequestHanlder와 Controller를 구현하여
특정 메서드를 오버라이딩
해서 사용하는 핸들러와는 다르게,어노테이션 기반의 핸들러는 매개변수와 리턴타입이 딱 하나로 정의되어 있지 않고, 개발자가 핸들러를 구현할 때, 여러 케이스를 고려할 수 있다.
ArgumentResolver
는 핸들러 어댑터에서 호출할 핸들러 메서드의 파라미터 정보를 가공하는 역할을 한다.(사진 출처 : https://platanus.me/post/1678)
Return Value Handler
는 핸들러의 반환 값을 지원하는 반환타입인지 확인 후 반환 값을 처리한다.이 메서드를 처리하는 구현체들을 간략하게 살펴보자
@PostMapping("/plays")
public ResponseEntity<ResultResponse> play(@RequestBody final NamesAndCountRequest namesAndCount) {
ResultResponse resultResponse = racingCarService.playGame(namesAndCount);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.body(resultResponse);
}
[RequestResponseBodyMethodProcessor(공식문서 링크)]
@ReqeustBod
y나@ResponseBody
의 어노테이션을 보고 메서드의 매개변수나 반환값을 HttpMessageConverter로 처리한다.
[HttpEntityMethodProcessor(공식문서 링크)]
HttpEntity... 즉,
자식인RequestEntity
를 매개변수로 받을 때나
자식인ResponseEntity
를 리턴타입으로할 때 처리한다.
MappingJackson2HttpMessageConverter
로 변환한다.