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의 필수 요소입니다. 설명서 에 따라 각 경로는 동사 , 경로콜백 의 세 가지 간단한 부분으로 구성됩니다 .

  1. 동사 는 HTTP 메서드에 해당하는 메서드입니다. 동사 메서드에는 get, post, put, delete, head, trace, connectoptions 가 포함됩니다.
  2. 경로 ( 경로 패턴이라고도 함)는 경로가 수신 대기하고 응답을 제공해야 하는 URI를 결정합니다.
  3. 콜백 은 해당 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 프로젝트 에서 이 예제의 모든 소스를 찾을 수 있습니다 .

REST footer banner