
학습할 내용
- REST API 핵심 기능
- URI의 Resource에 대해 알아보기
(ex :/users,/users/{id}/posts.. etc )
- 상호작용하는
Http Request Method에 대해 알아보기
(ex :GET, POST, PUT, DELETE.. etc)
JSONformat의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구현
- 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를 정해진 규칙에 맞게.jsonor.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-uiPage를 접속<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 UIpage에서는 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 typeorMIME type)을 서버에 전달하는데 사용.
。JSON,XMLformat 등의 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 RequestGET /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가 서버에게AcceptHeader로 특정 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-xmldependency 추가
jackson-dataformat-xml:
。XMLdata를 직렬화( Serializaion ) , 역직렬화( Deserializaion )하느데 사용하는 확장 모듈
▶XMLformat으로 객체를 변환하거나XML을 객체로 변환.<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>。 이후
API Tester에서HEADERS를Accept으로 설정 후 ,application/xml입력 후 전송하면 Http Response Body에서JSON이 아닌,XMLformat으로 반환.
▶ 동일한 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객체 생성하기.
。MessageSourceinstance를 생성 후 생성자기반 의존성주입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.
용어 정리
{ "name" : "Bob Charlie" } { "name" : { "firstName" : "Bob", "lastName" : "Charlie" } }。
REST API에서 기존의Http ResponseJSON 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 APIVersioning방법 결정시 고려사항
- 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")); }
。다음처럼AcceptHeader를 통해 전달한 값으로 method의 produces값과 비교하여 Response가 반환.
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,WebMvcLinkBuilderHATESOAS 구현 방법
- 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/1URL로 API를 전송하여 검색할 경우, 해당 Bean을 Return 시 id=1의 Bean의 데이터와 함께 모든 Bean에 대해 검색하는/usersURL도 함께 반환.
。Bean의 Structure를 변경해서는 안된다! =>EntityModel사용.
。static methodEntityModel.of(Resource Bean)를 사용하여EntityModel객체를 생성 후 반환.
。WebMvcLinkBuilder의 method를 전부 static import.import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;。
WebMvcLinkBuilder의linkTo() , methodOnstatic 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 methodlinkTo,methodOn을 사용하여 자동으로 Link 객체를 생성.
。linkTo:
해당 resource에 관한 link 객체를 생성.
。methodOn:
해당 class의 method를 참조하는데 사용.
- 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에 반영됨.
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-actuatordependency 추가.<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와 메모리를 많이 사용한다는 의미.
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/actuatorURL을 전송 시 ,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하여 보여줌.