• Hateoas(Hypermedia as the Engine of Application State)
    • 현재 리소스와 연관된 자원 상태 정보를 제공
    • WebMvcLinkBuilder

 

RESTful API를 사용하는 클라이언트가 서버와 동적인 상호작용이 가능하도록 하는 것을 HATEOAS라고 한다.

서버는 현재 리소스와 연관된 링크 정보를 클라이언트에게 제공하고
클라이언트는 연관된 링크 정보를 바탕으로 리소스에 접근할 수 있게 된다.

 

요청 URI이 변경되더라도 동적으로 생성된 URI을 사용할 수 있기에 코드를 변경하지 않아도 되는 편리함을 제공한다.

 

 

 // UserController.java
 
    @GetMapping("/users/{id}")
    public EntityModel<User> retrieveUser(@PathVariable int id) {
        User user = service.findOne(id);

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

        // HATEOAS
        EntityModel<User> model = new EntityModel<>(user);
        WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); // user가 사용할 수 있는 추가적 정보
        model.add(linkTo.withRel("all-users")); // 링크작업

        return model;
    }

WebMvcLinkBuilder를 이용하여 링크를 추가해 주었다. 스프링 2.2 이전에서는 ControllerLinkBuilder를 이용한다고 한다.

 

 

요청결과를 확인해 보면 링크가 추가된것을 볼 수 있다.

 

 

'Study > Restful' 카테고리의 다른 글

[Restful] API 버전관리  (0) 2021.03.05
[Restful] 데이터 필터링  (0) 2021.03.04
[Restful] 유효성 체크 및 다국어 처리  (0) 2021.03.04
[Restful] Http status Code제어  (0) 2021.02.14
[Restful] 사용자 목록 조회 및 등록  (0) 2021.02.14

 

  • Restful API 버전관리
    • URL을 이용한 버전관리
    • Request 파라미터를 이용한 버전 관리
    • Header값을 이용한 버전관리
    • Mime Type을 이용한 버전관리

 

REST API가 변경되거나 추가되는 경우가 있기 때문에 API 버전관리가 필요하다.

버전관리에는 크게 4가지 방법이 있다.

URL과 Request 파라미터를 이용한 방법은 일반브라우저에서 실행할 수 있으며

헤더값과 미디어타입을 이용한 방법은 일반브라우저에서 실행이 불가하여

포스트맨과 같은 별도의 프로그램을 이용해야 한다.

 

 

먼저 기존의 User를 상속받아 UserV2 객체를 생성하였다.

@Data
@RequiredArgsConstructor
@JsonFilter("UserInfoV2")
public class UserV2 extends User {

    private String grade;
    
}

 

URI을 이용한 버전관리

/{버전}/users/{id} 형식으로 URL을 받아 버전별 정보를 설정해 주었다. 

 // AdminUserController.java
 
    @GetMapping("/v1/users/{id}") // URI를 이용한 버전관리
    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", "joinDate"); // 포함시키고자 하는 필터

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter);
        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

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

        // User -> User2
        UserV2 userV2 = new UserV2();
        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;
    }

 

 

 

v1 호출
v2 호출

 

 

 

Request 파라미터를 이용한 버전 관리

params={버전정보} 을 설정하여 파라미터로 버전정보를 받도록 하였다.

 // AdminUserController.java
 
    @GetMapping(value = "/users/{id}/", params = "version=1") // request 파라미터를 이용한 버전관리
    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", "joinDate"); // 포함시키고자 하는 필터

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

        // User -> User2
        UserV2 userV2 = new UserV2();
        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;
    }

 

기존 URL에 파라미터를 붙여 버전별 API를 호출할 수 있다.

 

 

헤더값을 이용한 버전관리

headers={버전정보} 을 설정하여 헤더값으로 버전정보를 받도록 하였다.

 // AdminUserController.java
 
   @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", "joinDate"); // 포함시키고자 하는 필터

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

        // User -> User2
        UserV2 userV2 = new UserV2();
        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;
    }

포스트맨을 통해 헤더값을 설정해주고 API를 실행하였다. 헤더값을 설정하지 않고 요청하면 404에러가 발생하게 된다.

 

 

 

Mime Type을 이용한 버전관리

produces={버전정보} 을 설정하여 헤더값으로 버전정보를 받도록 하였다.

 // AdminUserController.java
 
   @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", "joinDate"); // 포함시키고자 하는 필터

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

        // User -> User2
        UserV2 userV2 = new UserV2();
        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;
    }

 

 

 

github

'Study > Restful' 카테고리의 다른 글

[Restful] HATEOAS  (0) 2021.03.21
[Restful] 데이터 필터링  (0) 2021.03.04
[Restful] 유효성 체크 및 다국어 처리  (0) 2021.03.04
[Restful] Http status Code제어  (0) 2021.02.14
[Restful] 사용자 목록 조회 및 등록  (0) 2021.02.14
  • 데이터 필터링
    • @JsonIgnore, @JsonIgnoreProperties
    • @JsonFilter

 

비밀번호나 주민번호 같은 중요한 정보를 노출하면 안되기에

데이터를 필터링하여 필요한 정보만 제공할 수 있다.

 

@JsonIgnore, @JsonIgnoreProperties 를 이용한 데이터 필터링
// User.java

@Data
@AllArgsConstructor
public class User {
    private Integer id;

    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    private String name;

    @Past // 과거 데이터만 가능
    private Date joinDate;

    @JsonIgnore
    private String password;

    @JsonIgnore
    private String ssn;
}

@JsonIgnore 어노테이션을 이용하여 개별적으로 필터링 설정을 할 수 있다.

 

@Data
@AllArgsConstructor
@JsonIgnoreProperties(value = {"password", "ssn"})
public class User {
    private Integer id;

    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    private String name;

    @Past // 과거 데이터만 가능
    private Date joinDate;

    //@JsonIgnore
    private String password;

    //@JsonIgnore
    private String ssn;
}

@JsonIgnoreProperties 를 이용하는 방법도 있다. 블록안에서 한번에 설정해 줄 수 있다.

 

 

필터링 적용 전
필터링 적용 후

 

@JsonFilter 를 이용한 데이터 필터링

@JsonFilter를 이용하는 방법도 있다. 포함시키고자 하는 필드만 추가해주어 원하는 값만 제공할 수 있다.

@Data
@AllArgsConstructor
@JsonFilter("UserInfo")
public class User {
    private Integer id;

    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    private String name;

    @Past // 과거 데이터만 가능
    private Date joinDate;

    //@JsonIgnore
    private String password;

    //@JsonIgnore
    private String ssn;
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/admin")
public class AdminUserController {

    private final UserDaoService service;

    @GetMapping("/users")
    public MappingJacksonValue retrieveAllUsers() {

        List<User> users = service.findAll();

        SimpleBeanPropertyFilter filter
                = SimpleBeanPropertyFilter.filterOutAllExcept("id", "name", "joinDate", "password"); // 포함시키고자 하는 필터

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter);
        MappingJacksonValue mapping = new MappingJacksonValue(users);
        mapping.setFilters(filters);


        return mapping;
    }

    @GetMapping("/users/{id}")
    public MappingJacksonValue retrieveUser(@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", "joinDate"); // 포함시키고자 하는 필터

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter);
        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

        return mapping;
    }
}

 

 

 

 

github

'Study > Restful' 카테고리의 다른 글

[Restful] HATEOAS  (0) 2021.03.21
[Restful] API 버전관리  (0) 2021.03.05
[Restful] 유효성 체크 및 다국어 처리  (0) 2021.03.04
[Restful] Http status Code제어  (0) 2021.02.14
[Restful] 사용자 목록 조회 및 등록  (0) 2021.02.14
  • 유효성 체크를 위한 Validation 사용
    • handleMethodArgumentNotValid를 재정의 하여 오류메세지 출력
  • 다국어 처리를 위한 Internationalization 구현
    • LocaleResolver

 

 

 

유효성 체크를 위한 Validation

Rest Api 에서도 Validation을 추가하여 유효성을 체크 할 수 있다.

//User.java

@Data
@AllArgsConstructor
public class User {
    private Integer id;

    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    private String name;

    @Past // 과거 데이터만 가능
    private Date joinDate;
}
//UserController.java

	@PostMapping("/users")
  	public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        // Post 메서드, put 과 같은 http 메서드에서 오브젝트형태의 데이터를 받기위해 매개변수 타입에 @RequestBody 선언해야함
        User saveUser = service.save(user);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest() // 현재 요청되어진 request 값 사용
                .path("/{id}") // 반환시켜주고자 하는 path
                .buildAndExpand(saveUser.getId()) // 가변번수 id에 새롭게 만들어진 id값 지정
                .toUri(); // uri 형태로 변경

        return ResponseEntity.created(location).build();
    }
