Spring Boot 3 & Spring Framework 6 - Section 12 : REST API 고급 기능

TopOfTheHead·2024년 8월 21일

Udemy학습-Spring & React

목록 보기
10/20
post-thumbnail

학습할 내용

  • REST API 핵심 기능
    • URI의 Resource에 대해 알아보기
      (ex : /users, /users/{id}/posts.. etc )

    • 상호작용하는 Http Request Method에 대해 알아보기
      (ex : GET, POST, PUT, DELETE.. etc)

    • JSON format의 Request와 Response의 구조 정의 학습하기
      。Http Body와 Http Header 구조 학습

    • Request 시 알맞은 Http Status Code을 설정하여 Client에 Status Code 반환하기
      (ex : 200, 404, 500 ... etc)

    • Security , Validation, Exception Handling 학습하기.


  • REST API 고급 기능
    • Internationalization :
      。사용자 언어에 따라 REST API Response를 정의. ( i18n )

    • HATEOAS

    • Versioning :
      。여러 버전의 REST API 생성

    • Documentation :
      。REST API에 관한 문서 생성( Open API, Swagger )

    • Content Negotiation :
      。Client가 XML Response를 원하는 경우.

    • Static Filtering

    • Dynamic Filtering

    • Monitoring


  • REST API를 DB에 연결하는 방법
    • JPA와 Hibernate를 활용하여 H2 , MySQL DB 연결하기


  • 번외
    • Spring Security로 Authentication 구현

REST API Documentation

  • API 문서 : API 정보
    。설계된 API를 개발자가 파악하여 사용하도록 명확한 가이드라인을 제공하는 역할을 수행.
    ▶ Web application 개발 시 백엔드팀이 구축한 API를 swagger로 문서화하여 프론트엔드로 전달하여 API logic의 이해를 높이는 역할을 수행.
    ex) 카카오 REST API Document

  • REST API Documentation Specification 종류
    。REST API의 문서화를 수행하기 위한 API Specification format
    ▶ REST API를 문서화하여 개발자가 쉽게 이해하고 테스트할 수 있도록함.

    。RESTful API를 정해진 규칙에 맞게 .json or .yaml 형식으로 정의
    • Swagger Specification :
      。초창기 REST API 문서화 표준
      ▶ RESTful API Design에 대한 정의.

      。JSON , YAML을 지원.
      。현재 OAS ( Open API specification )으로 이름이 변경되어 대체되었고, Swagger라는 키워드는 Documentation tool의 일종으로 사용됨.

    • OAS ( Open API Specification ) :
      。Swagger Specification 기반으로 발전된 형태의 API Specification format.
      ▶ 현재 API Documentation의 공식 표준

      。JSON , YAML을 지원.
      。누구나 사용하도록 API endpoint가 개방됨.
      ▶ 이를 통해 API Source code를 보거나 추가적인 API Document를 참고할 필요 없이 서비스를 이해할 수 있음.


  • REST API Documentation Tool
    • Swagger :
      。개발한 RESTful API를 OAS 사양으로 API의 Specification을 정의하는 Open API Document를 자동으로 생성하는 framework.
      ▶ 해당 REST API가 어떤 logic을 수행하고, logic을 수행하기위해 어떤 value를 요청하며, 이에 따른 응답값은 무엇인지 정리하여 문서화.

      。SwaggerUI를 통해 OAS의 API Document에 대해 시각화 및 이용할 수 있는 기능을 추가 제공.

      SwaggerUI :
      。OAS의 REST API Document와 상호작용하여 Web UI로 볼 수 있는 Application.
      。REST API Document를 시각화하는 코드가 자동 생성되고, 간편하게 API를 테스트할 수 있으며 API Document를 직접 작성할 필요가 없음.

REST API Document 생성 시 고려사항

  • REST API 개발 시 Client에게 전달해야하는 사항
    。Resource(자원)
    。Actions(수행 작업)
    。Constraints와 Validations를 포함한 HTTP Request & Response Message Structure
    ▶ Client가 다음 사항을 잘 알고 있어야 REST API를 사용 가능하므로.

  • Accuracy : REST API Document가 최신으로 Up-to-date 되었는지, 코드와 정확하게 동기화되었는지 확인하기

  • Consistency : 모든 REST API Document가 일관된 형식을 유지하는지 보장하기
    。일반적인 기업에서는 수백개의 REST API가 존재할 수 있으므로.

REST API를 다루는 2가지 Option

  • REST API를 관리하는 API Document 또는 HTML 파일을 수동으로 관리.
    。 수동으로 관리하는 경우 코드와 Document의 동기화를 유지관리해야하는 문제가 존재.

  • 코드를 사용하여 API Document를 생성
    。현재 과정에서 할 것!

