https://www.baeldung.com/spring-graphql 를 번역함.
1. 소개
GraphQL 은 웹 API용 REST의 대안으로 청구되는 Facebook의 비교적 새로운 개념입니다.
이 기사에서는 Spring Boot를 사용하여 GraphQL 서버를 설정하여 기존 애플리케이션에 추가하거나 새 애플리케이션에서 사용할 수 있도록 하는 방법을 소개합니다.
2. GraphQL 이란 무엇입니까 ?
기존 REST API는 서버가 관리하는 리소스 개념으로 작동합니다. 이러한 리소스는 다양한 HTTP 동사에 따라 몇 가지 표준 방식으로 조작할 수 있습니다. 이것은 API가 리소스 개념에 맞는 한 매우 잘 작동하지만, 이를 벗어나야 할 때 빠르게 무너집니다.
이것은 클라이언트가 동시에 여러 리소스의 데이터를 필요로 하는 경우에도 발생합니다. 예를 들어 블로그 게시물과 댓글을 요청합니다. 일반적으로 이것은 클라이언트가 여러 요청을 하도록 하거나 서버가 항상 필요하지 않을 수 있는 추가 데이터를 제공하도록 하여 더 큰 응답 크기로 이어짐으로써 해결됩니다.
GraphQL은 이 두 가지 문제에 대한 솔루션을 제공합니다 . 이를 통해 클라이언트는 단일 요청에서 하위 리소스 탐색을 포함하여 원하는 데이터를 정확하게 지정할 수 있으며 단일 요청에서 여러 쿼리를 허용합니다.
또한 표준 필수 작업 집합 대신 명명된 쿼리 및 변형을 사용하여 훨씬 더 RPC 방식으로 작동합니다. 이것은 API 개발자가 가능한 것을 지정하고 API 소비자가 원하는 것을 지정하여 제어가 속한 위치에 두기 위해 작동합니다.
예를 들어 블로그에서 다음 쿼리를 허용할 수 있습니다.
query {
recentPosts(count: 10, offset: 0) {
id
title
category
author {
id
name
thumbnail
}
}
}
이 쿼리는 다음을 수행합니다.
- 가장 최근 게시물 10개 요청
- 각 게시물에 대해 ID, 제목, 카테고리 요청
- 각 게시물 요청 작성자, ID, 이름 및 썸네일 반환
기존 REST API에서는 11개의 요청(게시물에 대해 1개, 작성자에 대해 10개)이 필요하거나 게시물 세부 정보에 작성자 세부 정보를 포함해야 합니다.
2.1. GraphQL 스키마
GraphQL 서버는 API를 설명하는 스키마를 노출합니다. 이 체계는 유형 정의로 구성됩니다. 각 유형에는 각각 0개 이상의 인수를 사용하고 특정 유형을 반환하는 하나 이상의 필드가 있습니다.
그래프는 이러한 필드가 서로 중첩되는 방식으로 구성됩니다. 그래프가 순환적일 필요는 없습니다. 주기는 완벽하게 허용되지만 방향이 있습니다. 즉, 클라이언트는 한 필드에서 자식으로 가져올 수 있지만 스키마가 이를 명시적으로 정의하지 않는 한 자동으로 부모로 돌아갈 수는 없습니다.
블로그에 대한 예제 GraphQL 스키마에는 게시물, 게시물 작성자 및 블로그에서 가장 최근 게시물을 가져오기 위한 루트 쿼리를 설명하는 다음 정의가 포함될 수 있습니다.
type Post {
id: ID!
title: String!
text: String!
category: String
author: Author!
}
type Author {
id: ID!
name: String!
thumbnail: String
posts: [Post]!
}
# The Root Query for the application
type Query {
recentPosts(count: Int, offset: Int): [Post]!
}
# The Root Mutation for the application
type Mutation {
writePost(title: String!, text: String!, category: String) : Post!
}
NS "!" 일부 이름 끝에는 이것이 nullable이 아닌 유형임을 나타냅니다. 이것이 없는 모든 유형은 서버의 응답에서 null이 될 수 있습니다. GraphQL 서비스는 이를 올바르게 처리하므로 nullable 형식의 자식 필드를 안전하게 요청할 수 있습니다.
또한 GraphQL 서비스는 표준 필드 세트를 사용하여 스키마 자체를 노출하므로 모든 클라이언트가 미리 스키마 정의를 쿼리할 수 있습니다.
이렇게 하면 클라이언트가 스키마가 변경될 때 자동으로 감지할 수 있고 클라이언트가 스키마 작동 방식에 동적으로 적응할 수 있습니다. 이것의 매우 유용한 예 중 하나는 나중에 설명할 GraphiQL 도구로, 모든 GraphQL API와 상호 작용할 수 있습니다.
3. GraphQL 스프링 부트 스타터 소개
봄 부팅 GraphQL 스타터는 매우 짧은 시간에 GraphQL 서버를 실행 얻을 수있는 훌륭한 방법을 제공합니다 . GraphQL Java 도구 라이브러리 와 결합하여 서비스에 필요한 코드만 작성하면 됩니다.
3.1. 서비스 설정
이것이 작동하는 데 필요한 것은 올바른 종속성뿐입니다.
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.2.4</version>
</dependency>
Spring Boot는 이를 자동으로 선택하고 자동으로 작동하도록 적절한 핸들러를 설정합니다.
기본적으로 이것은 우리 애플리케이션 의 /graphql 엔드포인트 에 GraphQL 서비스를 노출 하고 GraphQL 페이로드가 포함된 POST 요청을 수락합니다. 이 끝점은 필요한 경우 application.properties 파일 에서 사용자 지정할 수 있습니다 .
3.2. 스키마 작성
GraphQL 도구 라이브러리는 GraphQL 스키마 파일을 처리하여 올바른 구조를 만든 다음 이 구조에 특수 빈을 연결하여 작동합니다. Spring Boot GraphQL 스타터는 이러한 스키마 파일을 자동으로 찾습니다 .
이 파일은 확장자 ". graphqls "이며 클래스 경로의 모든 위치에 존재할 수 있습니다. 또한 원하는 만큼 이러한 파일을 가질 수 있으므로 스키마를 원하는 대로 모듈로 분할할 수 있습니다.
한 가지 요구 사항은 정확히 하나의 루트 쿼리와 최대 하나의 루트 돌연변이가 있어야 한다는 것입니다. 나머지 구성표와 달리 파일 간에 분할할 수 없습니다. 이것은 Java 구현이 아니라 GraphQL 스키마 정의 자체의 제한 사항입니다.
3.3. 루트 쿼리 리졸버
루트 쿼리에는 이 루트 쿼리의 다양한 필드를 처리하기 위해 Spring 컨텍스트에 정의된 특수 빈이 있어야 합니다 . 스키마 정의와 달리 루트 쿼리 필드에는 단일 Spring Bean만 있다는 제한이 없습니다.
유일한 요구 사항은 빈이 GraphQLQueryResolver를 구현 하고 스키마의 루트 쿼리에 있는 모든 필드에 동일한 이름을 가진 이러한 클래스 중 하나의 메서드가 있다는 것입니다.
public class Query implements GraphQLQueryResolver {
private PostDao postDao;
public List<Post> getRecentPosts(int count, int offset) {
return postsDao.getRecentPosts(count, offset);
}
}
메서드 이름은 다음 중 하나여야 합니다. 이 순서대로:
- <필드>
- is<field> – 필드가 부울 유형인 경우에만
- get<필드>
메서드에는 GraphQL 스키마의 매개변수에 해당하는 매개변수가 있어야 하며 선택적으로 DataFetchingEnvironment 유형의 최종 매개변수를 사용할 수 있습니다.
메서드는 또한 우리가 곧 보게 될 GraphQL 체계의 유형에 대한 올바른 반환 유형을 반환해야 합니다. String, Int, List 등의 모든 단순 유형 은 동등한 Java 유형과 함께 사용할 수 있으며 시스템은 이를 자동으로 매핑합니다.
위에서 정의한 스키마 의 lastPosts 필드에 대한 모든 GraphQL 쿼리를 처리하는 데 사용할 getRecentPosts 메서드를 위에서 정의했습니다 .
3.4. 빈을 사용하여 유형 표현하기
GraphQL 서버의 모든 복합 유형 은 루트 쿼리에서 로드하든 구조의 다른 곳에서 로드하든 상관없이 Java bean으로 표시됩니다 . 동일한 Java 클래스는 항상 동일한 GraphQL 유형을 나타내야 하지만 클래스 이름은 필요하지 않습니다.
Java bean 내부의 필드는 필드 이름을 기반으로 GraphQL 응답의 필드에 직접 매핑됩니다 .
public class Post {
private String id;
private String title;
private String category;
private String authorId;
}
GraphQL 스키마에 매핑되지 않는 Java bean의 모든 필드 또는 메소드는 무시되지만 문제를 일으키지는 않습니다. 이것은 필드 리졸버가 작동하는 데 중요합니다.
예를 들어 여기 의 필드 authorId 는 이전에 정의한 스키마의 어떤 것과도 일치하지 않지만 다음 단계에서 사용할 수 있습니다.
3.5. 복소수 값에 대한 필드 해석기
때로는 필드 값을 로드하는 것이 중요하지 않습니다. 여기에는 데이터베이스 조회, 복잡한 계산 등이 포함될 수 있습니다. GraphQL 도구에는 이러한 목적으로 사용되는 필드 해석기의 개념이 있습니다. 데이터 bean 대신 값을 제공할 수 있는 Spring bean입니다.
필드 리졸버는 데이터 빈과 이름이 같고 접미사가 Resolver 이고 GraphQLResolver 인터페이스 를 구현하는 Spring 컨텍스트의 모든 빈입니다 . 필드 해석기 빈의 메소드는 데이터 빈에서와 동일한 규칙을 모두 따르지만 데이터 빈 자체도 첫 번째 매개변수로 제공됩니다.
필드 리졸버와 데이터 빈이 모두 동일한 GraphQL 필드에 대한 메소드를 가지고 있으면 필드 리졸버가 우선합니다.
public class PostResolver implements GraphQLResolver<Post> {
private AuthorDao authorDao;
public Author getAuthor(Post post) {
return authorDao.getAuthorById(post.getAuthorId());
}
}
이러한 필드 리졸버가 Spring 컨텍스트에서 로드된다는 사실이 중요합니다. 이를 통해 DAO와 같은 다른 Spring 관리 빈과 함께 작업할 수 있습니다.
중요한 것은 클라이언트가 필드를 요청하지 않으면 GraphQL 서버가 필드를 검색하는 작업을 수행하지 않는다는 것 입니다. 즉, 클라이언트가 Post를 검색하고 Author를 요청하지 않으면 위 의 getAuthor() 메서드가 실행되지 않고 DAO 호출이 만들어지지 않습니다.
3.6. 널 입력 가능 값
GraphQL 스키마에는 일부 유형은 nullable이고 다른 유형은 그렇지 않다는 개념이 있습니다.
이것은 null 값을 직접 사용하여 Java 코드에서 처리할 수 있지만 마찬가지로 Java 8 의 새로운 Optional 유형은 nullable 유형에 대해 직접 사용할 수 있으며 시스템은 값으로 올바른 작업을 수행합니다.
이것은 우리의 Java 코드가 메소드 정의의 GraphQL 스키마와 더 명확하게 동일하다는 것을 의미하기 때문에 매우 유용합니다.
3.7. 돌연변이
지금까지 우리가 한 모든 것은 서버에서 데이터를 검색하는 것이었습니다. GraphQL에는 돌연변이를 통해 서버에 저장된 데이터를 업데이트하는 기능도 있습니다.
코드의 관점에서 Query가 서버의 데이터를 변경할 수 없는 이유는 없습니다. 인수를 받아들이고 새 데이터를 저장하고 변경 사항을 반환하는 쿼리 해석기를 쉽게 작성할 수 있습니다. 이렇게 하면 API 클라이언트에 놀라운 부작용이 발생하며 이는 나쁜 습관으로 간주됩니다.
대신 Mutations를 사용하여 저장 중인 데이터가 변경될 것임을 클라이언트에 알려야 합니다 .
돌연변이를 구현하는 클래스를 사용하여 자바 코드에 정의되어 GraphQLMutationResolver 대신 GraphQLQueryResolver을 .
그렇지 않으면 쿼리와 동일한 규칙이 모두 적용됩니다. 그런 다음 Mutation 필드의 반환 값은 Query 필드와 정확히 동일하게 처리되어 중첩된 값도 검색할 수 있습니다.
public class Mutation implements GraphQLMutationResolver {
private PostDao postDao;
public Post writePost(String title, String text, String category) {
return postDao.savePost(title, text, category);
}
}
4. GraphiQL 소개
GraphQL에는 GraphiQL이라는 동반 도구도 있습니다. 이것은 모든 GraphQL 서버와 통신하고 이에 대한 쿼리 및 변형을 실행할 수 있는 UI입니다. 다운로드 가능한 버전은 Electron 앱으로 존재하며 여기 에서 검색할 수 있습니다 .
GraphiQL Spring Boot Starter 종속성을 추가하여 웹 기반 버전의 GraphiQL을 애플리케이션에 자동으로 포함할 수도 있습니다.
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
이것은 /graphql 의 기본 끝점에서 GraphQL API를 호스팅하는 경우에만 작동 하므로 그렇지 않은 경우 독립 실행형 애플리케이션이 필요합니다.
5. 요약
GraphQL은 웹 API 개발 방식을 잠재적으로 혁신할 수 있는 매우 흥미로운 신기술입니다.
Spring Boot GraphQL Starter와 GraphQL Java 도구 라이브러리를 결합하면 이 기술을 새로운 또는 기존 Spring Boot 애플리케이션에 매우 쉽게 추가할 수 있습니다.
코드 조각은 GitHub 에서 찾을 수 있습니다 .