모범 사례 및 튜토리얼

스프링 부트 애플리케이션에 랫팩 사랑을 불어넣으세요

8 분 읽기

저는 최근에 Ratpack을 사용하기 시작했는데 꽤 마음에 듭니다. 주로 처음부터 시작하는 간단한 프로젝트들을 진행했습니다. 하지만 기존 스프링 부트(Spring Boot) 애플리케이션에서 전통적인 MVC 부분을 대체하기 위해 그것을 사용하고 싶습니다. 이것은 모든 것이 이미 잘 마련되어 있기 때문에 실제로 쉽게 할 수 있습니다. 스프링 모듈.

이 블로그를 구독하신다면 에 관한 예전 게시물을 기억하실지도 모릅니다 저장, 색인 및 검색 ~가 있는 파일 카우치베이스 그리고 스프링 부트를 사용할 것입니다. 관련 코드 예를 들어, 핵심 아이디어는 Spring MVC를 Ratpack으로 교체하고 기존의 레거시, 동기식, 블로킹 서비스를 비동기 및 넌블로킹(non-blocking) 방식으로 전환하는 것입니다. 결과 코드는 다음에서 확인할 수 있습니다. 깃허브 또한.

올바른 의존성 추가하기

나는 Gradle을 사용하고 있다. Ratpack은 이것과 매우 잘 통합되어 있다. 모듈을 추가하기 위해 해야 할 일은 올바른 의존성을 호출하여 추가하는 것뿐이다. ratpack.dependency("myFavoriteModule"). 따라서 저희의 경우, Spring Boot에 대한 지원을 추가하려면 추가해야 합니다 ratpack.dependency("spring-boot"). 안타깝게도 Ratpack이 자동으로 관리하는 버전은 자동 Couchbase 구성을 지원하는 버전인 1.4.0.M3보다 낮습니다. 따라서 이번에는 의존성을 수동으로 추가해야 합니다.

    dependencies {
      compile ratpack.dependency("guice"),
              ratpack.dependency("rx"),
              ratpack.dependency("handlebars"),
              "com.couchbase.client:java-client:2.3.1",
              "org.springframework.boot:spring-boot-autoconfigure:1.4.0.M3",
              "io.ratpack:ratpack-spring-boot:1.3.3",
              "org.slf4j:slf4j-simple:1.7.12",
              "org.codehaus.plexus:plexus-utils:3.0.21",
              "commons-codec:commons-codec:1.10" 
    }

여기서 볼 수 있는 것은 ratpack.dependency("spring-boot") 추가하는 바로 가기입니다 org.springframework.boot:spring-boot-autoconfigure:1.4.0.M3 그리고 io.ratpack:ratpack-spring-boot:1.3.3. 이 모듈을 사용하면 Ratpack 서버를 Spring 애플리케이션에 통합할 수 있습니다. Ratpack 컨텍스트에서 Spring @Bean을 가져오고, 핸들러를 Spring 구성으로 선언할 수 있습니다.

Ratpack 구성 선언

Spring Boot에서 정말 좋아해야 할 한 가지는 자동 설정입니다. Couchbase SDK가 클래스패스에 있고 속성이 있는지 확인하기만 하면 됩니다. spring.couchbase.bootstrap-hosts 가 선언됩니다. 그 순간 Spring 빈이 기본 버킷을 위해 인스턴스화됩니다. 그리고 이 버킷 인스턴스는 빈으로 또는 Ratpack의 컨텍스트에서 사용할 수 있게 됩니다. 따라서 Ratpack 레이어에 Couchbase용 바인딩을 선언할 필요가 없습니다.