API 문서 자동생성하기
。문서화 도구 Swagger를 통해 OAS( Open API Document )를 자동 생성.

  • springdoc-openapi 라이브러리 Dependency 추가.
    springdoc-openapi : spring-doc 사이트
    。Spring Boot 프로젝트를 활용한 API Document 생성을 자동화하는 library
    。 Runtime 시 Spring Configuration, Class Structure, 다양한 Annotation들을 기반으로 Spring Application을 검사하여 API의 의미를 추론.
    。JSON, YAML 및 HTML format page로 문서를 자동 생성.
    。springdoc-openapi v2.6.0 기준 OpenAPI 3, Spring-boot v3, swagger-ui를 지원.
    。localhost:8080/swagger-ui.html을 통해 swagger-ui Page를 접속
<dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.6.0</version>
</dependency>

。Dependency를 pom.xml에 추가한 후 localhost:8080/swagger-ui.html을 검색하면 도출되는 Swagger UI를 통한 REST API Document를 확인 가능.

。Swagger UI page에서는 REST API Application에서 Controller Method를 통해 Mapping한 Resource URL을 Controller class 별로 아래와 같이 자동으로 문서화.
▶ Swagger UI를 통해 간단하게 REST API의 명세를 확인할 수 있으며, Request와 Response Test를 진행 가능.

OpenAPI definition
。 REST API에 대한 OAS의 API definition와 swagger-UI를 통한 시각화 제공.
▶ /v3/api-docs를 누를 경우 해당 REST API에 관한 OpenAPI definition을 JSON format의 API Document로 확인 가능.

。OAS Definition을 통해 Client가 해당 API의 Resource, Action, HTTP Message Structure에 관한 정보를 손쉽게 알 수 있다.

。openapi : OAS의 Version.
。info : API Document를 설명하는 metadata
。servers : 테스트가 가능한 서버에 관한 정보
。paths : REST API Application에서 Mapping한 Resource URL에 대한 정보
。components : REST API에서 참조하는 Bean의 schema 정보

Content Negotiation

