본문 바로가기
Programming/SpringBoot

[Spring Boot] RESTful Service 강의 정리 (5) - REST API Version 관리(URI, Request Parameter, Header, Mime Type 이용)

by prinha 2020. 8. 24.
반응형

 

 

[Spring Boot] RESTful Service 강의 정리 (4) - Response 데이터 제어를 위한 Filtering / 사용자 조회 예제

[Spring Boot] RESTful Service 강의 정리 (3) - Response 데이터 형식 변환(XML format) [Spring Boot] RESTful Service 강의 정리 (3) - 유효성체크(Validation API)와 다국어처리(LocaleResolver) [Spring Boot..

prinha.tistory.com

developers.facebook.com/docs/apps/versions

developers.kakao.com/product


버전 관리라는 것은 단순히 사용자에게 보여지는 화면만을 제어하는 것이 아니라

REST API의 설계가 변경되거나, application의 구조가 바뀔 때에도 버전을 변경해서 사용해야한다.

또한 사용자에게도 어떠한 API의 버전을 사용하는지 적절히 명시해주어야한다.

 

Versioning

 1) URI Versioning - 일반 브라우저에서 실행 가능

    - Twitter

 

 2) Request Parameter Versioning - 일반 브라우저에서 실행 가능

    - Amazon

 

 3) (Custom) Headers Versioning - 일반 브라우저에서 실행 불가

    - Microsoft

 

 4) Media Type Versioning - 일반 브라우저에서 실행 불가

    (a.k.a "content negotiation" or "accept header")

    - GitHub

 

Factors

 - URI Pollution

 - Misuse of HTTP Headers

 - Caching

 - Can we execute the request on the browser?

 - API Documentation

 

No Perfect Solution!


(공통) User를 상속받는 UserV2 클래스 생성

// UserV2

package com.example.restfulwebservice.user;
import com.fasterxml.jackson.annotation.JsonFilter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonFilter("UserInfoV2")
public class UserV2 extends User{
    private String grade; // 회원 등급

}

 

 

1) URI에 버전을 포함시켜 버전을 관리하는 방법

  - AdminUserController  version1

// 개별 사용자 조회
// GET방식 /admin/users/1 - /admin/v1/users/1 (버전관리 URI)
@GetMapping("/v1/users/{id}")
public MappingJacksonValue retrieveUserV1(@PathVariable  int id){
    User user = service.findOne(id);

    if(user==null){
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }

    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
            .filterOutAllExcept("id","name","password","ssn");

    FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo",filter);

    MappingJacksonValue mapping = new MappingJacksonValue(user);
    mapping.setFilters(filters);

    return mapping;
}

 

  - AdminUserController  version2

// GET방식 /admin/users/1 - /admin/v2/users/1 (버전관리 URI)
@GetMapping("/v2/users/{id}")
public MappingJacksonValue retrieveUserV2(@PathVariable  int id){
    User user = service.findOne(id);

    if(user==null){
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }

    // 반환받았던 데이터를 UserV2로
    UserV2 userV2 = new UserV2();

    // BeanUtils : 스프링 프레임워크에서 제공하는 bean들간의 작업을 도와주는 클래스
    BeanUtils.copyProperties(user,userV2);
    userV2.setGrade("VIP");

    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
            .filterOutAllExcept("id","name","joinDate","grade");

    FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2",filter);

    MappingJacksonValue mapping = new MappingJacksonValue(userV2);
    mapping.setFilters(filters);

    return mapping;
}

 

 

 

2) Request Parameter를 이용한 버전 관리 방법 params =""

// AdminUserController

    @GetMapping(value = "/users/{id}/",params = "version=1")
    public MappingJacksonValue retrieveUserV1(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","password","ssn");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

        return mapping;
    }


    @GetMapping(value="/users/{id}/", params = "version=2")
    public MappingJacksonValue retrieveUserV2(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        // 반환받았던 데이터를 UserV2로
        UserV2 userV2 = new UserV2();

        // BeanUtils : 스프링 프레임워크에서 제공하는 bean들간의 작업을 도와주는 클래스
        BeanUtils.copyProperties(user,userV2);
        userV2.setGrade("VIP");

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","joinDate","grade");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(userV2);
        mapping.setFilters(filters);

        return mapping;
    }

?version=1

 

?version=2

 

 

 

3) header 값을 이용해 버전을 관리하는 방법

// AdminUserController

    @GetMapping(value="/users/{id}", headers="X-API-VERSION=1")
    public MappingJacksonValue retrieveUserV1(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","password","ssn");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

        return mapping;
    }


    @GetMapping(value="/users/{id}", headers="X-API-VERSION=2")
    public MappingJacksonValue retrieveUserV2(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        // 반환받았던 데이터를 UserV2로
        UserV2 userV2 = new UserV2();

        // BeanUtils : 스프링 프레임워크에서 제공하는 bean들간의 작업을 도와주는 클래스
        BeanUtils.copyProperties(user,userV2);
        userV2.setGrade("VIP");

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","joinDate","grade");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(userV2);
        mapping.setFilters(filters);

        return mapping;
    }

  

Header 값을 입력하지않으면 오류 발생

 

X-API-VERSION=1

 

X-API-VERSION=2

 

 

 

4) Mime Type을 이용해 버전을 관리하는 방법

  - Multipurpose Internet Mail Extensions : 간단히 말해 파일 변환

  - 인코딩 : 바이너리 파일에서 텍스트 파일로 변환
  - 디코딩 : 텍스트 파일에서 바이너리 파일로 변환

// AdminUserController

    @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv1+json")
    public MappingJacksonValue retrieveUserV1(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","password","ssn");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

        return mapping;
    }


    @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv2+json")
    public MappingJacksonValue retrieveUserV2(@PathVariable  int id){
        User user = service.findOne(id);

        if(user==null){
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        // 반환받았던 데이터를 UserV2로
        UserV2 userV2 = new UserV2();

        // BeanUtils : 스프링 프레임워크에서 제공하는 bean들간의 작업을 도와주는 클래스
        BeanUtils.copyProperties(user,userV2);
        userV2.setGrade("VIP");

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id","name","joinDate","grade");

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2",filter);

        MappingJacksonValue mapping = new MappingJacksonValue(userV2);
        mapping.setFilters(filters);

        return mapping;
    }

 

application/vnd.company.appv1+json

 

application/vnd.company.appv2+json

 

 

 

반응형