이 영역을 누르면 첫 페이지로 이동
천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

페이지 맨 위로 올라가기

천천히 꾸준히 조용히

천천히 꾸준히 조용히.. i3months 블로그

[Spring Web MVC] 요청 정보 다루기

  • 2022.08.17 13:47
  • Spring/Spring Web MVC
반응형

 

 

 

	@GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String id){
        log.info("logging... {}", id);
        return "chk";
    }

 

 

URL 경로에 변수를 넣어 템플릿 형식으로 설정할 수 있고, @PathVariable 애너테이션으로 그 값을 꺼내 메서드에서 사용할 수 있다. (경로 변수)

 

 

 

	@GetMapping("/mapping/users/{userId}/order/{orderID}")
    public String mappingPath(@PathVariable("userId") String id, @PathVariable("orderID") String order){
        log.info("logging... {} {}", id, order);
        return "chk";
    }

 

 

다중 매핑도 지원한다.

 

 

@GetMapping(value ="/mapping/", params = "mode=debug")
    public String mappingTest(){
        log.info("logging..");
        return "chk";
    }

 

해당 URL 요청이 들어왔을 때 특정 파라미터가 있어야 요청이 수락되도록 할 수 있다. 

(사실 경로 변수 방식을 더 많이 사용한다.)

 

params 대신 headers를 사용하면 특정 헤더가 있어야지만 요청이 수락된다.

 

params 대신 consumes를 사용하면 해당 요청의 content-type이 consumes로 등록한 값과 일치할 때 요청이 수락된다.

사실 headers를 사용해서 값을 조금 조작해도 되는데, consumes로 등록 시 내부적으로 좀 더 처리하는 작업이 있다.

 

params 대신 produces를 사용하면 해당 요청의 accept 값이 produces로 등록한 값과 일치할 때 요청이 수락된다.

 

 

 


 

 

 

회원 관리 API를 설계해보자.

 

회원 목록 조회: GET /users

회원 등록: POST /users

회원 조회: GET /users/{userId}

회원 수정: PATCH /users/{userId}

회원 삭제: DELETE /users/{userId}

 

 

@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {


    @GetMapping
    public String user(){
        return "get users";
    }

    @PostMapping
    public String addUser(){
        return "post user";
    }

    @GetMapping("/{userId}")
    public String findUser(@PathVariable String userId){
        return "get userId=" + userId;
    }

    @PatchMapping("/{userId}")
    public String updateUser(@PathVariable String userId){
        return "update userId=" + userId;
    }

    @DeleteMapping("/{userId}")
    public String deleteUser(@PathVariable String userId){
        return "delete userId=" + userId;
    }


}

 

 

URL 매핑만 처리한다고 하면 이렇게 작성하면 된다.

 

 

 

@Slf4j
@RestController
public class RequestHeaderController {

    @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 = true) 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 헤더 관련 정보를 조회하는 방법을 살펴보자.

 

 

Request와 Response는 이미 많이 봤고..

 

HttpMethod : 요청 메서드를 조회한다.

 

Locale : Locale 정보를 조회한다. (지역)

 

@RequestHeader MultiValueMap : 모든 HTTP 헤더를 MultiValueMap 형식으로 조회한다.

여기서 MultiValueMap은 Map 자료구조와 유사한데, 하나의 키에 여러 가지 값을 받을 수 있는 자료구조이다.

같은 헤더에 같은 값이 들어가는 경우 사용한다. (키로 조회 시 리스트가 반환된다.)

 

@RequestHeader("host") : 특정 HTTP 헤더를 조회한다.

 

@CookieValue(...) : 특정 쿠키를 조회한다.

 

이 외에도 다양한 파라미터를 받을 수 있으니, 스프링 공식 문서를 참고하자.

 

 

 


 

 

 

@Controller
@Slf4j
public class RequestParamController {
    @RequestMapping("/request-param-v1")
    public void requestParam1(HttpServletRequest request, HttpServletResponse response)throws IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        log.info("{}, {}", username, age);