。Client와 Server 간 HTTP Transaction에서 Client가 header를 설정하여 원하는 Language 또는 Data format( JSON, XML , HTML 등 )을 협상하여 Server가 적절한 Response를 제공하는 과정.

  • 단일 URL에 대한 단일 Resource가 부여되지만, header를 전송하여 Client가 선호하는 다양한 Language, Data Format을 가질 수 있다.
    。Client가 원하는 설정을 가진 header를 Server로 전송 시 Server는 해당 정보를 기반으로 적절한 response를 전달.

  • Accept header :
    。Client가 원하는 Contents type ( data type or MIME type )을 서버에 전달하는데 사용.
    。JSON, XML format 등의 data type을 설정.
    ▶ application/xml 또는 application/json으로 Server에 전달 시 XML or JSON response을 받을 수 있음.

    Accept: text/html;q=0.8, application/json;q=1.0
    。q값 ( [ 0.0,1.0 ] )을 설정하여 우선순위를 지정.
    ▶ JSON을 가장 선호하고 html은 JSON이 불가능할 경우 사용하여 Response.

  • Accept-Language header :
    Accept-Language: en-US,en;q=0.9,ko-KR;q=0.8,ko;q=0.7
    。Client가 원하는 Language를 Server에 전달하는 header.
    ▶ Client가 선호하는 Language( en, nl, fr )으로 설정하여 Request URL을 Server에 전달 시 Server는 해당 Language의 문서를 제공.

    。Client가 선호하는 자연어(natural language), Locale를 지시.
    Locale : 사용자의 언어, 국가 및 인터페이스에서 선호하는 사항을 지정한 매개변수의 모임.

    Accept-Language: en-US,en;q=0.9,ko-KR;q=0.8,ko;q=0.7
    • Accept-Language를 활용한 HTTP Transaction 예제
      。HTTP Request
    GET /hello HTTP/1.1
    Host: example.com
    Accept-Language: en-US

    。HTTP Response

    HTTP/1.1 200 OK
    Content-Type: text/plain
    Hello!

    .

    • Header Accept과 Content-Type의 차이점
      。둘 다 Data type ( MIME )을 다루는 header.

    • Accept :
      。Client가 서버에게 Accept Header로 특정 data type을 설정하여 전송 시 Client가 보낸 Header의 특정 data type으로만 response 해야하는 규약 설정.

    • Content-Type :
      。현재 전송하는 data가 어떤 type인지만 설명.

      MIME(Multipurpose Internet Mail Extensions) :
      。file 또는 data의 type을 지정하는 표준.
      ▶ 웹에서 문서, 이미지, 오디오, 비디오 등 다양한 형식의 데이터를 올바르게 전송하고 해석하기 위해 사용

  • HTTP Request에 Accept Header를 포함하여 Server에 전송하여 Resource에 XML로 Response 받기.
    。Application을 Content Negotiation하기 위한 특정 Respresentation ( = Data type : XML )을 활성화 하기 위해서는 해당 Representation을 정의하는 dependency를 추가하는것으로 간단하게 활성화가 가능.
    • jackson-dataformat-xml dependency 추가
      jackson-dataformat-xml :
      。XML data를 직렬화( Serializaion ) , 역직렬화( Deserializaion )하느데 사용하는 확장 모듈
      ▶ XML format으로 객체를 변환하거나 XML을 객체로 변환.
    <dependency>
                <groupId>com.fasterxml.jackson.dataformat</groupId>
                <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>

    。 이후 API Tester에서 HEADERS를 Accept으로 설정 후 , application/xml 입력 후 전송하면 Http Response Body에서 JSON이 아닌, XML format으로 반환.


    ▶ 동일한 Resource에 대하여 단일 Resource URL을 가지지만, JSON, XML 등의 다양한 Data type의 Representation을 가질 수 있음!



  • HTTP Request에 Accept-Language Header를 포함하여 Server에 전송하여 Resource에 다른 Language로 Response 받기.
    Internationalization : 국제화(i18n)
    。Message를 언어별로 다른 Message를 보이도록 한다.
    ▶ 다른 사용자 언어에 대해 사용자 정의를 할 경우 사용한다.

    。총 18개의 character로 구성되어있어 i18n이라고 한다.
    。기본적으로 HTTP Request의 Accept-Language Header 기반으로 처리됨.
    • 각각의 언어를 정의하는messages.properties파일 생성.
      。src/main/resources 경로에 생성.
      。Spring Boot에서 Internationalization 활용 시 반드시 messages.properties라는 이름으로 파일을 생성하여 정의해야한다.
      ex ) 네덜란드어로 언어 이름 생성 시 messages_nl.properties 이름으로 파일을 생성.
       // messages.properties
    greeting=Hello!
    welcome.message=Welcome
      // messages_ko.properties
    greeting=Hello!
    welcome.message=Welcome korean
      // messages_nl.properties
    greeting=Hello!
    welcome.message=Welcome dutch
      • messages.properties :
        。Message의 국제화( i18n, internationalization ) 기능을 제공하면서 다국어 처리하는 방법을 정의.
        ▶ Spring에서 다국어 지원을 할때 주로 사용.


    • Controller Class에 MessageSource 객체 생성하기.
      。MessageSource instance를 생성 후 생성자기반 의존성주입
    private MessageSource messageSource;
        private UserDaoService userDaoService;
        // @Autowired  생성자 기반 Denpendency Injection.
        public UserResource(UserDaoService userDaoService, MessageSource messageSource) {
            this.userDaoService = userDaoService;
            this.messageSource = messageSource;
        }
    • Controller Method에 MessageSource객체.getMessage()를 활용하여 해당 언어의 문자열 반환받기.
      LocaleContextHolder.getLocale() :
      。Client에서의 HTTP Request에 포함한 Header ( = Accept-Language header )와 연관된 Locale를 반환하며 해당하지 않는 경우 System default Locale를 반환.

      Locale : 사용자의 언어, 국가 및 인터페이스에서 선호하는 사항을 지정한 매개변수의 모임.
    @GetMapping(path="/hello-world-internationalized")
        public String helloWorldInternationalized(){
            Locale locale = LocaleContextHolder.getLocale();
            return messageSource.getMessage("welcome.message", null,"Default Message", locale);
        }


    。API Tester에서 HEADERS를 Accept-Language로 설정 후 ko로 입력하여 Mapping된 URL로 HTTP Request 전송 시 Accept-Language Header에 연관된 Locale에 따른 messages_ko.properties의 코드값이 도출.
    ▶ Client가 Accept-Language Header에 정의한 특정 언어로 Response를 반환 가능.

    • MessageSource :
      。Spring Framework에서 국제화( i18n, internalization )을 지원하는 interface.
      ▶ messages.properties를 관리하고, 언어 및 지역에 맞는 message를 제공하는역할을 수행.
      • MessageSource객체.getMessage(message.properties변수이름 , default message , default message, locale)
        。locale에 해당하는 message.properties의 message를 return.

REST API Versioning

용어 정리

{
        "name" : "Bob Charlie"
}
{
        "name" : {
        	"firstName" : "Bob",
            "lastName" : "Charlie"
       }
}

。REST API에서 기존의 Http Response JSON format의 구조를 "name"를 "firstName" , "lastName"로 나누는 구조로 변경하여 breaking change를 하는 경우, REST API를 사용하는 Client가 변경된 Response에 맞게 Code를 Update 해야하는 번거로움이 발생함.

breaking change :
。기존 code와 호환성을 깨뜨리는 변경을 하는 것.
。ex : 회사가 API를 변경하여 올바르게 작동하는 API Integration을 망가뜨릴때.

