로깅 간단히 알아보기
- System.out.println()같은 시스템 콘솔로 출력하지 않고, 별도의 로깅 라이브러리를 이용해서 로그를 출력
- 변수 출력할 때, + 를 이용하면 출력하지도 않는 문자열을 위해 문자열 더하기 연산이 이뤄나서 성능적으로 좋지 않다. 그러므로 변수를 출력할 때는 {}를 이용하자.
- 성능도 System.out보다 좋기 때문에, 실무에서는 꼭 로그를 사용하자.
@Slf4j // private final Logger log = LoggerFactory.getLogger(this.getClass()); 자동으로 넣어줌
@RestController // RestController에서 반환하는 문자열은 뷰 이름이 아니라,그냥 문자열 그대로 반환
public class LogTestController {
// private final Logger log = LoggerFactory.getLogger(this.getClass());
@GetMapping("/log-test")
public String logTest() {
String name = "Spring";
// 로컬 PC에서는 이정도 레벨로(모든 로그 출력). logging.level.hello.springmvc=trace
log.trace("trace log = {}", name);
// 개발 서버에서는 이정도 레벨로. logging.level.hello.springmvc=debug
log.debug("debug log = {}", name);
// 운영 서버에서 이정도 레벨로. 디폴트가 이 수준. logging.level.hello.springmvc=info
log.info(" info log = {}", name);
log.warn(" warn log = {}", name);
log.error(" error log = {}", name);
return "ok";
}
}
| HTTP Method | 사용 목적 |
| GET | 데이터를 조회할 때. (= 서버에서 정보만 가져옴) |
| POST | 데이터를 전송/저장할 때. (= 서버에 뭔가 보냄) |
| PUT | 기존 데이터를 수정할 때 |
| DELETE | 데이터를 삭제할 때 |
📦 HTTP 요청 방식(method)
요청 매핑
- 스프링이 요청을 어떻게 다루는지에 대한 매커니즘
@Slf4j
@RestController
public class MappingController {
@RequestMapping(value = "/hello-basic", method = RequestMethod.GET)
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
@RequestMapping(value = "/mappingGetV1", method = RequestMethod.GET)
public String mappingGetV1() {
log.info("mappingGetV1");
return "ok";
}
@GetMapping("/mappingGetV2")
public String mappingGetV2() {
log.info("mappingGetV2");
return "ok";
}
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능
*
* @PathVariable("userId") String userId -> @PathVariable String userId
*/
// ?userId=userA는 쿼리 파라미터 방식. 해당 방식은 /mapping/userA
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId = {}", data);
return "ok";
}
/**
* PathVariable 사용 다중
*/
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable("userId") String userId, @PathVariable("orderId") Long orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
/**
* 파라미터로 추가 매핑
* params="mode",
* params="!mode"
* params="mode=debug"
* params="mode!=debug" (! = )
* params = {"mode=debug","data=good"}
*/
// http://localhost:8080/mapping-param?mode=debug
// 이렇게 파라미터에 mode=debug가 있어야 호출될 수 있음
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
/**
* 특정 헤더로 추가 매핑
* headers="mode",
* headers="!mode"
* headers="mode=debug"
* headers="mode!=debug" (! = )
*/
// HTTP 요청 헤더에 mode=debug가 포함되어 있어야 이 핸들러가 실행.
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
/**
* Content-Type 헤더 기반 추가 매핑 Media Type
* consumes="application/json"
* consumes="!application/json"
* consumes="application/*"
* consumes="*\/*"
* MediaType.APPLICATION_JSON_VALUE
*/
// HTTP 요청 헤더의 Content-Type이 application/json으로 설정되어야 호출 될 수 있음.
// 즉, 클라이언트가 JSON 데이터를 보낼 때만 이 메서드가 응답하는 구조
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
/**
* Accept 헤더 기반 Media Type
* produces = "text/html"
* produces = "!text/html"
* produces = "text/*"
* produces = "*\/*"
*/
// HTTP 요청 헤더의 Accept이 text/html로 설정되어야 호출될 수 있음.
// 스프링은 서버가 어떤 형식의 응답을 생성할 수 있는지 미리 명시
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
}
요청 매핑 - API 예시
- 회원 목록 조회: GET /users
- 회원 등록: POST /users
- 회원 조회: GET /users/{userId}
- 회원 수정: PATCH /users/{userId}
- 회원 삭제: DELETE /users/{userId}
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
@GetMapping
public String users() {
return "get users";
}
@PostMapping
public String addUser() {
return "post user";
}
@GetMapping("/{userId}")
public String findUser(@PathVariable("userId") String userId) {
return "get userId= " + userId;
}
@PatchMapping("/{userId}")
public String updateUser(@PathVariable("userId") String userId) {
return "update userId= " + userId;
}
@DeleteMapping("/{userId}")
public String deleteUser(@PathVariable("userId") String userId) {
return "delete userId= " + userId;
}
}
HTTP 요청 - 기본, 헤더 조회
- HTTP 요청의 헤더, 쿠키, 메타정보 등을 스프링 방식으로 편하게 추출
- 기존 서블릿에서 HttpServletRequest 객체에서 .getXXX()로 추출하지 않아도 된다.
@Slf4j
@RestController
public class RequestHeaderController {
@RequestMapping("/headers")
public String header(HttpServletRequest request,
HttpServletResponse response,
HttpMethod httpMethod,
Locale locale,
@RequestHeader MultiValueMap<String, String> headerMap,
@RequestHeader("host") String host,
@CookieValue(value = "myCookie", required = false) String cookie) {
log.info("request={}", request);
log.info("response={}", response);
log.info("httpMethod={}", httpMethod);
log.info("locale={}", locale);
log.info("headerMap={}", headerMap);
log.info("header host={}", host);
log.info("myCookie={}", cookie);
return "ok";
}
}
HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form
- GET - 쿼리 파라미터
- POST - HTML Form
- 위 두가지 요청 파라미터는 동일한 형식이므로, request.getParameter()를 통해 모두 조회할 수 있다.
@Slf4j
@Controller
public class RequestParamController {
@RequestMapping("/request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
log.info("username = {}, age = {}", username, age);
PrintWriter w = response.getWriter();
w.write("ok");
}
}
✅ 스프링으로 개선되는 버전
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody // 반환하는 문자열이 논리 경로인 viewName이 아니고, 그냥 http 응답 바디에 문자열 자체를 반환
@RequestMapping("/request-param-v2")
public String requestParamV2(@RequestParam("username") String username,
@RequestParam("age") int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
// 버전 이슈와, 비직관적이라서 잘 사용 X
// @ResponseBody
// @RequestMapping("/request-param-v3")
// public String requestParamV3(@RequestParam String username,
// @RequestParam int age) {
//
// log.info("username = {}, age = {}", username, age);
//
// return "ok";
// }
// 버전 이슈와, 비직관적이라서 잘 사용 X
// @ResponseBody
// @RequestMapping("/request-param-v4")
// public String requestParamV4(String username, int age) {
// log.info("username = {}, age = {}", username, age);
//
// return "ok";
// }
// username = 인 경우, 빈 문자가 들어와버림. 즉, 오류가 발생 안해버림.
@ResponseBody
@RequestMapping("/request-param-required") // username 파라미터가 무조건 들어와야 함
public String requestParamRequired(@RequestParam(value = "username", required = true) String username,
@RequestParam(value = "age", required = false) Integer age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
// 파라미터 안들어오면, 디폴트 값으로 대체. username = 인(빈 문자) 경우에도 디폴트로 값 넣어줌. 굿.
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(@RequestParam(value = "username", defaultValue = "guest") String username,
@RequestParam(value = "age", defaultValue = "-1") int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
// 요청 파라미터 전부 가져오기.
// 파라미터의 값이 중복될 수 있을 것 같으면, MultiValueMap을 사용하자.
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username = {}, age = {}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
}
HTTP 요청 파라미터 - @ModelAttribute
// 마법처럼 HelloData 객체가 생성되고, 요청 파라미터 값도 모두 들어가 있음 -> 프로퍼티
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
return "ok";
}
// 심지어 생략해도 잘 됨
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
return "ok";
}
- @ModelAttribute HelloData helloData
→ 스프링이 HelloData 객체를 먼저 생성함 (기본 생성자 필요) - HTTP 요청 파라미터 username=seol&age=24
→ 파라미터 이름을 기반으로, setUsername("seol"), setAge(24) 호출 - 자바빈 프로퍼티 규약 기반 덕분에 가능:
- 필드는 private, 게터/세터는 public
- setXxx(...)를 호출할 수 있으면 됨
- 결과적으로 객체가 채워진 채로 컨트롤러에 전달
HTTP 요청 메시지 - 단순 텍스트
- HttpEntity를 통해, HTTP header, body 정보를 편리하게 조회.
- 요청 파라미터를 조회하는 @RequestParam, @ModelAttribute와는 관계가 없음.
- 메시지 바디 정보 직접 반환할 수 있고, 헤더 정보 포함 가능함. view 조회는 없음.
- @RequestBody를 사용하면, 더욱 편하게 메시지 바디를 조회 가능.(바디만 조회함)
-> HttpMessageConverter가 메시지 바디 전체를 문자열로 변환하고 파라미터로 넘겨줌. - 헤더 정보가 필요하면, @RequestBody 말고, HttpEntity나 @RequestHeader를 사용하면 됨.
@Slf4j
@Controller
public class RequestBodyStringController {
@PostMapping("/request-body-string-v1")
public void requestBodyString(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody = {}", messageBody);
PrintWriter w = response.getWriter();
w.write("ok");
}
@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputStream, Writer responseWriter) throws IOException {
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody = {}", messageBody);
responseWriter.write("ok");
}
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
String messageBody = httpEntity.getBody();
log.info("messageBody = {}", messageBody);
return new HttpEntity<>("ok");
}
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
log.info("messageBody = {}", messageBody);
return "ok";
}
}
| 방법 | 메시지 바디 | HTTP 헤더 | 응답 방식 |
| v1 (Servlet API) | 직접 읽음 | 직접 처리 | response.getWriter() |
| v2 (InputStream) | 주입됨 | 직접 처리 | Writer 사용 |
| v3 (HttpEntity) | getBody() | getHeaders() | HttpEntity 반환 |
| v4 (@RequestBody) | 자동 주입 | 헤더 불가 -> @RequestHeader 써야 | 문자열 직접 반환 |
HTTP 요청 메시지 - JSON
- HTTP 요청시에 content-type이 application/json인지 꼭 확인해야 한다. 그래야 JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.
- @RequestBody 요청
- JSON 요청 -> HTTP 메시지 컨버터 -> 객체
- @ResponseBody 응답
- 객체 -> HTTP 메시지 컨버터 -> JSON 응답
/**
* {"username":"hello", "age":20}
* content-type: application/json
*/
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody = {}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
response.getWriter().write("ok");
}
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
log.info("messageBody = {}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
return "ok";
}
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) throws IOException {
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
return "ok";
}
// 요청 헤더까지 함께 보고 싶을 때 유용
@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> helloData) throws IOException {
HelloData messageBody = helloData.getBody();
log.info("username = {}, age = {}", messageBody.getUsername(), messageBody.getAge());
return "ok";
}
// 응답도 JSON으로 내보내야 할 때
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData helloData) throws IOException {
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());
return helloData;
}
}
| 버전 | 방식 | 어노테이션/타입 | 요청 바디 처리 | 응답 처리 | 헤더 접근 |
| v1 | 서블릿 직접 처리 | HttpServletRequest, InputStream | 수동으로 Stream → String → 객체 파싱 | response.getWriter().write() | request.getHeader() 등 직접 |
| v2 | @RequestBody String | @RequestBody String | 문자열 자동 주입, 객체 변환은 수동 (ObjectMapper) | 문자열 반환 | X 직접 처리 필요 |
| v3 | @RequestBody 객체 | @RequestBody HelloData | JSON → 객체 자동 변환 (HttpMessageConverter) | 문자열 직접 반환 | X |
| v4 | HttpEntity<객체> | HttpEntity<HelloData> | 자동 변환, getBody()로 바디 접근 | new HttpEntity<>("ok") 직접 작성 | getHeaders()로 헤더 가능 |
| v5 | 객체 반환 (응답까지 자동) | @RequestBody + return 객체 | 자동 변환 | 객체 → JSON 자동 변환 | X |
HTTP 응답 - 정적 리소스, 뷰 템플릿
✅ 정적 리소스
- src/main/resources/static 또는 /public, /resources, /META-INF/resources 등 클래스패스 하위 폴더에 HTML, JS, CSS 등을 넣는다.
- 브라우저 요청이 들어오면 가공 없이 그대로 파일이 응답됨
- 예: /static/basic/hello-form.html → http://localhost:8080/basic/hello-form.html
✅ 뷰 템플릿
- 템플릿 파일 경로: src/main/resources/templates/
- 컨트롤러에서 Model 객체에 데이터 담고 → 논리 뷰 이름 반환 → 뷰 리졸버가 실제 템플릿 파일 찾아 렌더링
- 예: return "response/hello" → /templates/response/hello.html 렌더링
@Controller
public class ResponseViewController {
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data", "hello1");
return mav;
}
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello2");
return "response/hello";
}
// 사용하지 말자. 이런 것도 있다 정도로 알자.
@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
model.addAttribute("data", "hello3");
}
}
| 분류 | 방식 | 특징 |
| 정적 리소스 | /static, /public 등 | - HTML, CSS, JS 파일 등 가공 없이 그대로 응답됨 - 스프링이 아니라 톰캣 정적 리소스 핸들러가 처리함 |
| 뷰 템플릿 | Model + return viewName | - 컨트롤러가 Model에 데이터 담고, return "논리 뷰 이름" → 뷰 리졸버가 HTML 렌더링 |
| 뷰 템플릿 (void) | return 타입 없음 (URL → 뷰 이름 추론) |
- 요청 URL로부터 뷰 이름 추론 → 명시성 부족하여 비추천 |
HTTP 응답 - HTTP API, 메시지 바디에 직접 입력
- 뷰 템플릿이나 정적 리소스가 아닌, HTTP 메시지 바디 자체에 데이터(문자열, JSON 등)를 직접 담아 반환하는 방식
- @RestController로 구현하다가, 상태 코드나 응답 헤더를 세밀하게 제어하고 싶다면, ResponseEntity<T>를 이용.
@Slf4j
// @Controller
// @ResponseBody
@RestController // -> @Controller, @ResponseBody 들어가 있음. Rest API 만들 때 사용하는 컨트롤러
public class ResponseBodyController {
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException {
response.getWriter().write("ok");
}
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() throws IOException {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
// @ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
@GetMapping("/response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return new ResponseEntity<>(helloData, HttpStatus.OK);
}
// 동적으로 응답 상태 반환하려면, ResponseEntity 사용
@ResponseStatus(HttpStatus.OK)
// @ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return helloData;
}
}
HTTP 메시지 컨버터
- HTTP 메시지 바디와 Java 객체 간의 변환을 담당하는 컴포넌트
- 요청 시 (역직렬화): @RequestBody, HttpEntity 등을 사용하면 클라이언트가 전송한 메시지 바디(JSON, XML, plain text 등)를 String, byte[], 객체 등 Java 타입으로 변환
- 응답 시 (직렬화): @ResponseBody, ResponseEntity, @RestController 등을 사용하면 컨트롤러가 반환한 Java 객체를 JSON, 문자열 등으로 변환하여 HTTP 메시지 바디에 기록
✅ @ResponseBody를 사용시
- 문자열이나 객체를 반환했을 때, 그걸 HTTP 메시지 바디에 메시지 컨버터가 적절한 형식으로 변환해서 실어주게 됨.
- viewResolver 대신에 HttpMessageConverter가 동작
- 기본 문자처리: StringHttpMessageConverter
- 기본 객체처리: MappingJackson2HttpMessageConverter
- byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