        PrintWriter writer = response.getWriter();

        writer.write("check");
    }
}

 

 

클라이언트에서 서버로 데이터를 전송할 때는 쿼리 파라미터 / HTML Form / HTTP 메세지 바디 방식을 사용했었는데, 이 중에서 쿼리 파라미터와 HTML Form을 통해 전송하는 경우는 getParameter 메서드를 통해 서버에서 요청 데이터를 가져올 수 있었다.

 

 

    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(
            @RequestParam("username") String username,
            @RequestParam("age") int age,){

        log.info("{} {}", username, age);
        return "check";


    }

 

 

@RequestParam 애너테이션을 통해 getParameter() 메서드를 사용하지 않고도 데이터를 읽을 수 있다.

여기서 @ResponseBody 애너테이션은 반환값을 HTTP 응답 본문(body)으로 보낼 때 사용한다. 

 

 

 

    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(
            @RequestParam String username,
            @RequestParam int age){

        log.info("{} {}", username, age);
        return "da";


    }

 

 

지금처럼 파라미터 이름과 변수명이 같으면 파라미터 이름을 생략해서 사용할 수 있다.

 

 

    @ResponseBody
    @RequestMapping("/request-param-v4")
    public String requestParamV4(
             String username,
             int age){

        log.info("{} {}", username, age);
        return "dasafd";
        
    }

 

 

파라미터의 이름과 변수명이 같으면 @RequestParam 애너테이션도 생략할 수 있다.

이 때 타입은 String int 등 단순 타입이어야 한다.

 

그런데 너무 생략해도 코드를 이해하기 쉽지 않을 수 있으니 적당히 사용하자.

 

 

 

    @ResponseBody
    @RequestMapping("/request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true) String username,
            @RequestParam(required = false) Integer age){

        log.info("{} {}", username, age);
        return "dassdfaafd";
    }

 

 

@RequestParam 애너테이션에 required 옵션을 추가해서 요청 메세지에 해당 요소가 들어올 경우만 요청을 수락하도록 할 수 있다. (기본값 true)

 

여기서 int 대신 Integer를 사용했는데, false로 설정 시 null 이 들어가야 하지만 Primitive 타입에는 null을 넣을 수 없어 null을 사용할 수 있는 래퍼 클래스로 설정해야 한다. 

 

 

    @ResponseBody
    @RequestMapping("/request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true) String username,
            @RequestParam(required = false, defaultValue = "231") int age){

        log.info("{} {}", username, age);
        return "dassdfaafd";
    }

 

 

defaultValue를 지정해 값이 들어오지 않을 경우 기본값을 설정할 수 있다.

이렇게 하면 int를 그대로 사용할 수 있다.

 

 

    @ResponseBody
    @RequestMapping("/request-param-map")
    public String requestParamMap(@RequestParam Map<String, Object> paramMap){

        log.info("{} {}", paramMap.get("username"), paramMap.get("age"));
        return "dafd";
    }

 

 

파라미터로 Map 또는 MultiValueMap을 주면 모든 파라미터를 조회할 수 있다.

 

 

 


 

 

 

요청 파라미터를 받아서 응답에 필요한 객체를 만들고 그 객체에 값을 넣어줘야 한다.

 

    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttribute(
            @RequestParam String username,
            @RequestParam int age){

        HelloData helloData = new HelloData();
        helloData.setAge(32);
        helloData.setUsername("asdf");

        

        return "32";
    }

 

이런 식으로.. 요청 파라미터에서의 데이터를 저장할 객체를 만들어야 했다.

 

 

    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttribute(@ModelAttribute HelloData helloData){
        
        return "32";
    }

 

@ModelAttribute 애너테이션을 사용하면 모델 객체를 알아서 생성하고 요청 파라미터의 값도 알아서 채워준다.

 

