Spring Data REST #1 Introduction

2020. 9. 4. 16:09 Spring Framework/Spring boot

  Spring Camp 2017에서 Spring Data REST를 소개하는 발표를 했는데, 45분이라는 시간으로 Spring Data REST(이하 Data REST)를 모두 전달하기에는 짧은 시간이다. 그래서 블로그를 통해 좀 더 많은 내용을 전달하려고 한다.

Introduction

Spring Data REST 레퍼런스 문서에서 REST API는 애플리케이션 통합에서 중요한 수단이고, Spring MVC를 통해 쉽게 만들 수 있지만, 간단한 REST API를 만들기에는 boilerplate code가 많이 생긴다고 설명한다.

Feature

  • JPA, MongoDB, Neo4j, Solr, Cassandra, Gemfire 제공 (Spring Data 기반)
  • METADATA를 json으로 표현
  • HAL browser 제공
  • REST API를 쉽고 빠르게 만들 수 있다.

REST API 표준

  이 글에서는 REST API가 무엇인지 자세히 설명하지 않는다. (참고) Data REST는 REST API 표준을 잘 지킨다. 예를 들어 REST API 설계하기 위해 지켜야할 URI 표준을 보면 Data REST가 REST API 표준에 따라 코드가 작성되며 제공되는 것을 알 수 있다.

@RepositoryRestResource
public interface TodoRepository extends CrudRepository<Todo, Long> {}

  Data REST는 Repository interface 선언만으로 REST API를 만들 수 있다. 아직 Data REST를 설명하기 전이지만, 이 코드를 통해 Todo entity에 대한 REST API를 만들었다.

  URI 매핑은 /todoes다. 예를 들면 GET : /todoes/1은 1번 todo를 조회하는 URI이다. Data REST는 generic type으로 설정된 Todo entity 명을 기준으로 evo-inflector 라이브러리를 통해 복수 URI로 만든다.

일관성 있는 웹 서비스 인터페이스 설계를 위한 REST API 디자인 규칙을 읽어보면 좋다.

Spring MVC

  Data REST로 REST API를 만들기 전에 Spring MVC를 사용해 todo list REST API를 만들어보자. Spring으로 Web Application을 만들어본 개발자라면 익숙한 코드와 layer다.

TodoController

@AllArgsConstructor(onConstructor = @__(@Autowired))
@Controller
@RequestMapping("/todoes")
public class TodoController {

    private TodoRepository todoRepository;

    private TodoService todoService;

    @GetMapping("/{id}")
    public ResponseEntity<?> get(@PathVariable("id") Todo todo) {
        return ResponseEntity.ok(todo);
    }

    @GetMapping
    public ResponseEntity<?> list(Pageable pageable) {
        return ResponseEntity.ok(todoRepository.findAll(pageable));
    }

    @PostMapping
    public ResponseEntity<?> post(@RequestBody TodoDto todoDto) {
        Todo newTodo = this.todoService.todo(todoDto);
        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(newTodo);
    }

    @PutMapping("/{id}")
    public ResponseEntity<?> update(@PathVariable("id") Long todoId, @RequestBody TodoDto todoDto) {
        Todo updateTodo = this.todoService.update(todoId, todoDto);
        return ResponseEntity
                .status(HttpStatus.ACCEPTED)
                .body(updateTodo);
    }

    @Setter
    @Getter
    public static class TodoDto {
        private String todo;
        private LocalDate dueDate;
        private String username;

        public TodoDto() {}

        public TodoDto(String todo, LocalDate dueDate, String username) {
            this.todo = todo;
            this.dueDate = dueDate;
            this.username = username;
        }
    }

}

TodoController는 조회, 리스트 조회, 생성, 수정 REST API를 지원한다. TodoService를 통해서 조회를 구현하지 않고, TodoRepository에 바로 의존해 조회 API를 구현했다.

TodoService

@Service
@AllArgsConstructor(onConstructor = @__(@Autowired))
@Transactional
public class TodoService {

    private TodoRepository todoRepository;

    private MemberRepository memberRepository;

    public Todo todo(TodoController.TodoDto todoDto) {
        Member member = memberRepository.findByUsername(todoDto.getUsername());
        return todoRepository.save(new Todo(member, todoDto.getDueDate(), todoDto.getTodo()));
    }

    public Todo update(Long todoId, TodoController.TodoDto todoDto) {
        Todo todo = todoRepository.findOne(todoId);
        todo.update(todoDto);
        return todo;
    }
}

TodoRepository

@Repository
public interface TodoRepository extends PagingAndSortingRepository<Todo, Long> {}

예제로 보여주고있는 프로젝트는 Spring Data JPA를 사용해 코드를 작성했다. 자세한 코드 설명은 생략한다.

  예제는 todo list를 만들기 위한 코드를 보여주려는 의도는 아니다. Spring Data REST를 소개할때 boilerplate code 코드가 많이 생긴다고 설명했는데, Spring MVC를 사용해 REST API를 만들때 boilerplate code가 많이 생긴다는것을 보여주기 위함이다.

이게 틀리다는 말은 아니다. 하지만 정답도 아니다. 흔히 말하는 CRUD(생성, 조회, 수정, 삭제)는 대부분 단순하다. 위 코드를 보면 알겠지만, 복잡성도 높지 않고 단순하다. 요구사항에 todo 등록은 회원만 가능 하다고 했는데, Member 도메인을 위한 REST API를 만들기 위해서는 비슷한 코드(Controller -> Service -> Repository)를 작성해야한다.

Spring Data REST

Spring MVC로 만든 REST API를 Spring Data REST로 변경해보자.

TodoContoller

지우자!

TodoService

지우자!

TodoRepository

@RepositoryRestResource
public interface TodoRepository extends PagingAndSortingRepository<Todo, Long> {}

TodoController TodoService는 지웠다. TodoRepository @Repository @RepositoryRestResource로 변경했다. 그럼 어떻게될까? Data REST로 REST API를 모두 만들었다. 심지어 더 많은 API를 지원한다.

  • GET : /todoes/{id}
  • GET : /todoes{?page=&size=&sort}
  • PUT : /todoes/{id}
  • PATCH : /todoes/{id}
  • POST : /todoes
  • DELETE : /todoes/{id}

예제

출처 : https://blog.woniper.net/345?category=699184