티스토리 뷰

GraphQL의 정의하는 기본 scalar type은 아래와 같이 4가지 이다.

Int: A signed 32‐bit integer.
Float: A signed double-precision floating-point value.
String: A UTF‐8 character sequence.
Boolean: true or false.

 

이외의 type을 사용하기 위해서는
graphql-java에서 제공하는 GraphQLScalarType 사용하여 custom scalar type을 구현해야 한다.
이전에는 GraphQLScalarType을 상속받아서 구현하였으나

현재 해당 방식은 deprecated 되었고, 현재는 builder를 사용해야 한다.

json 형태의 data를 추가한다면 아래와 같이 builder를 사용할 수 있다.
(sample code는  kotlin으로 구현되었음)

GraphQLScalarType
  .newScalar()
  .name("JSON")
  .description("com.fasterxml.jackson.databind.JsonNode")
  .coercing(object : Coercing<ObjectValue, JsonNode> {
      @Throws(CoercingSerializeException::class)
      override fun serialize(dataFetcherResult: Any): JsonNode {
          return if (dataFetcherResult is JsonNode) {
              dataFetcherResult
          } else {
              throw CoercingSerializeException("Unable to serialize " + dataFetcherResult
                      + ". Wrong Type. Expected JsonNode.class, Got " + dataFetcherResult.javaClass
              )
          }
      }

      @Throws(CoercingParseValueException::class)
      override fun parseValue(input: Any): ObjectValue {
          return if (input is ObjectValue) {
              input
          } else {
              throw CoercingParseValueException("Unable to parseValue " + input
                      + ". Wrong Type. Expected ObjectValue.class, Got " + input.javaClass
              )
          }
      }

      @Throws(CoercingParseLiteralException::class)
      override fun parseLiteral(input: Any): ObjectValue {
          return if (input is ObjectValue) {
              input
          } else {
              throw CoercingParseLiteralException("Unable to parseLiteral " + input
                      + ". Wrong Type. Expected ObjectValue.class, Got " + input.javaClass
              )
          }
      }
  })
  .build()


Builder method 중 다음과 같이 3가지를 사용한다.

@Override
public Builder name(String name) {
  super.name(name);
  return this;
}

@Override
public Builder description(String description) {
  super.description(description);
  return this;
}

public Builder coercing(Coercing coercing) {
  this.coercing = coercing;
  return this;
}


name(String name) : graphql schema 에서 사용하기 위한 이름을 정의
description(String description) : 어디서 사용되는지 아직 못찾았음 (인터넷에서 찾은 다양한 예제들에서 대부분 구현)
coercing(Coercing coercing) : input, output 데이터를 정의하고 serialize, parse 를 위한 구현을 제공


Coercing은 graphql-java library에서 제공하는 interface 이며
아래와 같이 Input 타입(client에서 받는 type )과, Output 타입(server가 return하는 type)을 정의하게 되어 있다.

public interface Coercing<I, O>


Coercing interface의 abstract method는 다음과 같다.

O serialize(Object dataFetcherResult) throws CoercingSerializeException;
I parseValue(Object input) throws CoercingParseValueException;
I parseLiteral(Object input) throws CoercingParseLiteralException;


serialize : dataFetcherResult의 결과로 return되는 java object를 scalar type 에 맞게 변환하기 위한 메소드
parseValue : query에서 값을 parsing 할 때 사용되는 메소드
parseLiteral : 요청받은 query를 validation 할 때 사용되는 메소드

예제의 경우 input은 ObjectValue, output은 JsonNode로 정의하였다.
(graphql-java를 사용하는 경우 query에 있는 json type은 ObjectValue class로 읽어들인다.)

위의 serialize 예제에서는 resolver에서 JsonNode를 return 한다는 가정하에 class type 만 확인하고 그대로 return 하였으나
만약 resolver에서 다른 type을 return 한다면 converting 작업을 구현해야 한다.

예를 들어서 resolver에서 String 형태로 json 을 return 한다면
serialize는 다음과 같이 구현될 것이다.

@Throws(CoercingSerializeException::class)
override fun serialize(dataFetcherResult: Any): JsonNode {
    return if (dataFetcherResult is String) {
        ObjectMapper().readTree(dataFetcherResult)
    } else {
        throw CoercingSerializeException("Unable to serialize " + dataFetcherResult
                + ". Wrong Type. Expected String.class, Got " + dataFetcherResult.javaClass
        )
    }
}


위와 같이 custom scalar type을 정의하면
GraphQL Configuration에서 아래와 같이 추가를 해줘야 한다.

@Configuration
class GraphQLConfig {
    @Bean
    fun scalars(): Array = arrayOf(customJson)

    ...
}

val customJson : GraphQLScalarType = GraphQLScalarType
        .newScalar()
        .name("JSON")
        .description("com.fasterxml.jackson.databind.JsonNode")
        .coercing(object : Coercing<ObjectValue, JsonNode> {
            @Throws(CoercingSerializeException::class)
            override fun serialize(dataFetcherResult: Any): JsonNode {
                return if (dataFetcherResult is JsonNode) {
                    dataFetcherResult
                } else {
                    throw CoercingSerializeException("Unable to serialize " + dataFetcherResult
                            + ". Wrong Type. Expected JsonNode.class, Got " + dataFetcherResult.javaClass
                    )
                }
            }

            @Throws(CoercingParseValueException::class)
            override fun parseValue(input: Any): ObjectValue {
                return if (input is ObjectValue) {
                    input
                } else {
                    throw CoercingParseValueException("Unable to parseValue " + input
                            + ". Wrong Type. Expected ObjectValue.class, Got " + input.javaClass
                    )
                }
            }

            @Throws(CoercingParseLiteralException::class)
            override fun parseLiteral(input: Any): ObjectValue {
                return if (input is ObjectValue) {
                    input
                } else {
                    throw CoercingParseLiteralException("Unable to parseLiteral " + input
                            + ". Wrong Type. Expected ObjectValue.class, Got " + input.javaClass
                    )
                }
            }
        })
        .build()


GraphQL Schema 파일에서는 다음과 같이 scalar를 정의하고 사용할 수 있다.
("JSON Type"은 GraphiQL에서 Schema를 확인할 때 description 에 표시되는 내용이다.)

"JSON Type"
scalar JSON

## Query
type Query {
    getJsonValue(value: JSON): JSON
}

 

Input으로 들어오는 ObjectValue는 아래와 같이 ObjectField array 를 가지고 있으며,
getObjectFields()를 사용하여 해당 값을 가져올 수 있다.

private final List objectFields = new ArrayList<>();

public List getObjectFields() {
    return new ArrayList<>(objectFields);
}


ObjectField는 아래와 같이 name, value를 가지고 있으며,
getName(), getValue()를 사용하여 값을 가져올 수 있다.

private final String name;
private final Value value;

public String getName() {
    return name;
}

public Value getValue() {
    return value;
}


아래와 같이 for 문으로 입력받은 json 의 값을 확인할 수 있다.

for (tmp: ObjectField in value.objectFields) {
    println("${tmp.name}, ${tmp.value}")
}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함