| 대상 타입 | 컨버터 | 지원 미디어 타입 | 예시 요청 | 예시 응답 |
| 파일 등 바이너리 변환 | 0 = ByteArrayHttpMessageConverter | application/octet-stream, */* | @RequestBody byte[] data |
return byte[]; (예: 이미지 파일) |
| 문자열 변환 | 1 = StringHttpMessageConverter | text/plain, */*, application/json |
@RequestBody String data |
return "ok"; |
| JSON등 객체 변환 | 2 = MappingJackson2HttpMessageConverter | application/json | @RequestBody HelloData data |
return helloData; (JSON 자동 변환) |
✅ 스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용
- HTTP 요청: @RequestBody, HttpEntity(RequestEntity) canRead()로 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크하고, read()로 메시지 읽기 가능
- HTTP 응답: @ResponseBody, HttpEntity(ResponseEntity) CanWrite()로 메시지 컨버터가 해당 클래스, 미디어 타입을 지원하는지 체크하고. write()로 메시지 쓰기 가능
요청 매핑 핸들러 어뎁터 구조
- 요청 파라미터 처리: 핸들러 메서드의 파라미터 생성 -> Argument Resolver
- @RequestParam → RequestParamMethodArgumentResolver
- @PathVariable → PathVariableMethodArgumentResolver
- HttpServletRequest → ServletRequestMethodArgumentResolver
- @RequestBody → ✅ RequestResponseBodyMethodProcessor (uses HttpMessageConverter.read())
- HttpEntity<T> → ✅ HttpEntityMethodProcessor (uses HttpMessageConverter.read())
- 핸들러 실행 후 반환값 처리: 클라이언트에 보낼 응답 생성 -> Return Value Handler
- String (View 이름) → ViewNameMethodReturnValueHandler
- ModelAndView → ModelAndViewMethodReturnValueHandler
- @ResponseBody → ✅ RequestResponseBodyMethodProcessor (uses HttpMessageConverter.write())
- HttpEntity<T> → ✅ HttpEntityMethodProcessor (uses HttpMessageConverter.write())

