JiwonDev

#2 HTTP API ์š”์ฒญ ๋งคํ•‘, ํ—ค๋” ์กฐํšŒ

by JiwonDev

์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” API ๊ทœ์น™์€ ๋งŒ๋“ค๊ธฐ ๋‚˜๋ฆ„์ด๊ฒ ์ง€๋งŒ, ๋ณดํ†ต์€ REST API๋ฅผ ๋งŽ์ด ๊ตฌํ˜„ํ•œ๋‹ค.

 

# RESTful API๋ž€?

REpresentational State Transfer '๋Œ€ํ‘œ์ ์ธ ์ƒํƒœ ์ „๋‹ฌ'

REST๋ž€ ์›น์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์ž์›์— ๊ณ ์œ ํ•œ URI๋ฅผ ๋ถ€์—ฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค. 

RESTful API๋ž€ HTTP ๋ฉ”์„œ๋“œ๋ฅผ RESTํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ์•„ํ‚คํ…์ณ ์Šคํƒ€์ผ์ด๋‹ค. ๊ธฐ์กด์˜ ๋ฉ”์„œ๋“œ๋“ค (Post, Get, Put, Delete)๋ฅผ ์šฉ๋„์— ๋”ฐ๋ผ ๋‚˜๋ˆ„๊ณ  ํ•ด๋‹น ์ž์›์— ๋Œ€ํ•œ  CRUD๋ฅผ ์ ์šฉ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

  • ์ž์›(Resource) : URI๋กœ ํ‘œํ˜„, ๋ชจ๋“  ์ž์›์—๋Š” ๊ณ ์œ ํ•œ ID๊ฐ€ ์กด์žฌํ•œ๋‹ค.
  • ํ–‰์œ„(Verb) : HTTP Method ์‚ฌ์šฉ, GET, POST๋“ฑ์œผ๋กœ ์„œ๋ฒ„์— ํ•ด๋‹น ์ž์›์— ๋Œ€ํ•œ ์กฐ์ž‘์„ ์š”์ฒญํ•œ๋‹ค.
  • ํ‘œํ˜„ : REST์—์„œ ์ž์›์€ json, xml, text, rss๋“ฑ ์—ฌ๋Ÿฌ ํ˜•ํƒœ๋กœ ํ‘œํ˜„ํ•˜์—ฌ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
์žฅ์  - HTTP ์ธํ”„๋ผ๋ฅผ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•œ๋‹ค.
- ์ฆ‰ HTTP๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
- REST ๋ฉ”์‹œ์ง€๊ฐ€ ์ง๊ด€์ ์ด๋ผ ์ฝ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๋‹ค.
- ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์—ญํ• ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
๋‹จ์  - ํ‘œ์ค€์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ•์ œํ•  ์ˆ˜ ์—†๋‹ค.
- ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ 4๊ฐœ ๋ฐ–์— ์—†๋‹ค.

 

@ REST์˜ ํŠน์ง•

