Post

백엔드 웹 개발 노트6.2 - 매핑

1. 요청 매핑

1.1 기본 매핑

기본적인 형태의 매핑 방식이다.

저번에 작성한 양식이랑 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello.springmvc.basic.reqeustmapping;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MappingController {

    private Logger log = LoggerFactory.getLogger(getClass());

    @RequestMapping("/kms-basic-request")
    public String basicreq(){
        log.info("basic reqeust  kms");
        return "ok";
    }
}

그냥 “/kms-basic-reqeust”로 요청이 들어오면 로그로 string을 찍어주고 _@RestController_로 인해 웹에 ok를 뿌려주는 간단한 로직이다.

  • 참고로 대부분의 속성을 배열[]로 제공하므로 다중 설정이 가능하다.
    ex) “/kms-basic-request , /kms-basic-requestMap”
  • 다음 두 요청은 다른 URL이지만 스프링은 같은 요청으로 매핑한다.
    ex) “/kms-basic-request”, “/kms-basic-request/”

1.2 HTTP 메서드 매핑

1
2
3
4
5
6
7
8
9
     * method 특정 HTTP 메서드 요청만 허용
     * GET, HEAD, POST, PUT, PATCH, DELETE
     */
    @RequestMapping(value = "/kms-mapping-get-v1", method = RequestMethod.GET)
    public String mappingGetv1(){
        log.info("mappingGetv1");
        return "ok";
    }

애노테이션 값안에 method 파라미터를 달아줘서 요청을 지정해줄수 있다.

즉 저 URL로 POST요청이 오면 스프링 MVC는 HTTP 405(Method Not Allowed)를 반환한다.

사진을 보면 POST 요청을 보냈을 때 로그가 Warn로 해당 메서드는 지원하지 않는다고 로그를 찍는걸 볼수 있다.

위에 로그들은 GET요청을 보냈을 때 정상적으로 로직이 수행된 것이다.

1.3 HTTP 메서드 축약

1
2
3
4
5
6
7
8
9
10
11
12
13
    /**
     * 편리한 축약 애노테이션
     * @GetMapping
     * @PostMapping
     * @PutMapping
     * @DeleteMapping
     * @PatchMapping
     */
    @GetMapping(value = "/kms/mapping-get-v2")
    public String mappingGetv2(){
        log.info("mappingGetv2");
        return "ok";
    }

즉, 위처럼 GetMapping 라는 애노테이션을 달아주면 @RequestMappingmethod = RequestMethod.GET 을 써준거나 똑같아진다.

1.4 PathVariable(경로 변수)

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

  • /mapping/userA (userA는 변수)
  • /users/1 (1은 변수)

이렇게 말이다.

이렇게 경로에 변수가 있을때 꺼내게 해주는 애노테이션이 있다.

1
2
3
4
5
6
7
8
9
10
11
    /**
     * PathVariable 사용.
     * @param id
     * @return
     */
    @GetMapping("/kms/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String id){
        log.info("mapping path userId = {}",id);
        return "ok";
    }

@GetMapping 안에는 ‘{}’에 변수 값이 들어온다는 것을 알려주고 _@PathVariable 속성값으로 변수를 받아올 수 있다.

참고로 @PathVariable 의 이름과 파라미터 이름이 같으면 생략할 수 있다.

1.5 PathVariable 사용 - 다중

1
2
3
4
5
    @GetMapping("/kms/mapping/{userId}/champion/{chamname}")
    public String mappingPath(@PathVariable String userId, @PathVariable String chamname){
        log.info("mapping path userId = {}, champion name = {}",userId,chamname);
        return "ok";
    }

@PathVariable을 다중으로 사용했을때, 파라미터 이름이 같으면 속성값을 생략할 수 있다는 것을 보여주기 위해 만든 예제이다.

자알된다.

1.6 특정 파라미터 조건 매핑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /**
     * 파라미터로 추가 매핑
     * parmas = "lang"
     * params ="!lang"
     * params = "lang=java"
     * params = "lang!=java"
     * params = {"lang=java","name="kms"}
     * 위와 같은 표현도 가능하다는 것.
     */
    @GetMapping(value = "/kms-mapping-param",params = "lang=java")
    public String mappingParam(){
        log.info("mappingParam");
        return "ok";
    }

이런식으로 작성하면 요청값에 꼭 “lang=java”라는 특정 파라미터 값이 들어와야한다.

보다시피 ‘kms=kms’라는 쓰잘데기 없는 값을 추가해도 “ok”가 떨어진다.

특정 파라미터 값이라는 ‘lang=java’를 없애보겠다.

400에러뜬다.

이런 기능이다.

1.7 특정 헤더 조건 매핑

1
2
3
4
5
6
7
8
9
10
11
12
    /**
     * 특정 헤더로 추가 매핑
     * headers = "champion"
     * headers = "!champion"
     * headers = "champion = vayne"
     * headers = "champion != vayne"
     */
    @GetMapping(value = "/kms-mapping-headers", headers = "champion=vayne")
    public String mappingHeader(){
        log.info("mappingHeader");
        return "ok";
    }

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

champion 헤더와 그 값으로 vayne을 갖는 헤더를 추가해서 요청하면 ‘ok’가 떨어진다.

1.8 미디어 타입 조건 매핑 - ContentType, consume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   /**
     * Content-Type 헤더 기반 추가 매핑 Media Type
     * consumes = "application/json"
     * consumes = "!application/json"
     * consumes = "aplication/*"
     * consumes = "*\/*"
     * MediaType.APPLICATION_JSON_VALUE
     */

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

1.9 미디어 타입 조건 매핑 Accept, produce

1
2
3
4
5
6
7
8
9
10
11
12
   /**
    * Accept 헤더 기반 Media Type
    * produces = "text/html"
    * produces = "!text/html"
    * produces = "text/*"
    * produces = "*\/*"
    */
    @PostMapping(value = "/kms-mapping-produce", produces = "text/html")
    public String mappingProduces() {
        log.info("mappingProduces");
        return "ok";
    }

consume과 비슷하게 produce는 Accept헤더를 보고 작동한다.

1.9.1 Accept, Conent-Type

  • Accept은 클라이언트가 선호하는 표현을 요청한다. 즉, 클라이언트 입장에서 응답을 받을때, Accept 헤더에 있는 데이터 타입이 오지 않으면 거절한다. 즉, 서버입장에서는 생성(produce)한 데이터 타입이 맞지 않으면 거절.

  • Content-Type은 해당 헤더에 있는 데이터가 오지 않았을 경우 서버 입장에서 요청 거부 즉 서버는 헤더요청을 소비(consume)함.

2. 요청 매핑 - API 예시

나는 롤을 많이했으므로 롤에 대해 예시를 들겠다.

롤 챔피언 관리를 HTTP API로 만든다 생각하고 매핑을 어떻게 하는지 보자.

  • 챔피언 목록 조회 : GET /champions
  • 챔피언 등록 : POST /champions
  • 챔피언 조회 : GET /champions/{championId}
  • 챔피언 수정 : PATCH /champions/{championId}
  • 챔피언 삭제 : DELETE /champions/{championId}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package hello.springmvc.basic.reqeustmapping;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/kms-mapping/champions")
public class ChampionController {

    /**
     * GET /kms-mapping/champions
     */
    @GetMapping
    public String champions(){
        return "get champions";
    }

    /**
     * POST /kms-mapping/champions
     */
    @PostMapping
    public String addChampions(){
        return "post champions";
    }

    /**
     * GET /kms-mapping/champions/{champions}
     */
    @GetMapping("/{championId}")
    public String findChampion(@PathVariable String championId){
        return "get championId :" + championId;
    }

    /**
     * PATCH /kms-mapping/champions/{champions}
     */
    @PatchMapping("/{championId}")
    public String updateChampion(@PathVariable String championId){
        return "patch championId : " + championId;
    }

    /**
     * DELETE /kms-mapping
     */
    @DeleteMapping("/{championId}")
    public String deleteChampion(@PathVariable String championId){
        return "delete championId : " + championId;
    }
}

Postman으로 다 테스트하면 다 잘된다.

3. 요청 매핑 - 기본, 헤더 조회

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

직관적이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package hello.springmvc.basic.reqeustmapping;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

@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 = "kms-cookie",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("kms-cookie = {}",cookie);

        return "ok";
    }
}

결과

설명할 부분이 많지는 않다.

  • 먼저, @RequestHeader MultiValueMap<String,String> headerMap 이부분에서 MultiValueMap이 뭔지 궁금하다.

    크게 다른건 없다. 하나의 key와 여러 value를 갖는 맵이다.

  • @RequestHeader(“host”) String host 는 특정 HTTP 헤더를 조회한다.
  • @CookieValue 또한 특정 쿠키를 조회한다. 코드에는 쿠키설정 을 안 해줘서 null로 나온다.
This post is licensed under CC BY 4.0 by the author.