1. 개요

마이크로서비스 아키텍처 및 클라우드 네이티브 애플리케이션 개발의 인기로 인해 빠르고 가벼운 애플리케이션 서버에 대한 요구가 증가하고 있습니다.

이 입문 사용방법(예제)에서는 RESTful 웹 서비스를 만들고 사용하기 위해 Open Liberty 프레임워크를 탐색합니다 . 또한 제공되는 몇 가지 필수 기능을 살펴보겠습니다.

2. 오픈 리버티

Open Liberty는 Eclipse MicroProfileJakarta EE 플랫폼 의 기능을 사용하여 마이크로서비스를 개발할 수 있는 Java 에코시스템용 개방형 프레임워크 입니다 .

클라우드 네이티브 마이크로서비스 개발에 유망해 보이는 유연하고 빠르며 가벼운 Java 런타임입니다.

프레임워크를 사용하면 앱에 필요한 기능만 구성할 수 있으므로 시작하는 동안 메모리 공간이 더 작아집니다. 또한 DockerKubernetes 와 같은 컨테이너를 사용하여 모든 클라우드 플랫폼에 배포할 수 있습니다 .

빠른 반복을 위해 코드를 실시간으로 다시 로드하여 신속한 개발을 지원합니다.

3. 구축 및 실행

먼저 open-liberty 라는 간단한 Maven 기반 프로젝트를 만든 다음 최신 liberty-maven-plugin 플러그인을 pom.xml 에 추가합니다 .

<plugin>
    <groupId>io.openliberty.tools</groupId>
    <artifactId>liberty-maven-plugin</artifactId>
    <version>3.3-M3</version>
</plugin>

또는 liberty-maven-plugin 에 대한 대안으로 최신 openliberty-runtime Maven 의존성을 추가할 수 있습니다 .

<dependency>
    <groupId>io.openliberty</groupId>
    <artifactId>openliberty-runtime</artifactId>
    <version>20.0.0.1</version>
    <type>zip</type>
</dependency>

마찬가지로 최신 Gradle 의존성을 build.gradle 에 추가할 수 있습니다 .

dependencies {
    libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '20.0.0.1'
}

그런 다음 최신 jakarta.jakartaee-web-api 및  microprofile Maven 의존성을 추가합니다 .

<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>8.0.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.eclipse.microprofile</groupId>
    <artifactId>microprofile</artifactId>
    <version>3.2</version>
    <type>pom</type>
    <scope>provided</scope>
</dependency>

그런 다음 기본 HTTP 포트 속성을 pom.xml 에 추가해 보겠습니다 .

<properties>
    <liberty.var.default.http.port>9080</liberty.var.default.http.port>
    <liberty.var.default.https.port>9443</liberty.var.default.https.port>
</properties>

다음으로 src/main/liberty/config 디렉토리 에 server.xml 파일을 생성합니다 .

<server description="Baeldung Open Liberty server">
    <featureManager>
        <feature>mpHealth-2.0</feature>
    </featureManager>
    <webApplication location="open-liberty.war" contextRoot="/" />
    <httpEndpoint host="*" httpPort="${default.http.port}" 
      httpsPort="${default.https.port}" id="defaultHttpEndpoint" />
</server>

여기에 응용 프로그램의 상태를 확인하는 mpHealth-2.0 기능을 추가했습니다 .

이것으로 모든 기본 설정이 끝났습니다. Maven 명령을 실행하여 처음으로 파일을 컴파일해 보겠습니다.

mvn clean package

마지막으로 Liberty에서 제공하는 Maven 명령을 사용하여 서버를 실행해 보겠습니다.

mvn liberty:dev

짜잔! 애플리케이션이 시작되고  localhost:9080 에서 액세스할 수 있습니다 .

Screen-Shot-2020-01-15-at-1.46.17-PM

또한 localhost:9080/health 에서 앱의 상태에 액세스할 수 있습니다 .

{"checks":[],"status":"UP"}

liberty :dev 명령은 서버를 다시 시작하지 않고 코드 또는 구성에 대한 모든 변경 사항을 핫 리로드하는 개발 모드에서 Open Liberty 서버를 시작합니다 .

마찬가지로 liberty:run 명령을 사용하여 프로덕션 모드에서 서버를 시작할 수 있습니다.

또한 liberty:start-serverliberty: stop-server를 사용하여 백그라운드에서 서버를 시작/중지할 수 있습니다 .

4. 서블릿

앱에서 서블릿을 사용하기 위해 server.xmlservlet-4.0 기능을 추가합니다 .

<featureManager>
    ...
    <feature>servlet-4.0</feature>
</featureManager>

pom.xml 에서 openliberty-runtime Maven 의존성을 사용하는 경우 최신 servlet-4.0 Maven 의존성을 추가합니다 .

<dependency>
    <groupId>io.openliberty.features</groupId>
    <artifactId>servlet-4.0</artifactId>
    <version>20.0.0.1</version>
    <type>esa</type>
</dependency>

