본문 바로가기
공부/etc

GraphQL에는 유연한 객체가 존재할까 (GraphQL 스칼라 타입에 대해)

by Piva 2025. 5. 5.
  • 어느날 서버 코드를 작업하다가… 의문이 들었다. GraphQL에는 Any 같은 타입이 존재하나?
  • 이것이 갑자기 궁금해진 이유는, 구조가 정해지지 않은 객체 타입을 반환하고 싶은데, 이걸 그냥 JS/TS에서 하듯이 하는 것이 불가능해보였기 때문이다.
// 참고로 이런 방식을 얘기한다
{ [key:string]: string }

그래서 GraphQL에는 이런 타입을 지원할까.

→ 결론부터; GraphQL은 강한 타입 시스템 때문인지 ‘일단은’ 이런 타입이 없다.

  • 그렇지만, 편법처럼 어떤 모양의 객체든 허용하게끔 만드는 것은 가능하다.
  • 이걸 가능하게 해주는 것이 '커스텀 GraphQL 스칼라 타입' 이라는데, 이 부분에 충분한 이해가 없어 조금 더 알아봤다.

 

GraphQL이 지원하는 타입

스칼라 타입

  공식 스펙 문서를 조금 읽어보면 “GraphQL의 응답은 트리 구조를 가진다” 라고 표현한다. 이 트리 구조란, 흔히 생각하는 오브젝트와 같은 key-value 형태가 중첩된 구조를 의미한다. 아래는 공식 튜토리얼 문서의 예제를 하나 가져왔다.

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}
  • 위의 GraphQL 응답 예제를 살펴보면, 최상위에 루트인 data가 존재하고,
  • 그 하위로 hero, hero의 하위로 name, appearsIn이 존재한다.
    ⇒ 이러한 점에서 우리가 흔히 아는 트리 구조를 띄고 있음을 알 수 있다.

  스칼라 타입은 이러한 구조에서 말단, 즉 리프 노드 역할을 하는 실제 값을 의미한다. 그러므로 하위 필드로 나누는 것이 불가능하고, 구체적인 값을 가진다.

  GraphQL이 제공하는 기본 스칼라 타입은 총 5가지가 있다.

Int 부호가 있는 32비트 정수
Float 부호가 있는 부동소수점 값
String UTF-8 문자열
Boolean true 혹은 false
ID 고유 식별자를 가리키는 값. 문자열과 비슷하게 동작한다.

 

 

커스텀 스칼라 타입

  GraphQL이 제공하는 기본 스칼라 타입은 5가지 뿐이지만, 개발자가 스칼라 타입을 직접 만드는 것도 가능하다. 이를 위에서도 언급한 ‘커스텀 스칼라 타입’이라고 하는데, 해당 타입의 이름이나 직렬화/역 직렬화 방법, 유효성 검사 방법 등을 정의함으로써 구현할 수 있다.

  GraphQL-JS를 통해 구현할 경우, 아래와 같은 값들을 GraphQLScalarType 생성자에 넘겨 구현한다.

import { GraphQLScalarType } from 'graphql';

const NewCustomScalarType = new GraphQLScalarType({
  name, // 새로운 커스텀 스칼라 타입의 이름
  description, // 커스텀 스칼라 타입에 대한 설명
  parseValue, // 입력받은 값을 파싱할 때 호출되는 함수
  serialize, // 서버에서 클라이언트로 보내는 응답 데이터에 대해 호출되는 함수 (DB에서 가져온 값을 GraphQL 값으로 변환할 때)
  parseLiteral, // 요청에 바로 입력한 리터럴 값을 파싱할 때 호출되는 함수
});

 

 

그래서 유연한 객체를 만드는 방법이란;

  위에서 내가 고민했던, 속성이 구체적으로 정해지지 않은 모양새의 객체를 받기 위해 커스텀 스칼라 타입을 정의할 수 있다.

(코드 출처) 이 코드는 객체는 객체 그대로, 문자열 형태는 JSON.parse를 통해 파싱하여 사용할 수 있도록 하는 코드이다. 따라서 객체 안에 어떤 값을 넣든지에 관계 없이 사용이 가능하다.

const ObjectScalar = new GraphQLScalarType({
	name: 'Object',
	description: 'Any object',
	parseValue: (value) => {
		return typeof value === 'object' ? value
      : typeof value === 'string' ? JSON.parse(value)
      : null
	},
	serialize: (value) => {
    return typeof value === 'object' ? value
      : typeof value === 'string' ? JSON.parse(value)
      : null
  },
  parseLiteral: (ast) => {
    switch (ast.kind) {
      case Kind.STRING: return JSON.parse(ast.value)
      case Kind.OBJECT: throw new Error(`Not sure what to do with OBJECT for ObjectScalarType`)
      default: return null
    }
  }
});

 

  이렇게 구현하는 것이 과한 것 같다면, 그냥 이미 정의된 JSON 타입을 설치해 사용할 수도 있다. 위의 코드도 JSON을 파싱하는 흐름으로 되어있기도 하고… 이미 언급했듯 GraphQL은 JSON 타입을 지원하지 않지만, graphql-type-json 패키지를 사용하면 미리 정의된 JSON 타입을 GraphQL에서 사용할 수 있다.

 

scalar JSON

type CustomError {
	message: String
	detail: JSON // detail은 동적인 구조를 가질 수 있게 된다
}

 


  • 참고한 문서들

https://graphql.org/learn/schema/#scalar-types

 

Schemas and Types | GraphQL

Copyright © 2025 The GraphQL Foundation. All rights reserved. For web site terms of use, trademark policy and general project policies please see https://lfprojects.org

graphql.org

https://spec.graphql.org/draft/#sec-Scalars

 

GraphQL

GraphQL allows directives that are defined as repeatable to be used more than once on the definition they apply to, possibly with different arguments. In contrast, if a directive is not repeatable, then only one occurrence of it is allowed per location. Fo

spec.graphql.org

 

  • GraphQL을 실무로 무작위로 접했다보니, 이론적인 부분에 취약하다는 생각이 든다. 핵심을 파악할 수 있는 공부를 해야지...

'공부 > etc' 카테고리의 다른 글

PostgreSQL 로컬 DB 세팅기  (0) 2025.03.23
Three.js and TypeScript (11)  (2) 2024.12.03
Three.js and TypeScript (10)  (0) 2024.11.25
Three.js and TypeScript (9)  (0) 2024.11.22
Three.js and TypeScript (8)  (1) 2024.11.21