1. 개요

이 기사에서는 주로 Spring REST API 및 간단한 AngularJS 프런트 엔드 에서 서버 측 페이지 매김을 구현하는 데 중점을 둘 것 입니다.

또한 Angular에서 UI Grid 라는 이름으로 일반적으로 사용되는 테이블 그리드를 탐색합니다 .

2. 의존성

여기에서는이 기사에 필요한 다양한 의존성을 자세히 설명합니다.

2.1. 자바 스크립트

Angular UI Grid가 작동하려면 HTML로 가져온 아래 스크립트가 필요합니다.

2.2. 메이븐

백엔드의 경우 Spring Boot 를 사용하므로 아래 의존성이 필요합니다.

<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>

참고 : 다른 의존성은 여기에 지정되지 않았습니다. 전체 List을 보려면 GitHub 프로젝트 에서 전체 pom.xml확인하세요 .

3. 신청에 관하여

이 응용 프로그램은 사용자가 페이지가 매겨진 표 그리드에서 학생 세부 정보를 볼 수있는 간단한 학생용 디렉토리 앱입니다.

이 애플리케이션은 Spring Boot를 사용 하며 임베디드 데이터베이스가있는 임베디드 Tomcat 서버에서 실행됩니다.

마지막으로 API 측면에서 페이지 나누기를 수행하는 몇 가지 방법이 있습니다. 여기에있는 SpringREST Pagination 기사에 설명되어 있습니다.이 기사와 함께 읽어 보는 것이 좋습니다.

여기서 우리의 솔루션은 간단합니다. / student / get? page = 1 & size = 2 와 같이 URI 쿼리에 페이징 정보를 포함합니다 .

4. 클라이언트 측

먼저 클라이언트 측 로직을 생성해야합니다.

4.1. UI 그리드

우리의 index.html을은 우리가 필요로하는 수입과 테이블 그리드의 간단한 구현을해야합니다 :

<!DOCTYPE html>
<html lang="en" ng-app="app">
    <head>
        <link rel="stylesheet" href="https://cdn.rawgit.com/angular-ui/
          bower-ui-grid/master/ui-grid.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/
          1.5.6/angular.min.js"></script>
        <script src="https://cdn.rawgit.com/angular-ui/bower-ui-grid/
          master/ui-grid.min.js"></script>
        <script src="view/app.js"></script>
    </head>
    <body>
        <div ng-controller="StudentCtrl as vm">
            <div ui-grid="gridOptions" class="grid" ui-grid-pagination>
            </div>
        </div>
    </body>
</html>

코드를 자세히 살펴 보겠습니다.

  • ng-app – 모듈 app 을로드하는 Angular 지시문입니다 . 이 아래의 모든 요소는 모듈의 일부가됩니다.
  • ng-controllervm 별칭을 사용 하여 컨트롤러 StudentCtrl 을로드하는 Angular 지시문입니다 . 이 아래의 모든 요소는 StudentCtrl 컨트롤러의 일부입니다.
  • UI - 그리드 - 각도에 속하는 각도 지침입니다 UI 그리드 및 사용 gridOptions , 기본 설정으로 gridOptions이 아래에 선언 $ 범위 에서 app.js

4.2. AngularJS 모듈

먼저 app.js 에서 모듈을 정의 하겠습니다 .

var app = angular.module('app', ['ui.grid','ui.grid.pagination']);

모듈을 선언하고 UI-Grid 기능을 활성화하기 위해 ui.grid삽입 했습니다 . 또한 페이지 매김 지원을 활성화하기 위해 ui.grid.pagination삽입 했습니다 .

다음으로 컨트롤러를 정의합니다.

app.controller('StudentCtrl', ['$scope','StudentService', 
    function ($scope, StudentService) {
        var paginationOptions = {
            pageNumber: 1,
            pageSize: 5,
        sort: null
        };

    StudentService.getStudents(
      paginationOptions.pageNumber,
      paginationOptions.pageSize).success(function(data){
        $scope.gridOptions.data = data.content;
        $scope.gridOptions.totalItems = data.totalElements;
      });

    $scope.gridOptions = {
        paginationPageSizes: [5, 10, 20],
        paginationPageSize: paginationOptions.pageSize,
        enableColumnMenus:false,
    useExternalPagination: true,
        columnDefs: [
           { name: 'id' },
           { name: 'name' },
           { name: 'gender' },
           { name: 'age' }
        ],
        onRegisterApi: function(gridApi) {
           $scope.gridApi = gridApi;
           gridApi.pagination.on.paginationChanged(
             $scope, 
             function (newPage, pageSize) {
               paginationOptions.pageNumber = newPage;
               paginationOptions.pageSize = pageSize;
               StudentService.getStudents(newPage,pageSize)
                 .success(function(data){
                   $scope.gridOptions.data = data.content;
                   $scope.gridOptions.totalItems = data.totalElements;
                 });
            });
        }
    };
}]);