API Integration :
。활성화된 서로 다른 Application간의 API를 통한 연결.

  • Versioning : 버전관리
    。API가 breaking change하더라도 Client의 사용성을 유지하도록 기존 Version을 사용하거나 새로운 유형의 API가 추가될 경우 Client가 원할 때 새 버전의 API로 변경할 수 있는 유연성을 확보.
    。Versioning에 관해 완벽한 Sollution은 없으며, 각 Versioning 기법의 장단점을 따져서 사용해야한다.
    。각 기업체의 REST API는 반드시 일관된 Versioning만을 사용해야함.
    ▶ 하나의 기업에서 하나의 Versioning으로 여러 프로젝트와 Application에 활용.

    。지속적 통합 ( CI : Continuous Integration ) :
    Versioning System에 대한 새로운 코드를 작성하는 등의 변경 사항을 정기적으로 Repository에 Commit하여 통합함으로써 모든 사람에게 동일 작업 기반을 제공

    Versioning의 방법 종류
    。breaking change 발생 시 취할수있는 대응.

    • URL Versioning
      。각각의 Version에 대해 다른 url을 Mapping하여 사용.
    #1 Versioning - URL : ex) twitter
    http://localhost:8080/v1/person
    http://localhost:8080/v2/person
    • Request Parameter Versioning
      。동일한 URL에 대해 각각의 Request Parameter 값으로 Version을 지정.
    #2 Versioning - Request Parameter : ex) Amazon
    http://localhost:8080/person?version=1
    http://localhost:8080/person?version=2
    • Header Versioning
      。동일한 URL에 대해 사용자가 정의한 header로 version을 호출.
    #3 Versioning - (Custom) headers : ex) Microsoft
    동일 URL / headers=[X-API-VERSION=1]
    동일 URL / headers=[X-API-VERSION=2]
    • Media / MIME Type Versioning
      。MIME : file 또는 data의 type을 지정하는 표준.
    #4 Versioning - Media type
    동일 URL / produces=application/vnd.company.app-v1+json
    동일 URL / produces=application/vnd.company.app-v2+json


  • REST API Versioning 방법 결정시 고려사항
    • URL Pollution :
      。URL Versioning, Request Parameter Versioning의 경우 새로운 버전마다 URL을 생성하므로 URL pollution이 많이 발생!
      。headers Versioning , Media type Versioning은 동일 URL 사용으로 URL pollution이 상대적으로 적다.

    • HTTP Header의 오용 :
      。HTTP Header를 Versioning에 사용하면 발생.
      ▶ Header Versioning, Media type Versioning은 HTTP Header를 오용.

    • Caching
      。Caching은 일반적으로 URL 기반으로 작동하므로, headers Versioning , Media type Versioning은 URL을 구분할 수 없기에 Caching할때마다 Header를 확인해야한다.
      • Cache :
        。동일 데이터에 반복 접근 시 결과를 빠르게 이용하고자 성능이 좋거나 가까운 곳에 저장하는것.
        。컴퓨터 성능을 향상하기 위해 사용하는 메모리

      • Caching :
        。해당 Cache 영역으로 데이터를 가져와서 접근하는 방식.


    • 브라우저 실행
      。URL Versioning, Request Parameter Versioning의 경우 URL을 통해 브라우저에서 간편하게 실행이 가능.
      。headers Versioning, Media type Versioning은 브라우저에서 Header를 구분해야하므로 상대적으로 까다롭다.

    • API Document
      。URL Versioning, Request Parameter Versioning의 경우 각 버전의 URL이 다르므로 API Document 생성이 편하다.
      。headers Versioning, Media type Versioning은 API Document Tool ( ex. swagger )에서 Header를 기준으로 구분한 Document 생성이 지원이 안되는 경우가 존재.

Vesioning 활용하기.

  • Version 별 Bean 생성하기.
    。Version 마다 다른 Bean을 정의하는 class를 생성.
    。Person interface는 임의로 생성함.
public class PersonV1 implements Person {
    private String name;
    public PersonV1(String name) {
        this.name = name;
    }
    public String getName(){
        return name;
    }
    @Override
    public String toString() {
        return "PersonV1{" +
                "name='" + name + '\'' +
                '}';
    }
}
>```
```java
public class PersonV2 implements Person {
    private Name name;
    public PersonV2(Name name) {
        this.name = name;
    }
    public Name getName() {
        return name;
    }
    @Override
    public String toString() {
        return "PersonV2{" +
                "name=" + name +
                '}';
    }
}
public class Name {
    private String firstname;
    private String lastname;
    public Name(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }
    public String getFirstname() {
        return firstname;
    }
    public String getLastname() {
        return lastname;
    }
}
  • #1 Versioning - URL : ex) twitter
    。각각의 버전에 대해 다른 url을 Mapping하여 사용.
http://localhost:8080/v1/person
http://localhost:8080/v2/person

。URL을 사용하여 소비자가 원하는 Version에 따른 API를 사용.

  • Versioning을 수행하는 Rest API Controller class 구축.
    。각각의 URL을 Mapping하는 Controller method마다 다른 Version의 Bean을 반환.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class VersioningPersonController {
    @GetMapping("/v1/person")
    public Person getFirstVersionOfPerson(){
        return new PersonV1("Bob Charlie");
    }
    @GetMapping("/v2/person")
    public Person getSecondVersionOfPerson(){
        return new PersonV2(new Name("Bob","Charlie"));
    }
}



。URL마다 다른 Version의 Response를 제공한다!

  • #2 Versioning - Request Parameter
    ex) Amazon
    。동일한 URL에 대해 각각의 Request Parameter 값으로 Version을 지정.
http://localhost:8080/person?version=1
http://localhost:8080/person?version=2
  • Request Parameter을 활용한 Controller class 구축
    RequestParam
    。@RequestMapping(path="" ,method = , params="") 활용
