티스토리 뷰

# 시작

지난 포스팅에서 SpringBoot를 세팅하고 PostgreSQL도 연동했습니다.

이제 GraphQL API를 적용합니다.


# 준비

- GraphQL for VSCode : VS Code 확장 프로그램 마켓 플레이스 ( Ctrl + Shift + X )에서 설치

- PlayGround : https://github.com/prisma/graphql-playground/releases

 

prisma/graphql-playground

🎮 GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration) - prisma/graphql-playground

github.com

위 링크에서 desktop app을 클릭하면 쿼리를 테스트해볼 수 있는 PlayGround를 설치할 수 있습니다.

만약 프로그램을 설치하는게 싫다면 아래 데모 사이트를 통해 테스트해볼 수 있습니다.

https://www.graphqlbin.com/v2/6RQ6TM

 

GraphQL Playground

GraphQL Playground Logo Loading GraphQL Playground

www.graphqlbin.com


# GraphQL 세팅

/build.gradle에 GraphQL 의존성 추가

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'

	// GraphQL
	implementation 'com.graphql-java:graphql-java:11.0'
	implementation 'com.graphql-java:graphql-java-spring-boot-starter-webmvc:1.0'
	implementation 'com.google.guava:guava:26.0-jre'

	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'

	// DB
	compile group: 'org.postgresql', name: 'postgresql', version: '42.2.5'

	// Logging
	compile group: 'org.bgee.log4jdbc-log4j2', name: 'log4jdbc-log4j2-jdbc4.1', version: '1.16'
}

이번에는 샘플 데이터용 타입을 정의합니다.

src/main/resources/static/graphql 디렉토리를 만들고 그 밑에 schema.graphqls 파일을 생성합니다.

앞으로 프로젝트를 진행하면서 생겨날 graphql파일들은 전부 이 파일로 병합될 겁니다.

type Query {
  allCities: [City]
  city(id: Int): City
}

type City {
  id: Int!
  name: String
  population: Int
}

콜론(:)을 기준으로 왼쪽은 변수명, 오른쪽은 해당 변수의 타입이며 타입의 '!'는 필수값이라는 표시입니다.

또한 Query의 city에 붙어있는 소괄호는 파라미터를 정의한 것이며 allCities는 City의 배열 타입인 것이죠.

GraphQL의 타입 및 스키마에 관해 궁금하신 분은 여기서 https://graphql-kr.github.io/learn/schema/

 

GraphQL: API를 위한 쿼리 언어

GraphQL은 API에 있는 데이터에 대한 완벽하고 이해하기 쉬운 설명을 제공하고 클라이언트에게 필요한 것을 정확하게 요청할 수 있는 기능을 제공하며 시간이 지남에 따라 API를 쉽게 진화시키고 강력한 개발자 도구를 지원합니다.

graphql-kr.github.io


이제 샘플 데이터의 Entity를 생성해보겠습니다.

com/graphql/blog/sample 패키지를 생성하고 CityEntity.java 파일을 생성합니다.

package com.graphql.blog.sample;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Getter;
import lombok.Setter;

@Getter
@Entity
@Table(name = "cities")
public class CityEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    private int population;

}

@Table은 선언된 Entity와 매핑할 DB Table을 지정합니다. 이 Entity는 이전 포스팅에서 생성했던 테이블 cities와 매핑됩니다.

@Id는 선언된 필드를 기본키로 매핑합니다.

@GeneratedValue은 키값의 생성 전략을 정의합니다. 현재 AUTO로 지정하여 INSERT 전에 자동적으로 1씩 증가하게 됩니다.

@Getter는 Lombok에서 제공하는 어노테이션이며 각 필드의 get메서드를 자동적으로 생성해줍니다.

Lombok과 관련해서는 흥미로운 글이 있어서 링크 덧붙입니다.

https://www.popit.kr/%EC%8B%A4%EB%AC%B4%EC%97%90%EC%84%9C-lombok-%EC%82%AC%EC%9A%A9%EB%B2%95/

 

실무에서 Lombok 사용법 | Popit

해당 코드는 Github 에 공개되어 있습니다. Lombok은 자바 컴파일 시점에서 특정 어노테이션으로 해당 코드를 추가할 수 있는 라이브러리입니디. 이는 코드의 다이어트? 가독 성 및 유지 보수에 많은 도움이 됩니다. 하지만 편리한 만큼 잘못 사용하기 쉬운 것이 Lombok 입니다. 거창하게 실무에서 사용하는 Lombok이라고 표현했지만 어디까지 저의 주관적인 생각이기 때문에 각자 환경과 상황에 알맞게 사용하는 것이 바람직합니다. 지금 부터 제가 Lom

www.popit.kr


이번엔 com/graphql/blog/sample/CityRepository.java 파일을 생성합니다.

package com.graphql.blog.sample;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CityRepository extends JpaRepository<CityEntity, Long> {
    CityEntity findById(int id);
}

기본 제공 쿼리 외에 findById 쿼리를 추가합니다.

JpaRepository 인터페이스를 상속하면 기본적으로 findAll, findOne, count, save, delete 등의 쿼리가 기본적으로 제공됩니다.

