[GraphQL] GraphQL Java

김성은·2024년 5월 5일
0

GraphQL-Study

목록 보기
2/2
post-thumbnail

1. GraphQL Java?

1.1 GraphQL Java

  • GraphQL Java는 GraphQL을 위한 Java(서버)의 구현체입니다
  • GraphQL Java 엔진은 쿼리 실행에만 관련됩니다
  • GraphQL Java 서버 생성 주요 단계
    1. GraphQL 스키마 정의
    2. 쿼리에 대한 실제 데이터를 가져오는 방법 결정

1.2 Schema first vs Code first

  • GraphQL API를 구축하는 접근법에는 두 가지가 존재합니다
    - Schema-first
    - Code-first

Schema-first

  • 스키마를 첫 번째 우선순위로 두고 스키마의 정의들을 따라 코드를 작성하는 접근법입니다
  • GraphQL 스키마를 먼저 디자인하고 정의하는 것에 중점을 두는 접근법입니다
  • 스키마를 디자인하고 정의한 뒤에 resolver 함수들을 추가합니다
  • SDL(Schema Definition Language)을 사용해 스키마를 정의합니다
  • 장점
    • API 디자인에 먼저 집중할 수 있기 때문에, 잘 디자인된 API를 구현할 수 있게 해줍니다
    • API 디자인이 완성되고, 스키마가 정의되면 프론트엔드와 백엔드 팀이 동시에 작업할 수 있으므로 개발 시간을 줄일 수 있습니다
    • DIP에 따르면 더 추상적이고 덜 종속적입니다
  • 단점
    • 스키마 정의가 resolver 함수들과 정확히 동기화 되어야 합니다. 그러지 않을 경우에 문제가 발생합니다
    • SDL 정의는 쉽게 재사용할 수 없으므로 코드 중복이 발생할 수 있습니다
    • 분산 스키마를 단일 스키마로 결합하기 어렵습니다

Code-first

  • Schema-first의 단점을 해결하기 위해 소개된 접근법입니다
  • 스키마를 프로그래밍 방식으로 정의하고 구현하는 접근법입니다
  • resolver 함수를 먼저 작성하면 스키마는 자동 생성됩니다
  • 장점
    • resolver와 스키마를 동기화시키지 않아도 됩니다
    • API를 수정해야 하는 경우, 스키마는 동적으로 생성되므로 신경쓰지 않고, 단순히 함수만 변경하면 되기 때문에 유지보수를 편리하게 해줍니다
    • 장기적인 관점에서 확장성이 좋고 유지보수가 편리합니다
  • 단점
    • 스키마 정의와 resolver가 같이 있어서 가독성이 떨어집니다
    • API 디자인이 비즈니스 모델이 아니라 구현에 영향을 받습니다

2. 예제 API : BOOK

2.1 스프링부트 프로젝트 생성

  • 스프링부트 프로젝트
    • Gradle Project
    • Spring Boot 3
    • Java 17 버전 이상
  • 프로젝트 메타데이터
    • Group: com.graphqljava.tutorial
    • Artifact: bookDetails
  • dependencies 추가
    • Spring Web
    • Spring for GraphQL

2.2 스키마 생성

  • /src/main/resources/graphql 경로에 schema.graphqls 파일을 추가합니다
  • 파일의 내용은 다음과 같습니다
    • 아래 스키마는 특정 도서의 세부정보를 반환하는 쿼리를 정의합니다
    • 또한 Book 클래스에 정의된 필드 유형을 정의합니다
type Query {
  bookById(id: ID): Book
}

type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}

type Author {
  id: ID
  firstName: String
  lastName: String
}

2.3 데이터 소스 추가

  • bookDetails/Book.java 코드를 다음과 같이 작성합니다
package com.graphqljava.tutorial.bookDetails;

import java.util.Arrays;
import java.util.List;

record Book(String id, String name, int pageCount, String authorId) {

    private static List<Book> books = Arrays.asList(
        new Book("book-1", "Harry Potter and the Philosopher's Stone", 223, "author-1"),
        new Book("book-2", "Moby Dick", 635, "author-2"),
        new Book("book-3", "Interview with the vampire", 371, "author-3")
    );