@RequestMapping(path="/person",method= RequestMethod.GET ,params="version=1")
    public Person getFirstVersionOfPersonRequestParameter(int version){
        return new PersonV1("Bob Charlie");
    }
    @GetMapping(path="/person" ,params="version=2")
    public Person getSecondVersionOfPersonRequestParameter(int version){
        return new PersonV2(new Name("Bob","Charlie"));
    }

。@RequestParam 활용

@RequestMapping(path="/person",method= RequestMethod.GET)
    public Person getVersionOfPersonRequestParameter(@RequestParam int version){
        switch(version){
            case 2:
                return new PersonV2(new Name("Bob","Charlie"));
            default :
                return new PersonV1("Bob Charlie");
        }
    }
  • #3 Versioning - (Custom) headers
    ex) Microsoft
    。동일한 URL에 대해 사용자가 정의한 header로 version을 호출.
headers=[X-API-VERSION=1]
headers=[X-API-VERSION=2]
  • Header를 활용한 Controller class 구축
@GetMapping(path="/person/header", headers = "X-API-VERSION=1")
    public Person getFirstVersionOfPersonRequestHeaders(){
        return new PersonV1("Bob Charlie");
    }
@GetMapping(path="/person/header", headers = "X-API-VERSION=2")
    public Person getSecondVersionOfPersonRequestHeaders(){
        return new PersonV2(new Name("Bob","Charlie"));
    }


。다음처럼 사용자가 임의로 정의한 이름의 header로 값을 할당하여 검색 시 header를 Mapping한 method의 header값과 비교하여 Response가 반환.

  • #4 Versioning - Media type
    ex) GitHub
    。Custom Header가 아닌 동일한 URL로 콘텐츠협상에 사용되는 Accept , Accept-Language 등의 정의된 Header에 대해서도 Versioning에 활용이 가능.
produces=application/vnd.company.app-v1+json
produces=application/vnd.company.app-v2+json
  • Controller class 구축
    。produces : Accept , Accept-Language의 Header가 전달하는 값을 Spring MVC를 통해@RequestMapping(produces="비교값")로 전달되어 전달값과 비교값을 비교하여 일치하면 메소드 실행.
    @GetMapping(path="/person/accept", produces="application/vnd.company.app-v1+json")
    public Person getFirstVersionOfPersonAcceptHeaders(){
        return new PersonV1("Bob Charlie");
    }
    @GetMapping(path="/person/accept", produces="application/vnd.company.app-v2+json")
    public Person getSecondVersionOfPersonAcceptHeaders(){
        return new PersonV2(new Name("Bob","Charlie"));
    }


。다음처럼 Accept Header를 통해 전달한 값으로 method의 produces값과 비교하여 Response가 반환.

REST API HATEOAS

  • HATESOAS (Hypermedia as the Engine of Application State) :
    。REST API의 HTTP Response에서 Resource에 관한 link( = Hypermedia )를 포함하여 Client가 동적으로 API를 탐색할 수 있도록 하는 개념.
    ▶ REST API를 이용하는 Client가 Server와 Hypermedia를 통해 동적상호작용이 가능하도록 설계하는 방식.

    。Client가 API를 Request할때, Server는 Resource를 포함한 Response와 함께 Client가 다음에 행할 추가적인 Request(= Action)에 대한 URL(= link)까지 함께 반환.
    ▶ 해당 URL를 통해 Client는 Application State를 다음 Request로 transfer하여 후속 작업을 수행하는것이 가능.

    。중요 개념 : EntityModel, WebMvcLinkBuilder

    HATESOAS 구현 방법

    • Custom Format으로 임의 설계 후 구현
      。Spring Bean에서 해당 link에 관한 구조를 생성한 후 구현해야하므로 유지보수가 까다롭다.

    • Standard Implementation :
      。HAL이 정의한 표준을 사용!
      ▶ 표준을 가지므로 모든 application이 해당 표준을 따르게되어 HAL REST API 사용 시 동일한 format의 link를 받을 수 있다.
      • HAL (JSON Hypertext Application Language) :
        API의 Resource 간 HyperLink를 생성하는 일관적인 방법을 제공하는 format.

        ▶ "_links"라는 요소를 생성하여 여러 link를 정의하면서 REST API의 다른 Resource에 연결하는 방법을 정의.

      • Spring HATEOAS :
        。Resource에 대한 HyperLink로 HAL Response를 생성.

HATEOAS 구현
。Client에게 data 뿐만 아니라, link도 제공!

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

。Spring HATEOAS dependency를 import.

  • User Bean(= Resource)을 lapping한 EntityModel 객체를 생성하여 Link 객체를 등록하고 return하는 Controller method 생성.
    。기존의 Bean의 id=1 인 Bean을 검색하는 /users/1 URL로 API를 전송하여 검색할 경우, 해당 Bean을 Return 시 id=1의 Bean의 데이터와 함께 모든 Bean에 대해 검색하는 /users URL도 함께 반환.
    。Bean의 Structure를 변경해서는 안된다! => EntityModel 사용.
    。static method EntityModel.of(Resource Bean)를 사용하여 EntityModel 객체를 생성 후 반환.
    。WebMvcLinkBuilder의 method를 전부 static import.
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