// CustomizedResponseEntityExceptionHandler.java

    // 유효성 검사 오류가 있을때 오류메세지를 출력하기 위한 메서드
    @Override // 재정의
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(),
                "Validation Failed", ex.getBindingResult().toString());
        return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST);
    }

위와 같이 User 클래스 및 컨트롤러에 validation 체크를 추가하고

handleMethodArgumentNotValid 메서드를 재정의 하여 오류메세지를 보여줄 수 있다.

 

 

유효성 오류가 있을 때 details에서 상세 정보를 확인할 수 있다. 

handleMethodArgumentNotValid에서 메세지를 "Validation Failed" 로 변경하여

정보를 바로 파악할 수 있도록 하였다.

 

name 에 message 를 정의 해주어 더 간단하게 오류정보를 출력할 수 있다.

 

 

다국어 처리를 위한 Internationalization 구현
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }


    // 스프링부트가 초기화 될 때 정보에 해당하는 값이 메모리에 올라가서 다른 클래스에서 사용할 수 있다.
    @Bean
    public SessionLocaleResolver localResolver(){
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        localeResolver.setDefaultLocale(Locale.KOREA);

        return localeResolver;
    }
}

먼저 다국어 처리에 필요한 Bean이 등록되도록 추가시켜 준다.

 

//application.yml

spring:
  messages:
    basename: messages

application.yml에 사용할 다국어 파일을 설정한다.

 

// messages.properties
greeting.msg=안녕하세요

// messages_en.properties
greeting.msg=Hello

// messages_fr.properties
greeting.msg=Bonjour

resource 밑에 다국어 properties를 추가해 준다.

 

// HelloWorldController.java   
   
    @Autowired
    private MessageSource messageSource;

    @GetMapping(path = "/hello-word-internationalized")
    public String helloWorldInternationalized(@RequestHeader(name="Accept-Language", required = false) Locale locale){
        return messageSource.getMessage("greeting.msg", null ,locale);
    }

컨트롤러에 다국어 처리를 위한 의존성 주입 및 메서드를 추가시켜준다.

 

 

header 에 Accept-Language를 설정해주어 다국어를 확인할 수 있다.

 

 

'Study > Restful' 카테고리의 다른 글

[Restful] HATEOAS  (0) 2021.03.21
[Restful] API 버전관리  (0) 2021.03.05
[Restful] 데이터 필터링  (0) 2021.03.04
[Restful] Http status Code제어  (0) 2021.02.14
[Restful] 사용자 목록 조회 및 등록  (0) 2021.02.14
  • Http status Code 제어
    • Servleturicomponentsbuilder로 uri를 생성하여 id 반환
    • ResponseEntity 응답코드 생성
  • Exception Handing
    • ControllerAdvice를 이용한 에러 처리

 

 

사용자 목록, 조회 및 등록 api를 실행하여 서버로 부터 200응답 코드를 받아 정상적으로 실행된 것을 확인하였다.

목록과 등록 api는 둘 다 localhost:8088/users 로 각각 get, post로 요청된다. 

용도에 맞춰 응답코드를 다르게 사용하는 것이 좋다. 

 

 

사용자 등록 - Servleturicomponentsbuilder 를 이용한 uri 반환
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // Post 메서드, put 과 같은 http 메서드에서 오브젝트형태의 데이터를 받기위해 매개변수 타입에 @RequestBody 선언해야함
        User saveUser = service.save(user);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest() // 현재 요청되어진 request 값 사용
                .path("/{id}") // 반환시켜주고자 하는 path
                .buildAndExpand(saveUser.getId()) // 가변번수 id에 새롭게 만들어진 id값 지정
                .toUri(); // uri 형태로 변경

        return ResponseEntity.created(location).build();
    }

등록에 성공한 경우, 생성한 id를 반환 시켜주도록 하였다. id를 반환하면 서버에 또 한번 물어보지 않아도 되기에 더 효율적이다.

servleturicomponentsbuilder 을 이용하면 사용자에게 특정값을 포함한 uri를 전달할 수 있다.

ResponseEntity는 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스이며 응답 상태코드를 설정할 수 있다.

uri와 함께 201 create 코드를 반환하도록 하였다.

 

201 응답코드를 받았으며 생성된 id값인 4가 반환되는 것을 볼 수 있다.

 

 

