티스토리 뷰
# 시작
지난 포스팅에서 SpringBoot를 세팅하고 PostgreSQL도 연동했습니다.
이제 GraphQL API를 적용합니다.
# 준비
- GraphQL for VSCode : VS Code 확장 프로그램 마켓 플레이스 ( Ctrl + Shift + X )에서 설치
- PlayGround : https://github.com/prisma/graphql-playground/releases
위 링크에서 desktop app을 클릭하면 쿼리를 테스트해볼 수 있는 PlayGround를 설치할 수 있습니다.
만약 프로그램을 설치하는게 싫다면 아래 데모 사이트를 통해 테스트해볼 수 있습니다.
https://www.graphqlbin.com/v2/6RQ6TM
# 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/
이제 샘플 데이터의 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/
이번엔 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
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
# 참고 문서
https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/#graphql-in-3-minutes
'프로젝트 > 나만의 블로그' 카테고리의 다른 글
React & Parcel 개발 환경 구성 [FE] (0) | 2019.08.24 |
---|---|
Spring Boot & GraphQL (2) [BE] (2) | 2019.08.18 |
Spring Boot & PostgreSQL [BE] (0) | 2019.08.18 |
Spring Boot 개발 환경 구성 [BE] (0) | 2019.08.18 |
[나만의 블로그]시작 (0) | 2019.08.16 |
- Total
- Today
- Yesterday
- execution context
- Jenkins
- JPA
- Spring Boot
- Handshake
- typescript
- Docker
- react
- Kubernetes
- 프로그래머스[Lv1]
- 프로그래머스[정렬]
- CI
- javascript
- 프로그래머스
- Nashorn
- CRP 최적화
- 프로그래머스[힙]
- 프로그래머스[해시]
- 알고리즘
- Pipeline
- 동적계획법
- CD
- graphql
- 웹 사이트 최적화
- PostgreSQL
- 프로그래머스[스택/큐]
- 실행 문맥
- Apollo
- 프로그래머스[이분탐색]
- Web
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |