GraphQL 명세 ④: 스키마가 자기 자신을 설명하는 인트로스펙션(Introspection)
GraphiQL이나 Apollo Studio 같은 도구를 써보셨다면, 필드 이름을 한 글자만 입력해도 가능한 필드 목록이 주르륵 뜨는 경험을 하셨을 텐데요. 신기하게도 도구는 우리가 쓰는 서버의 스키마를 미리 다 알고 있는 것처럼 동작합니다. 누가 그 정보를 알려준 걸까요?
답은 GraphQL 서버 자신입니다. GraphQL 스키마는 자기 자신에 대한 정보를 GraphQL 쿼리로 되물을 수 있도록 설계되어 있는데요. 이 능력을 인트로스펙션(Introspection) 이라고 부르고, 명세 제4장이 이걸 다룹니다. 지난 편에서 본 타입 시스템이 “스키마를 어떻게 정의하는가”였다면, 이번 편은 “그 스키마를 어떻게 들여다보는가”에 대한 이야기입니다.
스키마에게 스키마를 물어볼 수 있습니다
인트로스펙션의 핵심 아이디어는 간단합니다. 데이터를 조회하는 것과 똑같은 방식으로, 스키마 자체를 조회하는 것입니다. 이를 위해 명세는 특별한 메타 필드(meta-field) 를 정의하는데요. 이름이 모두 밑줄 두 개(__)로 시작합니다.
가장 대표적인 게 __schema입니다. 이 필드를 쿼리하면 스키마 전체의 정보를 받을 수 있습니다.
{
__schema {
queryType {
name
}
types {
name
}
}
}
이 쿼리는 “이 스키마의 query 루트 타입 이름이 뭐고, 정의된 타입 전체 목록은 뭐냐”고 묻는 건데요. 응답은 평범한 GraphQL 응답과 똑같은 모양으로 돌아옵니다.
{
"data": {
"__schema": {
"queryType": { "name": "Query" },
"types": [{ "name": "Query" }, { "name": "User" }, { "name": "Post" }]
}
}
}
밑줄 두 개로 시작하는 이름은 명세가 인트로스펙션 전용으로 예약해두었습니다. 그래서 우리가 스키마를 정의할 때 __로 시작하는 이름은 쓸 수 없는데, 이건 일반 데이터 필드와 메타 필드가 서로 충돌하지 않게 하려는 약속입니다.
__type으로 특정 타입을 깊이 들여다봅니다
스키마 전체가 아니라 특정 타입 하나가 궁금할 때는 __type 메타 필드를 씁니다. 타입 이름을 인자로 넘기면 그 타입의 상세 정보를 돌려주는데요.
{
__type(name: "User") {
name
kind
fields {
name
type {
name
}
}
}
}
User 타입에 어떤 필드들이 있고 각 필드의 타입이 뭔지를 묻는 쿼리입니다. 여기서 kind라는 필드가 눈에 띄는데요. 이건 이 타입이 객체인지, 스칼라인지, 열거형인지 같은 “타입의 종류”를 알려줍니다. OBJECT, SCALAR, INTERFACE, UNION, ENUM, INPUT_OBJECT, LIST, NON_NULL 중 하나가 오는데, 지난 편에서 본 타입들이 그대로 여기 대응됩니다.
특히 LIST와 NON_NULL이 kind에 들어 있다는 점이 중요합니다. [Post!]! 같은 래핑 타입을 표현하려면, 인트로스펙션 결과에서 타입이 양파처럼 겹겹이 중첩되어 나오는데요. 바깥에서부터 NON_NULL → LIST → NON_NULL → OBJECT(Post) 순으로 ofType을 따라 들어가야 전체 모양을 알 수 있습니다. 도구들이 [Post!]!라는 표기를 그려낼 수 있는 것도 이 중첩 구조를 풀어내기 때문입니다.
__typename으로 응답의 실제 타입을 확인합니다
또 하나 자주 쓰는 메타 필드가 __typename입니다. 이건 어떤 필드에든 붙일 수 있고, 그 자리에 실제로 들어온 객체의 타입 이름을 문자열로 돌려줍니다.
{
search(text: "graphql") {
__typename
... on User {
name
}
... on Post {
title
}
}
}
지난 편에서 본 유니온이나 인터페이스를 기억하시나요? 한 자리에 여러 타입이 섞여 나올 수 있을 때, 클라이언트는 응답으로 받은 객체가 도대체 어떤 타입인지 알아야 화면에 맞게 처리할 수 있습니다. __typename이 바로 그 답을 주는데요.
{
"data": {
"search": [
{ "__typename": "User", "name": "김철수" },
{ "__typename": "Post", "title": "GraphQL 입문" }
]
}
}
그래서 Apollo Client 같은 라이브러리는 캐시에서 객체를 식별하기 위해 우리가 요청하지 않아도 __typename을 자동으로 끼워 넣곤 합니다. 응답의 각 객체가 어떤 타입인지 알아야 캐시를 정확히 관리할 수 있기 때문입니다.
그래서 도구들이 똑똑해집니다
이제 처음의 질문으로 돌아가 보겠습니다. GraphiQL은 어떻게 스키마를 다 알고 있을까요? 정답은 이렇습니다. 도구가 접속하는 순간 거대한 인트로스펙션 쿼리를 한 번 던집니다. __schema로 모든 타입, 필드, 인자, 설명문, 디렉티브 정보를 통째로 받아오는 거죠. 그 결과를 손에 쥐고 있으니 자동완성도, 타입별 문서도, 쿼리 검증도 전부 클라이언트 쪽에서 해낼 수 있는 겁니다.
코드 생성(codegen) 도구의 원리도 똑같습니다. 인트로스펙션으로 스키마를 받아와서 그걸 바탕으로 TypeScript 타입이나 클라이언트 코드를 자동으로 찍어내는데요. 서버가 자기 스키마를 표준 방식으로 노출하기 때문에, 한 번 만든 도구가 어떤 GraphQL 서버에도 그대로 동작합니다. 이게 GraphQL 생태계에 풍부한 도구가 빠르게 자리 잡은 큰 이유 중 하나입니다.
다만 이 강력함에는 그늘도 있습니다. 인트로스펙션이 켜져 있으면 누구나 스키마 구조를 통째로 들여다볼 수 있어서, 운영 환경에서는 보안을 이유로 인트로스펙션을 꺼두는 경우가 많은데요. 명세는 인트로스펙션 기능을 정의할 뿐 “운영에서 꼭 켜둬야 한다”고 강제하지는 않기 때문에, 이런 선택은 각 서비스의 정책에 맡겨져 있습니다.
설명문도 인트로스펙션으로 드러납니다
인트로스펙션이 돌려주는 건 타입과 필드의 구조만이 아닙니다. 스키마에 적어둔 설명문(description) 도 함께 따라 나오는데요. 타입이나 필드 위에 삼중 따옴표로 설명을 달아두면, 그 텍스트가 인트로스펙션 결과의 description 필드로 노출됩니다.
{
__type(name: "User") {
description
fields {
name
description
}
}
}
이 쿼리는 User 타입과 각 필드에 적힌 설명을 그대로 받아옵니다. GraphiQL에서 필드에 마우스를 올리면 친절한 설명이 툴팁으로 뜨는 것도, 바로 이 description을 읽어 보여주는 거죠. 그래서 스키마에 설명문을 꼼꼼히 달아두면 별도의 API 문서를 만들지 않아도 도구가 알아서 문서 역할을 해줍니다.
원래 이 설명문은 스키마 쪽에만 달 수 있었는데, 1편에서 예고했듯 September 2025 에디션에서는 우리가 작성하는 쿼리 같은 실행 문서에도 설명문을 달 수 있게 확장됐습니다. 이 이야기는 시리즈 마지막 편에서 자세히 다루겠습니다.
인트로스펙션도 결국 스키마의 일부입니다
흥미로운 점은 __schema나 __type 같은 메타 필드, 그리고 그것들이 반환하는 __Type이나 __Field 같은 타입이 모두 명세에 정식 타입으로 정의되어 있다는 사실입니다. 다시 말해 인트로스펙션 시스템 자체가 GraphQL 타입 시스템으로 기술되어 있는데요.
그래서 인트로스펙션 결과를 받는 일은 마법이 아니라, 그냥 미리 약속된 메타 타입들을 평범한 쿼리로 조회하는 것일 뿐입니다. 스키마를 설명하는 언어가 스키마 자신의 일부라는 이 자기 참조적인 구조가, 인트로스펙션을 우아하면서도 일관되게 만들어줍니다.
마치며
이번 편에서는 GraphQL 명세 제4장 Introspection을 따라, 스키마가 자기 자신을 설명하는 방법을 살펴봤습니다. __schema로 스키마 전체를, __type으로 특정 타입을, __typename으로 응답 객체의 실제 타입을 조회할 수 있다는 점, 그리고 이 메타 필드들 덕분에 GraphiQL의 자동완성이나 코드 생성 도구가 동작한다는 점을 확인했는데요. 인트로스펙션 시스템 자체가 GraphQL 타입으로 정의되어 있다는 자기 참조적 구조도 짚어봤습니다.
그런데 도구가 인트로스펙션으로 스키마를 알고 나면, 우리가 작성한 쿼리가 그 스키마에 맞는지 어떻게 판단할까요? 잘못된 필드를 요청하거나 타입에 안 맞는 인자를 넘기면 어떻게 걸러질까요? 그 규칙을 다루는 것이 다음 편 검증(Validation)입니다. 인트로스펙션으로 알아낸 스키마 정보가 검증의 기준이 되므로, 두 편은 자연스럽게 이어집니다.
인트로스펙션 시스템의 전체 타입 정의는 GraphQL September 2025 명세의 Introspection 장에서 확인할 수 있습니다.
This work is licensed under
CC BY 4.0