그러나 liberty-maven-plugin 플러그인을 사용하는 경우에는 필요하지 않습니다.

그런 다음 HttpServlet 클래스를 확장하는 AppServlet 클래스를 만듭니다 .

@WebServlet(urlPatterns="/app")
public class AppServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
        String htmlOutput = "<html><h2>Hello! Welcome to Open Liberty</h2></html>";
        response.getWriter().append(htmlOutput);
    }
}

여기에서 지정된 URL 패턴에서 AppServlet을 사용할 수 있도록 하는 @WebServlet 어노테이션을 추가했습니다 .

localhost:9080/app 에서 서블릿에 액세스해 보겠습니다 .

Screen-Shot-2020-01-18-at-4.21.46-PM

5. RESTful 웹 서비스 생성

먼저 server.xmljaxrs-2.1 기능을 추가해 보겠습니다 .

<featureManager>
    ...
    <feature>jaxrs-2.1</feature>
</featureManager>

그런 다음 RESTful 웹 서비스에 Endpoints을 제공하는 ApiApplication 클래스를 만듭니다 .

@ApplicationPath("/api")
public class ApiApplication extends Application {
}

여기서는 URL 경로에 @ApplicationPath 어노테이션을 사용했습니다 .

그런 다음 모델을 제공하는 Person 클래스를 생성해 보겠습니다 .

public class Person {
    private String username;
    private String email;

    // getters and setters
    // constructors
}

다음으로 HTTP 매핑을 정의하는 PersonResource 클래스를 만듭니다 .

@RequestScoped
@Path("persons")
public class PersonResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> getAllPersons() {
        return Arrays.asList(new Person(1, "normanlewis", "normanlewis@email.com"));
    }
}

여기에서 /api/persons Endpoints 에 대한 GET 매핑을 위한 getAllPersons 메서드를 추가했습니다 . 이제 RESTful 웹 서비스를 사용할 준비가 되었으며 liberty:dev 명령이 변경 사항을 즉석에서 로드합니다.

curl GET 요청을 사용하여 /api/persons RESTful 웹 서비스에 액세스해 보겠습니다 .

curl --request GET --url http://localhost:9080/api/persons

그런 다음 응답으로 JSON 배열을 얻습니다.

[{"id":1, "username":"normanlewis", "email":"normanlewis@email.com"}]

마찬가지로 addPerson 메소드 를 생성하여 POST 매핑을 추가할 수 있습니다 .

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response addPerson(Person person) {
    String respMessage = "Person " + person.getUsername() + " received successfully.";
    return Response.status(Response.Status.CREATED).entity(respMessage).build();
}

이제 curl POST 요청으로 엔드포인트를 호출할 수 있습니다.

curl --request POST --url http://localhost:9080/api/persons \
  --header 'content-type: application/json' \
  --data '{"username": "normanlewis", "email": "normanlewis@email.com"}'

응답은 다음과 같습니다.

Person normanlewis received successfully.

6. 끈기

6.1. 구성

RESTful 웹 서비스에 지속성 지원을 추가해 보겠습니다.

먼저 pom.xml 에 derby Maven 의존성을 추가합니다 .

<dependency>
    <groupId>org.apache.derby</groupId>
    <artifactId>derby</artifactId>
    <version>10.14.2.0</version>
</dependency>

그런 다음 jpa-2.2 , jsonp-1.1cdi-2.0 과 같은 몇 가지 기능을 server.xml추가합니다 .

<featureManager>
    ...
    <feature>jpa-2.2</feature> 
    <feature>jsonp-1.1</feature>
    <feature>cdi-2.0</feature>
</featureManager>

여기에서 jsonp-1.1 기능은 JSON 처리를 위한 Java API를 제공하고 cdi-2.0 기능은 범위 및 의존성 주입을 처리합니다.

다음으로 src/main/resources/META-INF 디렉토리 에 persistence.xml을 생성합니다 .

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="jpa-unit" transaction-type="JTA">
        <jta-data-source>jdbc/jpadatasource</jta-data-source>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
            <property name="eclipselink.ddl-generation.output-mode" value="both" />
        </properties>
    </persistence-unit>
</persistence>

여기서는 EclipseLink DDL 생성을 사용하여 데이터베이스 스키마를 자동으로 생성했습니다. Hibernate와 같은 다른 대안을 사용할 수도 있습니다.

그런 다음 dataSource 구성을 server.xml추가해 보겠습니다 .

<library id="derbyJDBCLib">
    <fileset dir="${shared.resource.dir}" includes="derby*.jar"/> 
</library>
<dataSource id="jpadatasource" jndiName="jdbc/jpadatasource">
    <jdbcDriver libraryRef="derbyJDBCLib" />
    <properties.derby.embedded databaseName="libertyDB" createDatabase="create" />
</dataSource>

참고로 jndiName 에는 persistence.xml 의 jta-data-source 태그 에 대한 동일한 참조가 있습니다 .

6.2. 엔티티 및 DAO

그런 다음 Person 클래스에 @Entity 어노테이션과 식별자를 추가합니다 .

