본문 바로가기

IT/Spring

[SpringBoot] HATEOAS 적용하기

1. hateoas란

hateoas는 Hypermedia As The Engine Of Application State의 약자로 REST API의 필수 구성요소 중 한가지입니다.

특정 API 요청 시 리소스 정보를 받아 볼 수 있는데, 이때 리소스 정보 뿐 만 아니라 리소스에 대한 다양한 링크정보를 리소스정보와 함께 반환하는 것을 의미합니다.

예를 들어서 유저 정보 생성 시 생성된 유저의 정보를 반환받는데, 이때 유저의 상세정보, 수정, 설정 등.. 유저와 관련된 다양한 링크페이지를 함께 반환받을 수 있습니다.

다시말해서 hateoas를 적용하면 API 요청 시, Resource와 Links를 함께 반환 받을 수 있습니다.

 

2. spring boot hateoas 적용

간단하게 Member를 저장하는 API를 작성해보록 하겠습니다.
DB를 별도로 사용하지 않았으며 IdentityHashMap 클래스를 이용해서 Member를 Key,Value 형식으로 저장할 수 있도록 구현하였습니다.

1) pom.xml

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

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

 

2) Member.java

public class Member {
    private Integer id;
    private String name;
    
    //setter,getter 생략

}

 

3) MemberModel.java

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.springframework.hateoas.RepresentationModel;

public class MemberModel extends RepresentationModel<MemberModel> {

    public MemberModel(Member member) {
        this.member = member;
    }

    @JsonUnwrapped
    private final Member member;

}
hateoas 기능을 사용하려면 RepresentationModel 클래스를 상속받은 클래스를 구현해야합니다.
멤버변수로 hateoas를 적용할 클래스를 선언해주면 됩니다.

@JsonUnwrapped 어노테이션을 사용하면 member 반환 시 member depth를 없애고 member의 멤버변수를 바로 출 력 받을 수 있습니다.

 

4) MemberController.java

import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;
import java.util.IdentityHashMap;
import java.util.Map;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;

@RestController
@RequestMapping(value = "/api/member", produces = MediaTypes.HAL_JSON_VALUE)
public class MemberController {

    private Map<Integer, Member> db = new IdentityHashMap<>();
    private Integer id = 1;

    @PostMapping
    public ResponseEntity createMember(@RequestBody Member member) {
        member.setId(id++);

        /*
            /api/member
        */
        WebMvcLinkBuilder listLink= linkTo(MemberController.class);
        
        /*
            /api/member/{id}
        */
        WebMvcLinkBuilder selfLink = listLink.slash(member.getId());

        //hateoas model 객체 생성
        MemberModel memberModel = new MemberModel(member);

        //list link
        memberModel.add(listLink.withRel("list"));

        //self link
        memberModel.add(selfLink.withSelfRel());

        //update link
        memberModel.add(selfLink.withRel("update"));

        return ResponseEntity.created(selfLink.toUri()).body(memberModel);
    }
}
POST /api/member 요청 시 HAL_JSON 방식으로 리소스를 반환합니다.

반환 정보는 생성된 Member 정보와 Member와 관련된 링크 정보로 구성됩니다.
링크 정보는 list, self, update 정보를 담고 있습니다.

( HAL이란 Hypertext Application Language의 약자로 JSON 또는 XML 코드 내의 외부 리소스에 대한 링크와 같은 하이퍼 미디어를 정의하기 위한 표준 규칙입니다. )

 

3. 테스트

Request
URL : POST http://localhost:8080/api/member

content-type : application/hal+json
body : {"name":"kim"}


Response
Body : 


 

위 예제는 기초적인 hateoas 라이브러리 기능만 사용했으며, 자세한 정보는 아래 링크를 확인해주세요.
https://docs.spring.io/spring-hateoas/docs/current/reference/html/#fundamentals.representation-models