ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • gRPC ③ gRPC + 스프링부트 프로젝트 구성해보기
    네트워크 & 인프라 2022. 10. 5. 22:20

     

     

     

    이번엔 gRPC의 이해를 높이기 위해서 스프링부트 프로젝트를 이용하여 client 모듈과 gRPC 서버 모듈로 분리하여 서로 통신하는 것을 확인해보자. 

     

     

     

    참고한 글 
     

    Spring Boot + gRPC (and, my experience of gRPC)

    이번 포스트는 google에서 개발한 HTTP-based RPC Framework, gRPC를 소개하려 한다. Spring Boot를 사용해 gRPC Server를 만드는 방법을 알아보자.

    medium.com

     

    gRPC 사용법, gRPC 예제 코드 실행해보기, 원리는 몰라도 gRPC 입문은 가능하다 (grpc java example)

    이 포스트는 springcamp2017에서 grpc발표를 하신 오명운님의 발표 자료 및 github소스를 참고해서 작성한 것입니다. gRPC의 장점 service 정의가 단순하다 여러 프로그래밍 언어나 플랫폼에서 사용이 가

    jeong-pro.tistory.com

     

    GitHub - LogNet/grpc-spring-boot-starter: Spring Boot starter module for gRPC framework.

    Spring Boot starter module for gRPC framework. . Contribute to LogNet/grpc-spring-boot-starter development by creating an account on GitHub.

    github.com

     

     

     

     

    참고한 영상 
     

    GitHub - DevProblems/grpc-with-springboot: This repo is part of my youtube video. It covers gRPC with spring boot demo

    This repo is part of my youtube video. It covers gRPC with spring boot demo - GitHub - DevProblems/grpc-with-springboot: This repo is part of my youtube video. It covers gRPC with spring boot demo

    github.com

     

     

     

     

     

     

     

     

     

    1. 기본 프로젝트 실행하기

     

     

     

    • 앞으로 구성할 전체적인 프로젝트 구조는 다음과 같다.

     

     

     

     

     

    2. 프로젝트에 proto 모듈 생성
    • proto 모듈에는 기본적으로 grpc의 proto base files를 생성하기위한 정보를 담는다.
    • 먼저 pom.xml에 다음과 같이 grpc 사용을 위한 의존관계를 추가해준다.
    • 여기서 ‘:osx-x86_64’는 자신의 컴퓨터 버전에 알맞게 설정해준다.
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>grpc-with-springboot</artifactId>
            <groupId>com.example</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>proto</artifactId>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
                <version>1.30.0</version>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
                <version>1.30.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <extensions>
                <extension>
                    <groupId>kr.motd.maven</groupId>
                    <artifactId>os-maven-plugin</artifactId>
                    <version>1.6.1</version>
                </extension>
            </extensions>
            <plugins>
                <plugin>
                    <groupId>org.xolstice.maven.plugins</groupId>
                    <artifactId>protobuf-maven-plugin</artifactId>
                    <version>0.6.1</version>
                    <configuration>
                        <protocArtifact>
                            com.google.protobuf:protoc:3.3.0:exe:osx-x86_64
                        </protocArtifact>
                        <pluginId>grpc-java</pluginId>
                        <pluginArtifact>
                            io.grpc:protoc-gen-grpc-java:1.4.0:exe:osx-x86_64
                        </pluginArtifact>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>compile-custom</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    </project>

     

     

     

     

    • proto / src / main / proto 폴더에 schema.proto 파일을 생성해준다.
    • unary - synchronous, server streaming - asynchronous, client streaming - asynchronous, bidirectional streaming - asynchronous 종류의 서비스를 생성
    // 프로토 최신 버전사용
    syntax = "proto3";
    
    // 사용할 패키지
    package com.example;
    
    // 파일이 자바 멀티플 파일 바탕으로 생성되도록
    option java_multiple_files = true;
    
    // 책과 작가의 클래스 정의
    message Book {
      int32 book_id = 1;
      string title = 2;
      float price = 3;
      int32 pages = 4;
      int32 author_id = 5;
    }
    
    message Author {
      int32 author_id = 1;
      string first_name = 2;
      string last_name = 3;
      string gender = 4;
      int32 book_id = 5;
    }
    
    service BookAuthorService {
    
      // unary - synchronous
      // client will send one request and server will respond with one response
      rpc getAuthor(Author) returns(Author){}
    
      // server streaming - asynchronous
      // client will send one request and server will respond with stream of messages to the client
      rpc getBookByAuthor(Author) returns(stream Book){}
    
      // client streaming - asynchronous
      // client will send stream of messages and server will respond with one response
      rpc getExpensiveBook(stream Book) returns(Book){}
    
      // bidirectional steaming - asynchronous
      // client will send stream of messages and server will respond with stream of messages to the client
        rpc getBookByAuthorGender(stream Book) returns(stream Book){}
    }

     

     

     

    • 생성된 proto 폴더를 sources root로 지정해준다.

     

     

     

    • proto / src / main / java / com / example에 TempDb(테스트용 데이터베이스)를 추가로 작성해준다.
    package com.example;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TempDb {
        public static List<Author> getAuthorsFromTempDb() {
            return new ArrayList<Author>() {
                {
                    add(Author.newBuilder().setAuthorId(1).setBookId(1).setFirstName("Charles").setLastName("Dickens").setGender("male").build());
                    add(Author.newBuilder().setAuthorId(2).setFirstName("William").setLastName("Shakespeare").setGender("male").build());
                    add(Author.newBuilder().setAuthorId(3).setFirstName("JK").setLastName("Rowling").setGender("female").build());
                    add(Author.newBuilder().setAuthorId(4).setFirstName("Virginia").setLastName("Woolf").setGender("female").build());
                }
            };
        }
    
        public static List<Book> getBooksFromTempDb() {
            return new ArrayList<Book>() {
                {
                    add(Book.newBuilder().setBookId(1).setAuthorId(1).setTitle("Oliver Twist").setPrice(123.3f).setPages(100).build());
                    add(Book.newBuilder().setBookId(2).setAuthorId(1).setTitle("A Christmas Carol").setPrice(223.3f).setPages(150).build());
                    add(Book.newBuilder().setBookId(3).setAuthorId(2).setTitle("Hamlet").setPrice(723.3f).setPages(250).build());
                    add(Book.newBuilder().setBookId(4).setAuthorId(3).setTitle("Harry Potter").setPrice(423.3f).setPages(350).build());
                    add(Book.newBuilder().setBookId(5).setAuthorId(3).setTitle("The Casual Vacancy").setPrice(523.3f).setPages(450).build());
                    add(Book.newBuilder().setBookId(6).setAuthorId(4).setTitle("Mrs. Dalloway").setPrice(623.3f).setPages(550).build());
                }
            };
        }
    }

     

     

     

    • proto 모듈의 maven compile을 실행하여 proto base files가 다음과 같이 자동 생성되는 것을 확인한다.

     

     

     

    • grpc-java 폴더와 java 폴더를 generated sources root로 지정해준다.

     

     

     

    • proto 모듈의 전체적인 구조는 다음과 같다.

     

     

     

     

     

     

     

     

     

    3. 프로젝트에 grpc-service 모듈 생성
    • grpc 서버 파트를 담당할 모듈을 생성하고 다음과 같이 dependency를 추가해준다.
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>grpc-with-springboot</artifactId>
            <groupId>com.example</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>grpc-service</artifactId>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>proto</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.grpc</groupId>
                        <artifactId>grpc-stub</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>io.grpc</groupId>
                        <artifactId>google-protobuf</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-spring-boot-starter</artifactId>
                <version>2.9.0.RELEASE</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>

     

     

     

    • grpc-service / src / main / java / com / example / BookAuthorServerService 클래스를 생성한다.
    • BookAuthorServerService 클래스는 BookAuthorServiceGrpc.BookAuthorServiceImplBase를 상속받고 메서드를 오버라이딩 해준다.
    • 각각의 메서드는 StreamObserver 는 client streaming 유무에 따라 다르게 작성된다.
    package com.example;
    
    import io.grpc.stub.StreamObserver;
    import net.devh.boot.grpc.server.service.GrpcService;
    
    import java.util.ArrayList;
    import java.util.List;
    
    // proto에 정의한 메서드를 만듦
    @GrpcService
    public class BookAuthorServerService extends BookAuthorServiceGrpc.BookAuthorServiceImplBase {
        // onNext : back to client, onCompleted : call the successful end of the method
        @Override
        public void getAuthor(Author request, StreamObserver<Author> responseObserver) {
            TempDb.getAuthorsFromTempDb().stream()
                    .filter(author -> author.getAuthorId() == request.getAuthorId())
                    .findFirst()
                    .ifPresent(responseObserver::onNext);
            responseObserver.onCompleted();
        }
    
        @Override
        public void getBookByAuthor(Author request, StreamObserver<Book> responseObserver) {
            TempDb.getBooksFromTempDb().stream()
                    .filter(book -> book.getAuthorId() == request.getAuthorId())
                    .forEach(responseObserver::onNext);
            responseObserver.onCompleted();
        }
    
        @Override
        public StreamObserver<Book> getExpensiveBook(StreamObserver<Book> responseObserver) {
            return new StreamObserver<Book>() {
                Book expensiveBook = null;
                float priceTrack = 0;
    
                // 각각의 책들은 book 파라미터로 넘어올 것이다
                @Override
                public void onNext(Book book) {
                    if (book.getPrice() > priceTrack) {
                        priceTrack = book.getPrice();
                        expensiveBook = book;
                    }
                }
    
                @Override
                public void onError(Throwable throwable) {
                    responseObserver.onError(throwable);
                }
    
                @Override
                public void onCompleted() {
                    // 다시한번 onNext 를 call (book 1개를 리턴하기 때문)
                    responseObserver.onNext(expensiveBook);
                    responseObserver.onCompleted();
                }
            };
        }
    
        @Override
        public StreamObserver<Book> getBookByAuthorGender(StreamObserver<Book> responseObserver) {
            return new StreamObserver<Book>() {
                List<Book> books = new ArrayList<>();
    
                @Override
                public void onNext(Book book) {
                    TempDb.getBooksFromTempDb().stream()
                            .filter(booksFromDb -> book.getAuthorId() == booksFromDb.getAuthorId())
                            .forEach(books::add);
                }
    
                @Override
                public void onError(Throwable throwable) {
                    responseObserver.onError(throwable);
                }
    
                @Override
                public void onCompleted() {
                    // 클라이언트의 옵저버를 부름
                    books.forEach(responseObserver::onNext);
                    responseObserver.onCompleted();
                }
            };
        }
    }

     

     

     

    • 가장 메인 클래스인 ServerApplication은 다음과 같이 작성해준다.
    package com.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class ServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServerApplication.class, args);
        }
    }

     

     

     

    • resources / application.ym에 client 서버와 포트를 다르게 설정하기 위해 다음과 같이 작성한다.
    • 톰캣 포트는 8081에, grpc 포트(기본 9090)는 9000에 열린다.
    server:
      port: 8081
    grpc:
      server:
        port: 9000

     

     

     

    • grpc-service 모듈의 구조는 다음과 같다.

     

     

     

     

     

     

     

     

     

    4. 프로젝트에 client-service 모듈 생성
    • grpc 서버를 이용할 클라이언트 모듈을 생성하고 다음과 같이 dependency를 추가해준다.
    • 여기서 grpc-client-spring-boot-starter가 서버와 다름을 확인할 수 있다.
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>grpc-with-springboot</artifactId>
            <groupId>com.example</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>client-service</artifactId>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>proto</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.grpc</groupId>
                        <artifactId>grpc-stub</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>io.grpc</groupId>
                        <artifactId>google-protobuf</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-client-spring-boot-starter</artifactId>
                <version>2.9.0.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>3.12.0</version>
                <scope>compile</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

     

     

     

     

    • client-service / src / main / java / com / example / controller 폴더에 BookAuthorController 에 다음과 같이 작성한다.
    • 이를 통해 client 호스트를 통해서 들어온 Rest API 요청이 전달된다.
    • 전체적인 흐름
      • 클라이언트의 BookAuthorController 를 통해 요청이 들어옴
      • BookAuthorClientService의 메서드를 호출 (asynchronous 의 경우에 CountDownLatch를 사용)
      • streamObserver를 이용해서 BookAuthorClientService의 옵저버를 작성한다.
      • 해당 서비스 메서드에서 gRPC 서버의 옵저버를 호출하여 onNext동작
      • gRPC의 서비스 메서드에서는 클라이언트 서비스의 옵저버를 통해 onNext 및 onComplete동작 수행
      • 클라이언트 서비스 메서드에서 onComplete 수행 및 반환값 반환
      • 결과가 controller로 전해짐
    import com.google.protobuf.Descriptors;
    
    import java.util.List;
    import java.util.Map;
    
    @RestController
    public class BookAuthorController {
    
        private final BookAuthorClientService bookAuthorClientService;
    
        public BookAuthorController(BookAuthorClientService bookAuthorClientService) {
            this.bookAuthorClientService = bookAuthorClientService;
        }
    
        @GetMapping("/authors/{authorId}")
        public Map<Descriptors.FieldDescriptor, Object> getAuthor(@PathVariable String authorId){
            return bookAuthorClientService.getAuthor(Integer.parseInt(authorId));
        }
    
        @GetMapping("/books/authors/{authorId}")
        public List<Map<Descriptors.FieldDescriptor, Object>> getBooksByAuthor(@PathVariable String authorId) throws InterruptedException {
            return bookAuthorClientService.getBooksByAuthor(Integer.parseInt(authorId));
        }
    
        @GetMapping("/expensive-book")
        public Map<String, Map<Descriptors.FieldDescriptor, Object>> getExpensiveBook() throws InterruptedException {
            return bookAuthorClientService.getExpensiveBook();
        }
    
        @GetMapping("/books/by-authors-gender/{gender}")
        public List<Map<Descriptors.FieldDescriptor, Object>> getBooksByAuthorGender(@PathVariable String gender) throws InterruptedException {
            return bookAuthorClientService.getBooksByAuthorGender(gender);
        }
    }

     

     

     

     

    • client-service / src / main / java / com / example / service 폴더에 BookAuthorClientService 에 다음과 같이 작성한다.
    • @GrpcClient 어노테이션을 통해서 grpc 통신에 사용할 채널을 정해준다.
    • synchronous, asynchronous 등의 여부에 따라서 BookAuthorServiceBlockingStub, BookAuthorServiceStub 등을 정한다.
    package com.example.service;
    
    import com.example.Author;
    import com.example.Book;
    import com.example.BookAuthorServiceGrpc;
    import com.example.TempDb;
    import com.google.protobuf.Descriptors;
    import io.grpc.stub.StreamObserver;
    import net.devh.boot.grpc.client.inject.GrpcClient;
    import org.springframework.stereotype.Service;
    
    import java.util.*;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    @Service
    public class BookAuthorClientService {
    
        // (채널)을 열어준다
        @GrpcClient("grpc-example-service")
        BookAuthorServiceGrpc.BookAuthorServiceBlockingStub synchronousClient;
    
        @GrpcClient("grpc-example-service")
        BookAuthorServiceGrpc.BookAuthorServiceStub asynchronousClient;
    
        public Map<Descriptors.FieldDescriptor, Object> getAuthor(int authorId) {
            Author authorRequest = Author.newBuilder().setAuthorId(authorId).build();
            Author authorResponse = synchronousClient.getAuthor(authorRequest);
            return authorResponse.getAllFields();
        }
    
    		// 비동기 설정 
        public List<Map<Descriptors.FieldDescriptor, Object>> getBooksByAuthor(int authorId) throws InterruptedException {
            // streamObserver는 다른 쓰레드에서 동작하게 된다
            // main Thread는 다른 쓰레드가 완료하기 까지 기다려야만 한다
            // rest api는 반응을 빠르게 갖는다
            final CountDownLatch countDownLatch = new CountDownLatch(1);
            Author authorRequest = Author.newBuilder().setAuthorId(authorId).build();
            final List<Map<Descriptors.FieldDescriptor, Object>> response = new ArrayList<>();
            asynchronousClient.getBookByAuthor(authorRequest, new StreamObserver<Book>() {
                @Override
                public void onNext(Book book) {
                    response.add(book.getAllFields());
                }
    
                // 만약 오류가 발생하면 countDown을 하여 메인 쓰레드가 기다리지 않도록 한다
                @Override
                public void onError(Throwable throwable) {
                    countDownLatch.countDown();
                }
    
                @Override
                public void onCompleted() {
                    countDownLatch.countDown();
                }
            });
            // 하나의 쓰레드가 완료하기 까지 1분 기다림
            boolean await = countDownLatch.await(1, TimeUnit.MINUTES);
            // 완료 되었으면 반응을 return, 아니면 빈 리스트 반환
            return await ? response : Collections.emptyList();
        }
    
        public Map<String, Map<Descriptors.FieldDescriptor, Object>> getExpensiveBook() throws InterruptedException {
            final CountDownLatch countDownLatch = new CountDownLatch(1);
            final Map<String, Map<Descriptors.FieldDescriptor, Object>> response = new HashMap<>();
            // callback registration
            StreamObserver<Book> responseObserver = asynchronousClient.getExpensiveBook(new StreamObserver<Book>() {
                @Override
                public void onNext(Book book) {
                    response.put("ExpensiveBook", book.getAllFields());
                }
    
                @Override
                public void onError(Throwable throwable) {
                    countDownLatch.countDown();
                }
    
                @Override
                public void onCompleted() {
                    countDownLatch.countDown();
                }
            });
    
            TempDb.getBooksFromTempDb().forEach(responseObserver::onNext);
            responseObserver.onCompleted();
            boolean await = countDownLatch.await(1, TimeUnit.MINUTES);
            return await ? response : Collections.emptyMap();
        }
    
        public List<Map<Descriptors.FieldDescriptor, Object>> getBooksByAuthorGender(String gender) throws InterruptedException {
            final CountDownLatch countDownLatch = new CountDownLatch(1);
            final List<Map<Descriptors.FieldDescriptor, Object>> response = new ArrayList<>();
            // callback registration
            StreamObserver<Book> responseObserver = asynchronousClient.getBookByAuthorGender(new StreamObserver<Book>() {
                // 서버에서 부른 클라이언트의 옵저버 onNext
                @Override
                public void onNext(Book book) {
                    response.add(book.getAllFields());
                }
    
                @Override
                public void onError(Throwable throwable) {
                    countDownLatch.countDown();
                }
    
                @Override
                public void onCompleted() {
                    countDownLatch.countDown();
                }
            });
    
            TempDb.getAuthorsFromTempDb()
                    .stream()
                    .filter(author -> author.getGender().equalsIgnoreCase(gender))
                    // server의 옵저버를 부름
                    .forEach(author -> responseObserver.onNext(Book.newBuilder().setAuthorId(author.getAuthorId()).build()));
            responseObserver.onCompleted();
            boolean await = countDownLatch.await(1, TimeUnit.MINUTES);
            return await ? response : Collections.emptyList();
        }
    
    }

     

     

     

     

    • 가장 메인 클래스인 ClientApplication은 다음과 같이 작성해준다.
    package com.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class ClientApplication {
        public static void main(String[] args) {
            SpringApplication.run(ClientApplication.class, args);
        }
    }
    • grpc 서버의 설정을 위해 다음과 같이 resources / application.yml 파일을 작성해준다.
    grpc:
      client:
        grpc-example-service:
          address: static://localhost:9000
          negotiationType: plaintext

     

     

     

     

    • client-service 모듈의 구조는 다음과 같다.

     

     

     

     

     

     

     

    5. 프로젝트 실행 및 API 콜
    • 이제 차례대로 올바르게 결과값을 반환하는지 확인해보자.
    • client-service와 grpc-service를 run한 상태에서 API를 조회한다.
    • 먼저 작가의 아이디를 통해 작가정보를 반환하는 unary - synchronous getAuthor 메소드

     

     

     

    • 두번째로, 작가의 아이디를 통해 작가의 책 정보들을 반환하는  server streaming - asynchronous getBooksByAuthor 메소드

     

     

     

     

    • 세번째로, 주어진 TempDb의 책중에서(client streaming) 가장 비싼 책 정보를 반환하는 client streaming - asynchronous의 getExpensiveBook 메소드 

     

     

     

     

     

    • 마지막으로, 주어진 TempDb의 책중에서(client streaming) 주어진 gender 정보와 일치하는 책 리스틑 정보를(server streaming) 반환하는  bidirectional streaming - asynchronous의 getBooksByAuthorGender 메소드 

     

     

    • 알파벳 케이스 ignore를 이용했기 때문에 다음과 같이 조회해도 올바른 결과 값을 조회하는 것을 확인이 가능하다. 

     

     

     

     

     

     

     

     

Designed by Tistory.