🔷 GraphQL1. 스키마 & SDL📖 개요

01 · 스키마 & SDL

이 챕터가 답하는 질문: GraphQL 스키마는 무엇이고, SDL로 어떻게 표현하며, 디렉티브의 진짜 역할은 무엇인가? 한 줄 답 (Pyramid Top): “스키마는 클라이언트와 서버 사이의 런타임 강제 가능한 계약이고, SDL은 그 계약을 사람이 읽는 형태로 적은 것이다.”


챕터 지도 (Mermaid)


Why — 왜 이 챕터를 먼저 보나

GraphQL에서 발생하는 사고의 절반은 스키마를 잘못 적은 것에서 출발한다.

  • “왜 user.email 필드가 null로 오지?” → 필드에 !를 안 붙였다 → 01-sdl-syntax
  • “왜 input UserInputtype UserInput으로 적으면 에러나지?” → 인풋과 아웃풋은 분리되어 있다 → 04-input-types-and-arguments
  • “왜 __typename이 필요하지?” → union·interface의 구체 타입을 구분하려면 → 03-interface-union-enum
  • “왜 Date 스칼라는 스펙에 없지?” → 표준 스칼라는 5종뿐, Date는 커스텀 → 06-custom-scalars
  • “왜 @deprecated가 코드를 안 바꿀까?” → 디렉티브는 원래 동작 변경자, 표준 ones는 부작용 없음 → 05-directives
  • “왜 우리 팀은 Nexus를 쓰는데 옆 팀은 .graphql 파일을 쓰지?” → schema-first와 code-first의 철학 차이 → 07-schema-first-vs-code-first

이 챕터는 스키마라는 단어 안에 숨어 있는 7개의 개념에 이름을 붙인다.


How — 어떻게 읽나

#문서읽는 데핵심 키워드
0101-sdl-syntax8분type, Query/Mutation/Subscription, !, [T], [T!]!
0202-types-scalars-objects10분Int/Float/String/Boolean/ID, object type, field, argument
0303-interface-union-enum12분interface, union, enum, __typename, fragment
0404-input-types-and-arguments10분input, default value, mutation input pattern
0505-directives15분@deprecated, @skip, @include, @specifiedBy, custom directive
0606-custom-scalars12분DateTime, JSON, EmailAddress, parseValue/parseLiteral/serialize
0707-schema-first-vs-code-first12분schema-first, code-first, Apollo/Yoga vs Pothos/Nexus/TypeGraphQL

의존성: 02는 01의 문법을 가정한다. 03·04는 02 위에 쌓인다. 05·06은 어디서 읽어도 되지만 04 다음을 추천. 07은 챕터 전체를 어떻게 관리할 것인가로 닫는다.


What — 한 페이지 요약

문서한 줄 결론
01SDL의 핵심은 타입과 nullability다. 기본은 nullable이고 !가 붙어야 non-null인 이 결정이 GraphQL의 가장 자주 틀리는 부분이다.
02표준 스칼라는 5종(Int·Float·String·Boolean·ID)뿐이고, 그 외 모든 문자열·정수가 아닌 값은 커스텀 스칼라거나 object type이다.
03interface공통 필드를 강제하고, union공통 필드 없이 선택지를 묶고, enum닫힌 집합의 값을 강제한다 — 셋 다 __typename으로 구체 타입을 가른다.
04인풋 타입과 오브젝트 타입은 문법적으로 분리되어 있다 — 인풋은 input-only, 아웃풋은 output-only로 강제한다.
05디렉티브는 주석이 아니라 동작 변경자다. 표준 directive(@deprecated·@skip·@include)는 부작용이 약하지만, 커스텀 directive는 스키마 변환을 한다.
06Date·JSON·Email은 모두 커스텀 스칼라다. parseValue / parseLiteral / serialize 세 함수를 적어야 한다.
07schema-first(SDL이 SoT)와 code-first(코드가 SoT)는 철학 차이다. 어느 쪽도 우월하지 않고, 팀의 type system 의존도가 답을 정한다.

What-if — 이 챕터를 건너뛰면

  • nullability를 모르면: 클라이언트가 ?. 체이닝을 도배하다 런타임에서 깨진다.
  • interface·union을 모르면: union 응답에서 __typename을 안 받아 판별 불가해진다.
  • input/object 분리를 모르면: 한 타입을 양쪽에서 쓰려다 스키마 등록부터 실패한다.
  • directive를 메타데이터로 오해하면: @auth 같은 커스텀 directive가 runtime에서 동작하지 않는 미신적 사고가 생긴다.
  • schema-first/code-first를 혼동하면: 두 진영 코드가 한 레포에 섞여 SoT가 두 개가 된다.

Insight — 한 단락 이야기

“SDL은 처음엔 spec에 없었다”

2015년 GraphQL 첫 공개 당시, 스키마는 JavaScript 코드로만 정의되었다. new GraphQLObjectType({ ... }). SDL — 사람이 읽는 문법 — 은 2016년 RFC로 추가되었고 2018년에야 공식 스펙(October 2016 → June 2018)에 포함되었다. 즉 code-first가 원조schema-first가 후발이다. 그러나 SDL이 추가된 순간 서버 구현 언어와 무관한 공용어가 생겼다 — Python·Go·Rust 서버도 같은 .graphql 파일을 읽을 수 있게 된 것이다. 추상화의 출발은 코드였지만, 표준화의 도착지는 텍스트였다.


한 단락 요약

스키마는 런타임 강제력을 가진 계약이고, SDL은 그 계약의 텍스트 표현이다. 이 챕터를 끝내면 Query.user(id: ID!): User 한 줄에 들어 있는 7개의 결정(Query라는 root, user라는 필드명, id라는 인자명, ID!라는 non-null 스칼라, User라는 object type, 그 nullability, 그리고 이 한 줄이 SDL인지 코드인지)을 분리해서 읽을 수 있게 된다. 다음 챕터(02-execution-resolvers)는 이 계약을 어떻게 평가하는가를 다룬다.