[사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Event Paging 불러오기

Event Read All 테스트 구현


이번에는 Event를 Paging을 사용해서 10개씩 가져오는 기능을 구현할 것이다.

그와 관련해서 고려해야 할 사항은 다음과 같다.

  • Paging이 제대로 이뤄지는가.
  • Paging을 통해 받은 Result도 HATEAOS를 만족하는가.
  • Page 내의 Event들이 HATEAOS를 만족하는가.

이를 테스트 코드로 구현하면 다음과 같다.

	@Test
    public void get_sorted_event_list_by_paging() throws Exception {
        // Given
        IntStream.range(1,30).forEach( i ->{
            Event event = createEvent(i);
            this.eventRepository.save(event);
        });

        //When
        //Then
        mockMvc.perform(get("/api/events")
                        .param("page","1")
                        .param("size","10")
                        .param("sort", "name,DESC")
                )
                .andDo(print())
                .andExpect(status().isOk())
                // Result가 HATEOAS를 만족하는가
                .andExpect(jsonPath("_links").exists())
                // 제대로 Paging이 이뤄졌는가
                .andExpect(jsonPath("page").exists())
                // Page 내의 Event들도 제대로 HATEAOS를 만족하는가.
                .andExpect(jsonPath("_embedded.eventResourceList.[0]._links.self").exists());
    }

Serivce 수정


Page를 통해서 Event를 읽어오는 readWithPage 기능구현과 함께 create 기능 일부를 수정했다.

  • create()내에서 Event -> EventResource로 바꿔서 반환했는데 이를 Event로 반환하도록 바꿈 -> Controller에서 EventResource 변환 및 URI 연결을 담당하도록 하기 위해서
package com.carrykim.restapi.event.service;

import com.carrykim.restapi.event.infra.EventJpaRepository;
import com.carrykim.restapi.event.infra.EventRepository;
import com.carrykim.restapi.event.model.Event;
import com.carrykim.restapi.event.model.dto.EventDto;
import com.carrykim.restapi.event.model.dto.EventResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.PagedModel;
import org.springframework.stereotype.Service;

@Service
public class EventService {

    private final EventRepository eventRepository;

    public EventService(EventRepository eventRepository) {
        this.eventRepository = eventRepository;
    }

    public Event create(EventDto eventDto){
        Event newEvent = eventDto.toModel();
        return eventRepository.save(newEvent);
    }

    public Page<Event> readWithPage(Pageable pageable){
        return this.eventRepository.findAll(pageable);
    }

}

Controller 수정


  • Controller에서 Event -> EventResource 변환 및 URI 매핑 담당,
  • URI 매핑 부분을 addLink() 메소드로 분리
package com.carrykim.restapi.event.controller;

import com.carrykim.restapi.event.model.Event;
import com.carrykim.restapi.event.model.dto.EventDto;
import com.carrykim.restapi.event.model.dto.EventResource;
import com.carrykim.restapi.event.service.EventService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.net.URI;

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

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

    private final EventService eventService;

    public EventController(EventService eventService) {
        this.eventService = eventService;
    }

    @PostMapping("")
    public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
        Event event = this.eventService.create(eventDto);
        EventResource eventResource = new EventResource(event);
        addLinks(eventResource);
        URI uri = linkTo(methodOn(EventController.class)
                .create(new EventDto()))
                .slash(eventResource.getEvent().getId()).toUri();
        return ResponseEntity.created(uri).body(eventResource);
    }

    @GetMapping("")
    public ResponseEntity readAll(Pageable pageable, PagedResourcesAssembler pagedResourcesAssembler) {
        var result =  pagedResourcesAssembler
                .toModel(this.eventService.readWithPage(pageable).map(event -> {
                    EventResource eventResource =  new EventResource(event);
                    addLinks(eventResource);
                    return eventResource;
                }));
        return ResponseEntity.ok(result);
    }

    private void addLinks(EventResource eventResource){
        WebMvcLinkBuilder selfAndUpdateLink =  linkTo(methodOn(EventController.class)
                .create(new EventDto()))
                .slash(eventResource.getEvent().getId());
        WebMvcLinkBuilder queryLink =  linkTo(methodOn(EventController.class));
        eventResource.add(queryLink.withRel("query-events"));
        eventResource.add(selfAndUpdateLink.withRel("update-event"));
        eventResource.add(selfAndUpdateLink.withSelfRel());
    }
}

테스트 결과


좋은 웹페이지 즐겨찾기