스프링 MVC 1편 - 6. 스프링 MVC - 기본 기능

Updated:

김영한님의 인프런 강의 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
위 강의를 정리한 내용입니다.

I. 로깅 알아보기

  • 실제 운영 시스템에서는 System.out.println()과 같은 콘솔로 출력하는 행위는 사용하지 않는다.
  • 로깅 라이브러리를 사용하자.
  • 수많은 라이브러리들을 통합해서 인터페이스로 제공하는 것이 SLF4J이고, 구현체로 보통은 LOGBACK을 사용한다.

선언

private final Logger log = LoggerFactory.getLogger(getClass());
  • Lombok의 기능을 통해 @Slf4j 어노테이션으로 대체할 수 있다.

주요 기능

log.trace("trace log={}", name);
log.debug("debug log={}", name);
log.info(" info log={}", name);
log.warn(" warn log={}", name);
log.error("error log={}", name);
  • 심각도 별로 레벨을 나누어 로그를 찍을 수 있다.
  • application.properties에서 logging.level.패키지명=?과 같은 식으로 어느 레벨부터 보고싶은지 지정할 수 있다.
  • log.trace("trace log=" + name)과 같은 형식으로 사용하면, 설령 출력하지 않더라도 자바 특성상 두 문자를 더하는 연산이 일어나므로 메모리 낭비가 생긴다.

II. HTTP 요청

매핑

@RequestMapping

  • 대부분의 속성을 배열로 제공하여 다중 설정이 가능하다.
  • {"/hello-basic", "/hello-go"}
  • HTTP메서드를 따로 지정하지 않으면 메서드와 무관하게 호출된다.
  • PathVariable, 경로 변수 사용 가능
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
  log.info("mappingPath userId={}", data);
  return "ok";
}
  • 그 외에도 헤더, 파라미터, 미디어 타입 등을 조건으로 설정할 수 있다.

기본, 헤더 조회

HttpServletRequest request
HttpServletResponse response
HttpMethod httpMethod //HTTP메서드 조회
Locale locale //Locale 정보 조회
@RequestHeader MultiValueMap<String, String> headerMap // 모든 HTTP 헤더를 MultiValueMap 형식으로 조회
@RequestHeader("host") String host // 특정 HTTP헤더를 조회
@CookieValue(value = "myCookie", required = false) String cookie // 특정 쿠키를 조회

요청 파라미터 - 쿼리 파라미터, HTML Form

  • 기존 배웠던 방식과 동일
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String username = request.getParameter("username");
    int age = Integer.parseInt(request.getParameter("age"));
    log.info("username={}, age={}", username, age);

    response.getWriter().write("ok");
}

요청 파라미터 - @RequestParam

public String requestParamV2(
            @RequestParam("username") String memberName,
            @RequestParam("age") int memberAge)

public String requestParamV3(
            @RequestParam String username,
            @RequestParam int age)

public String requestParamV4(String username, int age)
  • 모두 같은코드
  • 단, 실제 넘어오는 파라미터 이름과 변수 이름이 같아야 한다.
  • 단순 타입의 경우에는 @RequestParam도 생략할 수 있다.
  • @RequestParam이 있으면 명확하게 요청 파라미터에서 데이터를 읽는다는 것을 알 수 있다. -> 생략이 꼭 좋은것만은 아니다.

required

@RequestParam(requried = false)

  • 기본값은 true이다.
  • true일 경우, 해당 파라미터가 주어지지 않으면 Bad Request를 반환한다.

defaultValue

@RequestParam(defaultValue = “guest”)

  • 해당 파라미터가 주어지지 않았을 경우, 기본값을 갖게 된다.

파라미터를 Map으로 조회

@RequestParam Map<String, Object> paramMap

  • 파라미터를 Map으로 일괄조회할 수 있다.
  • 파라미터의 값이 1개가 확실하다면 Map을 사용해도 되지만, 그렇지 않다면 MultiValueMap을 사용하자.

요청 파라미터 - @ModelAttribute

public String modelAttributeV1(@ModelAttribute HelloData helloData) {
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());

    return "ok";
}
  • 객체가 생성되고, 요청 파라미터의 값이 모두 자동으로 들어간다.
    • 알아서 객체의 setter를 호출하여 값을 채워넣는다.
  • @ModelAttribute마저도 생략할 수 있다.
    • 단순 타입은 @RequestParam, 나머지는 @ModelAttribute를 생략한 것으로 본다.
    • 몇몇은 argument resolver로 지정되어 제외된다.

요청 메세지 - 단순 텍스트

  • HttpEntity: HTTP header, body정보를 편리하게 조회
    • 응답에도 사용 가능
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {

      String messageBody = httpEntity.getBody();
      log.info("messageBody={}", messageBody);

      return new HttpEntity<>("ok");
  }
  • @RequestBody를 통해 메세지바디를 직접 받아올 수도 있다.