이제 $ scope.gridOptions 에서 사용자 지정 페이지 매김 설정을 살펴 보겠습니다 .

  • paginationPageSizes – 사용 가능한 페이지 크기 옵션을 정의합니다.
  • paginationPageSize – 기본 페이지 크기를 정의합니다.
  • enableColumnMenus – 열 메뉴를 활성화 / 비활성화하는 데 사용됩니다.
  • useExternalPagination – 서버 측에서 페이지를 매기 는 경우 필수입니다.
  • columnDefs – 서버에서 반환 된 JSON 개체에 자동으로 매핑 될 열 이름입니다. 서버에서 반환 된 JSON 개체의 필드 이름과 정의 된 열 이름이 일치해야합니다.
  • onRegisterApi – 그리드 내부에 공개 메소드 이벤트를 등록하는 기능. 여기서 우리 는 페이지가 변경 될 때마다이 함수를 트리거하도록 UI-Grid에 알리기 위해 gridApi.pagination.on.paginationChanged등록했습니다 .

API에 요청을 보내려면 :

app.service('StudentService',['$http', function ($http) {

    function getStudents(pageNumber,size) {
        pageNumber = pageNumber > 0?pageNumber - 1:0;
        return $http({
          method: 'GET',
            url: 'student/get?page='+pageNumber+'&size='+size
        });
    }
    return {
        getStudents: getStudents
    };
}]);

5. 백엔드와 API

5.1. RESTful 서비스

다음은 페이지 매김을 지원하는 간단한 RESTful API 구현입니다.

@RestController
public class StudentDirectoryRestController {

    @Autowired
    private StudentService service;

    @RequestMapping(
      value = "/student/get", 
      params = { "page", "size" }, 
      method = RequestMethod.GET
    )
    public Page<Student> findPaginated(
      @RequestParam("page") int page, @RequestParam("size") int size) {

        Page<Student> resultPage = service.findPaginated(page, size);
        if (page > resultPage.getTotalPages()) {
            throw new MyResourceNotFoundException();
        }

        return resultPage;
    }
}

@RestController는 암시 적 선언의 편의를 어노테이션으로 Spring 4.0에 도입 된 @Controller@ResponseBody를.

API의 경우 클라이언트에 반환 할 레코드 수를 결정하는 페이지 와 크기 인 두 개의 매개 변수를 허용하도록 선언했습니다 .

또한 페이지 번호가 총 페이지 수보다 높으면 MyResourceNotFoundException을 발생 시키는 간단한 유효성 검사를 추가했습니다 .

마지막으로 페이지 를 응답으로 반환 합니다. 이것은 페이지 매김 데이터를 보유한 S pring 데이터 의 매우 유용한 구성 요소입니다 .

5.2. 서비스 구현

당사 서비스는 컨트롤러가 제공 한 페이지 및 크기에 따라 레코드를 반환합니다.

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentRepository dao;

    @Override
    public Page<Student> findPaginated(int page, int size) {
        return dao.findAll(new PageRequest(page, size));
    }
}

5.3. 리포지토리 구현

지속성 계층의 경우 임베디드 데이터베이스와 SpringData JPA를 사용하고 있습니다.

먼저 지속성 구성을 설정해야합니다.

@EnableJpaRepositories("com.baeldung.web.dao")
@ComponentScan(basePackages = { "com.baeldung.web" })
@EntityScan("com.baeldung.web.entity") 
@Configuration
public class PersistenceConfig {

    @Bean
    public JdbcTemplate getJdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        EmbeddedDatabase db = builder
          .setType(EmbeddedDatabaseType.HSQL)
          .addScript("db/sql/data.sql")
          .build();
        return db;
    }
}

지속성 구성은 간단합니다. @EnableJpaRepositories 를 사용하여 지정된 패키지를 스캔하고 SpringData JPA 저장소 인터페이스를 찾습니다.

여기에는 모든 빈을 자동으로 스캔 하는 @ComponentScan 이 있고 엔티티 클래스를 스캔하는 @EntityScan (Spring Boot)이 있습니다.

또한 시작시 제공되는 SQL 스크립트를 실행하는 내장 데이터베이스를 사용하여 간단한 데이터 소스를 선언했습니다.

이제 데이터 저장소를 만들 차례입니다.

public interface StudentRepository extends JpaRepository<Student, Long> {}

이것은 기본적으로 우리가 여기서해야 할 모든 것입니다. 매우 강력한 SpringData JPA를 설정하고 사용하는 방법에 대해 자세히 알아 보려면 여기에서 가이드를 읽어보십시오 .

6. 페이지 매김 요청 및 응답

API – http : // localhost : 8080 / student / get? page = 1 & size = 5를 호출 할 때 JSON 응답은 다음과 같습니다.