。WebMvcLinkBuilder의 linkTo() , methodOn static method를 사용하여 ListAllUsers() controller method가 Mapping한 URL로 접근하는 Link 객체 자동생성.
。EntityModel객체.add(Link객체.withRel("resource-link 간 관계명")); : EntityModel 객체에 Link 객체를 등록.

@GetMapping(path="/users/{id}")
    public EntityModel<User> GetUserById(@PathVariable int id){
        User user = userDaoService.findById(id);
        if (user == null){
            throw new UserNotFoundException("id :" + id);
        }
        // User Bean을 lapping한 EntityModel 객체 생성.
        EntityModel<User> entityModel = EntityModel.of(user);
        // ListAllUsers() method로 접근하는 Link 객체 생성.
        //
        WebMvcLinkBuilder link = linkTo(methodOn(this.getClass()).ListAllUsers());
        // Entity 객체에 Link 객체 등록.
        // "all-users"는 link와 resource 간 관계명.
        entityModel.add(link.withRel("all-users"));
        // Entity 객체 반환.
        return entityModel;
    }


。다음처럼 "_links" 요소 아래 ListAllUsers() controller method가 Mapping한 URL 접근하는 Link가 자동 생성되어 데이터와 함께 반환.
。특정 Resource에 따라 "_links" 요소 아래 여러 link를 추가 가능.

  • EntityModel :
    。RepresentationModel을 상속하는 class.
    。RepresentationModel은 WebMvcLinkBuilder이 생성하는 Link 객체를 담고, EntityModel은 해당 Link 객체를 가진 Resource 객체를 담을수 있다.
    。Spring HATEOAS에서는 EntityModel을 이용해 Response에 사용할 Resource 객체를 캡슐화 및 Link 객체를 등록하여 Client에게 접근할 수 있는 자원임을 명시하는 RESTful Response를 생성.
  • WebMvcLinkBuilder :
    。다른 resource에 접근하는 link 객체를 하드코딩이 아닌 자동생성하기위해 사용하는 class.
    => 하드코딩을 통해 URL를 직접 입력하여 접근이 가능하지만, 추후 URL 변경 시 함께 변경해야하는 번거로움이 생기므로, URL을 자동생성하여 해결.
    。Controller class의 Controller method를 기반으로 static method linkTo, methodOn을 사용하여 자동으로 Link 객체를 생성.
    。linkTo :
    해당 resource에 관한 link 객체를 생성.
    。methodOn :
    해당 class의 method를 참조하는데 사용.

REST API Static Filtering

  • Filtering :
    。Bean의 Field 중 Http Response Body에서 선택된 Field만 반환하도록 Customizing.
  • Serialization : 직렬화
    。Obejct를 data stream( ex: JSON, XML )으로 Convert하는 작업
    => ex) List<User>를 JSON 또는 XML로 변환
    。Java의 가장 많이 사용하는 JSON Serialization Framework : Jackson

Customizing REST API Reponse :
。Jackson Framewok가 반환하는 REST API Response를 Customizing.

  • #1 Response의 Field명 Customizing
    @JSONProperty("변경할 field명") :
    。Bean의 변수에 Annotation을 선언하여 Bean의 기존 field명이 아닌 선언된 Annotation의 명칭으로 Http Response Body의 field명에 반영하여 변경.
    。비어있는 값일 경우 수정없이 기존 field명으로 사용됨.
  @JsonProperty("user_name")
  private String name;


