ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링 인 액션] Chapter 6 - REST 서비스 생성하기 :: 데이터 기반 서비스 활성화하기
    개발서적읽기/Spring in Action 제 5판 2020. 8. 20. 00:16



    3장에서 보았듯이, 스프링 데이터는 우리가 코드에 정의한 인터페이스를 기반으로


    리퍼지터리 구현체를 자동으로 생성하고 필요한 기능을 수행한다.


    그에 더해 스프링 데이터에는 애플리케이션의 API를 정의하는 데 


    도움을 줄 수 있는 스프링 데이터 REST라는 기능도 있다.


    스프링 데이터 REST는 스프링 데이터의 또 다른 모듈이며


    스프링 데이터가 생성하는 리퍼지터리의 REST API를 자동 생성한다.


    따라서 스프링 데이터 REST를 빌드에 추가하면 


    정의된 각 리퍼지터리 인터페이스를 사용하는 API를 얻을 수 있다.


    스프링 데이터 REST를 사용하기 위해 아래와 같이 의존성을 추가하자.

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>

    이렇게 의존성만 지정하면 스프링 데이터를 사용 중인 프로젝트에서 


    REST API를 노출시킬 수 있다.


    우리 빌드에 포함된 스프링 데이터 REST 스타터가


    스프링 데이터가 생성한 모든 리퍼지터리(ex 스프링 데이터 JPA, 스프링 데이터 몽고)의


    REST API가 자동 생성될 수 있도록 자동-구성하기 때문이다.


    스프링 데이터 REST가 생성하는 REST 엔드포인트는 꽤 쓸만하다.


    그리고 이 엔드포인트를 사용하려면 지금까지 생성했던 @RestController 애노테이션이


    지정된 모든 클래스들을 이 시점에서 제거해야 한다.


    스프링 데이터 REST가 자동 제공하는 엔드포인트를 기존에 작성했던 


    타코, 식자재, 주문, 사용자의 GET 요청들에 적용해보자.


    http://localhost:8080/ingredients를 실행하면 식자재에 대한 hateoas response를 얻을 수 있다.


    http://localhost:8080/ingredients/FLTO를 실행하면 'Flour Tortilla' 식자재 항목에 대한 


    hateoas response를 얻을 수 있다.


    GET 말고도 POST, PUT, DELETE 메서드도 지원한다.


    스프링 데이터 REST가 자동 생성한 API에 추가하여 우리가 한 가지 해야 할 일은


    해당 API의 기본 경로를 설정하는 것이다.


    해당 API의 엔드포인트가 우리가 작성한 모든 다른 컨트롤러와 충돌하지 않게 하기 위함이다.


    스프링 데이터 REST가 자동 생성한 API의 기본 경로는 다음과 같이


    spring.data.rest.base-path 속성에 설정한다.

    spring:
    data:
    rest:
    base-path: /api

    이제 식자재의 엔드포인트는 /api/ingredients다.




    ■리소스 경로와 관계 이름 조정하기


    한편 스프링 데이터 REST는 복수형의 리소스를 자체적으로 지정한다.


    즉, 스프링 데이터 REST는 스프링 데이터 repository의 엔드포인트를 생성할 때,


    해당 엔드포인트와 관련된 엔티티 클래스 이름의 복수형을 사용한다.


    그렇기 때문에 taco의 복수형으로 tacos가 아닌 tacoes을 선택한다.


    만약 tacos를 사용하고 싶다면 Taco 엔티티 클래스 상단에

    @RestResource(rel = "tacos", path = "tacos")

    위와 같이 설정한다. rel과 path 모두 tacos로 설정했다.


    스프링 데이터 REST는 또한 노출된 모든 엔드포인트의 링크를 갖는 


    홈(home) 리소스도 노출시킨다.


    curl http://localhost:8080/api 를 호출하면


    orders, ingredients, tacoes, users, profile에 대한 기본 링크를 반환해준다.




    ■페이징과 정렬


    지금부터는 스프링 데이터 REST 엔드포인트의 결과를 분류하는 방법을 알아보자.


    홈 리소스의 모든 링크는 선택적 매개변수인  page, size, sort를 제공한다.


    /api/tacos와 같은 컬렉션 리소스를 요청하면 


    기본적으로 한 페이지당 20개의 항목이 반환된다.


    그러나 page와 size 매개변수를 지정하면 


    요청에 포함될 페이지 번호와 페이지 크기를 조정할 수 있다.


    예를 들어, 페이지 크기가 5인 첫 번째 페이지를 요청할 때는 


    다음과 같이 GET 요청을 하면 된다.


    curl "http://localhost:8080/api/tacos?size=5"


    5개 이상의 타코가 있다고 가정할 때, 다음과 같이 page 매개변수를


    추가하면 두 번째 페이지의 타코를 요청할 수 있다.


    curl "http://localhost:8080/api/tacos?size=5&page=1"


    page 매개변수의 값은 0부터 시작한다.


    또한 curl과 같은 여러 명령행 셀에서는 요청 속에 앰퍼샌드(&)를


    포함하므로 URL 전체를 겹따옴표로 둘러싸야 한다.


    그리고 HATEOAS는 처음(first), 마지막(last), 다음(next), 이전(previous) 페이지들의 링크를


    요청 응답에 제공한다.


    sort 매개변수를 지정하면 엔티티의 속성을 기준으로 결과 리스트를 정렬할 수 있다.


    예를 들어 최근 생성된 12개의 타코를 가져와야 한다면


    다음과 같이 페이징과 정렬 매개변수를 같이 지정하면 된다.


    curl "http://localhost:8080/api/tacos?sort=createdAt,desc&size=12&page=0




    ■커스텀 엔드포인트 추가하기


    스프링 데이터 REST에서 생성하는 엔드포인트 이외에 


    커스텀한 엔드포인트를 생성해야할 경우가 있다.


    이때 @RestController 애노테이션이 지정된 빈을 구현하여


    스프링 데이터 REST가 자동 생성하는 엔드포인트에 보충할 수도 있다.


    예를 들어, 이번 장 앞에 나왔던 DesignTacoController를 다시 사용하면서


    이것이 스프링 데이터 REST가 제공하는 엔드포인트와 함께 작동하도록 할 수 있다.


    이때, 다음 두 가지를 고려하여 우리의 API 컨트롤러를 작성해야 한다.


    - 커스텀 엔드포인트 컨트롤러는 스프링 데이터 REST의 기본 경로로 매핑되지 않는다.


    따라서 이때는 스프링 데이터 REST의 기본 경로를 포함하여 우리가 원하는


    기본 경로가 앞에 붙도록 매핑시켜야 한다. 그러나 기본 경로가 변경될 때는


    해당 컨트롤러의 매핑이 일치되도록 수정해야 한다.


    - 커스텀 엔드포인트 컨트롤러에 정의한 엔드포인트는 스프링 데이터 REST 


    엔드포인트에서 반환되는 리소스의 하이퍼링크에 자동으로 포함되지 않는다.


    따라서 클라이언트가 관계 이름을 사용해서 커스텀 엔드포인트를 찾을 수 없다.


    우선, 기본 경로에 관한 문제부터 살펴보자.


    스프링 데이터 REST는 @RepositoryRestController를 포함한다.


    이것은 스프링 데이터 REST 엔드포인트에 구성되는 것와 동일한 기본 경로로 매핑되는


    컨트롤러 클래스에 지정하는 새로운 애노테이션이다.


    다시 말해, @RepositoryRestController가 지정된 컨트롤러의 모든 경로 매핑은


    spring.data.rest.base-path 속성의 값이 앞에 붙은 경로를 갖는다.


    이전의 DesignTacoController는 우리가 필요 없는 여러 핸들러 메서드를 갖고 있으므로


    이것을 다시 활용하는 대신 여기서는 recentTacos() 메서드만 갖는 새로운 컨트롤러를


    생성할 것이다. 아래 RecentTacosController에는 스프링 데이터 REST의 기본 경로를


    요청 매핑에 적용하기 위해 @RepositoryRestController가 지정되었다.

    package tacos.web.api;

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

    import java.util.List;

    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.rest.webmvc.RepositoryRestController;
    import org.springframework.hateoas.Resources;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;

    import tacos.Taco;
    import tacos.data.TacoRepository;

    @RepositoryRestController
    public class RecentTacosController {

    private TacoRepository tacoRepo;

    public RecentTacosController(TacoRepository tacoRepo) {
    this.tacoRepo = tacoRepo;
    }

    @GetMapping(path="/tacos/recent", produces="application/hal+json")
    public ResponseEntity<Resources<TacoResource>> recentTacos() {
    PageRequest page = PageRequest.of(
    0, 12, Sort.by("createdAt").descending());
    List<Taco> tacos = tacoRepo.findAll(page).getContent();

    List<TacoResource> tacoResources =
    new TacoResourceAssembler().toResources(tacos);
    Resources<TacoResource> recentResources =
    new Resources<TacoResource>(tacoResources);

    recentResources.add(
    linkTo(methodOn(RecentTacosController.class).recentTacos())
    .withRel("recents"));
    return new ResponseEntity<>(recentResources, HttpStatus.OK);
    }

    }

    여기서 @GetMapping은 /tacos/recent 경로로 매핑되지만


    RecentTacosController 클래스에 @RepositoryRestController 애노테이션이 


    지정되어 있으므로 맨 앞에 스프링 데이터 REST의 기본 경로가 추가된다.


    따라서 recentTacos() 메서드는 /api/tacos/recent의 GET 요청을 처리하게 된다.


    여기서 한 가지 중요한 것이 있다.


    @RepositoryRestController는 @RestController와 이름이 유사하지만


    @RestController와 동일한 기능을 수행하지 않는다는 것이다.


    특히 @RepositoryRestController는 핸들러 메서드의 반환값을 


    요청 응답의 몸체에 자동으로 수록하지 않는다.


    따라서 해당 메서드에 @ResponseBody 애노테이션을 지정하거나


    해당 메서드에서 응답 데이터를 포함하는 ResponseEntity를 반환해야 한다.


    RecentTacosController가 실행되면 /api/tacos/recent의 GET 요청을 할 때


    가장 최근에 생성된 타코를 12개까지 반환한다.


    그러나 /api/tacos를 요청할 때는 여전히 하이퍼링크 리스트에 나타나지 않는다.


    이것을 해결해보자.




    ■커스텀 하이퍼링크를 스프링 데이터 엔드포인트에 추가하기


    리소스 프로세서 빈을 선언하면 스프링 데이터 REST가 자동으로 포함시키는


    링크 리스트에 해당 링크를 추가할 수 있다. 스프링 데이터 HATEOAS는 


    ResourceProcessor를 제공한다. 이것은 API를 통해 리소스가 반환되기 전에 


    리소스를 조작하는 인터페이스이다.


    여기서는 PagedResources<Resource<Taco>> 타입(/api/tacos 엔드포인트의 반환 타입)의


    리소스에 recents 링크를 추가하는 ResourceProcessor를 구현해야 한다.

    package tacos.web.api;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.hateoas.EntityLinks;
    import org.springframework.hateoas.PagedResources;
    import org.springframework.hateoas.Resource;
    import org.springframework.hateoas.ResourceProcessor;

    import tacos.Taco;

    @Configuration
    public class SpringDataRestConfiguration {

    @Bean
    public ResourceProcessor<PagedResources<Resource<Taco>>>
    tacoProcessor(EntityLinks links) {

    return new ResourceProcessor<PagedResources<Resource<Taco>>>() {
    @Override
    public PagedResources<Resource<Taco>> process(
    PagedResources<Resource<Taco>> resource) {
    resource.add(
    links.linkFor(Taco.class)
    .slash("recent")
    .withRel("recents"));
    return resource;
    }
    };
    }

    }

    이제 컨트롤러에서 PagedResources<Resource<Taco>>가 반환된다면


    가장 최근에 생성된 타코들의 링크를 받게 되며, /api/tacos의 요청 응답에도


    해당 링크들이 포함된다.


    다음 포스팅에선 타코 클라우드 애플리케이션의 빌드와 실행에 대해 살펴본다.





    댓글

Designed by Tistory.