1. 객체 생성

2. 요청 파라미터의 이름으로 해당 객체의 프로퍼티(setter, getter)를 찾아 값을 바인딩한다. 

 

요청 시 타입이 어긋나면 BindException을 뱉는다.

 

 

    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttribute(HelloData helloData){

        return "32";
    }

 

사실 @ModelAttribute 애너테이션은 생략할 수 있다.

그런데 @RequestParam도 생략할 수 있지 않았나? 좀 헷갈릴 것 같은데...

 

생략 시 규칙이 적용된다.

String int 등 단순 타입은 @RequestParam을 사용하고, 나머지 타입은 @ModelAttribute를 사용한다. 

나머지 타입 중 argument resolver로 지정해 둔 타입은 @ModelAttribute 가 적용되지 않는데..

 

@RequestParam 은 요청 파라미터를 받는 작업을 수행하고, 바인딩은 스스로 해야 한다.

@ModelAttribute 는 요청 파라미터를 받아서 객체 생성부터 바인딩, 모델에 담기까지의 작업을 모두 처리한다.

 

이 정도로 이해하자.

 

 

 

 


 

 

 

메세지 바디를 통해 직접 데이터가 넘어오는 경우는 @RequestParam 과 @ModelAttribute 애너테이션을 사용할 수 없다.

 

 

    @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);
    }

 

 

가장 단순한 방법으로 InputStream 으로 메세지 바디의 데이터를 직접 읽어올 수 있다.

 

 

    @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("check");
    }

 

파라미터 값으로 InputStream을 바로 받아올 수 있다. (Writer도 마찬가지)

String 은 바이트 코드이기 때문에 Charset을 지정해 줘야 한다. (미지정 시 OS에서 기본값으로 설정된 Charset으로 설정됨)

 

 

    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {

        String body = httpEntity.getBody();
        log.info("messagebody {}", body);
        return new HttpEntity<>("check");
    }

 

더 간소화 할 수 있다.

HttpEntity는 HTTP 헤더, 바디 등 여러 정보를 편하게 조회할 수 있도록 해 준다.

메세지 바디 정보를 바로 조회할 수 있고, 이전처럼 요청 파라미터를 조회하는 기능과는 전혀 연관관계가 없다.

 

리턴 타입으로 HttpEntity<String>을 사용해 응답에도 HttpEntity를 사용할 수 있다.

메세지 바디 정보를 직접 반환하고, 뷰를 조회하지 않는다. 

 

HttpEntity를 상속받은 RequestEntity, ResponseEntity를 사용해 요청과 응답에서 사용할 수도 있다.

 

 

    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {

        log.info("messagebody {}", messageBody);
        return "ok123";
    }

 

 

여기서 더 간단하게 바꿀 수 있다.

 

파라미터로 @RequestBody 애너테이션을 사용해 메세지 바디의 내용을 바로 가져올 수 있고, 메서드에 @ResponseBody 애너테이션을 붙여 뷰를 찾지 않고 응답 결과를 메세지 바디에 담아 그대로 반환하도록 할 수 있다.

 

이 방법을 가장 많이 사용한다.

 

 

그런데 메세지 바디에 텍스트만 적어서 보내지는 않는다.

주로 사용하는 데이터 형식인 JSON 형식을 다뤄보자.

 

 

기본적으로 ObjectMapper를 사용한다.

request 와 response를 통해 읽는 경우 / @RequestBody 애너테이션으로 읽고 HelloData 객체로 변환해서 읽는 경우는 앞의 예시와 동일하니 넘어가자.

 

 

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

 

@RequestBody에 객체를 파라미터로 넘긴다.

HTTP 메세지 컨버터는 HTTP 메세지 바디의 내용을 원하는 문자나 객체로 변환해준다.

 

즉, 원하는 타입이 JSON 타입이면 (content-type 이 application/JSON 이면)

HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);