또한, 쿼리를 임의로 추가할 때 지원되는 키워드들이 있습니다. 해당 키워드들은 아래 링크의 4.3 항목에서 확인 가능합니다.

https://arahansa.github.io/docs_spring/jpa.html

 

스프링 데이터 JPA 레퍼런스 번역 문서입니다.

어플리케이션의 데이터 접근 레이어를 구현하는 것은 꽤 성가신 일이었습니다. 간단한 쿼리와, 페이징, auditing를 위해 너무 많은 보일러플레이트 코드가 쓰여져야했습니다. 스프링데이터JPA는 실제 필요한 부분에 대해서 많은 양의 노력을 줄이게 함으로써, 데이터 접근 레이어의 향상에 목표를 두고 있습니다. 개발자가 커스텀 찾기 메소드를 포함해 리파지토리 인터페이스를 작성하기만 하면, 스프링은 구현체를 자동적으로 제공할 것입니다.

arahansa.github.io


com/graphql/blog/sample/CityDataFetcher.java을 생성합니다.

package com.graphql.blog.sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import graphql.schema.DataFetcher;

@Component
public class CityDataFetcher {
  
  @Autowired
  private CityRepository cityRepository;

  public DataFetcher<?> allCities () {
    return environment -> {
      return cityRepository.findAll();
    };
  }

  public DataFetcher<?> city () {
    return environment -> {
      int id = environment.getArgument("id");
      return cityRepository.findById(id);
    };
  }

}

샘플 데이터를 반환하는 DataFetcher를 반환하는 각 메서드를 정의합니다.


이제 GraphQL API를 생성해보겠습니다.

com/graphql/blog/config/GraphQLAPI.java를 생성합니다.

package com.graphql.blog.config;

import java.io.IOException;
import java.net.URL;

import javax.annotation.PostConstruct;

import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.graphql.blog.sample.CityDataFetcher;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import graphql.schema.idl.TypeRuntimeWiring;

@Component
public class GraphqlAPI {

  @Autowired CityDataFetcher dataFetcher;

  private GraphQL graphQL;

  @Value("classpath:static/graphql/schema.graphqls") 
  Resource resource;

  // (3)
  @Bean 
  public GraphQL graphQL() {
    return graphQL;
  }

  // (1)
  @PostConstruct
  public void init() throws IOException {
    URL url = resource.getURL();
    String sdl = Resources.toString(url, Charsets.UTF_8);
    GraphQLSchema graphQLSchema = buildSchema(sdl);
    this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
  }

  // (2)
  private GraphQLSchema buildSchema(String sdl) {
    TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
    RuntimeWiring runtimeWiring = buildWiring();
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
  }

  private RuntimeWiring buildWiring() {
    return RuntimeWiring.newRuntimeWiring()
    .type(
      TypeRuntimeWiring
      .newTypeWiring("Query")
      .dataFetcher("allCities", dataFetcher.allCities())
      .dataFetcher("city", dataFetcher.city())      
    )
    .build();
  }

}

@실행 순서

(1) GraphqlAPI.java의 인스턴스가 초기화될 때 실행됩니다. Graphql 인스턴스를 생성합니다.

(2) 위에서 정의한 schema.graphqls에서 타입 정보를 추출합니다. 또한 buildWiring메서드를 통해 DI받은 DataFetcher의 쿼리들을 가져온 뒤 추출한 타입 정보와 함께 등록합니다.

(3) 스프링 컨테이너에 Graphql을 Bean으로 등록합니다.


# 실행

설치했던 PlayGround를 실행합니다. 만약 설치하지 않았다면 데모 사이트로 테스트하세요.

URL ENDPOINT를 입력하고 OPEN 합니다.

※EndPoint : http://localhost:8000/graphql

 

그럼 아래와 같은 화면이 나올 텐데요. 우측에 DOCS와 SCHEMA를 확인하면 위에서 정의했던 스키마 & 타입을 확인할 수 있습니다.


테스트 쿼리를 한번 날려봅시다.

잘 실행되네요. 그럼 이번엔 City 목록 중 id 3번의 데이터만을 가져와 보도록 하겠습니다.

보다시피 데이터가 정상적으로 호출되었습니다. 딱 원하는 데이터만 들고 말이죠.


# 마치며

이렇게 SpringBoot에 Graphql를 적용하는 것도 어렵지 않게 마무리되었습니다.

하지만 GraphqlAPI에서 DataFetcher를 등록하는 부분이 좀 걸리네요.

앞으로 쿼리가 계속 늘어날 텐데 저렇게 사용하는 건 좀 별로인 것 같습니다.

다음 포스팅에서는 DataFetcher를 쉽고 간단하게 등록할 수 있게 해 보겠습니다.


# GitHub

https://github.com/eonnine/MyBlog

 

eonnine/MyBlog

Spring Boot, Graphql, PostgreSQL, React-Apollo, Parcel - eonnine/MyBlog

github.com


# 참고 문서

https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/#graphql-in-3-minutes

 

GraphQL Java

This is a tutorial for people who want to create a GraphQL server in Java. It requires some Spring Boot and Java knowledge and while we give a brief introduction into GraphQL, the focus of this tutorial is on developing a GraphQL server in Java. GraphQL in

www.graphql-java.com

댓글