。Response Body의 Field명이 "name"이 아닌, "user_name"으로 표시.

  • #2 Filtering : Response에서 선택된 Field만 반환하도록 Customizing.
    。ex ) Bean에 PW가 정의되어있는데, Response에 해당 PW를 전송하지 않을 경우 활용.
    。활용할 Bean :
    public class SomeBean {
      private String field1;
      private String field2;
      private String field3;
      public SomeBean(String value1, String value2, String value3) {
          this.field1=value1;
          this.field2=value2;
          this.field3=value3;
      }
      public String getField1() {
          return field1;
      }
      public void setField1(String field1) {
          this.field1 = field1;
      }
      public String getField2() {
          return field2;
      }
      public void setField2(String field2) {
          this.field2 = field2;
      }
      public String getField3() {
          return field3;
      }
      public void setField3(String field3) {
          this.field3 = field3;
      }
    }
    • Static Filtering
      。동일한 Spring Bean에 대해 각각의 다른 REST API 에 대해서도 동일한 filtering을 적용.
      ex : #1 REST API("/filtering")에서 동일한 Bean의 "field1"을 static filtering을 적용하여 전송하지 않는 경우, #2 REST API("/filtering-list")의 field1에 대해서 동일한 filtering을 적용.
      。List<Bean>에 대해서도 동일하게 적용이됨.
      。@JsonIgnore : filtering 할 Bean의 field에 선언하여 Response Body에 반영되지 않도록 설정.
      。@JsonIgnoreProperties({"field명1","field명2"...}) : filtering 할 Bean의 field명을 인자로 입력하여 선언하여 Response Body에 반영되지 않도록 설정.
        import com.fasterxml.jackson.annotation.JsonIgnore;
    	import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    	@JsonIgnoreProperties("field1")
    	public class SomeBean {
        	private String field1;
        	@JsonIgnore
        	private String field2;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.Arrays;
    import java.util.List;
    @RestController
    public class FilteringController {
        @GetMapping(path="/filtering")
        public SomeBean filtering(){
            return new SomeBean("value1","value2","value3");
        }
        @GetMapping(path="/filtering-list")
        public List<SomeBean> filteringList(){
            return Arrays.asList(new SomeBean("value1","value2","value3"),
                    new SomeBean("value4","value5","value6"),
                    new SomeBean("value1","value2","value3"));
        }
    }


    。@JsonIgnoreProperties과 @JsonIgnore에 의해 List의 모든 Bean의 변수 field1, field2에 대해서 static filtering이 적용된 Response를 반환.
    => static filtering 시 @JsonIgnore 사용을 권장.

    • Dynamic Filtering :
      。특정 REST API의 Bean에 대해서 Filtering을 Customizing.
      => 동일한 Bean에 대해 각각의 REST API 마다 각각의 다른 Customized된 filtering을 적용하여 Response의 일부로 field를 전송할지 동적으로 결정.
      ex ) #1 REST API("/filtering"))에서 동일한 Bean의 "field1"을 filtering을 적용하고, #2 REST API("/filtering-list")의 field3에 대해서 filtering.
      。REST API 마다 다른 Filtering을 적용해야하므로, Static Filtering처럼 Bean이 아닌 REST API Contoller method에 Filtering logic을 구현.

      MappingJacksonValue객체를 생성하고 filtering을 적용하여 반환하는 REST API Controller Method 생성.

      • MappingJacksonValue :
        。JSON Serialization을 처리하는데 이용되며 주로 Spring MVC 기반 Web Application에서 Java객체를 JSON으로 변환 또는 반대의 경우에 사용되는 class.
        => Bean(= data)과 추가적으로 Serialization logic을 제공.
        。new MappingJacksonValue(Resource Bean) : 객체 생성.
        。MappingJacksonValue객체.setFilters(FilterProvider객체) : MappingJacksonValue 객체에 FilterProvider 객체를 적용.
      • FilterProvider : 여러가지의 Filter들을 정의하는 class.
        。new SimpleFilterProvider().addFilter("@JsonFilter에 선언할 Filter명",filter객체) : @JsonFilter에 적용할 Filter명으로 filter 객체를 추가한 SimpleFilterProvider 객체 생성.
      • SimpleBeanPropertyFilter :
        。Filter의 규칙을 정의.
        。SimpleBeanPropertyFilter.filterOutAllExcept("field명1",..) :
        언급된 Field만 ResponseBody에 전달되어 JSON 변환.
        。SimpleBeanPropertyFilter.serializeAllExcept("field명1",..) : 지정된 Field를 제외한 나머지 field 모두를 JSON 변환.
      import com.fasterxml.jackson.databind.ser.FilterProvider;
      import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
      import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
      import org.springframework.http.converter.json.MappingJacksonValue;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import java.util.Arrays;
      import java.util.List;
      @RestController
      public class FilteringController {
          @GetMapping(path="/filtering")
          public MappingJacksonValue filtering(){
              SomeBean someBean = new SomeBean("value1","value2","value3");
              // MappingJacksonValue 객체 생성 => JSON Serialization 수행.
              MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(someBean);
              // Bean에서 "field1", "field3"의 field를 제외한 나머지를 filtering하는
              // SimpleBeanPropertyFilter 객체 생성.
              SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1","field3");
              // SimpleBeanPropertyFilter의 객체를 @JsonFilter에 적용한 filter명으로 정의하여
              // Filter를 정의하는 FilterProvider 객체 생성.
              FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter",filter);
              // MappingJacksonValue 객체에 FilterProvider 객체를 적용.
              mappingJacksonValue.setFilters(filters);
              // MappingJacksonValue 객체 반환.
              return mappingJacksonValue;
          }
          @GetMapping(path="/filtering-list")
          public MappingJacksonValue filteringList(){
              List<SomeBean> someBeanList = Arrays.asList(new SomeBean("value1","value2","value3"),
                      new SomeBean("value4","value5","value6"),
                      new SomeBean("value1","value2","value3"));
              MappingJacksonValue mappingJacksonValueList = new MappingJacksonValue(someBeanList);
              // Bean에서 "field1", "field3" field만 filtering하고 나머지 field를 가지는
              // SimpleBeanPropertyFilter 객체 생성.
              SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("field1","field3");
              FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanListFilter",filter);
              mappingJacksonValueList.setFilters(filters);
              return mappingJacksonValueList;
          }
      }
      • @JsonFilter("@JsonFilter에 선언할 Filter명") :
        。JSON Conversion시 사용할 filter 객체를 Bean에 선언하여 명시.
        。new SimpleFilterProvider().addFilter("@JsonFilter에 선언할 Filter명",filter객체)의 Filter명을 적용.
      //@JsonFilter("SomeBeanFilter")
      @JsonFilter("SomeBeanListFilter")
      public class SomeBean {


      。REST API("/filtering") 에 대해 "field2"가 filtering.

      。REST API("/filtering-list") 에 대해 "field1", "field3"이 filtering.
      => 동일한 Bean에 대해 각각의 다른 REST API( "/filtering", "/filtering-list" )마다 REST API Controller Method에 의해 다른 Filtering이 적용되어 JSON Serialization되어 HttpResponseBody에 반영됨.

REST API Monitoring

Spring Boot Actuator을 활용하여 API를 Monitoring.

  • Spring Boot Actuator : Spring Boot Actuator
    。Spring Boot의 Production-ready 기능으로서 Production 단계의 Application을 모니터링하고 관리하는 역할을 수행.
    。Application의 Background에서 endpoint를 통해 어떤 작업이 발생하는지 확인하는 등의 정보를 제공.
  • spring-boot-starter-actuator :
    。Application에 Spring Boot Actuator을 추가하는 Starter.
    。다양한 endpoint를 제공.
  • endpoint :
    。RESTful API에서 Client가 Server의 Resource에 접근할 수 있도록 Request를 전달하는 URL.

    endpoint 종류

    • beans endpoint :
      。ApplicationContext에 Load된 모든 spring bean을 해당 endpoint를 이용하여 선언.

      beans endpoint의 field

      • "scope" :
        。Bean이 존재할 수 있는 범위.
        。spring Bean의 default는 Singleton Scope.
      • "type" :
        。해당 Bean의 class path.
      • "resource" :
        。해당 Resource의 위치.
      • "dependencies" :
        。Bean에 정의된 dependency
    • health endpoint : application의 정상작동 여부 등 의 상태 정보를 확인
    • metrics endpoint :
      。Application에 의해 측정이 진행된 metrics를 제공.

      。ex) metrics endpoint로 진입하여 "http.server.requests"를 찾은 후 URL에 http://localhost:8080/actuator/metrics/http.server.requests를 검색 시 해당 Application으로 전송된 Http Request의 수에 관한 detail이 도출.
      。16개의 request가 전달되었고, 총 실행시간은 0.27초, 요청에 걸린 최대 시간은 0.02초 및 Response Status까지 확인 가능.
    • mappings endpoint :

      。application에서 정의된 모든 Request mapping 관련 Detail( HttpMethod, URL 등)을 제공.
    • configprops endpoint : application.properties에서 설정 가능한 모든 항목들이 표시.
    • env endpoint :
      。application의 실행환경에 관한 Detail을 모두 표시.
    • ...etc