@Entity
public class Person {
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private int id;
    
    private String username;
    private String email;

    // getters and setters
}

다음으로 EntityManager 인스턴스를 사용하여 데이터베이스와 상호 작용할 PersonDao 클래스를 생성해 보겠습니다 .

@RequestScoped
public class PersonDao {
    @PersistenceContext(name = "jpa-unit")
    private EntityManager em;

    public Person createPerson(Person person) {
        em.persist(person);
        return person;
    }

    public Person readPerson(int personId) {
        return em.find(Person.class, personId);
    }
}

@PersistenceContextpersistence.xmlpersistence-unit 태그 에 대한 동일한 참조를 정의합니다 .

이제 PersonResource 클래스 에 PersonDao 의존성을 주입합니다 .

@RequestScoped
@Path("person")
public class PersonResource {
    @Inject
    private PersonDao personDao;

    // ...
}

여기에서는 CDI 기능에서 제공하는 @Inject 어노테이션을 사용했습니다 .

마지막으로 Person 개체를 유지하기 위해 PersonResource 클래스 의 addPerson 메서드 를 업데이트합니다 .

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public Response addPerson(Person person) {
    personDao.createPerson(person);
    String respMessage = "Person #" + person.getId() + " created successfully.";
    return Response.status(Response.Status.CREATED).entity(respMessage).build();
}

여기에서 addPerson 메소드는 CDI 관리 Bean에서 트랜잭션을 제어하기 위해 @Transactional 어노테이션 으로 어노테이션됩니다 .

이미 논의한 curl POST 요청으로 Endpoints을 호출해 보겠습니다.

curl --request POST --url http://localhost:9080/api/persons \
  --header 'content-type: application/json' \
  --data '{"username": "normanlewis", "email": "normanlewis@email.com"}'

그러면 다음과 같은 텍스트 응답을 받게 됩니다.

Person #1 created successfully.

마찬가지로 Person 개체를 가져오기 위해 GET 매핑과 함께 getPerson 메서드를 추가해 보겠습니다 .

@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public Person getPerson(@PathParam("id") int id) {
    Person person = personDao.readPerson(id);
    return person;
}

curl GET 요청을 사용하여 Endpoints을 호출해 보겠습니다.

curl --request GET --url http://localhost:9080/api/persons/1

그런 다음 JSON 응답으로 Person 개체를 가져옵니다 .

{"email":"normanlewis@email.com","id":1,"username":"normanlewis"}

7. JSON-B를 사용하여 RESTful 웹 서비스 사용

먼저 server.xmljsonb-1.0 기능 을 추가하여 모델을 직접 직렬화 및 역직렬화하는 기능을 활성화합니다 .

<featureManager>
    ...
    <feature>jsonb-1.0</feature>
</featureManager>

그런 다음 ConsumeWithJsonb 메서드를 사용하여 RestConsumer 클래스를 생성해 보겠습니다 .

public class RestConsumer {
    public static String consumeWithJsonb(String targetUrl) {
        Client client = ClientBuilder.newClient();
        Response response = client.target(targetUrl).request().get();
        String result = response.readEntity(String.class);
        response.close();
        client.close();
        return result;
    }
}

여기에서는 ClientBuilder 클래스를 사용하여 RESTful 웹 서비스 엔드포인트를 요청했습니다.

마지막으로 /api/person RESTful 웹 서비스를 사용 하고 응답을 확인하는 단위 테스트를 작성해 보겠습니다.

@Test
public void whenConsumeWithJsonb_thenGetPerson() {
    String url = "http://localhost:9080/api/persons/1";
    String result = RestConsumer.consumeWithJsonb(url);        
    
    Person person = JsonbBuilder.create().fromJson(result, Person.class);
    assertEquals(1, person.getId());
    assertEquals("normanlewis", person.getUsername());
    assertEquals("normanlewis@email.com", person.getEmail());
}

여기서는 JsonbBuilder 클래스를 사용하여 String 응답을 Person 개체 로 구문 분석했습니다 .

또한 RESTful 웹 서비스를 사용하기 위해 mpRestClient-1.3 기능을 추가하여 MicroProfile Rest Client를 사용할 수 있습니다 . RESTful 웹 서비스 엔드포인트를 요청하기 위해 RestClientBuilder 인터페이스를 제공합니다 .

8. 결론

이 기사에서는 Eclipse MicroProfile 및 Jakarta EE 플랫폼의 모든 기능을 제공하는 빠르고 가벼운 Java 런타임인 Open Liberty 프레임워크를 살펴보았습니다.

먼저 JAX-RS를 사용하여 RESTful 웹 서비스를 만들었습니다. 그런 다음 JPA 및 CDI와 같은 기능을 사용하여 지속성을 활성화했습니다.

마지막으로 JSON-B를 사용하여 RESTful 웹 서비스를 사용했습니다.

평소와 같이 모든 코드 구현은  GitHub에서 사용할 수 있습니다 .

res – REST with Spring (eBook) (everywhere)