GraphQL API 설계 가이드: 외주 개발 후 유지보수 가능한 스키마·권한·성능 기준
GraphQL API 설계 가이드: 외주 개발 후에도 유지보수 가능한 스키마·권한·성능 기준
결론부터 말하면 GraphQL은 REST를 무조건 대체하는 기술이 아닙니다. REST API만으로 SaaS 관리자 화면의 복합 조회, 고객사별 권한, 웹·모바일·파트너 포털의 서로 다른 데이터 요구를 감당하기 어려울 때 검토할 만합니다. 다만 DB 테이블을 그대로 노출하거나, 필드 권한과 쿼리 제한 없이 도입하면 REST보다 장애와 보안 리스크가 커질 수 있습니다. 도입 여부는 화면 복잡도, 클라이언트 수, 권한 모델, 운영 인력, 외주 이후 유지보수 가능성을 기준으로 판단해야 합니다.

이 글은 GraphQL API 설계를 처음 검토하는 창업자, 정부지원사업 MVP 담당자, PM, 마케터, 비개발 경영진이 개발사 제안을 검토할 수 있도록 작성했습니다. 구현 프레임워크별 세부 코드보다 ‘무엇을 요구해야 안전한가’에 초점을 맞춥니다.
1. GraphQL을 도입할지 먼저 판단하는 기준
GraphQL의 핵심은 클라이언트가 필요한 필드를 요청하고, 서버는 스키마가 허용한 범위에서 데이터를 조합해 반환하는 것입니다. 공식 사양에서도 GraphQL 타입 시스템은 서비스가 제공하는 기능과 요청 유효성, 응답 형태를 설명하는 기준으로 다뤄집니다. 즉 GraphQL 스키마는 단순 DTO 목록이 아니라 서버와 클라이언트 사이의 계약입니다. ([spec.graphql.org](https://spec.graphql.org/September2025/))
| 판단 항목 | REST 유지가 유리한 경우 | GraphQL 검토가 필요한 경우 | 의사결정 메모 |
|---|---|---|---|
| 화면 구조 | 목록, 상세, 생성, 수정 중심의 단순 CRUD | 한 화면에서 고객, 계약, 결제, 사용자, 이벤트, 리포트를 함께 조회 | 화면 수보다 데이터 조합 다양성이 중요합니다. |
| 클라이언트 수 | 웹 프론트 한 곳만 소비 | 웹, 모바일, 관리자, 고객 포털, 파트너 포털이 다른 필드 조합을 요구 | 클라이언트별 REST endpoint 증식이 비용이 됩니다. |
| 권한 모델 | 엔드포인트 단위 권한으로 충분 | 동일 객체 안에서도 역할·요금제·고객사별로 보이는 필드가 다름 | 필드 단위 정책표가 없으면 GraphQL은 위험합니다. |
| 성능 운영 | 요청 패턴이 예측 가능하고 캐싱이 단순 | 중첩 조회, 리스트 조회, 다수 리졸버 호출을 제어할 준비가 있음 | DataLoader와 쿼리 제한은 선택이 아니라 기본 요구사항입니다. |
| 외부 공개 API | 파트너가 URL·HTTP cache·문서 중심으로 쓰는 공개 API | 1차 고객용 앱이 대부분이고 허용된 operation을 관리할 수 있음 | 공개 API는 REST 또는 REST+GraphQL 혼합이 현실적입니다. |
도입 기준은 ‘최신 기술을 쓰는가’가 아니라 ‘같은 도메인 데이터를 서로 다른 화면이 계속 다른 모양으로 필요로 하는가’입니다.

2. REST로 버티기 어려운 SaaS·관리자 화면 패턴
B2B SaaS 관리자 화면은 생각보다 REST endpoint가 빨리 늘어납니다. 예를 들어 고객사 상세 페이지에 기본 정보, 사용량, 계약 상태, 결제 이력, 최근 로그인 사용자, 권한 요청, CS 티켓, AI 분석 요약이 한 번에 필요하다고 가정해 보겠습니다. REST로는 /customers/:id, /contracts, /billing, /users, /events, /tickets처럼 여러 API를 호출하거나, 화면 전용 집계 endpoint를 계속 추가하게 됩니다.
처음에는 화면 전용 REST API가 빠릅니다. 하지만 관리자 화면이 10개, 20개로 늘어나면 문제가 달라집니다. 각 화면이 비슷하지만 조금씩 다른 데이터를 요구하고, 모바일 앱은 더 작은 응답을 원하며, 고객 포털은 민감 필드를 제외해야 합니다. 이때 GraphQL은 화면이 필요한 필드 조합을 클라이언트가 명시하게 하면서도, 서버가 하나의 스키마와 권한 정책으로 통제할 수 있는 선택지가 됩니다.
다만 GraphQL도 화면 편의를 위해 모든 내부 필드를 열어두면 곧바로 기술 부채가 됩니다. GraphQL은 ‘클라이언트가 마음대로 DB를 조회하는 문’이 아니라 ‘서버가 허용한 제품 기능을 조합해서 읽는 계약’으로 설계해야 합니다.
3. 스키마 설계: DB가 아니라 제품 언어로 만들 것
스키마 설계에서 가장 흔한 실패는 ORM이나 DB 테이블을 자동 변환해 GraphQL type으로 공개하는 방식입니다. Apollo의 수요 중심 스키마 설계 문서도 클라이언트와 제품 기능 기준으로 스키마를 설계해야 하며, 백엔드 데이터 소스 기반 자동 생성은 장기적으로 불필요한 필드와 의도치 않은 사용 사례를 만들 수 있다고 설명합니다. ([apollographql.com](https://www.apollographql.com/docs/deploy-preview/54a7d6c42af30e03a5cd/graphos/schema-design/guides/demand-oriented-schema-design))
스키마 설계 원칙
- Query는 화면의 질문을 표현해야 합니다.
customer,customerConnection,invoiceSummary처럼 제품 담당자도 의미를 이해할 수 있어야 합니다. - Mutation은 비즈니스 행위를 표현해야 합니다.
patchCustomer하나로 모든 필드를 바꾸기보다updateBillingContact,inviteWorkspaceMember,approvePlanChange처럼 의도가 드러나야 감사 로그와 권한 검사가 쉬워집니다. - 큰 리스트는 반드시 페이지네이션합니다. GraphQL.org는 대량 리스트 필드에 대해 cursor 기반 connection 모델을 소개하며, cursor가 불투명하면 내부 pagination 방식을 바꿔도 클라이언트 계약을 유지하기 쉽다고 설명합니다. ([graphql.org](https://graphql.org/learn/pagination/))
- ID 전략을 정합니다. 클라이언트 캐시는 객체를 식별할 수 있어야 합니다. GraphQL.org의 caching 문서는 객체 타입에 전역적으로 유일한 ID 필드를 두면 다양한 캐싱 전략에 도움이 된다고 정리합니다. ([graphql.org](https://graphql.org/learn/caching/))
- nullability는 약속입니다. GraphQL에서는 필드가 기본적으로 nullable이며, non-null은 클라이언트에 강한 보증을 줍니다. 장애, 권한, 외부 API 실패 가능성이 있는 필드를 성급히 non-null로 만들면 부분 실패가 전체 응답 실패로 번질 수 있습니다. ([graphql.org](https://graphql.org/learn/schema-design/))
| 나쁜 설계 | 왜 위험한가 | 개선 방향 |
|---|---|---|
customer_tb, billing_yn 같은 DB 명칭 노출 | DB 리팩터링이 API breaking change가 됩니다. | Customer, billingStatus처럼 제품 용어 사용 |
updateCustomer(input: AnyJson) | 권한·검증·감사 로그가 필드별로 분리되지 않습니다. | 행위별 mutation과 명시적 input type 설계 |
users: [User] 무제한 반환 | 고객사 사용자가 늘면 응답 크기와 DB 부하가 폭증합니다. | userConnection(first, after, filter) 적용 |
클라이언트가 tenantId를 자유 입력 | 다른 고객사 데이터 조회 취약점으로 이어질 수 있습니다. | 인증 context에서 tenant scope를 서버가 결정 |
| 내부 상태값 전체 enum 공개 | 운영용 임시 상태가 외부 계약으로 굳어집니다. | 클라이언트에 필요한 상태만 API enum으로 매핑 |
type Query { customer(id: ID!): Customer customerConnection(first: Int!, after: String, filter: CustomerFilter): CustomerConnection! } type Customer { id: ID! displayName: String! plan: Plan! billingSummary: BillingSummary recentActivities(first: Int!, after: String): ActivityConnection! }이 예시는 DB 테이블 목록이 아니라 고객 상세 화면과 고객 목록 화면이 실제로 묻는 질문을 중심으로 구성한 스키마입니다. 외주 개발사에 GraphQL을 맡긴다면 먼저 DB ERD가 아니라 화면별 데이터 요구표와 권한 정책표를 요구해야 합니다.
4. 권한 설계: 필드 단위로 보이되 정책은 비즈니스 로직에 둔다
GraphQL은 필드 단위 제어가 가능하기 때문에 권한 설계가 쉬워 보입니다. 그러나 실제로는 REST보다 권한 누락을 찾기 어려울 수 있습니다. GraphQL.org는 인증 정보를 GraphQL 실행 전에 context로 전달하되, 권한 판단은 GraphQL 계층에 흩뿌리기보다 비즈니스 로직 계층에 위임하는 것을 권장합니다. ([graphql.org](https://graphql.org/learn/authorization/))
SaaS에서는 최소한 다음 질문에 답해야 합니다. 이 사용자는 어느 고객사 소속인가, 현재 요청 고객사의 멤버인가, 역할은 무엇인가, 요금제상 볼 수 있는 기능인가, 해당 객체의 소유자 또는 승인자인가, 필드가 개인정보·결제정보·내부 운영정보인가. 고객사별 데이터 분리 기준은 다중 테넌시 데이터 모델링 가이드와 함께 검토하는 것이 좋습니다.
| 대상 | 예시 | 권한 정책 | 반환 방식 | 테스트 관점 |
|---|---|---|---|---|
| 객체 접근 | customer(id) | 사용자 tenant와 customer tenant 일치 | 권한 없음은 null 또는 not found로 통일 | 다른 고객사 ID로 조회 시 실패 |
| 민감 필드 | billingSummary | Owner, Finance 역할 또는 과금 관리자만 허용 | 필드 null 또는 권한 오류 | 일반 멤버가 같은 객체의 민감 필드 요청 |
| 행위 | inviteWorkspaceMember | Admin 이상, 좌석 수 제한 확인 | 명확한 business error | 좌석 초과, 중복 초대, 권한 부족 |
| 리포트 | exportUsageReport | 요금제·쿼터·기간 제한 | 비동기 job 반환 | 긴 기간·반복 요청·동시 요청 |
권한 설계에서 피해야 할 것
- 프론트엔드에서 버튼을 숨기는 것으로 권한 처리를 끝내는 방식
- resolver마다 if문을 복사해 정책이 흩어지는 방식
- 클라이언트가 전달한
tenantId를 신뢰하는 방식 - DataLoader나 캐시를 사용자·고객사 구분 없이 전역 공유하는 방식
- introspection을 끄면 권한 문제가 해결된다고 보는 방식
introspection 제한은 공격자가 스키마를 쉽게 알아내지 못하게 하는 보조 수단일 뿐입니다. GraphQL.org Security 문서도 introspection 비활성화만으로는 민감한 스키마와 사용자 데이터를 보호하기에 충분하지 않으며, trusted documents와 authorization을 함께 사용해야 한다고 설명합니다. ([graphql.org](https://graphql.org/learn/security/))
5. 성능·장애 예방: N+1보다 더 넓게 봐야 한다
GraphQL 성능 이슈를 말할 때 가장 많이 언급되는 것이 N+1 문제입니다. GraphQL.org Performance 문서는 리졸버가 순진하게 구현되면 하나의 요청이 여러 데이터 소스 호출로 번질 수 있으며, 이를 batching 기법과 DataLoader 같은 도구로 완화한다고 설명합니다. ([graphql.org](https://graphql.org/learn/performance/)) DataLoader 공식 문서도 batching과 caching을 데이터 로딩 계층의 핵심 기능으로 설명하며, 사용자별 권한이 다른 웹 서버에서는 보통 요청이 시작될 때 DataLoader 인스턴스를 만들고 요청 종료 후 재사용하지 않는 패턴을 안내합니다. ([github.com](https://github.com/graphql/dataloader))
중요한 점은 DataLoader가 만능 캐시가 아니라는 것입니다. DataLoader는 같은 요청 안에서 중복 로딩을 줄이는 장치이지, Redis나 CDN 같은 공유 캐시를 대체하지 않습니다. 특히 고객사별 권한이 있는 SaaS에서 전역 DataLoader를 쓰면 A 고객사의 캐시 결과가 B 고객사 요청에 섞이는 치명적인 사고가 날 수 있습니다.
운영 가능한 GraphQL 성능 가드레일
- 리스트 제한: 모든 대량 리스트에
first,after, 최대 page size, 정렬 기준을 둡니다. - 데이터 로딩: 리졸버별 DB 호출을 측정하고, 연관 객체 조회는 요청 단위 batching으로 묶습니다.
- 쿼리 제한: depth, list depth, top-level field 수, alias 수, batch operation 수를 제한합니다.
- 복잡도 예산: 비용이 큰 필드에 weight를 주고 사용자·조직별 query budget을 둡니다.
- 운영 모니터링: operationName, query hash, resolver latency, DB query count, error path를 로그와 대시보드에 남깁니다.
GraphQL.org Security 문서는 trusted documents, pagination, depth 제한, breadth·batch 제한, rate limiting, query complexity analysis를 GraphQL API의 demand control 수단으로 정리합니다. OWASP GraphQL Cheat Sheet 역시 과도하게 비싼 쿼리로 인한 DoS를 막기 위해 query cost analysis, depth·amount limiting, rate limiting, 보안 설정 점검을 권장합니다. ([graphql.org](https://graphql.org/learn/security/)) 쿼터와 과금까지 연결되는 API 제한은 API 레이트 리미트 설계 가이드도 함께 보면 좋습니다.
| 리스크 | 증상 | 예방책 | 인수 증거 |
|---|---|---|---|
| N+1 | 고객 100명 조회 시 DB 쿼리 수가 수백 개로 증가 | DataLoader, join 최적화, resolver profiling | 샘플 query별 DB query count 리포트 |
| 과도한 중첩 | 자기 참조 관계를 깊게 타며 응답 지연 | depth·list depth 제한 | 제한 초과 query 실패 테스트 |
| 무제한 리스트 | 관리자 목록에서 메모리·응답 크기 증가 | connection pagination, 최대 page size | 최대값 초과 요청 검증 테스트 |
| alias 남용 | 한 요청 안에서 같은 필드를 수십 번 호출 | alias·breadth 제한, complexity budget | 공격성 query 차단 로그 |
| 권한 없는 비싼 필드 | 권한이 없는데도 내부 조회 후 null 반환 | 권한 선검사 후 데이터 로딩 | 권한 실패 시 DB 호출 없음 확인 |
| 캐시 오염 | 다른 고객사 데이터가 응답에 섞임 | 요청 단위 DataLoader, tenant 포함 cache key | 교차 tenant 테스트 |

6. GraphQL over HTTP: 외주 산출물에서 놓치기 쉬운 통신 기준
GraphQL 자체 사양은 특정 전송 프로토콜에 묶여 있지 않지만, 웹 서비스에서는 HTTP가 가장 흔합니다. GraphQL.org의 Serving over HTTP 문서는 GraphQL API가 보통 하나의 endpoint, 예를 들면 /graphql로 제공되며, 인증 미들웨어 이후 GraphQL 실행 단계에서 권한을 판단하는 흐름을 설명합니다. 또한 클라이언트가 Accept: application/graphql-response+json을 보내고, 서버는 JSON 기반 요청·응답 형식을 지원하는 기준을 안내합니다. ([graphql.org](https://graphql.org/learn/serving-over-http/))
GraphQL over HTTP draft는 서버가 application/graphql-response+json 응답 media type을 지원해야 한다고 정리하고, well-formed request에 GraphQL 사양의 validation rule을 적용하며, 서버가 depth limit이나 complexity limit 같은 추가 검증 규칙을 적용할 수 있다고 설명합니다. ([graphql.github.io](https://graphql.github.io/graphql-over-http/draft/)) 외주 개발 인수 시에는 ‘API가 호출된다’가 아니라 다음 기준이 문서화되어야 합니다.
- Endpoint: GraphQL endpoint, health check endpoint, playground 또는 explorer의 운영 노출 여부
- Method: query·mutation의 POST 지원, GET query 허용 여부와 CDN 캐싱 조건
- Header:
Accept,Content-Type, 인증 header, CORS 정책 - Error: validation error, field error, auth error, business error의 응답 형식
- Status code: HTTP status와 GraphQL
errors배열을 클라이언트가 어떻게 해석할지 - Operation name: 운영 로그와 성능 분석을 위해 operationName을 필수화할지
- Upload: 파일 업로드를 GraphQL에 직접 넣을지, 별도 signed URL 흐름으로 분리할지
7. 캐싱 전략: GraphQL도 캐시된다. 다만 위치가 다르다
REST에서는 URL이 캐시 키 역할을 하기 쉽지만, GraphQL은 하나의 endpoint 안에서 query와 variables가 응답 모양을 결정합니다. 그래서 캐싱을 포기하는 것이 아니라 캐시 위치를 나눠 설계해야 합니다. GraphQL.org Caching 문서는 endpoint 기반 API에서 URL이 전역 식별자 역할을 하는 것과 달리 GraphQL에서는 객체 식별자를 스키마가 제공해야 클라이언트가 풍부한 캐시를 만들 수 있다고 설명합니다. ([graphql.org](https://graphql.org/learn/caching/))
| 캐시 위치 | 적합한 용도 | 주의점 |
|---|---|---|
| 클라이언트 normalized cache | 화면 이동, 상세 재방문, 동일 객체 재사용 | id, __typename, merge policy가 필요합니다. |
| DataLoader request cache | 한 요청 안의 중복 로딩 제거 | 요청 간 공유 금지. 권한·tenant context를 분리해야 합니다. |
| Resolver 또는 data source cache | 요금제 목록, 코드 테이블, 외부 API 응답 | 캐시 키에 tenant, role, locale, feature flag를 포함할지 결정합니다. |
| HTTP·CDN cache | 공개 콘텐츠, 비로그인 랜딩 데이터, persisted query 기반 GET | 개인화 응답은 public cache를 피해야 합니다. |
| Trusted documents·persisted query | 허용된 operation만 실행, 네트워크 payload 감소 | 1차 클라이언트 중심 서비스에 적합하며 공개 API에는 제약이 있습니다. |
캐시 전략은 ‘GraphQL이라 어렵다’가 아니라 ‘어떤 데이터가 누구에게 같은가’를 먼저 묻는 문제입니다. 고객사별 데이터, 개인정보, 결제정보, 운영자 전용 정보는 캐시 키와 만료 정책을 잘못 잡으면 성능 문제가 아니라 보안 사고가 됩니다.
8. 스키마 변경과 문서화: 버전 없는 API는 규칙이 있어야 유지된다
GraphQL은 기존 필드에 새 필드를 추가하는 방식으로 스키마를 점진적으로 확장하기 쉬워 버전 없는 API 운영을 지향합니다. GraphQL.org Schema Design 문서도 GraphQL이 명시적으로 요청한 데이터만 반환하기 때문에 새 타입이나 새 필드를 추가하는 방식으로 breaking change를 줄일 수 있다고 설명합니다. ([graphql.org](https://graphql.org/learn/schema-design/)) 그러나 규칙 없이 버전이 없다는 말은 ‘아무렇게나 바꿔도 된다’가 아닙니다.
- 필드 삭제·이름 변경 전에는 deprecation 사유와 제거 예정일을 명시합니다.
- nullable을 non-null로 바꾸는 변경, optional argument를 required로 바꾸는 변경은 클라이언트를 깨뜨릴 수 있습니다.
- enum 값 추가도 일부 클라이언트 코드 생성 환경에서는 처리 누락을 만들 수 있습니다.
- 스키마 description은 개발자 문서가 아니라 API 계약의 일부로 작성합니다.
- schema diff 테스트를 CI에 넣고, 외주 이후에도 변경 PR에서 breaking change를 확인합니다.
REST와 GraphQL을 함께 운영한다면 하위 호환성 기준은 API 버전 관리 가이드의 관점으로도 점검해야 합니다. GraphQL이 버전을 줄여줄 수는 있지만, 제품이 커질수록 schema ownership과 변경 승인 절차는 오히려 더 중요해집니다.

9. 외주 개발 인수 기준: ‘동작합니다’가 아니라 운영 증거를 받기
외주 개발에서 GraphQL은 화면이 멋지게 동작하면 완성된 것처럼 보입니다. 그러나 유지보수 단계에서 문제가 되는 것은 보통 보이지 않는 부분입니다. 누가 어떤 필드를 볼 수 있는지, 어느 query가 비싼지, schema를 어떻게 바꿔도 되는지, 장애 시 어떤 operation을 차단해야 하는지에 대한 기준이 없으면 담당자가 바뀌는 순간 운영이 불안정해집니다.
| 요구 산출물 | 확인 방법 | 미흡하면 생기는 문제 |
|---|---|---|
schema.graphql 또는 SDL 파일 | 타입·필드 description 포함 여부 확인 | 프론트와 백엔드 계약이 코드에만 숨어 있음 |
| 리졸버 책임표 | 필드별 데이터 소스, 권한 검사 위치, 캐시 사용 여부 | N+1과 권한 누락을 추적하기 어려움 |
| 권한 정책 매트릭스 | 역할, tenant, 요금제, 객체 소유권별 허용 표 | 고객사 간 데이터 노출 위험 |
| 성능 테스트 결과 | 대표 query, 최대 page size, DB query count, p95 latency | 사용자 증가 시 관리자 화면 지연 |
| 쿼리 제한 설정 | depth, alias, batch, complexity, timeout 차단 테스트 | 한 번의 요청으로 서버 부하 유발 |
| DataLoader·캐시 문서 | 요청 단위 생성 여부, cache key 구성, invalidation 기준 | 캐시 오염 또는 stale data |
| 테스트 코드 | 권한 실패, tenant 격리, pagination, mutation validation | 유지보수 때 회귀 버그 반복 |
| 운영 로그·대시보드 | operationName, query hash, resolver latency, error path | 장애 원인 파악 불가 |
| 변경 규칙 | deprecation, schema diff, client query inventory | 필드 변경 때 프론트가 예고 없이 깨짐 |
| 배포·롤백 runbook | 마이그레이션, feature flag, 긴급 차단 절차 | 장애 대응이 개발자 개인 기억에 의존 |
10. 정부지원 MVP·초기 SaaS에서는 어디까지 해야 하나
정부지원사업 MVP나 초기 SaaS는 예산과 기간이 제한되어 있습니다. 이 단계에서 GraphQL federation, 복잡한 schema registry, 고급 router 구성을 처음부터 모두 넣는 것은 과할 수 있습니다. 대신 다음처럼 현실적으로 나누는 것이 좋습니다.
- 단순 MVP: REST로 빠르게 만들고, 관리자 화면 복잡도가 확인된 뒤 GraphQL 또는 BFF를 검토합니다.
- 관리자 화면이 핵심인 MVP: 단일 GraphQL 서버로 시작하되, 스키마·권한·pagination·DataLoader·쿼리 제한은 처음부터 넣습니다.
- AI·자동화 기능이 있는 SaaS: AI 분석 결과, 작업 이력, 사용자 피드백, 과금 상태가 한 화면에 엮이면 GraphQL이 유리할 수 있습니다.
- 외부 연동·웹훅 중심 서비스: 외부 공개 API는 REST로 유지하고 내부 관리자·고객 포털만 GraphQL을 쓰는 혼합 구조가 현실적입니다.
AgentMit은 BizMit 기반 SaaS, 관리자 대시보드, 업무 자동화, AI 기능이 결합된 MVP를 설계할 때 GraphQL을 유행 기술로 먼저 권하지 않습니다. 화면별 데이터 요구와 권한 정책을 정리한 뒤 REST, BFF, GraphQL 중 유지보수 비용이 가장 낮은 구조를 선택하는 방식이 더 안전합니다.
11. 최종 선택 기준: 이런 팀은 GraphQL, 이런 팀은 REST
| 선택 | 적합한 상황 | 주의할 점 |
|---|---|---|
| REST 유지 | 단순 CRUD, 외부 파트너 API, 서버 운영 인력이 적은 초기 서비스 | 관리자 화면 전용 집계 endpoint가 너무 많아지는지 관찰 |
| REST + BFF | 프론트 화면 최적화가 필요하지만 GraphQL 운영 역량은 부족 | BFF가 또 다른 비공식 API 계약이 되지 않게 문서화 |
| 단일 GraphQL | 복합 관리자 화면, 다중 클라이언트, 필드 단위 권한이 필요한 SaaS | 스키마 변경·권한·성능 제한을 인수 기준에 포함 |
| Federated GraphQL | 여러 팀·도메인 서비스가 독립 배포되고 schema ownership이 명확 | 초기 MVP에는 대체로 과함. 조직 구조와 거버넌스가 먼저 필요 |
GraphQL 도입의 성공 기준은 ‘쿼리를 하나로 줄였다’가 아닙니다. 고객사별 데이터가 섞이지 않고, 권한 없는 필드를 안전하게 막고, 비싼 query를 사전에 차단하고, 스키마 변경이 프론트를 깨뜨리지 않으며, 외주 개발사가 빠진 뒤에도 내부팀이 운영할 수 있어야 합니다.
AgentMit 관점의 실행 제안
GraphQL API 설계가 필요한 경우 AgentMit은 보통 1단계로 화면별 데이터 요구표, 권한 정책 매트릭스, query budget, schema naming rule을 먼저 만듭니다. 이후 BizMit 기반 SaaS·관리자 화면·자동화 서버·AI 기능과 연결해 REST와 GraphQL의 경계를 나눕니다. 이미 외주 개발사가 GraphQL을 제안한 상황이라면 구현 착수 전 스키마 초안, 리졸버 책임, 성능 제한, 인수 산출물을 먼저 검토하는 것이 비용을 줄입니다.
관리자 대시보드, SaaS API, 정부지원 MVP, AI 서비스 백엔드를 함께 설계해야 한다면 BizMit 서비스 안내를 확인하거나 제작 문의로 현재 화면 목록과 권한 구조를 공유해 주세요. GraphQL이 맞는지부터 REST나 BFF가 더 나은지까지 함께 검토할 수 있습니다.
FAQ
Q1. REST API가 이미 있는데 GraphQL을 꼭 도입해야 하나요?
필수는 아닙니다. 단순 CRUD, 화면 수가 적은 MVP, 외부 연동 중심 API라면 REST가 더 단순할 수 있습니다. GraphQL은 여러 화면이 같은 도메인 데이터를 서로 다른 조합으로 자주 조회하거나, 웹·모바일·관리자·파트너 포털의 데이터 요구가 계속 갈라질 때 검토하는 편이 좋습니다.
Q2. GraphQL 스키마를 DB 테이블 구조 그대로 만들면 안 되나요?
초기 개발은 빨라 보이지만 장기 유지보수에는 위험합니다. DB 컬럼명, 조인 테이블, 내부 상태값이 클라이언트 계약으로 굳어져 변경이 어려워지고 불필요한 필드가 권한·보안 리스크가 됩니다. 스키마는 테이블이 아니라 제품 기능과 사용 화면의 언어로 설계해야 합니다.
Q3. SaaS에서 고객사별·필드별 권한은 어디서 처리해야 하나요?
인증은 GraphQL 실행 전에 처리하고, 권한 판단은 리졸버가 호출하는 비즈니스 로직 또는 도메인 서비스 계층에 두는 것이 안전합니다. 스키마 directive는 정책 표시나 공통 처리에 쓸 수 있지만, 실제 데이터 접근은 tenant scope, 역할, 요금제, 소유권 조건을 서버에서 다시 검증해야 합니다.
Q4. GraphQL N+1 문제와 과도한 중첩 조회는 어떻게 막나요?
DataLoader 같은 요청 단위 batching, 리스트 페이지네이션, 최대 depth·list depth·alias·batch 수 제한, 쿼리 complexity 예산, timeout, operationName 기반 모니터링을 함께 적용해야 합니다. DataLoader만 넣었다고 안전한 것은 아니며, 큰 리스트와 깊은 중첩은 별도의 제한이 필요합니다.
Q5. 외주 개발로 GraphQL API를 만들 때 인수 기준은 무엇인가요?
schema.graphql, 리졸버 책임표, 권한 정책 매트릭스, DataLoader·캐시 키 문서, 쿼리 제한 설정, 성능 테스트 결과, 운영 로그·대시보드, schema diff 테스트, deprecation 규칙, 배포·롤백 runbook을 받아야 합니다. 화면이 동작하는지만 확인하면 유지보수 단계에서 스키마 변경과 권한 버그가 반복됩니다.
참고한 공식 문서
- GraphQL September 2025 Specification: 타입 시스템과 스키마 계약 관점 확인. ([spec.graphql.org](https://spec.graphql.org/September2025/))
- GraphQL over HTTP Draft Specification: media type, validation, HTTP 응답 기준 확인. ([graphql.github.io](https://graphql.github.io/graphql-over-http/draft/))
- GraphQL.org Serving over HTTP: endpoint, 인증 위치, POST·GET 요청 기준 확인. ([graphql.org](https://graphql.org/learn/serving-over-http/))
- GraphQL.org Authorization: 권한 로직을 비즈니스 계층에 두는 기준 확인. ([graphql.org](https://graphql.org/learn/authorization/))
- GraphQL.org Security와 OWASP GraphQL Cheat Sheet: query 제한, trusted documents, introspection, 오류 노출, DoS 방어 기준 확인. ([graphql.org](https://graphql.org/learn/security/))
- GraphQL.org Performance와 GraphQL DataLoader: N+1, batching, 요청 단위 cache 기준 확인. ([graphql.org](https://graphql.org/learn/performance/))
- Apollo Demand Oriented Schema Design: 수요 중심 스키마와 자동 생성 스키마의 위험 확인. ([apollographql.com](https://www.apollographql.com/docs/deploy-preview/54a7d6c42af30e03a5cd/graphos/schema-design/guides/demand-oriented-schema-design))