API Monitoring

  • spring-boot-start r-actuator dependency 추가.
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

。이후 localhost:8080/actuator 로 진입 시 다음의 page로 진입.

。JSON Format으로 현재 URL로 연결되는 self link와 health URL로 연결되는 health link가 반환.
。Actuator의 추가 기능을 사용 시 application.properties에서 추가기능을 정의.
。기본적으로 Actuator을 활성화하면 actuator는 Application의 상태정보만 공개하므로, health endpoint만 노출되며 더 많은 endpoint를 좀 더 노출시키기 위해 다음 구문을 추가.

# health , metrics의 특정 endpoint 노출 시
management.endpoints.web.exposure.include=health,metrics
# 모든 end point를 노출 할 경우.
management.endpoints.web.exposure.include=*

endpoint를 많이 도출하도록 설정 시 해당 정보를 많이 수집한다는 것을 의미하며 이는 CPU와 메모리를 많이 사용한다는 의미.

REST API 탐색

  • HAL Explorer :
    。HAL( JSON Hypertext Application Language )를 이용하는 RESTful Hypermedia API를 탐색하는 API 탐색기.
    。주로 구축한 API에 대해 간단하게 Test하는 용도로 사용됨.
    。Spring HAL Explorer : spring-data-rest-hal-explorer
    Spring Boot Project를 위해 HAL Explorer를 자동 설정.
  <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-rest-hal-explorer</artifactId>
</dependency>

。다음 dependency를 정의하여 Spring HAL Explorer에 의해 HAL Explorer 사용이 가능.
。이후 localhost:8080/ 검색 시 HAL Explorer page 등장.

。http://localhost:8080/actuator URL을 전송 시 , Link , Response Status , Response Headers , Response Body를 확인 가능.
=> 브라우저에서 http://localhost:8080/actuator 의 URL을 전달 시 받는 Response Body와 Link들을 HAL Explorer를 통해 보여준다.
。Links 항목에서는 각 endpoint 마다 GET 등의 HttpMethod로 버튼을 통해 설정하여 endpoint를 전송 가능 .

。HATEOAS를 통해 EntityModel에 link를 추가하여 반환하는 API("/users/{id}")를 URL에 입력 시 다음처럼 데이터값과 Link값이 함께 도출됨을 확인 가능.
=> HAL Browser가 Data 부분과 Link 부분을 Parsing하여 보여줌.

profile
공부기록 블로그

0개의 댓글