Spring boot rest 를 이용하여 API 서버를 개발해보자!

Boot에 대해 알아봤으니 다음은 spring-boot-rest 대해 알아보자.
모르는분은 링크참조
rest중 우리는 jpa를 살펴볼것이다.

프로젝트 생성후 처음 할일은 메이븐에 디펜던시를 추가 하는일이다.
아래와같이 추가를 해보자.

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.6</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

살펴보면 spring-boot-starter-data-restweb, jackson과 관련된 라이브러리가 디펜던시 되어있다.
spring-boot-starter-data-jpa 아시다시피 jpa 관련 라이브러리다.
간단하게 살펴보는거니까 메모리 디비를 사용하겠다.
귀찮은 작업이 있으니 아주 좋은 lombok을 사용하겠다. 이놈은 선택사항이다. 모르시는분은 인터넷을 찾아보면 아주 자세히 나와있다.

메인 클래스를 만들자.

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

다음으론 entity(domain) 클래스를 만들자.

@Entity
@Data
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String email;

    private String password;

}

다음으론 Repository 인터페이스를 생성해보자

@RepositoryRestResource(collectionResourceRel = "account", path = "account")
public interface AccountRepository extends JpaRepository<Account, Long> {

    Page<Account> findByname(@Param("name") String name, Pageable pageable);
}

기본 데이터가 있어야 하기 때문에 resourcesimport.sql을 추가 한다. 아래와 같이 입력해보자

insert into account(id, name, email, password) values(1,'wonwoo','wonwoo@test.com','qwer')
insert into account(id, name, email, password) values(2,'kevin','kevin@test.com','asdf')
insert into account(id, name, email, password) values(3,'wonwoo1','kevin@test.com','qwqw')

혹은 java8을 이용한다면 좀더 멋지게 해보자

    @Bean
    CommandLineRunner runner(AccountRepository accountRepository) {
        return args -> {
            Arrays.asList(
                    new Account(1L, "wonwoo", "wonwoo@test.com", "qwer"),
                    new Account(2L, "kevin", "kevin@test.com", "asdf"),
                    new Account(3L, "wonwoo", "kevin@test.com", "qwqw")
            )
                    .forEach(account -> accountRepository.save(account));
            accountRepository.findAll().forEach(System.out::println);
        };
    }

아 물론 Account 클래스를 수정해야된다.

@Entity
@Data
@AllArgsConstructor //추가 모든 필드가 있는 생성자를 만든다.
@NoArgsConstructor  //추가 디폴트 생성자를 만든다. 이놈이 필요한이유는 JPA 덕분 
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String email;

    private String password;

}

그럼 이제 메인클래스를 실행해보자!
실행이 완료되었다면 http://localhost:8080/

{
  _links: {
    account: {
      href: "http://localhost:8080/account{?page,size,sort}",
      templated: true
    },
    profile: {
      href: "http://localhost:8080/profile"
    }
  }
}

웹브라우저에 이런 화면을 볼수 있다. 깔끔하게 볼라면 크롬 확장 프로그램을 설치하자.(JSONView) 였던걸로 기억한다.
이번엔 아래와 같이 계정 정보를 봐보자!
http://localhost:8080/account

{
  "_embedded": {
    "account": [
      {
        "name": "wonwoo",
        "email": "wonwoo@test.com",
        "password": "qwer",
        "_links": {
          "self": {
            "href": "http://localhost:8080/account/1"
          },
          "account": {
            "href": "http://localhost:8080/account/1"
          }
        }
      },
      {
        "name": "kevin",
        "email": "kevin@test.com",
        "password": "asdf",
        "_links": {
          "self": {
            "href": "http://localhost:8080/account/2"
          },
          "account": {
            "href": "http://localhost:8080/account/2"
          }
        }
      },
      {
        "name": "wonwoo1",
        "email": "kevin@test.com",
        "password": "qwqw",
        "_links": {
          "self": {
            "href": "http://localhost:8080/account/3"
          },
          "account": {
            "href": "http://localhost:8080/account/3"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/account"
    },
    "profile": {
      "href": "http://localhost:8080/profile/account"
    },
    "search": {
      "href": "http://localhost:8080/account/search"
    }
  },
  "page": {
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "number": 0
  }
}

흠. 잘 나온다. 만족하는 결과다.

이번엔 http://localhost:8080/account/search 브라우저에 띄우자

{
  "_links": {
    "findByname": {
      "href": "http://localhost:8080/account/search/findByname{?name,page,size,sort}",
      "templated": true
    },
    "self": {
      "href": "http://localhost:8080/account/search"
    }
  }
}

url 중 search 는 자동으로 생성되는듯하다. findByname 메소드 명이다.

마지막으로 이름으로 검색을 해보자
http://localhost:8080/account/search/findByname?name=wonwoo

{
  "_embedded": {
    "account": [
      {
        "name": "wonwoo",
        "email": "wonwoo@test.com",
        "password": "qwer",
        "_links": {
          "self": {
            "href": "http://localhost:8080/account/1"
          },
          "account": {
            "href": "http://localhost:8080/account/1"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/account/search/findByname?name=wonwoo"
    }
  },
  "page": {
    "size": 20,
    "totalElements": 1,
    "totalPages": 1,
    "number": 0
  }
}

이렇게 나왔다면 성공!!

한가지 살펴 볼 것이 있다.
바로 @RepositoryRestResource(collectionResourceRel = "account", path = "account") 이거다.
RepositoryRestResource 이 애노테이션은 선택인거 같다. 없어도 잘 된다(필자는 잘된다.). 물론 스캔 범위에 있다는 가정이다. 하지만 명시적인게 좋으니 써주는것도 나쁘지 않다. 없으면 엔티티 복수형으로 자동생성하는듯 하다.
애노테이션 속성중 collectionResourceRel은 key 속성이다. 다시말해

"account": { //이 속성
      "href": "http://localhost:8080/account{?page,size,sort}",
      "templated": true
},

그리고 path는 url path를 말하는거다.
마지막으로 findByname 메소드 안에 @Param("key") 어노테이션은 파라미터의 키이다. 없으면 에러가 난다. 감지를 할수 없다는 듯하다.
key를 변경해보자.

Page<Account> findByname(@Param("first_name") String name, Pageable pageable);

그럼 키가 변경된 것을 알 수있다.

{
  "_links": {
    "findByname": {
      "href": "http://localhost:8080/account/search/findByname{?first_name,page,size,sort}",
      "templated": true
    },
    "self": {
      "href": "http://localhost:8080/account/search"
    }
  }
}

다음편은 조금더 세부적으로 살펴보자!

참고 : 아래와 같이 maven에 추가해보자.

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-hal-browser</artifactId>
    </dependency>

그리고 다시 http://localhost:8080 브라우저를 열어보자!
쉽게 테스트 할 수 있다.