지난 몇 달 동안 저는 GraphQL에 대해 배우고 RESTful API 개발의 대안으로 사용하는 방법을 배웠습니다. 지금까지의 초점은 GraphQL 및 Golang 뿐만 아니라 GraphQL 및 Node.js. 이 두 가지 개발 언어에서 할 수 있는 모든 것을 짜낸 것 같아서 기어를 바꾸고 Java로 운을 시험해보기로 했습니다.
이 튜토리얼에서는 Java와 널리 사용되는 Spring Boot 프레임워크를 사용하여 GraphQL을 빠르게 시작하고 실행하는 방법을 살펴봅니다.
본론에 들어가기에 앞서, 공로를 인정할 만한 곳에 공로를 인정하고 싶습니다. Prithviraj Pawar가 훌륭한 글을 썼습니다, GraphQL Java 서버를 즉시 가동하고 실행하는 방법를 통해 모의 데이터 대신 데이터베이스 등 보완해야 할 점이 많았지만 많은 아이디어를 얻었습니다. 제 글을 읽은 후 그의 글을 읽어보시길 권합니다.
요구 사항
GraphQL과 개발 측면으로 들어가기 전에 충족해야 할 몇 가지 전제 조건이 있습니다.
이미 Couchbase를 설치, 구성 및 실행하고 있다고 가정하겠습니다. 기본 기능만 사용할 것이므로 Couchbase Server 5.0 이후 버전은 무엇이든 사용할 수 있습니다.
또한 Spring Boot 기본 프로젝트를 다운로드했다고 가정하겠습니다. 그리고 Spring Boot 웹사이트 에는 종속성을 정의할 수 있는 훌륭한 생성기가 있습니다. 이 가이드에 따라 일관성을 유지하려면 Maven이 아닌 Gradle 프로젝트를 만들어야 합니다.
Gradle 프로젝트 종속성 정의하기
Spring Boot 템플릿이 준비되어 있다면 매우 빈약할 것입니다. Gradle 구성에 사용하려는 종속성을 포함해야 합니다.
프로젝트의 build.gradle 파일을 열고 다음을 포함하세요:
1 2 3 4 5 6 |
종속성 { 컴파일('org.springframework.boot:spring-boot-starter-web') 컴파일('com.couchbase.client:java-client') 컴파일('com.graphql-java:graphql-java:9.0') testCompile('org.springframework.boot:spring-boot-starter-test') } |
GraphQL 엔드포인트를 제공하기 위해 Spring Boot, 데이터베이스와의 상호 작용을 위해 Couchbase Java SDK, 쿼리 처리를 위해 GraphQL 플러그인을 사용할 것입니다.
RESTful 엔드포인트로 Java API 부트스트랩하기
종속성을 설정했으니 이제 API 개발을 시작할 수 있습니다. 모든 일반적인 RESTful API를 대체하기 위해 GraphQL을 사용하고 있지만, 그렇다고 해서 REST가 완전히 배제된 것은 아닙니다. 여전히 GraphQL 쿼리를 보낼 엔드포인트가 필요합니다.
프로젝트, 패키지 등의 이름을 지정한 것에 따라 다음이 포함된 파일을 엽니다. 메인
함수를 호출하고 다음을 포함합니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
패키지 com.카우치베이스.그래프 쿼리; 가져오기 com.카우치베이스.클라이언트.자바.버킷; 가져오기 com.카우치베이스.클라이언트.자바.클러스터; 가져오기 com.카우치베이스.클라이언트.자바.카우치베이스클러스터; 가져오기 com.카우치베이스.클라이언트.자바.문서.json.JsonObject; 가져오기 그래프 쿼리.스키마.DataFetcher; 가져오기 org.스프링 프레임워크.콩.공장.주석.자동 유선; 가져오기 org.스프링 프레임워크.콩.공장.주석.가치; 가져오기 org.스프링 프레임워크.boot.스프링 애플리케이션; 가져오기 org.스프링 프레임워크.boot.자동 구성.스프링 부팅 애플리케이션; 가져오기 org.스프링 프레임워크.컨텍스트.주석.Bean; 가져오기 org.스프링 프레임워크.http.HttpStatus; 가져오기 org.스프링 프레임워크.http.응답 엔티티; 가져오기 org.스프링 프레임워크.웹.바인드.주석.RequestBody; 가져오기 org.스프링 프레임워크.웹.바인드.주석.요청 매핑; 가져오기 org.스프링 프레임워크.웹.바인드.주석.요청 메서드; 가져오기 org.스프링 프레임워크.웹.바인드.주석.RestController; 가져오기 그래프 쿼리.실행 입력; 가져오기 그래프 쿼리.실행 결과; 가져오기 그래프 쿼리.GraphQL; 가져오기 그래프 쿼리.스키마.그래프QLSchema; 가져오기 그래프 쿼리.스키마.정적 데이터 가져 오기; 가져오기 그래프 쿼리.스키마.idl.런타임 배선; 가져오기 그래프 쿼리.스키마.idl.스키마 생성기; 가져오기 그래프 쿼리.스키마.idl.스키마 파서; 가져오기 그래프 쿼리.스키마.idl.유형 정의 레지스트리; 가져오기 javax.주석.포스트 컨스트럭트; 가져오기 정적 그래프 쿼리.스키마.idl.런타임 배선.새로운 런타임 배선; 가져오기 자바.io.파일; 가져오기 자바.nio.파일.파일; 가져오기 자바.nio.파일.경로; 가져오기 자바.활용.해시맵; 가져오기 자바.활용.지도; @스프링 부팅 애플리케이션 @RestController public 클래스 Graphql애플리케이션 { public 정적 void 메인(문자열[] args) { 스프링 애플리케이션.실행(Graphql애플리케이션.클래스, args); } @요청 매핑(값="/", 메서드= 요청 메서드.GET) public 개체 루트 엔드포인트() { 지도<문자열, 개체> 응답 = new 해시맵<문자열, 개체>(); 응답.put("메시지", "Hello World"); 반환 응답; } @요청 매핑(값="/그래프QL", 메서드= 요청 메서드.POST) public 개체 그래프 쿼리(@RequestBody 문자열 요청) { JsonObject jsonRequest = JsonObject.fromJson(요청); 반환 jsonRequest; } } |
패키지와 클래스 이름이 제 이름과 일치하지 않을 수 있다는 점을 기억하세요. 적절한 경우 빈칸을 채우세요.
두 개의 엔드포인트가 있는데 하나는 완전히 선택 사항입니다. 저는 루트 엔드포인트
를 사용하여 RESTful API가 실제로 작동하는지 테스트했습니다. 그리고 그래프 쿼리
엔드포인트는 우리가 시간을 보낼 곳입니다. 여기에서는 요청 본문뿐만 아니라 POST 요청도 기대합니다. 요청 본문은 결국 실제 GraphQL 쿼리가 될 것이지만 지금은 그대로 두어도 됩니다.
GraphQL 스키마 문서 디자인
지금까지는 기본적인 Spring Boot 준비 작업만 수행했을 뿐 GraphQL과 실제로 관련된 작업은 없었습니다. GraphQL이 가능하려면 사용 가능한 쿼리와 작업 중인 데이터 유형을 설명하는 정의된 스키마를 알아야 합니다. 이를 수행하는 방법은 여러 가지가 있지만 여기서는 스키마 문서에 초점을 맞추겠습니다.
내 리소스 프로젝트의 디렉토리에 schema.graphql 파일에 다음을 포함하는 파일을 생성합니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
유형 쿼리{ 포켓몬: [포켓몬] 게임: [게임] 게임(id: 문자열): 게임 } 유형 게임 { id: 문자열 이름: 문자열 } 유형 포켓몬 { id: 문자열 게임: 게임 이름: 문자열 높이: Int 무게: Int } |
명확하지 않은 경우를 대비해 포켓몬 데이터로 작업하겠습니다. 위의 파일에는 사용 가능한 쿼리와 사용 가능한 두 가지 데이터 유형이 정의되어 있습니다.
시중에는 다양한 포켓몬 게임이 있으며 각 게임에는 서로 다른 포켓몬이 있기 때문에 저희가 정의한 관계가 있습니다. 진정한 포켓몬 팬이라면 이보다 더 많은 것이 있다는 것을 알겠지만, 이 예제에서는 이 정도면 충분합니다.
Couchbase NoSQL 데이터베이스용 가져오기 로직 개발하기
스키마가 정의되면 쿼리가 실행될 때 실행될 데이터베이스 로직을 만들어야 하며, 이 데이터베이스 로직은 두 데이터 유형 각각과 일치하는 결과를 반환해야 합니다.
프로젝트 유지 관리를 좀 더 쉽게 하기 위해 데이터베이스 관리를 위한 별도의 파일을 만들어 보겠습니다. 패키지 내에 Database.java 파일에 다음을 포함하는 파일을 생성합니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
패키지 com.카우치베이스.그래프 쿼리; 가져오기 com.카우치베이스.클라이언트.자바.버킷; 가져오기 com.카우치베이스.클라이언트.자바.문서.JsonDocument; 가져오기 com.카우치베이스.클라이언트.자바.문서.json.JsonObject; 가져오기 com.카우치베이스.클라이언트.자바.쿼리.N1qlQuery; 가져오기 com.카우치베이스.클라이언트.자바.쿼리.N1qlQueryResult; 가져오기 com.카우치베이스.클라이언트.자바.쿼리.N1qlQueryRow; 가져오기 그래프 쿼리.스키마.DataFetcher; 가져오기 자바.활용.ArrayList; 가져오기 자바.활용.해시맵; 가져오기 자바.활용.목록; 가져오기 자바.활용.지도; public 클래스 데이터베이스 { public 정적 DataFetcher 포켓몬 데이터 가져오기(버킷 버킷) { } public 정적 DataFetcher getGameData(버킷 버킷) { } public 정적 DataFetcher getGamesData(버킷 버킷) { } 비공개 정적 목록<지도<문자열, 개체>> 추출 결과 또는 던지기(N1qlQueryResult 결과) { } } |
대부분의 경우 위의 함수 집합은 GraphQL 스키마 파일에 있는 것과 어느 정도 일치하도록 정의했기 때문에 익숙하게 보일 것입니다.
각 기능을 채우기 전에 몇 가지 주의해야 할 사항이 있습니다:
- 버킷은 로직을 정의한 직후 메인 프로젝트 파일에 정의됩니다.
- GraphQL은 특정 유형의 응답을 기대하므로, 따라서
추출 결과 또는 던지기
래퍼 함수를 사용하세요.
이를 염두에 두고 래퍼 함수를 만들어 보겠습니다. 래퍼 함수의 추출 결과 또는 던지기
함수는 N1QL 쿼리에만 사용되며 다음과 같이 보입니다:
1 2 3 4 5 6 7 |
비공개 정적 목록<지도<문자열, 개체>> 추출 결과 또는 던지기(N1qlQueryResult 결과) { 목록<지도<문자열, 개체>> 콘텐츠 = new ArrayList<지도<문자열, 개체>>(); 에 대한 (N1qlQueryRow 행 : 결과) { 콘텐츠.추가(행.값().toMap()); } 반환 콘텐츠; } |
기본적으로 우리는 N1QL 결과 집합을 반복하고 목록
의 지도
를 클릭합니다.
데이터베이스에 게임 데이터가 있다고 가정하면, 데이터베이스의 getGamesData
함수입니다:
1 2 3 4 5 6 7 8 9 10 |
public 정적 DataFetcher getGamesData(버킷 버킷) { 반환 환경 -> { 시스템.out.println("게임 데이터 가져오기..."); 문자열 문 = "SELECT example.* " + "FROM 예제 " + "WHERE type = 'game'"; N1qlQueryResult 쿼리 결과 = 버킷.쿼리(N1qlQuery.simple(문)); 반환 추출 결과 또는 던지기(쿼리 결과); }; } |
N1QL을 사용하여 다음과 일치하는 문서를 쿼리할 수 있습니다. 유형
의 게임
를 사용하고 래퍼 함수로 결과를 처리합니다. 정말 엄격하게 처리하고 싶다면 와일드카드 별표를 사용하는 대신 스키마와 일치하는 속성을 정의할 수 있습니다.
다음 기능입니다, 포켓몬 데이터 가져오기
를 사용하면 게임 데이터와 유사한 방식으로 포켓몬 데이터를 쿼리할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 |
public 정적 DataFetcher 포켓몬 데이터 가져오기(버킷 버킷) { 반환 환경 -> { 시스템.out.println("포켓몬 데이터 불러오기..."); 문자열 문 = "SELECT example.* " + "FROM 예제 " + "WHERE 유형 = '포켓몬'"; N1qlQueryResult 쿼리 결과 = 버킷.쿼리(N1qlQuery.simple(문)); 반환 추출 결과 또는 던지기(쿼리 결과); }; } |
변경 사항 외에는 새로운 것이 없다는 것을 알 수 있습니다. 유형
속성을 추가합니다. 여기서 흥미로운 일이 벌어질 수 있습니다. 게임 데이터와 관련하여 포켓몬에 대한 데이터 관계가 있습니다. 현재 쿼리에서 가져올 수도 있고, 더 모듈화되고 잠재적으로 더 가벼워지도록 분할할 수도 있습니다. 여기서는 모듈식 경량화 옵션을 선택하겠습니다.
이를 통해 우리는 getGameData
함수입니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public 정적 DataFetcher getGameData(버킷 버킷) { 반환 환경 -> { 해시맵<문자열, 개체> 부모 = 환경.getSource(); JsonDocument 문서; 만약(부모 != null) { 시스템.out.println("에 대한 게임 데이터 가져오기" + (문자열)부모.get("game") + "..."); 문서 = 버킷.get((문자열) 부모.get("game")); } else { 시스템.out.println("에 대한 게임 데이터 가져오기" + (문자열)환경.getArgument("id") + "..."); 문서 = 버킷.get((문자열)환경.getArgument("id")); } 반환 문서.콘텐츠().toMap(); }; } |
위의 함수에는 두 가지 일이 발생하는데, 둘 다 N1QL을 사용하지 않습니다.
스키마에서는 게임 데이터에 대한 인수 기반 쿼리와 포켓몬 데이터의 데이터 관계를 정의했습니다. 동일한 함수에서 이 두 가지 시나리오를 모두 수용하려고 합니다.
사용 environment.getSource()
함수를 사용하면 요청과 함께 제공되는 상위 데이터가 있는지 확인할 수 있습니다. 이 상위 데이터의 예로는 포켓몬 데이터를 쿼리할 때를 들 수 있습니다. 이 경우 게임
필드는 getGameData
함수를 사용하고 이름, 몸무게 등 다른 데이터는 상위 데이터를 참조합니다. 데이터베이스에서 게임 정보는 ID로 저장되므로 상위 데이터에서 이를 추출한 후 게임 데이터를 조회할 수 있습니다.
다른 시나리오는 게임 ID를 나타내는 인수를 전달하는 경우입니다.
둘 다 유효하며 둘 다 다른 일을 합니다. 결국, 우리는 결과 배열이 아닌 단일 결과만 원합니다.
성공적인 GraphQL 처리를 위해 애플리케이션을 함께 배선하기
이제 막바지에 이르렀습니다. API와 데이터베이스 로직이 준비되었습니다. 마지막 단계는 GraphQL 스키마를 데이터베이스 로직에 연결하고 결과가 제대로 구현되는지 확인하는 것입니다.
이전 단계에서 버킷 정보 구성에 대해 언급했던 것을 기억하시나요? 그 부분을 먼저 처리해 보겠습니다. 프로젝트의 application.properties 파일에 있는 리소스 디렉터리에 추가합니다. 다음을 포함합니다:
1 2 3 4 |
호스트 이름=127.0.0.1 버킷=예제 사용자 이름=예제 비밀번호=123456 |
내 데이터베이스 정보는 예시일 뿐입니다. Couchbase 인스턴스에 사용 중인 정보로 바꾸어야 합니다.
메인 프로젝트 파일로 돌아가서 값을 매핑하고 인스턴스에 연결할 수 있습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
패키지 com.카우치베이스.그래프 쿼리; 가져오기 com.카우치베이스.클라이언트.자바.버킷; 가져오기 com.카우치베이스.클라이언트.자바.클러스터; 가져오기 com.카우치베이스.클라이언트.자바.카우치베이스클러스터; 가져오기 com.카우치베이스.클라이언트.자바.문서.json.JsonObject; 가져오기 그래프 쿼리.스키마.DataFetcher; 가져오기 org.스프링 프레임워크.콩.공장.주석.자동 유선; 가져오기 org.스프링 프레임워크.콩.공장.주석.가치; 가져오기 org.스프링 프레임워크.boot.스프링 애플리케이션; 가져오기 org.스프링 프레임워크.boot.자동 구성.스프링 부팅 애플리케이션; 가져오기 org.스프링 프레임워크.컨텍스트.주석.Bean; 가져오기 org.스프링 프레임워크.http.HttpStatus; 가져오기 org.스프링 프레임워크.http.응답 엔티티; 가져오기 org.스프링 프레임워크.웹.바인드.주석.RequestBody; 가져오기 org.스프링 프레임워크.웹.바인드.주석.요청 매핑; 가져오기 org.스프링 프레임워크.웹.바인드.주석.요청 메서드; 가져오기 org.스프링 프레임워크.웹.바인드.주석.RestController; 가져오기 그래프 쿼리.실행 입력; 가져오기 그래프 쿼리.실행 결과; 가져오기 그래프 쿼리.GraphQL; 가져오기 그래프 쿼리.스키마.그래프QLSchema; 가져오기 그래프 쿼리.스키마.정적 데이터 가져 오기; 가져오기 그래프 쿼리.스키마.idl.런타임 배선; 가져오기 그래프 쿼리.스키마.idl.스키마 생성기; 가져오기 그래프 쿼리.스키마.idl.스키마 파서; 가져오기 그래프 쿼리.스키마.idl.유형 정의 레지스트리; 가져오기 javax.주석.포스트 컨스트럭트; 가져오기 정적 그래프 쿼리.스키마.idl.런타임 배선.새로운 런타임 배선; 가져오기 자바.io.파일; 가져오기 자바.nio.파일.파일; 가져오기 자바.nio.파일.경로; 가져오기 자바.활용.해시맵; 가져오기 자바.활용.지도; @스프링 부팅 애플리케이션 @RestController public 클래스 Graphql애플리케이션 { @가치("${호스트명}") 비공개 문자열 호스트 이름; @가치("${버킷}") 비공개 문자열 버킷; @가치("${사용자 이름}") 비공개 문자열 사용자 이름; @가치("${비밀번호}") 비공개 문자열 비밀번호; public @Bean 클러스터 클러스터() { 클러스터 클러스터 = 카우치베이스클러스터.create(이.호스트 이름); 클러스터.인증(이.사용자 이름, 이.비밀번호); 반환 클러스터; } public @Bean 버킷 버킷() { 반환 클러스터().오픈버킷(이.버킷); } 비공개 GraphQL 빌드; public 정적 void 메인(문자열[] args) { 스프링 애플리케이션.실행(Graphql애플리케이션.클래스, args); } @요청 매핑(값="/", 메서드= 요청 메서드.GET) public 개체 루트 엔드포인트() { } @요청 매핑(값="/그래프QL", 메서드= 요청 메서드.POST) public 개체 그래프 쿼리(@RequestBody 문자열 요청) { } } |
두 개의 Bean
를 열고 속성 파일의 정보를 사용했습니다. 그렇게 해서 Couchbase에 연결되었습니다.
다음 단계는 GraphQL 스키마를 초기화하는 것입니다. 이 작업을 수행하는 방법은 여러 가지가 있지만 애플리케이션이 시작될 때 Spring Boot 어노테이션을 사용하는 것이 더 쉽다는 것을 알았습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@포스트 컨스트럭트() public void init() { 클래스 로더 classLoader = getClass().getClassLoader(); 파일 스키마 파일 = new 파일(classLoader.getResource("schema.graphql").getFile()); 스키마 파서 스키마 파서 = new 스키마 파서(); 유형 정의 레지스트리 유형 정의 레지스트리 = 스키마 파서.parse(스키마 파일); 런타임 배선 런타임 배선 = 런타임 배선.새로운 런타임 배선() .유형("쿼리",유형배선 -> 유형배선 .데이터 가져 오기("포켓몬",데이터베이스.포켓몬 데이터 가져오기(버킷())) ) .유형("쿼리",유형배선 -> 유형배선 .데이터 가져 오기("games",데이터베이스.getGamesData(버킷())) ) .유형("쿼리",유형배선 -> 유형배선 .데이터 가져 오기("game",데이터베이스.getGameData(버킷())) ) .유형("포켓몬",유형배선 -> 유형배선 .데이터 가져 오기("game",데이터베이스.getGameData(버킷())) ).빌드(); 스키마 생성기 스키마 생성기 = new 스키마 생성기(); 그래프QLSchema 그래프QLSchema = 스키마 생성기.실행 가능한 스키마 만들기(유형 정의 레지스트리, 런타임 배선); 이.빌드 = GraphQL.newGraphQL(그래프QLSchema).빌드(); } |
그리고 init
함수는 애플리케이션이 시작될 때 호출됩니다. 이 함수는 스키마 파일을 리소스 디렉터리를 만들고 배선을 정의합니다. 예를 들어 포켓몬
, 게임
및 게임(id: 문자열)
는 모두 쿼리 유형입니다. 각각은 적절한 데이터베이스 함수에 매핑되고 열린 버킷이 전달됩니다. 그런 다음 게임
를 포켓몬
유형으로 정의합니다. 필드와 쿼리가 데이터베이스 상호 작용에 어떻게 연결되는지 어느 정도 정의하고 있습니다.
스키마를 빌드하고 나면 클래스 변수에 스키마를 연결할 수 있습니다. 빌드된 스키마로 GraphQL RESTful 엔드포인트를 완성할 수 있습니다:
1 2 3 4 5 6 7 |
@요청 매핑(값="/그래프QL", 메서드= 요청 메서드.POST) public 개체 그래프 쿼리(@RequestBody 문자열 요청) { JsonObject jsonRequest = JsonObject.fromJson(요청); 실행 입력 실행 입력 = 실행 입력.새로운 실행 입력().쿼리(jsonRequest.getString("query")).빌드(); 실행 결과 실행 결과 = 이.빌드.실행(실행 입력); 반환 실행 결과.사양(); } |
엔드포인트에서는 요청과 함께 전달된 쿼리 문자열을 가져와서 빌드된 스키마로 실행합니다. 결과는 클라이언트가 쿼리에서 요청한 내용이 됩니다.
그렇다면 쿼리는 어떤 모습일까요? 다음을 예로 들어 보겠습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ 포켓몬 { 이름 무게 게임 { 이름 } } 게임 { 이름 } 게임(id: "game-2") { 이름 } } |
위의 쿼리는 누구나 실행할 필요는 없지만, 원한다면 실행할 수 있습니다. 위의 예에서는 모든 포켓몬 데이터와 포켓몬이 속한 게임에 대해 쿼리하고 있습니다. 또한 특정 게임뿐만 아니라 모든 게임에 대해 쿼리하고 있습니다. 쿼리의 세 부분은 모두 서로 관련이 없지만 동일한 쿼리에 존재합니다.
재미있는 사실은 포켓몬
쿼리에서 게임 데이터를 쿼리하지 않기로 선택하면 데이터베이스 함수는 호출되지 않습니다. GraphQL은 필요할 때만 함수를 실행합니다.
결론
방금 Spring Boot, Java, Couchbase Server를 NoSQL 데이터베이스로 사용하여 GraphQL 애플리케이션을 구축하는 방법을 살펴보았습니다. 샘플 쿼리에서 보셨듯이, RESTful API에서는 여러 개의 요청이 필요했을 여러 가지 서로 관련이 없는 데이터를 한 번의 요청으로 쿼리할 수 있었습니다.
Couchbase와 함께 Java SDK를 사용하는 방법에 대한 자세한 내용은 다음을 참조하세요. 카우치베이스 개발자 포털.