부트 개념과 활용-20.Spring HATEOAS


스프링 웹 MVC 10부: Spring HATEOAS

  • HATEOAS를 구현하기 위해 필요한 기능을 제공하는 일종의 툴.

    HATEOAS

  • HATEOAS는 Hypermedia As The Engine Of Application State의 약자
  • 하이퍼미디어를 REST API의 상태 정보를 관리하기 위한 매커니즘으로 활용하는 것

    하이퍼미디어 : 하이퍼미디어 정보는 이용자가 정보를 탐색할 때 어떤 제목에서 관련 제목으로 뛰어넘어 갈 수 있도록 연결.
    연결된 정보가 주로 문자 정보로 되어 있으면 하이퍼텍스트이고, 음악, 영상, 애니메이션 또는 다른 요소가 포함되어 있으면 하이퍼미디어.

REST API에서 클라이언트에 리소스를 넘겨줄 때,
추가적으로 특정 부가적인 리소스의 링크 정보를 넘겨주게 되며,
이를 통해 REST API의 리소스 상태에 따른 관리를 진행할 수 있다. 인데..
예제를 보면 바로 이해가 가긴 했는데.

설명할때 쓰인 용어들이나 관련 배경, 기존 방식 등 정확하게 이해하기 위해서 한번 서치 및 공부하고 내용을 다시 정리해 보았다.

쉽게 설명하면
결론적으로는 REST R Representational

“representation”의 의미: 어떤 리소스의 특정 시점의 상태를 반영하고 있는 정보
하나의 representation은 representation data와 representation metadata로 구성이 되는데..

HTTP/1.1 200 OK
Content-Length: 6
Date: Sun, 19 Mar 2017 10:20:47 GMT
Last-Modified: Sun, 19 Mar 2017 08:00:00 GMT
Content-Type: text/plain
Content-Language: en

hello

위의 예시에서는
representation data : “hello”
representation metadata : “Content-Type: text/plain”과 “Content-Language: en”
(HTTP 헤더들 중 representation metadata에 해당하는 것이 있고 그렇지 않은 것이 있다.)

즉,
Representational를 제공할 때 연관이 되어있는 링크 정보들까지 같이 제공을 한다.

그럼, 그 클라이언트는 그 연관된 링크 정보를 바탕으로 다른 정보에 접근이 가능하다.

ex. 보통 현재 페이지는 self 로 표현.
루트(/)를 조회 시에 Representational에 self(나 자신) 링크를 넣어주는 경우.

{
  "content":"Hello, jaeuk!"
  }

self(나 자신) 링크를 넣어주는 경우.

{
  "content":"Hello, jaeuk!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=jaeuk"
    }
  }
}

또 혹은..
루트(/) 요청시
다른 링크에는 웹사이트가 북리스트를 조회할 수 있는 연관 링크 정보들이 제공이 되어 있으면
(연관링크 booklist 넣어준 경우)

{
  "content":"Hello, jaeuk!",
  "_links":[{
    "self":{
      "href":"http://localhost:8080/greeting?name=jaeuk"
      },
      "booklist":{
        "href":"http://localhost:8080/booklist"
    }
  }]
}
  1. 루트를 연결한 다음.

  2. 루트와 연관된 리소스들이 이런게 있구나.. 하고 찾고.

  3. 난 근데 그 중에서 booklist에 가고싶다.

  4. booklist의 href를 읽은 다음에

  5. 그 링크로 간다.

즉, URL이 변경되더라도, 어차피 booklist의의 href를 읽어서 가기 때문에 상관이 없다. (깃허브 API 같은 곳에 적용이 되어 있다고 한다.)

잠깐 사담으로
회사에서 신규 차세대 MES를 MSA로 진행중인데..
이전에 관련 내용을 교육 및 실습하다가 API GateWay 및 DNS(도메인 네임 서버).. 관련 내용과 비슷한 내용인 것 같다.
(각 마이크로 공정 - 서버 IP가 바뀌더라도 어차피 DNS에서 관리-각 MSA는 네임으로 API 호출)