{
    "content":[
        {"studentId":"1","name":"Bryan","gender":"Male","age":20},
        {"studentId":"2","name":"Ben","gender":"Male","age":22},
        {"studentId":"3","name":"Lisa","gender":"Female","age":24},
        {"studentId":"4","name":"Sarah","gender":"Female","age":26},
        {"studentId":"5","name":"Jay","gender":"Male","age":20}
    ],
    "last":false,
    "totalElements":20,
    "totalPages":4,
    "size":5,
    "number":0,
    "sort":null,
    "first":true,
    "numberOfElements":5
}

여기서 주목해야 할 점은 서버가 학생 리소스를 래핑 하는 org.springframework.data.domain.Page DTO를 반환한다는 것 입니다.

페이지 객체에는 다음과 같은 필드가있을 것이다 :

  • last마지막 페이지 이면 true로 설정하고 그렇지 않으면 false로 설정합니다.
  • first첫 페이지 이면 true로 설정하고 그렇지 않으면 false
  • totalElements – 총 행 / 레코드 수입니다. 이 예에서는이를 ui-grid 옵션 $ scope.gridOptions.totalItems에 전달 하여 사용할 수있는 페이지 수를 결정했습니다.
  • totalPages – ( totalElements / size ) 에서 파생 된 총 페이지 수
  • size – 페이지 당 레코드 수, 매개 변수 크기 를 통해 클라이언트에서 전달됨
  • number – 클라이언트가 보낸 페이지 번호, 응답에서 숫자는 0입니다. 백엔드 에서는 0부터 시작하는 인덱스 Student 배열을 사용하고 있으므로 백엔드에서는 페이지 번호를 1 씩 감소시킵니다.
  • sort – 페이지의 정렬 매개 변수
  • numberOfElements – 페이지에 대해 반환되는 행 / 레코드 수

7. 페이지 매김 테스트

이제 RestAssured를 사용하여 페이지 매김 로직에 대한 테스트를 설정해 보겠습니다 . RestAssured 에 대해 자세히 알아 보려면 튜토리얼을 참조하십시오 .

7.1. 테스트 준비

테스트 클래스를 쉽게 개발하기 위해 정적 임포트를 추가 할 것입니다.

io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*

다음으로 Spring 지원 테스트를 설정합니다.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest("server.port:8888")

@SpringApplicationConfiguration은 Spring이로드하는 방법을 알고 도움 의 ApplicationContext를, 이 경우, 우리는 사용 Application.java를 우리의 구성 의 ApplicationContext를.

@WebAppConfiguration는 것을 Spring에게 정의 의 ApplicationContext가 해야한다로드 할 수 의 WebApplicationContext.

그리고 @IntegrationTest 는 테스트를 실행할 때 애플리케이션 시작을 트리거하도록 정의 되었으므로 REST 서비스를 테스트에 사용할 수 있습니다.

7.2. 테스트

다음은 첫 번째 테스트 사례입니다.

@Test
public void givenRequestForStudents_whenPageIsOne_expectContainsNames() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("content.name", hasItems("Bryan", "Ben"));
}

위의 테스트 사례는 페이지 1과 크기 2가 REST 서비스에 전달 될 때 서버에서 반환 된 JSON 콘텐츠의 이름이 BryanBen 이어야하는지 테스트하기위한 것 입니다.

테스트 케이스를 분석해 보겠습니다.

  • givenRestAssured 의 일부이며 요청 빌드를 시작하는 데 사용됩니다. with ()를 사용할 수도 있습니다.
  • getRestAssured 의 일부이며 사용되는 경우 get 요청을 트리거하면 post () 요청에 대해 사용
  • hasItems – 값이 일치하는지 확인하는 hamcrest의 일부

몇 가지 테스트 사례를 더 추가합니다.

@Test
public void givenRequestForStudents_whenResourcesAreRetrievedPaged_thenExpect200() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .statusCode(200);
}

이 테스트는 포인트가 실제로 호출 될 때 OK 응답을 수신한다고 주장합니다.

@Test
public void givenRequestForStudents_whenSizeIsTwo_expectNumberOfElementsTwo() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("numberOfElements", equalTo(2));
}

이 테스트는 페이지 크기 2가 요청 될 때 반환되는 페이지 크기가 실제로 2임을 주장합니다.

@Test
public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("first", equalTo(true));
}

이 테스트는 리소스가 처음 호출 될 때 첫 번째 페이지 이름 값이 참임을 확인합니다.

저장소에는 더 많은 테스트가 있으므로 반드시 GitHub 프로젝트를 살펴보십시오 .

8. 결론

이 기사에서는 AngularJS 에서 UI-Grid사용하여 데이터 테이블 그리드 를 구현하는 방법과 필요한 서버 측 페이지 매김을 구현하는 방법을 설명했습니다.

이러한 예제 및 테스트의 구현은 GitHub 프로젝트 에서 찾을 수 있습니다 . 이것은 Maven 프로젝트이므로 그대로 가져 와서 실행하기 쉽습니다.

Spring 부트 프로젝트를 실행하려면 mvn spring-boot : run을 수행 하고 http : // localhost : 8080 / 에서 로컬로 액세스하면됩니다 .