    public static Book getById(String id) {
        return books.stream().filter(book -> book.id().equals(id)).findFirst().orElse(null);
    }

}
  • bookDetails/Author.java 코드를 다음과 같이 작성합니다
package com.graphqljava.tutorial.bookDetails;

import java.util.Arrays;
import java.util.List;

record Author(String id, String firstName, String lastName) {

    private static List<Author> authors = Arrays.asList(
        new Author("author-1", "Joanne", "Rowling"),
        new Author("author-2", "Herman", "Melville"),
        new Author("author-3", "Anne", "Rice")
    );

    public static Author getById(String id) {
        return authors.stream().filter(author -> author.id().equals(id)).findFirst().orElse(null);
    }

}

2.4 데이터를 가져오는 코드 추가

  • Spring for GraphQL은 특정 GraphQL 필드에 대한 데이터를 가져오는 메서드 핸들러에 대한 어노테이션 기반 프로그래밍 모델을 제공합니다
  • 다음과 같이 bookDetails/BookController.java 코드를 작성합니다
package com.graphqljava.tutorial.bookDetails;

import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;

@Controller
class BookController {

    @QueryMapping
    public Book bookById(@Argument String id) {
        return Book.getById(id);
    }

    @SchemaMapping
    public Author author(Book book) {
        return Author.getById(book.authorId());
    }

}
  • @QueryMapping
    • 이 메서드를 쿼리에 바인딩하며, Query 타입 하위의 필드에 연결합니다.
    • 그런 다음 쿼리 필드는 메서드 이름인 bookById에서 결정됩니다.
    • 어노테이션에 선언할 수도 있습니다.
    • Spring for GraphQLRuntimeWiring.Builder를 사용하여 핸들러 메서드를 graphql.schema.DataFetcher로 등록하고 쿼리 필드인 bookById로 설정합니다.
  • GraphQL Java에서 DataFetchingEnvironment은 필드별 인자 값에 대한 맵에 접근할 수 있습니다.
    • @Argument 어노테이션을 사용하여 인자를 대상 객체에 바인딩하고 핸들러 메서드에 주입할 수 있습니다.
    • 기본적으로 메서드 매개변수 이름이 인자를 조회하는 데 사용됩니다.
    • 어노테이션에서 인자 이름을 지정할 수 있습니다.
  • @SchemaMapping 어노테이션은 핸들러 메서드를 GraphQL 스키마의 필드에 매핑하고 해당 필드의 DataFetcher로 선언합니다.
    • 필드 이름은 메서드 이름에 기본적으로 매핑되며, 타입 이름은 메서드에 주입된 소스/상위 객체의 간단한 클래스 이름에 기본적으로 매핑됩니다.
    • 이 예시에서 필드는 기본적으로 Author이고, 타입은 Book으로 기본 설정됩니다.
    • 어노테이션에서 타입과 필드를 지정할 수 있습니다.
  • 여기까지의 따라했다면 프로젝트 내 필요한 파일들의 구조는 다음과 같습니다

2.4 쿼리 실행

  • GraphiQL은 쿼리 작성 및 실행 등에 유용한 시각적 인터페이스입니다
  • application.properties에 다음과 같은 코드를 추가하여 GraphiQL을 활성화 할 수 있습니다
spring.graphql.graphiql.enabled=true
  • 그러면 경로 /graphiql에서 GraphiQL이 활성화됩니다
    • 해당 경로를 변경하고 싶다면 spring.graphql.graphiql.path를 통해 변경할 수 있습니다
  • 스프링부트 어플리케이션을 실행하고 웹 브라우저에서 http://localhost:8080/graphiql 또는 사용자 정의 URL로 접속합니다
  • 다음 쿼리를 입력합니다
query bookDetails {
  bookById(id: "book-1") {
    id
    name
    pageCount
    author {
      id
      firstName
      lastName
    }
  }
}

참고

profile
백엔드 개발자가 되고 싶은 눈송이입니다

0개의 댓글