똑같은 삽질은 2번 하지 말자

Spring으로 REST API No.4(Spring HATEOAS, ) 본문

Spring/Spring Boot

Spring으로 REST API No.4(Spring HATEOAS, )

곽빵 2020. 7. 2. 11:46

HATEOAS 란?

어떤 계좌를 조회하는 요청이 있다.

GET /accounts/12345 HTTP/1.1
Host: bank.example.com
Accept: application/vnd.acme.account+json

 

 

 

그에 대한 응답

HTTP/1.1 200 OK
Content-Type: application/vnd.acme.account+json
Content-Length: ...

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": 100.00
        },
        "links": {
            "deposit": "/accounts/12345/deposit",
            "withdraw": "/accounts/12345/withdraw",
            "transfer": "/accounts/12345/transfer",
            "close": "/accounts/12345/close"
        }
    }
}

 

계좌에 돈이 있으므로 rel = 이체, 돈뽑기, 입금, 계좌닫기 등등 할 수 있다.

 

 

다른 계좌에 대한 응답으로

Content-Type: application/vnd.acme.account+json
Content-Length: ...

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": -25.00
        },
        "links": {
            "deposit": "/accounts/12345/deposit"
        }
    }

 

이번 계좌에는 돈이 -25.00달러 이므로 입금밖에 안된다.

 

이처럼 상태에 따라 링크의 정보가 바뀌는걸 

Hypermedia as the Engine of Application State (HATEOAS) 라고 한다.

 

Spring HATEOAS는 이러한 링크 + 리소스를 쉽게 만들 수 있게 지원해준다.

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;

import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;

public class EventResource extends Resource<Event>{ // 자동으로 Unwrapped 된다.
	public EventResource(Event event, Link... links) {
		super(event, links);
		add(linkTo(EventController.class).slash(event.getId()).withSelfRel());
	}
}
@PostMapping
	public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
		
		if(errors.hasErrors())
			return ResponseEntity.badRequest().body(errors);
		
		eventValidator.validate(eventDto, errors);
		if(errors.hasErrors())
			return ResponseEntity.badRequest().body(errors);
		
		Event event = modelMapper.map(eventDto,Event.class);
		event.update();
		Event newEvent = this.eventRepository.save(event);
		URI createUri = 
				linkTo(EventController.class).slash(newEvent.getId()).toUri();
		EventResource eventResource = new EventResource(event);
		eventResource.add(linkTo(EventController.class).withRel("query-events"));
		eventResource.add(linkTo(EventController.class).slash(newEvent.getId()).withRel("update-events"));
		return ResponseEntity.created(createUri).body(eventResource);
		 // event는 자바빈 스펙을 준수하므로 BeanSerializer로 json으로 변환해서 자동으로 넘어간다.
 	}

링크정보를 담은 응답정보를 리턴..

 

그리고 ResourceSupport를 안쓰는 이유는 EventResource를 mapping을 object mapper가 serialization 할 때

BeanSerializer를 쓰는데 BeanSerializer는 기본적으로 그 객체의 이름으로 감싸서 보낸다.

@JsonUnwrapped 를 붙여서 보내도 되지만, Resource 에는 자동으로 붙여져 있으므로 안 붙여도 된다.

Comments