ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring MVC - 구조 이해
    카테고리 없음 2025. 1. 29. 01:57

     

    [ 직접 만든 MVC 프레임워크 구조 ]

    [ SpringMVC 구조 ]

     

     

     

     

    @RestController  vs  @Controller 

    @Controller  반환 값이 String이면 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링된다.

    @RestController 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다. 따라서

    실행 결과로 ok 메세지를 받을 있다. 

     

    [로그레벨 셋팅]

    application.properties 파일

     

    @Slf4j 롬복 사용하면 log 클래스를 정의할 필요 없음

     

     

     

    [ 요청매핑 RequestMapping ]

    단순히 url만 매핑할 뿐 아니라 여러가지 다양한 요소를 조합해서 매핑함

     

    @RequestMapping("/hello-basic")

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

    대부분의 속성을 배열[] 제공하므로 다중 설정이 가능

     {"/hello-basic", "/hello-go"}

     

    * HTTP 메서드

    @RequestMapping  method 속성으로 HTTP 메서드를 지정하지 않으면 HTTP 메서드와 무관하게 호출됨

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

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

    만약 여기에 POST 요청을 하면 스프링 MVC HTTP 405 상태코드(Method Not Allowed) 반환

     

    - 축약해서 사용 가능

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

     

     

    * PathVariable(경로 변수) 사용

    최근 HTTP API 다음과 같이 리소스 경로에 식별자를 넣는 스타일을 선호

    /mapping/userA

    /users/1

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

    @PathVariable 이름과 파라미터 이름이 같으면 생략 가능 (@PathVarible 자체를 생략하는건 아님)

    /**
    * PathVariable 사용
    * 변수명이 같으면 생략 가능
    * @PathVariable("userId") String userId -> @PathVariable String userId
    */
    
    @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 String userId, @PathVariable Long
    orderId) {
        log.info("mappingPath userId={}, orderId={}", userId, orderId);
        return "ok";
    }

     

    * 특정 헤더 조건 매핑,  미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume

    @PostMapping(value = "/mapping-produce", produces = "text/html")
    public String mappingProduces() {
        log.info("mappingProduces");
        return "ok";
    }
    @PostMapping(value = "/mapping-consume", consumes = "application/json")
    public String mappingConsumes(@RequestBody MyRequest request) {
        log.info("mappingConsumes");
        return "ok";
    }

     

     - produces란
    서버가 반환하는 데이터의 미디어 타입(Media Type, Content-Type)을 지정하는 속성

    클라이언트가 HTTP 요청 시 Accept 헤더에 원하는 미디어 타입을 설정하면, 서버는 produces에 맞는 미디어 타입으로 응답

    만약 Accept 헤더의 값과 produces의 값이 일치하지 않으면 HTTP 406 Not Acceptable 상태 코드가 반환됨

     

    • 위 코드에서 produces = "text/html"을 설정했습니다.
    • 즉, 이 엔드포인트는 "text/html" 타입의 데이터를 응답해야 합니다.
    • 클라이언트가 요청할 때 Accept: text/html을 헤더에 포함해야 합니다.
    • 만약 Accept: application/json으로 요청하면 406 Not Acceptable 에러가 발생합니다.

     

    - consumes란

    produces가 서버가 응답하는 미디어 타입이라면, consumes는 서버가 받을(소비할) 미디어 타입을 정의하는 속성

    클라이언트가 보낸 요청의 Content-Type 헤더 값이 consumes 조건과 맞지 않으면

    415 Unsupported Media Type 상태 코드가 반환

     

    • consumes = "application/json" 설정했기 때문에 클라이언트는 반드시 Content-Type: application/json으로 요청해야 함
    • 만약 Content-Type: text/plain으로 요청하면 415 Unsupported Media Type 오류가 발생합니다.

     

     

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

     

    - HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법

     

    1) GET - 쿼리 파라미터

    /url**?username=hello&age=20**

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

    ) 검색, 필터, 페이징등에서 많이 사용하는 방식

    http://localhost:8080/request-param?username=hello&age=2

     

    2) POST - HTML Form

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

    메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20

    ) 회원 가입, 상품 주문, HTML Form 사용

    POST /request-param ...
    content-type: application/x-www-form-urlencoded
    
    username=hello&age=20

     

    >> GET 쿼리 파리미터 전송 방식이든, POST HTML Form 전송 방식이든 둘다 형식이 같으므로 구분없이 조회 가능

    이것을 간단히 요청 파라미터(request parameter) 조회 한다.

     

    3) HTTP message body 데이터를 직접 담아서 요청

    HTTP API에서 주로 사용, JSON, XML, TEXT

    데이터 형식은 주로 JSON 사용

     

     

    * 스프링으로 요청 파라미터를 조회하는 방법

    request.getParameter()

     단순히 HttpServletRequest 제공하는 방식으로 요청 파라미터를 조회

    @Slf4j
    @Controller
    public class RequestParamController {
    /**
    * 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, view 조회X
    */
    @RequestMapping("/request-param-v1")
    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 사용하면 요청 파라미터를 매우 편리하게 사용 가능

    /**
    * @RequestParam 사용
    * - 파라미터 이름으로 바인딩
    * @ResponseBody 추가
    * - View 조회를 무시하고, HTTP message body에 직접 해당 내용 입력
    */
    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(
        @RequestParam("username") String memberName,
        @RequestParam("age") int memberAge) {
        log.info("username={}, age={}", memberName, memberAge);
        return "ok";
    }

    @ResponseBody : View 조회를 무시하고, HTTP message body 직접 해당 내용 입력

     

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

    /**
    * @RequestParam 사용
    * HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능
    */
    @ResponseBody
    @RequestMapping("/request-param-v3")
        public String requestParamV3(
        @RequestParam String username,
        @RequestParam int age) {
        log.info("username={}, age={}", username, age);
        return "ok";
    }

     

    >> String, int, Integer등의 단순 타입이면 @RequestParam도 생략 가능

    /**
    * @RequestParam 사용
    * String, int 등의 단순 타입이면 @RequestParam 도 생략 가능
    */
    @ResponseBody
    @RequestMapping("/request-param-v4")
    public String requestParamV4(String username, int age) {
        log.info("username={}, age={}", username, age);
        return "ok";
    }

     

    >> 파라미터를 Map으로 조회하기 - requestParamMap

    /**
    * @RequestParam Map, MultiValueMap
    * Map(key=value)
    * MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2])
    */
    @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 ]

    실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 객체에 값을 넣어주어야 한다.

    보통 다음과 같이 코드를 작성할 것이다.

    @RequestParam String username;
    @RequestParam int age;
    
    HelloData data = new HelloData();
    data.setUsername(username);
    data.setAge(age);

     

    @ModelAttribute는 이 과정을 완전 자동화해준다

    package hello.springmvc.basic;
    import lombok.Data;
    @Data
    public class HelloData {
        private String username;
        private int age;
    }

    롬복 @Data

    : @Getter , @Setter, @ToString , @EqualsAndHashCode@RequiredArgsConstructor 자동으로 적용

     

    /**
    설명
    * @ModelAttribute 사용
    * 참고: model.addAttribute(helloData) 코드도 함께 자동 적용됨
    */
    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttributeV1(@ModelAttribute HelloData helloData) {
        log.info("username={}, age={}", helloData.getUsername(),
        helloData.getAge());
        return "ok";
    }

    마치 마법처럼 `HelloData` 객체가 생성되고, 요청 파라미터의 값도 모두 들어가 있다.

     

    스프링MVC @ModelAttribute 있으면 다음을 실행

    1) HelloData객체를 생성한다.

    2) 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다.

    그리고 해당 프로퍼티의 setter 호출해서 파라미터의 값을 입력(바인딩) 한다.

    ) 파라미터 이름이 `username` 이면 `setUsername()` 메서드를 찾아서 호출하면서 값을 입력한다.

     

     

    [ HTTP 요청 메시지 - 단순 텍스트 ]

    복습)  HTTP message body 데이터를 직접 담아서 요청하는 경우

    HTTP API에서 주로 사용, JSON, XML, TEXT

    데이터 형식은 주로 JSON 사용

    POST, PUT, PATCH

    요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam, @ModelAttribute

    사용할 없음

    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) {
        log.info("messageBody={}", messageBody);
        return "ok";
    }

     

    @RequestBody

    `@RequestBody` 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 있다.

    참고로 헤더 정보가 필요하다면 `HttpEntity` 사용하거나 `@RequestHeader` 사용하면 된다.

    이렇게 메시지 바디를 직접 조회하는 기능은 요청 파라미터를 조회하는 `@RequestParam` , `@ModelAttribute`

    전혀 관계가 없다.

     

    * 요청 파라미터 vs HTTP 메시지 바디

    요청 파라미터를 조회하는 기능: `@RequestParam` , `@ModelAttribute`

    HTTP 메시지 바디를 직접 조회하는 기능: `@RequestBody`

     

    `@ResponseBody` 사용하면 응답 결과를 HTTP 메시지 바디에 직접 담아서 전달할 있다.

    물론 경우에도 view 사용하지 않는다.

     

     

    댓글

Designed by Tistory.