Spring HATEOAS API - TheOpenCloudEngine/uEngine-cloud GitHub Wiki

Spring HATEOAS API

HATEOAS๋Š” link๊ตฌ์กฐ๋ฅผ ๊ฐ€์ ธ์„œ, ์ž์‹ ๊ณผ ์—ฐ๊ด€๋œ ๋‹ค๋ฅธ microservice๋ฅผ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ํ•„์š”ํ•˜๋‹ค.

์ด๋ฒˆ ํŽ˜์ด์ง€์—์„œ๋Š” Spring MVC ๊ธฐ๋ฐ˜ RPC ์—์„œ ์„ค๋ช…ํ•œ ์˜ˆ์ œ๋ฅผ ๋ณด๊ฐ•ํ•˜์—ฌ HATEOAS API๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ ๊ฒƒ์ด๋‹ค.

SharedCalendarServiceImpl.java

@RestController
public class SharedCalendarServiceImpl implements SharedCalendarService {

    @Autowired
    ScheduleRepository scheduleRepository;

    @Override
    public Resources<Resource> getSchedules(@PathVariable("instructorId") Long instructorId, @PathVariable("date") String date) {
        Date realDate = convertToDate(date);
        List<Schedule> schedules = scheduleRepository.findByInstructorId(instructorId);
        List<Resource> list  = new ArrayList<Resource>();
        for(Schedule schedule : schedules) {
            if(DateUtils.isSameDay(schedule.getDate(), realDate))
                list.add(new Resource<Schedule>(schedule));
        }
        Resources<Resource> halResources = new Resources<Resource>(list);
        //return new ScheduleResource(schedules);
        return halResources;
    }
}

์ฐธ๊ณ  : https://github.com/uengine-oss/msa-tutorial-class-management-msa/blob/master/calendar/src/main/java/hello/SharedCalendarServiceImpl.java

์šฐ์„  ๋ˆˆ์— ๋„๋Š” ์ฐจ์ด์ ์€ ResourceSupport ๋กœ return ์„ ์•ˆํ•˜๊ณ  ์กฐ๊ธˆ ๋” detail ํ•˜๊ฒŒ,
ArrayList๋กœ ๋งŒ๋“ค์–ด์ง„ Resource ๊ฐ์ฒด๋ฅผ Resouces ๋กœ ๋ณ€ํ™˜์„ ํ•˜์—ฌ return์„ ์‹œํ‚จ๋‹ค.

  • Resource : ์œ ์ผํ•œ ์‹๋ณ„์ž/์ฃผ์†Œ(๋งํฌ) ํ˜•ํƒœ๋กœ ์ž์‹ ์„ ํ‘œํ˜„
  • Resources : Resource ์˜ Collection ํ˜•ํƒœ
  • ResourceSupport : Resource ๋ฅผ ์ถ”์ƒ์ ์œผ๋กœ ์ •์˜ (Resource extends ResourceSupport)

Resources๋ฅผ ์‚ฌ์šฉํ•ด์•ผ์ง€ ์ผ๋ฐ˜ HATEOAS API์ฒ˜๋Ÿผ _embedded์— ๋‹ด๊ฒจ์ง„๋‹ค.

Resources๋ฅผ ์ด์šฉํ•˜์—ฌ _embedded ํ˜•ํƒœ๋กœ ๋ฆฌํ„ดํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ๊ฐ์ฒด๊ฐ€ Jsonํ˜•ํƒœ๋กœ ๋„˜์–ด์˜ค๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ์ •๋ณด ์—†์ด ๋งํฌ๋งŒ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค.
_embedded๋Š” HAL(Hypertext Application Language) format์œผ๋กœ์„œ front-end์—์„œ Hateoas ์ˆ˜์ค€์œผ๋กœ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ๋‹ค๊ณ  ์ธ์ง€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ํ•˜๊ณ , ์‹ค์ œ _embedded ๋ฐฉ์‹์œผ๋กœ return์„ ํ•˜๋Š”์ง€ ํ™•์ธ์„ ํ•ด๋ณธ๋‹ค.

$ git clone https://github.com/uengine-oss/msa-tutorial-class-management-msa.git
$ cd msa-tutorial-class-management-msa/calendar
## port=8085 ์„œ๋ฒ„์‹œ์ž‘
$ mvn spring-boot:run -Dserver.port=8085
## ๊ฐ•์‚ฌ ์ถ”๊ฐ€
$ http localhost:8085/schedules instructorId=1 date="2018-3-14"
## getSchedules ๋ฉ”์„œ๋“œ remote call
$ http localhost:8085/calendar/1/2018-3-14
{
    "_embedded": {
        "schedules": [
            {
                "date": 1520985600000,
                "id": 1,
                "instructorId": 1,
                "title": null
            }
        ]
    }
}

