1. 소개
이 기사에서는 Spark 프레임워크 에 대한 간략한 소개를 제공 합니다. Spark 프레임워크는 Ruby용 Sinatra 프레임워크에서 영감을 받은 신속한 개발 웹 프레임워크이며 Java 8 Lambda Expression 철학을 기반으로 구축되어 다른 Java 프레임워크로 작성된 대부분의 애플리케이션보다 덜 장황합니다.
Java에서 웹 API 또는 마이크로 서비스를 개발할 때 Node.js 와 같은 경험 을 갖고 싶다면 좋은 선택 입니다. Spark를 사용하면 10줄 미만의 코드로 JSON을 제공할 준비가 된 REST API를 가질 수 있습니다.
"Hello World" 예제로 빠르게 시작한 다음 간단한 REST API를 사용합니다.
2. 메이븐 의존성
2.1. 스파크 프레임워크
pom.xml 에 다음 Maven 의존성을 포함합니다 .
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.5.4</version>
</dependency>
최신 버전의 Spark는 Maven Central 에서 찾을 수 있습니다 .
2.2. Gson 라이브러리
예제의 다양한 위치에서 JSON 작업을 위해 Gson 라이브러리를 사용합니다. 프로젝트에 Gson을 포함하려면 pom.xml 에 다음 의존성을 포함하세요 .
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
Maven Central 에서 최신 버전의 Gson을 찾을 수 있습니다 .
3. 스파크 프레임워크 시작하기
Spark 애플리케이션의 기본 빌딩 블록을 살펴보고 빠른 웹 서비스를 시연해 보겠습니다.
3.1. 노선
Spark Java의 웹 서비스는 경로와 핸들러를 기반으로 구축됩니다. 경로는 Spark의 필수 요소입니다. 설명서 에 따라 각 경로는 동사 , 경로 및 콜백 의 세 가지 간단한 부분으로 구성됩니다 .
- 동사 는 HTTP 메서드에 해당하는 메서드입니다. 동사 메서드에는 get, post, put, delete, head, trace, connect 및 options 가 포함됩니다.
- 경로 ( 경로 패턴이라고도 함)는 경로가 수신 대기하고 응답을 제공해야 하는 URI를 결정합니다.
- 콜백 은 해당 HTTP 요청에 대한 응답을 생성하고 반환하기 위해 지정된 동사 및 경로에 대해 호출되는 핸들러 함수입니다. 콜백은 요청 객체와 응답 객체를 인수로 사용합니다.
다음은 get 동사 를 사용하는 경로의 기본 구조를 보여줍니다 .
get("/your-route-path/", (request, response) -> {
// your callback code
});
3.2. 헬로월드 API
GET 요청에 대한 두 개의 경로가 있고 응답으로 "Hello" 메시지를 반환하는 간단한 웹 서비스를 만들어 봅시다. 이러한 경로 는 spark.Spark 클래스의 정적 가져오기인 get 메서드를 사용합니다 .
import static spark.Spark.*;
public class HelloWorldService {
public static void main(String[] args) {
get("/hello", (req, res)->"Hello, world");
get("/hello/:name", (req,res)->{
return "Hello, "+ req.params(":name");
});
}
}
get 메소드 의 첫 번째 인수 는 경로의 경로입니다. 첫 번째 경로에는 단일 URI( "/hello" ) 만 나타내는 정적 경로가 포함됩니다 .
두 번째 경로의 경로( "/hello/:name" )에는 "name" 매개 변수 앞에 콜론(":")을 추가하여 표시되는 자리 표시자가 포함되어 있습니다. 이 경로는 "/hello/Joe" 및 "/hello/Mary" 와 같은 URI에 대한 GET 요청에 대한 응답으로 호출됩니다 .
get 메서드 의 두 번째 인수 는 이 프레임워크에 기능적 프로그래밍 풍미를 제공하는 람다 식 입니다.
람다 식은 요청 및 응답을 인수로 가지며 응답을 반환하는 데 도움이 됩니다. 이 사용방법(예제)의 뒷부분에서 볼 수 있듯이 REST API 경로에 대한 람다 식에 컨트롤러 논리를 넣을 것입니다.
3.3. Hello World API 테스트
HelloWorldService 클래스를 일반 Java 클래스로 실행한 후 위의 get 메서드 로 정의된 경로를 사용하여 기본 포트 4567 에서 서비스에 액세스할 수 있습니다 .
첫 번째 경로에 대한 요청 및 응답을 살펴보겠습니다.
요구:
GET http://localhost:4567/hello
응답:
Hello, world
경로에 name 매개 변수를 전달하여 두 번째 경로를 테스트해 보겠습니다 .
요구:
GET http://localhost:4567/hello/baeldung
응답:
Hello, baeldung
경로 패턴 "/hello/:name" 과 일치시키기 위해 URI에서 텍스트 "baeldung" 의 배치가 어떻게 사용되어 두 번째 경로의 콜백 핸들러 함수가 호출되었는지 확인하십시오.
4. RESTful 서비스 설계
이 섹션에서는 다음 사용자 엔터티에 대한 간단한 REST 웹 서비스를 설계합니다.
public class User {
private String id;
private String firstName;
private String lastName;
private String email;
// constructors, getters and setters
}
4.1. 노선
API를 구성하는 경로를 나열해 보겠습니다.
- GET /users — 모든 사용자 List 가져오기
- GET /users/:id — 주어진 ID를 가진 사용자 가져오기
- POST /users/:id — 사용자 추가
- PUT /users/:id — 특정 사용자 편집
- 옵션 /users/:id — 주어진 ID를 가진 사용자가 존재하는지 확인
- DELETE /users/:id — 특정 사용자 삭제
4.2. 사용자 서비스
다음은 User 엔터티 에 대한 CRUD 작업을 선언하는 UserService 인터페이스입니다.
public interface UserService {
public void addUser (User user);
public Collection<User> getUsers ();
public User getUser (String id);
public User editUser (User user)
throws UserException;
public void deleteUser (String id);
public boolean userExist (String id);
}
데모를 위해 GitHub 코드에서 이 UserService 인터페이스의 Map 구현을 제공하여 지속성을 시뮬레이션합니다. 선택한 데이터베이스 및 지속성 계층으로 자체 구현을 제공할 수 있습니다.
4.3. JSON 응답 구조
다음은 REST 서비스에서 사용되는 응답의 JSON 구조입니다.
{
status: <STATUS>
message: <TEXT-MESSAGE>
data: <JSON-OBJECT>
}
상태 필드 값은 SUCCESS 또는 ERROR 일 수 있습니다 . 데이터 필드에는 User 또는 Users 컬렉션 과 같은 반환 데이터의 JSON 표현이 포함 됩니다 .
반환되는 데이터가 없거나 상태 가 ERROR 인 경우 오류 또는 반환 데이터 부족에 대한 이유를 전달하기 위해 메시지 필드를 채웁니다 .
Java 클래스를 사용하여 위의 JSON 구조를 표현해 보겠습니다.
public class StandardResponse {
private StatusResponse status;
private String message;
private JsonElement data;
public StandardResponse(StatusResponse status) {
// ...
}
public StandardResponse(StatusResponse status, String message) {
// ...
}
public StandardResponse(StatusResponse status, JsonElement data) {
// ...
}
// getters and setters
}
여기서 StatusResponse 는 아래와 같이 정의된 Enum 입니다.
public enum StatusResponse {
SUCCESS ("Success"),
ERROR ("Error");
private String status;
// constructors, getters
}
5. RESTful 서비스 구현
이제 REST API에 대한 경로와 핸들러를 구현해 보겠습니다.
5.1. 컨트롤러 만들기
다음 Java 클래스에는 동사와 경로 및 각 경로에 대한 핸들러 개요를 포함하여 API에 대한 경로가 포함되어 있습니다.
public class SparkRestExample {
public static void main(String[] args) {
post("/users", (request, response) -> {
//...
});
get("/users", (request, response) -> {
//...
});
get("/users/:id", (request, response) -> {
//...
});
put("/users/:id", (request, response) -> {
//...
});
delete("/users/:id", (request, response) -> {
//...
});
options("/users/:id", (request, response) -> {
//...
});
}
}
다음 하위 섹션에서 각 경로 처리기의 전체 구현을 보여줍니다.
5.2. 사용자 추가
다음은 User 를 추가할 post 메소드 응답 핸들러입니다 .
post("/users", (request, response) -> {
response.type("application/json");
User user = new Gson().fromJson(request.body(), User.class);
userService.addUser(user);
return new Gson()
.toJson(new StandardResponse(StatusResponse.SUCCESS));
});
참고: 이 예에서 User 개체 의 JSON 표현은 POST 요청의 원시 본문으로 전달됩니다.
경로를 테스트해 보겠습니다.
요구:
POST http://localhost:4567/users
{
"id": "1012",
"email": "your-email@your-domain.com",
"firstName": "Mac",
"lastName": "Mason1"
}
응답:
{
"status":"SUCCESS"
}
5.3. 모든 사용자 가져오기
다음은 UserService 에서 모든 사용자를 반환 하는 get 메서드 응답 핸들러입니다 .
get("/users", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(userService.getUsers())));
});
이제 경로를 테스트해 보겠습니다.
요구:
GET http://localhost:4567/users
응답:
{
"status":"SUCCESS",
"data":[
{
"id":"1014",
"firstName":"John",
"lastName":"Miller",
"email":"your-email@your-domain.com"
},
{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason1",
"email":"your-email@your-domain.com"
}
]
}
5.4. ID로 사용자 가져오기
다음은 주어진 ID 를 가진 사용자 를 반환하는 get 메서드 응답 핸들러입니다 .
get("/users/:id", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(userService.getUser(request.params(":id")))));
});
이제 경로를 테스트해 보겠습니다.
요구:
GET http://localhost:4567/users/1012
응답:
{
"status":"SUCCESS",
"data":{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason1",
"email":"your-email@your-domain.com"
}
}
5.5. 사용자 편집
다음은 경로 패턴에 제공된 ID 를 가진 사용자를 편집하는 put 메소드 응답 핸들러 입니다.
put("/users/:id", (request, response) -> {
response.type("application/json");
User toEdit = new Gson().fromJson(request.body(), User.class);
User editedUser = userService.editUser(toEdit);
if (editedUser != null) {
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(editedUser)));
} else {
return new Gson().toJson(
new StandardResponse(StatusResponse.ERROR,new Gson()
.toJson("User not found or error in edit")));
}
});
참고: 이 예에서 데이터는 편집할 사용자 개체 의 필드와 속성 이름이 일치하는 JSON 개체로 POST 요청의 원시 본문에 전달됩니다 .
경로를 테스트해 보겠습니다.
요구:
PUT http://localhost:4567/users/1012
{
"lastName": "Mason"
}
응답:
{
"status":"SUCCESS",
"data":{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason",
"email":"your-email@your-domain.com"
}
}
5.6. 사용자 삭제
다음은 주어진 ID 를 가진 사용자 를 삭제하는 delete 메소드 응답 핸들러입니다 .
delete("/users/:id", (request, response) -> {
response.type("application/json");
userService.deleteUser(request.params(":id"));
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS, "user deleted"));
});
이제 경로를 테스트해 보겠습니다.
요구:
DELETE http://localhost:4567/users/1012
응답:
{
"status":"SUCCESS",
"message":"user deleted"
}
5.7. 사용자가 존재하는지 확인
옵션 방법 은 조건부 검사에 적합합니다. 다음은 주어진 ID 를 가진 사용자 가 존재 하는지 여부를 확인하는 options 메서드 응답 핸들러입니다 .
options("/users/:id", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,
(userService.userExist(
request.params(":id"))) ? "User exists" : "User does not exists" ));
});
이제 경로를 테스트해 보겠습니다.
요구:
OPTIONS http://localhost:4567/users/1012
응답:
{
"status":"SUCCESS",
"message":"User exists"
}
6. 결론
이 기사에서는 신속한 웹 개발을 위한 Spark 프레임워크에 대해 간단히 소개했습니다.
이 프레임워크는 주로 Java에서 마이크로서비스를 생성하기 위해 홍보됩니다. JVM 라이브러리에 구축된 라이브러리를 활용하려는 Java 지식이 있는 Node.js 개발자는 이 프레임워크를 사용하는 것이 편안해야 합니다.
그리고 언제나처럼 Github 프로젝트 에서 이 예제의 모든 소스를 찾을 수 있습니다 .