Spring cloud를 사용해서 Zuul Proxy로 구현했던가… 의존성 추가하고 무튼..
물론 API GateWay는 더 많은 장점들이 있지만, 그 내용은 추후 MSA에 대해 공부할때 다시 정리해보겠다.

  • 서버: 현재 리소스와 연관된 링크 정보를 클라이언트에게 제공한다.
  • 클라이언트: 연관된 링크 정보를 바탕으로 리소스에 접근한다.
  • 연관된 링크 정보
    • Relation
    • Hypertext Reference

HATEOAS를 쓰는 이유?

기존 REST API의 단점을 보완하기 위한 것.

  1. REST API는 앤드포인트 URL이 정해지고 나면 이를 변경하기 어렵다는 단점
    만일 API의 URL을 변경하게 되면 모든 클라이언트의 URL까지 수정해야하기 때문에 번거로워지므로 기존 다른 API를 지속적으로 추가하게 됩니다.
    따라서 URL 관리가 어렵게 됩니다.

  2. 전달받은 정적 자원의 상태에 따른 요소를 서버 단에서 구현하기 어렵기 때문에 클라이언트 단에서 이 부분에 대한 로직을 처리해야 합니다.

위 단점들을 links 요소를 통해 href 값의 형태로 보내주기 때문에 자원 상태에 대한 처리를 링크에 있는 URL을 통해 처리할 수 있게됩니다. (뒤의 예시 참고)

참고 사항으로

그림 설명이 참 잘 되어 있는 외국 기사를 찾았다.
https://martinfowler.com/articles/richardsonMaturityModel.html

레벨 0. REST 도입 전 - 모든 전송과 응답을 POST로 하며 접근 가능한 엔드 포인트는 하나

HTTP의 body에 정보를 넣어 전송하는 기존의 리소스 전송 방식

레벨 1. 리소스 도입 - REST를 도입하며 고유의 URI로 각각의 제공하는 자원을 주고 받음

모든 자원을 제공함으로써 클라이언트는 다양한 자원과 포맷을 제공 받을 수 있음
ex) JSON, XML

레벨 2. HTTP - URL과 HTTP method를 적극적으로 활용. HTTP method로 어떻게 요청 하든 같은 응답을 받을 수 있음

가장 널리 알려진 방법.
method의 표준을 도입하고 에러 상황에 대한 상태 코드 전달과 대처를 고려

레벨 3. Hypermedia Controls - 하이퍼 미디어의 링크를 이용하는 방식

서버가 클리이언트에게 자원을 보내면서 다음 작업을 할 수 있는 URL을 링크로 같이
클라이언트는 링크를 확인하고 다음 작업을 할 수 있는 URL을 확인합니다. 이런 방식으로 REST API의 URL 변경시 단점을 해결

실습 (구버전)

@RestController // 그냥 Controller 아님
public class SampleController {

    @GetMapping("/hello")
    public Hello hello(){
        Hello hello = new Hello();
        hello.setPrefix("hey, ");
        hello.setName("jaeuk");
        Resource<Hello> helloResource = new Resource<>(hello);
        // 링크 생성 및 리소스에 add
        helloResource.add(linkTo(SampleController.class).hello()).withSelfRel())

        return helloResource;
    }
}
@ExtendWith(SpringExtension.class) // junit 5 기준
@WebMvcTest(SampleController.class)
class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;
    @Test
    public void hello() throws Exception {
        mockMvc.perform(get("/hello"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$._links.self").exists());
    }
}

실습 (최신버전)

https://www.baeldung.com/spring-hateoas-tutorial
해보기 ~~~

spring-boot-starter-hateoas 의존성 추가하게 되면..

  1. ObjectMapper 제공
    • spring.jackson.*
    • Jackson2ObjectMapperBuilder

      여러 가지 이유로 많이 사용됨
      객체를 json으로 변환할 때 (jackson)
      스프링 부트 스타터 웹 의존성만 있어도 빈으로 등록을 해 준다.(그냥 주입받아 사용가능)
      커스터마이징 하고싶으면 spring.jackson. … 커스텀 하면 됨

  2. LinkDiscovers 제공
    • 클라이언트 쪽에서 링크 정보를 Rel 이름으로 찾을때 사용할 수 있는 XPath 확장 클래스

겟 리소스 바이 릴레이션 사용해서 셀프에 해당하는 링크정보를 가져온다던가..

유틸리티성 클래스. 빈으로 등록되어있으니 그냥사영하면 됨.

참고문서






© 2019. by jaeuk