Ratpack으로 전통적으로 가장 먼저 하는 일은 서버를 시작하고 설정과 핸들러를 정의하는 것입니다. 여기서는 이미 실행 중인 Spring Boot 애플리케이션이 있습니다. @Configuration이 주석으로 지정된 모든 클래스는 자동으로 감지되어 애플리케이션 설정에 추가됩니다. 해당 설정을 선언하는 첫 번째 단계는 구현하는 클래스를 만드는 것입니다 RatpackServerCustomizer 그리고 @Configuration으로 어노테이트하세요. 이를 통해 핸들러, 바인딩, 그리고 서버 설정 목록을 정의할 수 있습니다. 다음 예제에서는 몇 가지 서버 속성을 등록하고 여러 클래스를 Ratpack의 컨텍스트에 바인딩하고 있습니다. ‘server.maxContentLength’ 속성은 업로드할 수 있는 파일의 최대 크기입니다.

    package org.couchbase.devex;

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;

    import org.couchbase.devex.domain.StoredFileRenderer;
    import org.couchbase.devex.service.SearchService;
    import org.springframework.context.annotation.Configuration;

    import com.google.common.collect.ImmutableMap;

    import ratpack.form.Form;
    import ratpack.func.Action;
    import ratpack.guice.BindingsSpec;
    import ratpack.handlebars.HandlebarsModule;
    import ratpack.handlebars.Template;
    import ratpack.handling.Chain;
    import ratpack.rx.RxRatpack;
    import ratpack.server.BaseDir;
    import ratpack.server.ServerConfigBuilder;
    import ratpack.spring.config.RatpackServerCustomizer;
    import rx.Observable;

    @Configuration
    public class RatpackConfiguration implements RatpackServerCustomizer {

        @Override
        public List getHandlers() {
            List handlers = new ArrayList();
            handlers.add(fileApi());
            return handlers;
        }

        @Override
        public Action getServerConfig() {
            return config -> config.baseDir(BaseDir.find())
                    .props(ImmutableMap.of("server.maxContentLength", "100000000", "app.name", "Search Store File"));

        }

        @Override
        public Action getBindings() {
            return bindingConfig -> bindingConfig.module(HandlebarsModule.class).bind(FileHandler.class)
                    .bind(StoredFileRenderer.class).bind(ErrorHandlerImpl.class).bind(ClientHandlerImpl.class);
        }

        private Action fileApi() {
            return chain -> chain.prefix("file", FileHandler.class).post("fulltext", ctx -> {
                ctx.parse(Form.class).then(form -> {
                    String queryString = form.get("queryString");
                    SearchService searchService = ctx.get(SearchService.class);
                    Observable<map> files = searchService.searchFulltextFiles(queryString);
                    RxRatpack.promise(files).then(response -> ctx
                            .render(Template.handlebarsTemplate("uploadForm", "text/html", m -> m.put("files", response))));
                });
            }).post("n1ql", ctx -> {
                ctx.parse(Form.class).then(form -> {
                    String queryString = form.get("queryString");
                    SearchService searchService = ctx.get(SearchService.class);
                    Observable<map> files = searchService.searchN1QLFiles(queryString);
                    RxRatpack.promise(files).then(response -> ctx
                            .render(Template.handlebarsTemplate("uploadForm", "text/html", m -> m.put("files", response))));
                });
            });
        }
    }
</map</map

애플리케이션 템플릿 시스템은 Handlebars를 기반으로 하므로 HandlebarsModule이 필요합니다. FileHandler는 ‘/file’ API에 대한 모든 호출을 처리하며, StoredFileRenderer는 StoredFile이 올바르게 렌더링되도록 보장합니다. 마지막 두 바인딩은 오류 처리를 위한 것입니다.

