Spring MVC HATEOAS
포스트
취소

Spring MVC HATEOAS

HATEOAS

HATEOAS 해당 리소스와 관련된 링크를 표시하는 것을 말합니다 이 원칙에 따르면 API는 각 서비스 응답과 함께 가능한 다음 단계 정보도 제공하며 클라이언트를 다음 단계로 가이드 할 수 있어야 한다

그럼 지난 시간에 했던 Student로 간단하게 HATEOAS를 만들어보겠습니다

의존성 추가

1
2
3
4
5
6
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>


이 의존성이 HATEOAS와 관련된 의존성입니다

Student 모델 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
@Setter
@ToString
public class Student extends RepresentationModel<Student> {

    private int id;
    private String studentId;
    private String studentName;
    private String schoolId;
    private String gender;
    private String higherGroup;
}


우리가 원래 사용하던 model에서 RepresentationModel를 상속받아 줍니다

GET 요청시 HATEOAS 생성해서 body 에 전달

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@RestController
@RequestMapping(value ="v1/school/{schoolId}/student")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping("/{studentId}")
    public ResponseEntity<Student> getStudent(
            @PathVariable("schoolId") String schoolId ,
            @PathVariable("studentId") String studentId
    )
    {
        Student student = studentService.getStudent(studentId , schoolId);

        Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).getStudent(schoolId , studentId)).withSelfRel();
        Link createLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).createStudent(schoolId , student , null)).withRel("createStudent");
        Link updateLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).updateStudent(schoolId , student , null)).withRel("updateStudent");
        Link deleteLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).deleteStudent(studentId , studentId , null)).withRel("deleteStudent");
        student.add(selfLink , createLink , updateLink , deleteLink);
        return new ResponseEntity<>(student, null , HttpStatus.OK);

    }

    @PostMapping
    public ResponseEntity<String> createStudent(
            @PathVariable("schoolId") String schoolId ,
            @RequestBody Student student,
            @RequestHeader(value = "Accept-Language" , required = false) Locale locale
    )
    {
        return new ResponseEntity<>(studentService.createStudent(student , schoolId , locale) , null , HttpStatus.OK);
    }

    @PutMapping
    public ResponseEntity<String> updateStudent (
            @PathVariable("schoolId") String schoolId ,
            @RequestBody Student student,
            @RequestHeader(value = "Accept-Language" , required = false) Locale locale
        )
    {
        return new ResponseEntity<>(studentService.updateStudent(student,schoolId ,locale) , null , HttpStatus.OK);
    }

    @DeleteMapping(value = "/{studentId}")
    public ResponseEntity<String> deleteStudent(
            @PathVariable("schoolId") String schoolId ,
            @PathVariable("studentId") String studentId ,
            @RequestHeader(value = "Accept-Language" , required = false) Locale locale


    )
    {
      return new ResponseEntity<>(studentService.deleteStudent(studentId , schoolId , locale) , null , HttpStatus.OK);
    }


}


지금 보면 GET 요청에 현재 사용하고 있는 메서드를 만들어서 return 하고 있는 모습을 볼 수 있습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**

이 링크는 자기 자신을 부를 때 사용하는 링크라서 마지막에 withSelfRel 을 두는 것이고

*/
Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).getStudent(schoolId , studentId)).withSelfRel();

/**

이 링크는 자기 자신을 부르는 것이 아니기 때문에 withRel 을 사용해서 메서드 명을 지정해 줍니다

*/
Link createLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(StudentController.class).createStudent(schoolId , student , null)).withRel("createStudent");



전체적으로 보면 StudentController 안에 핸들러에 각 메서드를 체이닝 걸고 그때 필요한 파라미터를 각각 넣어주면 됩니다 그리고 자기 자신의 핸들러에서는 withSelfRel로 link를 만들고 그렇지 않은 링크들은 withRel 과 메서드 명을 적어서 끝을 냅니다 이렇게 하고 기동을 하고 post-man 으로 get 요청을 시도하면

post-Man 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    "id": 4768,
    "studentId": "2205",
    "studentName": "Kim",
    "schoolId": "1",
    "gender": "Woman",
    "higherGroup": "N",
    "_links": {
        "self": {
            "href": "http://localhost:8080/v1/school/1/student/2205"
        },
        "createStudent": {
            "href": "http://localhost:8080/v1/school/1/student"
        },
        "updateStudent": {
            "href": "http://localhost:8080/v1/school/1/student"
        },
        "deleteStudent": {
            "href": "http://localhost:8080/v1/school/2205/student/2205"
        }
    }
}

이렇게 이렇게 클라이언트에게 다음의 액션을 취할 수 있게 링크를 제공합니다

정리하자면 클라이언트가 해당 웹사이트에서 사용할 수 있는 링크들을 탐색하고 싶을 때 서버 측에서 제공하는 서비스 기능 중 하나입니다 클라이언트는 서버가 제공해 주는 것을 보고 다음 스텝을 진행을 합니다