์„œ๋ฒ„ ์‹œ์ž‘์‹œ error๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, eureka๋ฅผ ์•ˆ๋„์šด ๋ถ€๋ถ„์ด๋‹ˆ๊น ํ˜„์žฌ ์˜ˆ์ œ์—์„œ๋Š” ๋ฌด์‹œํ•ด๋„ ์ƒ๊ด€์—†๋‹ค.
_embedded ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋Š” ๋‚˜์˜ค์ง€๋งŒ, ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ get์ด๋‚˜ delete๋ฅผ ํ•˜๊ธฐ ์œ„ํ•œ "_links" ์ •๋ณด๊ฐ€ ์—†๋‹ค.
Link ์ •๋ณด๋ฅผ ์ฃผ๊ธฐ ์œ„ํ•˜์—ฌ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

for(Schedule schedule : schedules) {
    if(DateUtils.isSameDay(schedule.getDate(), realDate))
         // list.add(new Resource<Schedule>(schedule));
         Resource<Schedule> resource = new Resource<Schedule>(schedule);
         resource.add(new Link("http://localhost:8085/calendar/" + instructorId + "/" + date, "_self"));
         list.add(resource);
}
$ http localhost:8085/schedules instructorId=1 date="2018-3-14"
## getSchedules ๋ฉ”์„œ๋“œ remote call
$ http localhost:8085/calendar/1/2018-3-14
{
    "_embedded": {
        "schedules": [
            {
                "_links": {
                    "_self": {
                        "href": "http://localhost:8085/calendar/1/2018-03-14"
                    }
                },
                "date": 1520985600000,
                "id": 1,
                "instructorId": 1,
                "title": null
            }
        ]
    }
}

_links์ •๋ณด๊ฐ€ ์ƒ๊ธด ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๊ฒƒ์€ self์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•˜์˜€๊ณ , ์‹ค์ œ๋กœ microservice๋ฅผ ์—ฎ๋Š” ์ž‘์—…์„ ํ•˜๋ ค๋ฉด
์ €๋ ‡๊ฒŒ uri ์ •๋ณด๋ฅผ ๋ฐ”๋กœ ์ž…๋ ฅํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

resource.add(linkTo(methodOn(SharedCalendarService.class).getSchedules(instructorId,date)).withSelfRel());
// resource.add(new Link("http://localhost:8085/calendar/" + instructorId + "/" + date, "_self"));

ํ•ด์„์„ ํ•˜์ž๋ฉด resource์— link๋ฅผ addํ•˜๋Š”๋ฐ ๋‚ด ์ž์‹ ์˜ JAX-RS ์ •๋ณด๋ฅผ ํ˜ธ์ถœํ• ์ˆ˜ ์žˆ๋Š” URL์„ ์ƒ์„ฑํ•˜์—ฌ self๋กœ ์—ฐ๊ฒฐ์„ ํ•˜์˜€๋‹ค. ์ด์ „ new Link ์™€ ๋‹ค๋ฅธ์ ์€ RequestMapping์˜ path์ •๋ณด๊ฐ™์€ spec์ด ๋ณ€๊ฒฝ๋˜์–ด๋„, ๋‚ด๋ถ€ ๊ตฌํ˜„์€ ๋ณ€๊ฒฝ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด์ œ self ์ •๋ณด๊ฐ€ ์•„๋‹Œ, ์—ฐ๊ณ„๋˜๋Š” request ์ •๋ณด๊ฐ€ ์žˆ์„๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
์šฐ์„  ์Šค์ผ€์ฅด์„ delay์‹œํ‚ค๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค.

    @RequestMapping(method = RequestMethod.PUT, path="/calendar/{instructorId}/{date}/delay")
    public Resources<Resource> delaySchedules(
            @PathVariable("instructorId") Long instructorId, @PathVariable("date") String date,
            @RequestParam("delayDays") int days
    ) {
        Resources<Resource> schedules = getSchedules(instructorId, date);
        // TODO day ์ฆ๊ฐ€
        return schedules;
    }

๊ทธ๋ฆฌ๊ณ  resource์— link๋ฅผ self๊ฐ€ ์•„๋‹Œ relation์ •๋ณด๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•˜์—ฌ ์ค€๋‹ค.

resource.add(linkTo(methodOn(SharedCalendarServiceImpl.class).delaySchedules(instructorId,date,0)).withRel("delay"));

test

$ http localhost:8085/schedules instructorId=1 date="2018-3-14"
## getSchedules ๋ฉ”์„œ๋“œ remote call
$ http localhost:8085/calendar/1/2018-3-14
{
    "_embedded": {
        "schedules": [
            {
                "_links": {
                    "delay": {
                        "href": "http://localhost:8085/calendar/1/2018-03-14/delay?delayDays=0"
                    },
                    "self": {
                        "href": "http://localhost:8085/calendar/1/2018-03-14"
                    }
                },
                "date": 1520985600000,
                "id": 1,
                "instructorId": 1,
                "title": null
            }
        ]
    }
}

delay๋ผ๋Š” link๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