다시, HTTP 메시지 컨버터
- 대부분의 ArgumentResolver는 단순히 쿼리 파라미터, 경로 변수, HttpServletRequest 등을 통해 값을 뽑음
- 그 중 일부, 메시지 바디가 필요한 상황에서만 HttpMessageConverter를 사용해서 값을 읽어들임
- 즉, ArgumentResolver나 ReturnValueHandler가 메시지 바디에 접근할 필요가 있을 때, 내부적으로 HttpMessageConverter를 사용

✅ 요청 시 메시지 컨버터를 사용하는 ArgumentResolver
- RequestResponseBodyMethodProcessor
→ @RequestBody 처리용
→ 내부에서 메시지 바디를 꺼내기 위해 HttpMessageConverter.read() 호출 - HttpEntityMethodProcessor
→ HttpEntity<T>, RequestEntity<T> 처리용
→ 마찬가지로 메시지 바디 읽기 위해 메시지 컨버터 사용
✅ 응답 시 메시지 컨버터를 사용하는 ReturnValueHandler
- RequestResponseBodyMethodProcessor
→ @ResponseBody 붙은 메서드 결과 처리
→ 반환값을 HttpMessageConverter.write()로 HTTP 메시지 바디에 기록 - HttpEntityMethodProcessor
→ HttpEntity<T>, ResponseEntity<T> 응답 처리용
→ 메시지 바디, 헤더, 상태코드까지 전부 이쪽에서 작성
요청 → DispatcherServlet
→ HandlerMapping (컨트롤러 선택)
→ RequestMappingHandlerAdapter (핸들러 어댑터)
1. ArgumentResolver 목록 탐색
→ supportsParameter() → resolveArgument()
→ 메시지 바디 필요 시, HttpMessageConverter.read()
2. 핸들러 메서드 실행
3. ReturnValueHandler 목록 탐색
→ supportsReturnType() → handleReturnValue()
→ 메시지 바디 필요 시, HttpMessageConverter.write()
매개변수 리졸버가 컨트롤러에 필요한 매개변수를, 요청 정보를 기반으로 생성해서 넘겨준다.
그리고 컨트롤러가 반환하는 값도 ReturnValueHandler가 적절한 방식으로 변환해준다.
메시지 바디를 읽거나 쓸 필요가 있는 경우에만, 내부적으로 HttpMessageConverter가 동작해서 변환을 수행한다.
'공부 > Spring' 카테고리의 다른 글
| [인프런 김영한] 실전! 스프링 부트와 JPA 활용1 - 1 (6) | 2025.08.12 |
|---|---|
| [스프링 MVC 7 / 인프런 김영한] 웹 페이지 만들기 (0) | 2025.03.31 |
| [스프링 MVC 5 / 인프런 김영한] 스프링 MVC - 구조 이해 (0) | 2025.03.30 |
| [스프링 MVC 4 / 인프런 김영한] MVC 프레임워크 만들기 (0) | 2025.03.29 |
| [스프링 MVC 3 / 인프런 김영한] 서블릿, JSP, MVC 패턴 (0) | 2025.03.28 |
