7. 스프링 Http 요청

김현준·2023년 7월 28일

Gaounuri_Spring_Study

목록 보기
8/10

Project Code File

📌 목차

  • 요청 매핑
  • 요청 매핑 - API
  • HTTP 요청 - 기본, 헤더 조회
  • HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form
  • HTTP 요청 파라미터 - @ModelAttribute
  • HTTP 요청 메시지 - 단순 텍스트
  • HTTP 요청 메시지 - JSON

📌 요청 매핑

이번에는 에노테이션을 통해 어떻게 RequestMapping 경로를 지정하고 매핑을 하는지 알아보자.

package hello.springmvc.basic.requestmapping;

@RestController
public class MappingController {

    private Logger log = LoggerFactory.getLogger(MappingController.class);

    @RequestMapping("/hello-basic")
    public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }

기본적인 코드는 위와 같다.

@RequestMapping(value = {"/hello-basic", "hello-go"}, method = RequestMethod.GET) 처럼 경로를 여러개 지정하거나 HTTP 상태 코드를 지정할 수 있다.

@RestController 를 사용하면 반환값을 HTTP 메시지 바디에 입력된다.

@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
    log.info("mapping-get-v2");
    return "ok";
}

상태코드를 따로 입력해서 지정해줄 수 있지만 @GetMapping 에노테이션을 통해서도 지정할 수 있다.

@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String data) {
    log.info("mappingPath userId = {}", data); // {} 로 해야함.
    return "ok";
}

자주 쓰이는 PathVariable (경로 변수) 을 사용하면 위 코드처럼 쓸 수 있다.
{} 괄호 안에 있는 값은 http://localhost:8080/mapping/userA 같은 링크로 이동할시 userA 가 자동으로 등록된다.

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

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

@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
    log.info("mappingParam");
    return "ok";
}

특정 파라미터가 있어야 하는 조건을 만들 수 있다.
http://localhost:8080/mapping-param?mode=debug 처럼 파라미터가 존재해야지 뷰를 읽을 수 있다.

@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
    log.info("mappingHeader");
    return "ok";
}

header= 에 값을 입력하면 특정 헤더가 있어야한다는 조건을 만들 수 있다.


//Content-Type 지정.
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
    log.info("mappingConsumes");
    return "ok";
}

consumes 를 통해 특정 Content-Type 를 지정할 수 있다. application/json 을 넣으면 json 형식의 데이터만 읽을 수 있다.

//  produces = MediaType.TEXT_HTML_VALUE
@PostMapping(value = "mapping-produce", produces = "text/html")
public String mappingProduces() {
    log.info("mappingProduces");
    return "ok";
}

마지막으로 미디어 타입도 지정할 수 있다. HTTP 요청의 Accept 헤더를 기반으로 매핑하는데 만약 맞지 않으면 406 상태 코드를 반환한다.

📌 요청 매핑 - API

회원관리를 HTTP API 로 만든다고 가정해보고 매핑을 어떻게 해보는지 직접 코드를 만들어 보자.

실제 데이터는 저장하지 않는다.

  • 회원 목록 조회: GET /users
  • 회원 등록: POST /users
  • 회원 조회: GET /users/{userId}
  • 회원 수정: PATCH /users/{userId}
  • 회원 삭제: DELETE /users/{userId}
package hello.springmvc.basic.requestmapping;

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

@RequestMapping 을 클래스 상단에 적어두면 경로의 중복을 제거할 수 있다.
@PathVariable 을 통해 파라미터를 가져올 수 있고 @PostMapping , @PatchMapping , @DeleteMapping , @GetMapping 등을 사용할 수 있다.

📌 HTTP 요청 - 기본, 헤더 조회

이번에는 HTTP 헤더 정보를 조회하는 법을 알아보자.

package hello.springmvc.basic.request;

@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 = 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";
    }
}

헤더 정보를 조회할때 많은 파라미터를 사용할 수 있다.
@RequestHeader MultiValueMap?age=20&age=10 같이 하나의 키에 여러 값을 받을 수 있다.

@RequestHeader("host") String host 은 특정 HTTP 헤더를 조회 할 수 있다.

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

package hello.springmvc.basic.request;