사용자 조회 - Exception Handling
    @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable int id) {
        User user = service.findOne(id);

        if (user == null) {
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }
        return user;
    }
// HTTP Status Code
// 2XX -> OK
// 4XX -> Client 측 적절하지 않은 요청(존재하지 않는 리소스, 권한 등)
// 5XX -> Server 측 문제(프로그램상 문제, 리소스 연결 문제 등)
@ResponseStatus(HttpStatus.NOT_FOUND) // 400번대 오류로 전송하기 위해
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

 

존재하지 않는 사용자를 요청하였을 때 200코드가 아닌, 적절한 Exception을 발생시키도록 하였다.

에러 처리를 위한 클래스를 따로 생성하여 사용자의 id가 없을 경우 404에러를 전송하도록 하였다.

 

존재하지 않는 사용자 조회 요청 시 404 코드를 확인 할 수 있다.

trace 통해 예외발생에 원인되는 코드라인 노출되는데 보안상의 문제가 있을 수 있기에 보완이 필요하다.

 

 

ControllerAdvice 를 이용한 예외클래스 생성
@RestController
@ControllerAdvice// 모든 컨트롤러가 실행될때 반드시 빈이 실행됨
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handlerAllException(Exception ex, WebRequest request) {
        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));

        return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); // 일반화된 오류 500번
    }

    @ExceptionHandler(UserNotFoundException.class)
    public final ResponseEntity<Object> handlerUserNotFoundException(Exception ex, WebRequest request) {
        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));

        return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); // 404 에러
    }
}
@Data
@AllArgsConstructor // 모든생성자
@NoArgsConstructor // default 생성자
public class ExceptionResponse {
    private Date timestamp;
    private String message;
    private String details;
}

ControllerAdvice 를 이용하여 exception를 처리한 결과이다.

전달하고자 하는 정보들만 반환된 것을 확인할 수 있다.

 

 

 

github

'Study > Restful' 카테고리의 다른 글

[Restful] HATEOAS  (0) 2021.03.21
[Restful] API 버전관리  (0) 2021.03.05
[Restful] 데이터 필터링  (0) 2021.03.04
[Restful] 유효성 체크 및 다국어 처리  (0) 2021.03.04
[Restful] 사용자 목록 조회 및 등록  (0) 2021.02.14
  • 사용자 restful api 생성
    • 전체 사용자 목록
    • 개별 사용자 목록
    • 사용자 등록

기본적인 restful api를 작성하고 postman을 이용하여 결과를 확인 해 보았다.

 

Restful api 생성
@RestController
@RequiredArgsConstructor
public class UserController {

    private final UserDaoService service;

    @GetMapping("/users") // 사용자 목록
    public List<User> retrieveAllUsers() {
        return service.findAll();
    }

    @GetMapping("/users/{id}") // 사용자 조회
    public User retrieveUser(@PathVariable int id){
        return service.findOne(id);
    }

    @PostMapping("/users") // 사용자 등록
    public void createUser(@RequestBody  User user){
        // Post 메서드, put 과 같은 http 메서드에서 오브젝트형태의 데이터를 받기위해 매개변수 타입에 @RequestBody 선언해야함
        User saveUser = service.save(user);
    }
}

 

전체 사용자 목록: GET, http://localhost:8088/users

개별 사용자 조회: GET, http://localhost:8088/users/{id}

@RestController 어노테이션을 사용하여 restful api를 만든다.

 

 

사용자 전체 목록

사용자 목록을 가져오는 api를 실행한 결과이다. 상태값이 200이고 사용자 목록을 잘 가져오는 것을 볼 수 있다.

 

 

사용자 개별 조회

id가 1번에 해당하는 사용자 정보를 가져온 결과이다.

 

 

사용자 등록

사용자 등록도 마찬가지로 postman을 이용하여 확인 할 수 있었다. body에 등록 json데이터를 설정하여 전송하였다.

상태코드가 200이므로 정상적으로 등록되었다.

 

 

 

github

'Study > Restful' 카테고리의 다른 글

[Restful] HATEOAS  (0) 2021.03.21
[Restful] API 버전관리  (0) 2021.03.05
[Restful] 데이터 필터링  (0) 2021.03.04
[Restful] 유효성 체크 및 다국어 처리  (0) 2021.03.04
[Restful] Http status Code제어  (0) 2021.02.14

+ Recent posts