@RequestBody String messageBody

요청 메세지 - JSON

@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) {
    
    log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());

    return "ok";
}
  • @RequestBody에 직접 만든 객체를 지정할 수 있다.
  • HTTP 메세지 컨버터가 대신 문자열처리를 해준다.
  • @RequestBody는 생략할 수 없다. (@ModelAttribute로 인식하게 된다)
  • @ResponseBody 를 사용하면 객체를 HTTP 메세지 바디에 직접 넣어 응답할 수 있다.

III. HTTP 응답

정적 리소스, 뷰 템플릿

  • 정적 리소스
    • 웹 브라우저에 정적인 HTML, css, js를 제공할 때
  • 뷰 템플릿
    • 웹 브라우저에 동적인 HTML을 제공할 때
  • HTTP 메세지 사용
    • HTTP API를 사용하는 경우

정적 리소스

  • src/main/resources에 리소스를 보관하면 스트링 부트가 정적 리소스로서 서비스를 제공한다.
  • src/main/resources/static/basic/hello-form.html -> http://localhost:8080/basic/hello-form.html

뷰 템플릿

  • src/main/resources/templates를 기본 뷰 템플릿 경로로 제공한다.
  • String을 반환하는 경우
    • @ResponseBody가 없으면 response/hello로 뷰 리졸버가 실행되어서 뷰를 찾고 렌더링한다.
    • @ResponseBody가 있다면 뷰 리졸버를 실행하지 않고 HTTP 메세지 바디에 response/hello가 입력된다.
  • Void를 반환하는 경우
    • 권장하지 않는다.

IV. HTTP 메세지 컨버터

  • @ResponseBody를 사용하면, viewResolver대싱 HttpMessageConverter가 동작
  • 스프링 MVC는 다음의 경우에 HTTP 메세지 컨버터를 적용
    • HTTP 요청 : @RequestBody, HttpEntity(RequestEntity)
    • HTTP 응답 : @ResponseBody, HttpEntity(ResponseEntity)
  • HttpMessageConverter는 인터페이스다.
    • canRead(), canWrite() 메서드를 통하여 메세지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크한다.
0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
  • ByteArrayHttpMessageConverter : byte[] 데이터를 처리한다.
    • 클래스 타입: byte[] , 미디어타입: */*
    • 요청 예) @RequestBody byte[] data
    • 응답 예) @ResponseBody return byte[] 쓰기 미디어타입 application/octet-stream
  • StringHttpMessageConverter : String 문자로 데이터를 처리한다.
    • 클래스 타입: String , 미디어타입: */*
    • 요청 예) @RequestBody String data
    • 응답 예) @ResponseBody return "ok" 쓰기 미디어타입 text/plain
  • MappingJackson2HttpMessageConverter : application/json
    • 클래스 타입: 객체 또는 HashMap , 미디어타입 application/json 관련
    • 요청 예) @RequestBody HelloData data
    • 응답 예) @ResponseBody return helloData 쓰기 미디어타입 application/json 관련

HTTP 요청 데이터 읽기

  • 컨트롤러에서 @RequestBody, HttpEntity파라미터를 사용하는지 확인
  • 메세지 컨버터가 읽을 수 있는지 확인하기 위해 canRead() 호출
    • 대상 클래스 타입을 지원하는가?
    • HTTP 요청의 Content-Type 미디어 타입을 지원하는가?
  • canRead()를 만족하면 read()를 호출해서 객체생성, 반환

HTTP 응답 데이터 생성

  • 컨트롤러에서 @ResponseBody, HttpEntity로 값이 반환
  • 메세지 컨버터가 메세지를 쓸 수 있는지 확인하기 위해 canWrite() 호출
    • 대상 클래스 타입을 지원하는가?
    • HTTP 요청의 Accept 미디어 타입을 지원하는가?
  • canWrite()를 만족하면 write()를 호출해서 HTTP 응답 메세지 바디에 데이터 생성

V. 요청 매핑 핸들러 어댑터 구조

ArgumentResolver

  • 어노테이션기반의 컨트롤러는 다양한 파라미터를 사용할 수 있다.
  • ArgumentResolver가 그 역할을 한다.
  • 어노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdaptorArgumentResolver를 호출해서 컨트롤러가 필요로 하는 다양한 파라미터값을 생성한다. 그리고 이렇게 파라미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.

동작 방식

ArgumentResolversupportsParameter()를 호출해서 해당 파라미터를 지원하는지 체크하고, 지원하면 resolveArgument()를 호출해서 실제 객체를 생성한다. 이 객체가 컨트롤러 호출 시 반환된다.

ReturnValueHandler

  • ArgumentResolver와 유사하게, 응답 값을 변환하고 처리한다.

Leave a comment