위의 코드를 HTTP 메세지 컨버터가 실행해준다.

 

여기서는 @RequestBody 애너테이션을 생략할 수 없다.

앞에서 살펴본 바와 같이 객체 타입은 @ModelAttribute가 요청 파라미터를 처리하기 때문에 생략 시 HTTP 메세지 바디가 아니라 요청 파라미터를 처리하게 된다.

 

 

    @ResponseBody
    @PostMapping("/request-body-json-v4")
    public HelloData requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
        HelloData data = httpEntity.getBody();
        log.info("username={}, age={}", data.getUsername(), data.getAge());
        return data;
    }

 

 

물론 앞에서와 같이 HttpEntity를 사용해도 된다.

 

JSON 타입으로 요청이 들어오면 HTTP 메세지 컨버터가 해당 타입을 객체로 변환하고,

응답할 때는 객체를 다시 JSON타입으로 변경 후 응답한다.

 

 

 

 

 

 

 

 

 

 

반응형
저작자표시 (새창열림)

'Spring > Spring Web MVC' 카테고리의 다른 글

[Spring Web MVC] Spring MVC와 Thymeleaf 예시  (0) 2022.08.18
[Spring Web MVC] 응답 정보 다루기  (0) 2022.08.17
[Spring Web MVC] Spring MVC  (0) 2022.08.16
[Spring Web MVC] Adaptor  (0) 2022.08.16
[Spring Web MVC] Model  (0) 2022.08.15

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [Spring Web MVC] Spring MVC와 Thymeleaf 예시

    [Spring Web MVC] Spring MVC와 Thymeleaf 예시

    2022.08.18
  • [Spring Web MVC] 응답 정보 다루기

    [Spring Web MVC] 응답 정보 다루기

    2022.08.17
  • [Spring Web MVC] Spring MVC

    [Spring Web MVC] Spring MVC

    2022.08.16
  • [Spring Web MVC] Adaptor

    [Spring Web MVC] Adaptor

    2022.08.16
다른 글 더 둘러보기

정보

천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

  • 천천히 꾸준히 조용히의 첫 페이지로 이동

검색

방문자

  • 전체 방문자
  • 오늘
  • 어제

카테고리

  • 분류 전체보기 (679)
    • Algorithm (205)
      • Data Structure (5)
      • Theory && Tip (33)
      • Baekjoon (166)
      • ALGOSPOT (1)
    • Spring (123)
      • Spring (28)
      • Spring Web MVC (20)
      • Spring Database (14)
      • Spring Boot (6)
      • Spring 3.1 (11)
      • Spring Batch (6)
      • Spring Security (16)
      • JPA (12)
      • Spring Data JPA (5)
      • QueryDSL (4)
      • eGovFramework (1)
    • Programming Language (74)
      • C (25)
      • C++ (12)
      • Java (19)
      • JavaScript (15)
      • Python (1)
      • PHP (2)
    • Computer Science (142)
      • Machine Learning (38)
      • Operating System (18)
      • Computer Network (28)
      • System Programming (22)
      • Universial Programming Lang.. (8)
      • Computer Architecture (4)
      • Compiler Design (11)
      • Computer Security (13)
    • Database (21)
      • Database (7)
      • MySQL (3)
      • Oracle (3)
      • Redis (5)
      • Elasticsearch (3)
    • DevOps (20)
      • Docker && Kubernetes (8)
      • Jenkins (4)
      • Amazon Web Service (8)
    • Mobile (28)
      • Android (21)
      • Flutter (7)
    • 💡 솔루션 (17)
    • 👥 모각코 (10)
    • 💬 기록 (8)
    • 📚 공부 (6)
    • -------------- (25)

최근 글

나의 외부 링크

메뉴

  • 홈
반응형

정보

i3months의 천천히 꾸준히 조용히

천천히 꾸준히 조용히

i3months

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. Copyright © i3months.

티스토리툴바