- 어느날 서버 코드를 작업하다가… 의문이 들었다. 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 |