@Slf4j
@Controller
public class RequestParamController {

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

기본적인 서블릿 파라미터를 사용하면 위처럼 사용할 수 있다.

@ResponseBody // RestController 와 동일한 기능
@RequestMapping("request-param-v2")
public String requestParamV2(
    @RequestParam("username") String memberName,
    @RequestParam("age") int memberAge
) {
    log.info("username = {} , age = {} ", memberName, memberAge);
    return "ok";
}

다음으로 @RequestParam 에노테이션을 통해 특정 파라미터 값을 받아 올 수 있다.
또한 @ResponseBody 를 사용시 @RestController 와 동일한 기능을 수행한다.

@RequestParam String username, @RequestParam int age

이때 HTTP 파라미터 이름이 변수 이름과 같다면 위 처럼 생략할 수 있다.

String username, int age

또한 String , int , Integer 같이 객체가 아니라 단순 타입이면 @RequestParam 에노테이션도 생략할 수 있다.

하지만 에노테이션을 사용하지 않으면 코드가 명확하지 않다는 단점도 있다.

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

@RequestParam 에서 required 옵션을 통해 파라미터 필수 여부를 결정할 수 있다. 만약 false 면 해당 파라미터가 없어도 코드가 실행된다.

defaultValue 옵션을 통해 기본값을 적용시킬 수 있다. required=false 일때만 의미가 있다.

?username= 처럼 "" 빈문자의 경우에도 적용이 된다.

@ResponseBody
@RequestMapping("request-param-map")
public String requestParamMap(@RequestParam Map<String, Objects> paramMap) {
    log.info("username = {} , age = {} ", paramMap.get("username"), paramMap.get("age"));
    return "ok";
}

파라미터를 Map 을 통해서도 받을 수 있다. 만약 중복키가 있을 수 있다면 MultiValueMap 도 사용 가능하다.

📌 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 에노테이션 기능을 제공한다.

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
    log.info("username = {} , age = {} ", helloData.getUsername(), helloData.getAge());
    return "ok";
}

따라서 위 코드처럼 @ModelAttribute 옵션을 통해 객체를 불러오고 동시에 파라미터 값을 해당 객체에 저장할 수 있다.

@ModelAttribute 에노테이션을 제거할 수 있다.

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

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

package hello.springmvc.basic.request;

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

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

서블릿만을 사용한다면 request.getInputStream() 을 통해 HTTP 메시지 바디를 읽어 올 수 있다.

InputStream inputStream , Writer responseWriter

만약 response , request 를 사용하지 않는다면 파라미터를 위처럼 변경할 수 있다.

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

HttpEntity 객체를 사용할 수 있다. 응답에도 사용가능하고 Htpp 메시지 컨버터 기능을 사용한다.

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

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

    return "ok";
}

@RequestBody 에노테이션을 사용하면 아주 편리하게 http 메시지 바디 정보를 조회할 수 있다.

📌 HTTP 요청 메시지 - JSON

이번에는 HTTP API 에서 주로 사용하는 json 형식의 데이터를 조회해보자.

package hello.springmvc.basic.request;

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

기본에 서블릿만을 사용할떄는 위처럼 ObjectMapper 을 사용했어야 했다.

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

하지만 @RequestBody 에노테이션을 사용하면 objectMapper 을 사용해 messageBody 에 있는 데이터를 helloData 에 저장만 하면 된다.

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

파라미터 자체를 HelloData 로 설정하면 메시지 컨버터 기능을 통해 objectMapper 가 했던 일을 대신 해준다. 다만 @RequestBody 에노테이션은 생략할 수 없다.

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

HttpEntity 를 통해서도 가능하다.

📌 정리

지금까지 요청 매핑을 하는 방법을 알아보았고 , HTTP 요청에서 헤더를 조회하는 법과 스프링 에노테이션을 통해 간략화 하는법 , HTTP 메시지 바디를 조회하는 방법중 쿼리파라미터와 메시지 바디 (텍스트,json) 을 스프링 에노테이션을 통해 간단하게 알아보는 방법을 살펴보았다.

다음에는 HTTP 응답에서 데이터를 어떻게 응답하는지 알아보자.

profile
울산대학교 IT융합학부

0개의 댓글