๋”๋ณด๊ธฐ
  • Uniform Interface (์ธํ„ฐํŽ˜์ด์Šค ์ผ๊ด€์„ฑ)
    - URI๋กœ ์ง€์ •ํ•œ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์กฐ์ž‘์ด ํ†ต์ผ๋˜๊ณ , ์š”์ฒญ ๋ฉ”์‹œ์ง€๋งŒ์œผ๋กœ ์˜๋ฏธ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.
     -ํŠน์ • ์–ธ์–ด๋‚˜ ๊ธฐ์ˆ ์— ์ข…์†๋˜์ง€์•Š๋Š”๋‹ค. HTTP๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ชจ๋“  ํ”Œ๋žซํผ์— ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ๋ช…ํ™•ํ•œ ์„œ๋ฒ„-ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ
    - ์ž์›์ด ์žˆ๋Š” ์ชฝ์ด Server, ์ž์›์„ ์š”์ฒญํ•˜๋Š” ์ชฝ์ด Client๊ฐ€ ๋œ๋‹ค.
    - Server: API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ๋ฐ ์ €์žฅ์„ ์ฑ…์ž„์ง„๋‹ค.
    - Client: ์‚ฌ์šฉ์ž ์ธ์ฆ์ด๋‚˜ context(์„ธ์…˜, ๋กœ๊ทธ์ธ ์ •๋ณด) ๋“ฑ์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๊ณ  ์ฑ…์ž„์ง„๋‹ค.

  • Stateless (๋ฌด์ƒํƒœ)
    - HTTP ์ž์ฒด๊ฐ€ ๋ฌด์ƒํƒœ์„ฑ์„ ๊ฐ–๊ธฐ๋„ ํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์˜ ์ƒํƒœ๋ฅผ URI๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์— ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
    - ์ฆ‰ ์„œ๋ฒ„๋Š” ๊ฐ๊ฐ์˜ ์š”์ฒญ์„ ๋ณ„๊ฐœ์˜ ๊ฒƒ์œผ๋กœ ์ธ์‹ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•œ๋‹ค.

  • Cacheable (์บ์‹œ ๊ฐ€๋Šฅ)
    ๋ฌด์ƒํƒœ์„ฑ ๋•๋ถ„์— ํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ , ๋Œ€๋Ÿ‰์˜ ์š”์ฒญ์„ ํšจ์œจ์ ์œผ๋กœ ์บ์‹œํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

  • Layered System (๊ณ„์ธตํ™”)
    REST Server๋ฅผ ๋‹ค์ค‘ ๊ณ„์ธต์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ธฐ ์‰ฝ๋‹ค.
    - API ์„œ๋ฒ„๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ทธ ์•ž ๋‹จ์— ํ”„๋ก์‹œ, ๋ณด์•ˆ, ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ, ์•”ํ˜ธํ™”๋“ฑ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์‰ฝ๋‹ค.
    - ์ฆ‰ ํ™•์žฅ์„ฑ์ด ๋›ฐ์–ด๋‚˜๋‹ค.

 

 


# API ์š”์ฒญ ๋งคํ•‘ํ•˜๊ธฐ

ํšŒ์› ๊ด€๋ฆฌ API๋กœ ์˜ˆ๋ฅผ ๋“ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ์ด๋ฅผ ์Šคํ”„๋ง MVC๋กœ ๋งคํ•‘ํ•ด๋ณด์ž.

  • ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ: GET /users
  • ํšŒ์› ๋“ฑ๋ก: POST /users
  • ํšŒ์› ์กฐํšŒ: GET /users/{userId}
  • ํšŒ์› ์ˆ˜์ •: PATCH /users/{userId}
  • ํšŒ์› ์‚ญ์ œ: DELETE /users/{userId}
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
    /**
     * GET /mapping/users
     */
    @GetMapping
    public String users() {
        return "get users";
    }

    /**
     * POST /mapping/users
     */
    @PostMapping
    public String addUser() {
        return "post user";
    }

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

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

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

 


# HTTP ํ—ค๋” ์กฐํšŒ

mapping๋œ ๋ฉ”์„œ๋“œ๋Š” ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ณจ๋ผ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž๋ฐ” ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •ํ•ด์ง„ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฐธ๊ณ ๋กœ MultiValueMap์€ Map๊ณผ ์œ ์‚ฌํ•œ๋ฐ, ํ•˜๋‚˜์˜ key์— ์—ฌ๋Ÿฌ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ž๋ฐ” ์ปฌ๋ ‰์…˜์ด๋‹ค.

 

* ๋กœ๊ทธ๊ฐ€ ํ•„์š”ํ•˜๋ฉด @Slf4j์„ ์‚ฌ์šฉํ•˜์ž. ์ด๋Š” ๋กฌ๋ณต ํƒœ๊ทธ์ธ๋ฐ, ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์„ ์–ธํ•ด์ค€๋‹ค. 

private static final org.slf4j.Logger log = 
org.slf4j.LoggerFactory.getLogger(RequestHeaderController.class);

 

์ถ”๊ฐ€์ ์œผ๋กœ ์‚ฌ์šฉํ•œ @์–ด๋…ธํ…Œ์ด์…˜

  • @RequestHeader MultiValueMap headerMap : ๋ชจ๋“  HTTP ํ—ค๋”๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  • @RequestHeader("host") String Host : ํŠน์ • HTTP ํ—ค๋”๋ฅผ ์ด๋ฆ„์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.
  • @CookieValue("myCookie", required = false) : ํŠน์ • ์ฟ ํ‚ค๋ฅผ ์กฐํšŒ. required๋Š” ํ•„์ˆ˜ ๊ฐ’ ์—ฌ๋ถ€(์—†์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ)
@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";
    }
}

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