minghxx.blog
  • [Spring] 스프링 MVC 1편 6) 스프링 MVC 기본 기능(1)
    2023년 11월 07일 09시 23분 12초에 업로드 된 글입니다.
    작성자: 민발자
    728x90

    스프링 MVC 1편 백엔드 웹 개발 핵심 기술 

    Session 6 스프링 MVC 기본 기능

    1. 프로젝트 생성

    1) 프로젝트 생성

    스프링 부트 스타터 사이트에서 생성

    https://start.spring.io/

    Packaing - Jar 선택 

    Dependencies - Lombok, Spring Web, Tyymeleaf 추가

    이전 포스팅 참고해서 세팅 ▶ Spring 프로젝트 생성

     


    2. 로깅 간단히 알아보기

    1) 로깅 라이브러리

    실무에서는 System.out.println() 같은 시스템 콘솔을 사용해 출력하지 않고 로그 라이브러리를 사용해 로그 출력

    스프링 부트 라이브러리를 사용하면 스프링 부트 로깅 라이브러리가 함께 포함

    스프링 부트 로깅 라이브러리는 기본으로 SLF4J, Logback을 사용한다.

    Logback, Log4J 등 수많은 라이브러리를 통합해 인터체이스로 제공하는 것이 SLF4J 라이브러리

    SLF4J은 인터페이스고 구현체로 Logback 같은 로그 라이브러리 선택

    실무에서는 대부분 기본으로 제공하는 Logback을 사용

     

    2) log-test

    @Slf4j
    @RestController
    public class LogTestController {
    
          // private final Logger log = LoggerFactory.getLogger(getClass());
          // @Slf4j사용시 필요 없음
          
          @GetMapping("/log-test")
          public String logTest() {
          	String name = "Spring";
            System.out.println("name = " + name); // 시스템 콘솔 이용
            
            // 로그 레벨에 따른 출력
            log.trace(" trace log={}", name);
            log.debug(" trace log={}", name);
            log.info(" info log={}", name);
            log.warn(" info log={}", name);
            log.error(" info log={}", name);
            
            return "ok";
          } 
    }

     

    - @RestController

    @Controller는 반환값이 String이면 뷰 이름으로 인식해 뷰를 찾고 뷰를 렌더링

    @ResrController는 반환값으로 뷰를 찾는 것이 아니라 HTTP 메시지 바디에 바로 입력

    - 테스트

    시간, 로그레벨, 프로세스ID, 스레드명, 클래스명, 로그메시지가 출력된다.

     

     

    3) 로그 레벨 설정

    -로그레벨

    TRACE > DEBUG > INFO > WARN > ERROR

    보통 개발서버는 debug, 운영서버는 info 레벨로 출력

    #전체 로그 레벨 설정(기본 info) 
    logging.level.root=info
    #hello.springmvc 패키지와 그 하위 로그 레벨 설정 
    logging.level.hello.springmvc=debug

     

    4) 올바른 로그 사용법

    log.debug(" info log={}"+ name); // 레벨과 상관없이 연산이 일어남
    log.debug(" info log={}", name);

    +로 작성하면 + 연산이 일어난다. 로그 레벨이 info여도 의미 없는 연산이 발생

    파라미터로 사용하면 로그 출력 레벨이 다르면 아무 연산도 일어나지 않는다. 

     

    5) 로그 사용시 장점

    쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고  출력 모양을 조장할 수 있다.

    로그 레벨에 따라 개발 서버에서는 모든 로그를 출력, 운영서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절가능

    파일이나 네트워크 등 로그를 별도의 위치에 남길 수 있고 파일로 남길 때는 일별, 특정 용량에 따라 로그 분할하는 것도 가능

    성능도 시스템 콘솔 출력보다 좋다.


    3. 요청 매핑

    1) MappingController

    /**
    * 기본 요청
    * 둘다 허용 /hello-basic, /hello-basic/ (스프링 3.0 이전)
    * HTTP 메서드 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE 
    */
    @RequestMapping("/hello-basic")
    public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }

     

    - @RequestMapping("/hello-basic")

    /hello-basic URL 호출이 오면 이 메서드 실행되도록 매핑

    속성을 배열로 제공하므로 다중 설정이 가능하다 {"/hello-basic", "/hello-go"}

    - HTTP 메서드 

    모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE 

    - 스프링 3.0 이후 버전

    /hello-basic과 /hello-basic/는 서로 다른 URL 요청을 사용해야 한다.

    이전 버전은 마지막 /를 제거했지만 3.0 이후부턴 유지

     

    2) HTTP 메서드 매핑

    /**
    * method 특정 HTTP 메서드 요청만 허용
    * GET, HEAD, POST, PUT, PATCH, DELETE
    */
      @RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
      public String mappingGetV1() {
          log.info("mappingGetV1");
          return "ok";
      }

    HTTP 메서드를 GET으로 제한한다. 여기에 POST 요청을 하면 스프링 mvc는 HTTP 405 상태코드를 반환

     

    3) HTTP 메서드 매핑 축약

    /**
    * 편리한 축약 애노테이션 (코드보기) 
    * @GetMapping
    * @PostMapping
    * @PutMapping
    * @DeleteMapping
    * @PatchMapping
    */
    @GetMapping(value = "/mapping-get-v2")
    public String mappingGetV2() {
    	log.info("mapping-get-v2");
    	return "ok";
    }

    HTTP 메서드를 축약한 애노테이션을 사용하는 것이 직관적

     

    4) PathVariable 경로 변수 사용

    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String data) {
      log.info("mappingPath userId={}", data);
      return "ok";
    }
    
    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable String userId) { // PathVariable이름과 파라미터 이름이 같으면 생략 가능
      log.info("mappingPath userId={}", userId);
      return "ok";
    }

    최근 HTTP API는 리소스 경로에 식별자를 넣는 스타일 선호

    @RequestMapping은 URL경로를 템플릿화 가능, @PathVariable을 사용하면 매칭되는 부분을 편리하게 조회가능

    @PathVariable의 이름과 파라미터의 이름이 같으면 생략할 수 있음(@PathVariable String userId) 

     

    5) PathVariable 경로 변수 다중 사용

    @GetMapping("/mapping/users/{userId}/orders/{orderId}")
    public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
      log.info("mappingPath userId={}, orderId={}", userId, orderId);
      return "ok";
    }

    경로 변수를 다중으로 사용 가능하다.

     

    6) 특정 파라미터 조건 매핑

    /**
    * 파라미터로 추가 매핑
    * params="mode",
    * params="!mode"
    * params="mode=debug"
    * params="mode!=debug" (! = )
    * params = {"mode=debug","data=good"}
    */
    @GetMapping(value = "/mapping-param", params = "mode=debug")
    public String mappingParam() {
      log.info("mappingParam");
      return "ok";
    }

    잘 사용하지는 않는다.

    특정 파라미터가 있거나 없는 조건을 추가할 수 있다.

    params = "mode=debug" → 파라미터에 모드는 debug라는게 있어야 호출됨 없으면 400 상태코드 반환

     

    7) 특정 헤더 조건 매핑

    /**
    *특정 헤더로 추가 매핑
    * headers="mode",
    * headers="!mode"
    * headers="mode=debug"
    * headers="mode!=debug" (! = )
    */
    @GetMapping(value = "/mapping-header", headers = "mode=debug")
    public String mappingHeader() {
      log.info("mappingHeader");
      return "ok";
    }

     

    파라미터 매핑과 비슷하지만 HTTP 헤더를 사용

    헤더에 mode debug가 있으면 호출, 없으면 호출되지 않는다.

     

    8) 미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume

    /**
    * Content-Type 헤더 기반 추가 매핑 Media Type
    * consumes="application/json"
    * consumes="!application/json"
    * consumes="application/*"
    * consumes="*\/*"
    * MediaType.APPLICATION_JSON_VALUE
    */
    @PostMapping(value = "/mapping-consume", consumes = "application/json")
    public String mappingConsumes() {
      log.info("mappingConsumes");
      return "ok";
    }

    Content-Type 헤더를 기반으로 미디어 타입 매핑

    application/json일 때만 호출 만약 맞지 않으면 415 상태코드 반환

     

    9) 미디어 타입 조건 매핑 HTTP 요청 Accept, produce

    /**
     * Accept 헤더 기반 Media Type * produces = "text/html"
     * produces = "!text/html"
     * produces = "text/*"
     * produces = "*\/*"
     */
    @PostMapping(value = "/mapping-produce", produces = "text/html")
    public String mappingProduces() {
        log.info("mappingProduces");
        return "ok";
    }

    Accept 헤더를 기반으로 미디어 타입 매핑

    맞지 않으면 406 상태코드 반환


    4. 요청 매핑 API 예시

    1) 회원 관리 HTTP API


    5. HTTP 요청 - 기본, 헤더 조회

    1) HTTP 헤더 정보 조회

    애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터 제공

    @RequestMapping("/headers")
    public String headers(HttpServletRequest request
            , HttpServletResponse response
            , HttpMethod httpMethod
            , Locale locale
            , @RequestHeader MultiValueMap<String, String> headerMap
            , @RequestHeader("host") String host
            , @CookieValue(value = "myCookie", required = false) String cookie) {
    
        ...
        
        return "ok";
    }

     

    HttpMethod : HTTP 메서드를 조회
    Locale : Locale 정보를 조회
    @RequestHeader MultiValueMap<String, String> headerMap : 모든 HTTP 헤더를 MultiValueMap 형식으로 조회

    MultiValueMap : 맵과 유사한데 하나의 키에 여러 값을 받을 수 있다. keyA=value1 & keyA=value2 → keyA=[value1, value2]
    @RequestHeader("host") : 특정 헤더를 조회
    @CookieValue(value = "myCookie", required = false) : 특정 쿠키 조회

     

    2) 참고

    @Controller 사용 가능한 파라미터 목록

    https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html

    @Controller 사용 가능한 응답 값 목록

    https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/return-types.html


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

    1) HTTP 요청 데이터 조회

    클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 3가지 방법 사용

    - GET + 쿼리 파라미터

    메시지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달

    - POST + HTML Form

    content-type: application/x-www-form-urlencoded

    메시지 바디에 쿼리 파라미터 형식으로 전달

    - HTTP 메시지바디

    HTTP API에서 주로 사용

    데이터 형식은 주로 JSON 사용

    POST, PUT, PATCH

     

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

    HttpServletRequest의 request.getParameter()를 사용하면 두 가지 요청 파라미터를 조회할 수 있다.

    GET + 쿼리 파라미터, POST + HTML Form 둘 다 형식이 같으므로 구분 없이 조회 가능

    이것을 간단히 요청 파라미터 조회라고 한다.

     

    3) 요청 파라미터 예시 RequestParamController

    HttpServletRequest가 제공하는 방식으로 요청 파라미터 조회


    7. HTTP 요청 파라미터 - @RequestParam

    1) requestParamV2

    @ResponseBody //restController처럼 메시지바디에 그대로 문자열 반환, Controller는 리턴이 String이면 뷰를 찾는다
    @RequestMapping("/request-param-v2")
    public String requestParamV2(@RequestParam("username") String memberName
            , @RequestParam("age") int memberAge) {
    
        log.info("memberName={}, memberAge={}", memberName, memberAge);
        return "ok";
    }

    @RequestParam() : 파라미터 이름으로 바인딩, request.getParameter() 동일한 기능

    @ResponseBody : view 조회를 무시하고 HTTP 미시지 바디에 직접 해당 내용 입력

     

    2) requestParamV3

    @RequestParam String username,
    @RequestParam int age

    HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xxx")에서 (name="xxx") 생략가능

     

    3) requestParamV4

    public String requestParamV4(String username, int age) {

    String, int, Integer 등 단순 타입이면 @RequestParam 생략가능, 파라미터와 변수 이름 같아야함!

    @RequestParam을 생략하면 스프링 mvc 내부에서는 required=false를 적용

     

    4) 파라미터 필수 여부 requestParamRequired 

    @RequestParam(required = true) String username
    , @RequestParam(required = false) Integer age
    // true -> 꼭 있어야함 없으면 상태코드 400
    // false -> 없어도 됨 대신 int는 null이 불가 Integer 타입으로 변경

     

    @RequestParam(required = xxx) : 파라미터 필수 여부, 기본값이 true

    true ▶ 없으면 상태코드 400 예외 발생

    파라미터 이름만 있고 값이 없는 경우 /request-param-required?username= ▶ 빈문자로 통과

    기본형에 null 입력 ▶ int에 null이면 500 예외 발생, null을 받을 수 있는 Integer로 변경하거나 defaultValue 사용

     

    5) 기본값 적용 requestParamDefault

    @RequestParam(required = true, defaultValue = "guest") String username
    , @RequestParam(required = false, defaultValue = "-1") int age

    defaultValue 기본값을 적용

    기본 값이 있기 때문에 required는 의미가 없다.

    파라미터 이름만 있고 값이 없는 경우 /request-param-required?username= ▶ 빈문자도 기본 값 적용

     

    6) 파라미터를 Map으로 조회하기

    @RequestParam Map<String, Object> paramMap
    log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"))

     

    파라미터를 Map, MultiValueMap으로 조회가능

    파라미터 값이 1개가 확실하다면 Map, 그렇지 않다면 MultuValueMap 사용

    728x90
    댓글