여기서 가장 중요한 일은 파일API 내 핸들러를 선언하는 메서드입니다. 핸들러는 사용자가 특정 URL을 호출했을 때 어떤 동작이 수행될지를 정의합니다. 여기서는 ‘/file/*’에 대한 모든 호출을 FileHandler 클래스와 연결합니다. 또한 ‘/fulltext’ 및 ‘/n1ql’에 대한 POST 요청의 동작도 정의합니다.

Ratpack은 Promise를 사용합니다. 따라서 POST 요청에서 Form을 파싱할 때 Promise를 얻게 됩니다. 각 POST에서 볼 수 있듯이 SearchService는 Ratpack의 컨텍스트에서 가져옵니다. 설정에서 바인딩된 적이 없더라도 말입니다. 이는 통합의 일환으로 Spring 빈을 컨텍스트에서 사용할 수 있기 때문입니다.

다음 단계는 Observable을 반환하는 해당 검색 서비스를 호출하는 것입니다. Observable용 래퍼를 제공하는 Ratpack의 rx-java 모듈을 사용할 수 있습니다. 그러면 이것이 promise로 래핑됩니다. 그런 다음 응답을 간단히 렌더링하면 됩니다.

이 시점에서 우리는 모든 Spring MVC를 제거했습니다 컨트롤러. 보시다시피 제 서비스는 Observable을 반환합니다. 이전 애플리케이션에서는 그렇지 않았습니다.

랫팩용 서비스 마이그레이션

대부분의 서비스가 카우치베이스(Couchbase)에 의존하고 있습니다. SDK가 RxJava 기반이어서 대부분 비동기 및 넌블로킹 방식으로 쉽게 전환하고 Observable을 반환하도록 만들 수 있습니다.

RxJava 사용하기

이것은 아주 간단한 예제입니다. 결과를 Map의 List로 매핑하는 N1QL 쿼리입니다. 처음 두 줄은 주로 쿼리를 정의하는 것이므로 전혀 변경되지 않습니다. 두 번째 버전에서는 동기식 버킷을 사용할 때 매핑이 더 자연스럽게 느껴지는 것을 볼 수 있습니다.

    public List<map<string, object="">> searchN1QLFiles(String whereClause) {
        N1qlQuery query = N1qlQuery.simple(
                "SELECT binaryStoreLocation, binaryStoreDigest FROM `default` WHERE type= 'file' " + whereClause);
        query.params().consistency(ScanConsistency.STATEMENT_PLUS);
        N1qlQueryResult res = bucket.query(query);
        List<map<string, object="">> filenames = res.allRows().stream().map(row -> row.value().toMap())
                .collect(Collectors.toList());
        return filenames;
    }
</map<string,></map<string,>

된다

    public Observable<map<string, object="">> searchN1QLFiles(String whereClause) {
        N1qlQuery query = N1qlQuery.simple(
                "SELECT binaryStoreLocation, binaryStoreDigest FROM `default` WHERE type= 'file' " + whereClause);
        query.params().consistency(ScanConsistency.STATEMENT_PLUS);
        return bucket.async().query(query).flatMap(AsyncN1qlQueryResult::rows).map(r -> r.value().toMap());
    }
</map<string,>

레거시 코드의 차단을 어떻게 해야 할까요?

내 서비스 중 일부는 오래된 블로킹 코드에 의존하고 있습니다. 이를 넌블로킹으로 만드는 마법 같은 방법은 없지만, 간단히 Promise로 감싸는 것은 가능합니다. 이를 통해 핸들러에서 쉽게 사용할 수 있습니다. 블로킹 호출을 감싸는 것은 매우 쉬우며, 함수를 ‘Blocking.get()’으로 감싸기만 하면 됩니다. 여기 아주 간단한 예시가 있습니다:

    public String getSha1Digest(InputStream is) {
        return DigestUtils.sha1Hex(is);
    }

된다

    public Promise getSha1Digest(InputStream is) {
        return Blocking.get(() -> DigestUtils.sha1Hex(is));
    }

결론

이제 Spring Boot 애플리케이션에 Ratpack을 적용하는 데 필요한 거의 모든 것을 알게 되었습니다. 빠진 부분이 있다고 느껴지시면 저에게 연락해 주세요 트위터 또는 아래 댓글에 남겨주세요.

이 기사 공유하기

작가

로랑은 파리에 사는 찌질한 매니아 기질이 있는 헤비메탈 마니아입니다. 그는 주로 자바(Java)로 코드를 작성하고 AsciiDoc으로 구조화된 텍스트를 작성하며, 데이터, 리액티브 프로그래밍 및 기타 유행어 같은 주제에 대해 자주 이야기합니다. 그는 또한 Clever Cloud와 Nuxeo의 전직 개발자 애드보케이트로 활동하며 해당 커뮤니티가 더 크고 강하게 성장하도록 시간과 전문 지식을 쏟아부었습니다. 현재는 카우치베이스(Couchbase)에서 개발자 관계(Developer Relations)를 총괄하고 있습니다.

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

개발 시작하기

NoSQL을 탐색하고, 리소스를 찾아보고, 튜토리얼을 시작하려면 개발자 포털을 확인하세요.

카펠라 프리 사용하기

단 몇 번의 클릭으로 카우치베이스(Couchbase)를 직접 체험해 보세요. Capella DBaaS는 시작하기 가장 쉽고 빠른 방법입니다.

연락해

Couchbase 제품군에 대해 더 알고 싶으신가요? 저희가 도와드리겠습니다.