GraphQL: 엔터프라이즈의 허니문은 끝났다
(johnjames.blog)- GraphQL은 데이터 과다 요청 문제를 해결하려 하지만, 대부분의 엔터프라이즈 환경에서는 이미 다른 방식으로 해결된 문제임
- BFF(Backend for Frontend) 구조가 일반화된 기업 시스템에서는 GraphQL의 주요 장점이 크게 줄어듦
- 구현 복잡도, 관찰성 저하, 캐싱 문제, ID 제약, 파일 처리의 불편함 등으로 실제 운영 환경에서 비용이 커짐
- REST는 단순하고 빠르며, 오류 처리와 온보딩이 쉬워 대규모 팀 환경에서 더 효율적임
- 결론적으로 GraphQL은 특정 상황에서는 유용하지만, 대부분의 엔터프라이즈에는 과도한 선택이라는 평가
GraphQL이 해결하려는 문제
- GraphQL의 핵심 목적은 overfetching(불필요한 데이터 과다 요청) 방지
- 클라이언트가 필요한 필드만 요청해 불필요한 데이터 전송을 줄이는 구조
- 새로운 UI 요구사항마다 백엔드 수정이 필요 없다는 장점
- 그러나 실제 환경에서는 이 이상적인 구조가 복잡한 현실과 맞지 않음
BFF로 이미 해결된 overfetching
- 대부분의 엔터프라이즈 프런트엔드는 BFF(Backend for Frontend) 계층을 사용
- UI에 맞게 데이터를 조합하고, 여러 다운스트림 호출을 통합하며, 백엔드 복잡성을 숨김
- REST 기반 BFF는 이미 필요한 데이터만 반환할 수 있어 GraphQL의 장점이 중복됨
- GraphQL 계층이 REST API로부터 데이터를 가져올 경우, overfetching이 단지 한 단계 아래로 이동
- 여러 페이지가 동일 엔드포인트를 공유할 때 GraphQL이 유용할 수 있으나,
- 그 이점은 몇 킬로바이트 절약을 위해 더 많은 설정과 유지보수 부담을 감수하는 수준
구현 복잡도와 생산성 저하
- GraphQL은 REST보다 구현 시간이 훨씬 길고 복잡함
- 스키마, 타입, 리졸버, 데이터 소스 정의 등 추가 작업 필요
- 스키마와 클라이언트 동기화 유지 부담 존재
- GraphQL은 소비(클라이언트 편의) 를 최적화하지만, 생산(서버 개발 속도) 을 희생
- 엔터프라이즈 환경에서는 생산 속도와 단순성이 더 중요함
관찰성과 모니터링 문제
- GraphQL의 HTTP 상태 코드 체계가 비일관적
- 200 응답에도 오류가 포함될 수 있어, 모니터링 시 성공/실패 구분이 어려움
- REST는 2XX/4XX/5XX로 명확히 구분되어 대시보드 필터링이 직관적
- Apollo 등에서 커스터마이징 가능하지만, 이는 추가 설정과 정신적 부담을 초래
- 운영 중 장애 대응 시 REST보다 문제 파악이 어렵고 복잡함
캐싱의 현실적 한계
- Apollo의 정규화 캐싱(normalized caching) 은 이론적으로 강력하지만 실제로는 취약하고 복잡
- 필드 하나만 다른 쿼리도 별도로 취급되어 수동 연결 필요
- 캐시 디버깅이 별도의 문제로 발전
- 반면 REST는 단순히 전체 응답을 캐싱해 안정적이고 유지보수 용이
ID 필드 제약의 문제
- Apollo는 모든 객체에 id 또는 _id 필드가 필요하다고 가정
- 많은 엔터프라이즈 API는 고유 ID가 없거나, 전역 식별자가 아님
- 이를 맞추기 위해 BFF가 로컬 ID 생성 로직을 추가해야 함
- 결과적으로 불필요한 필드와 로직 증가, overfetching 감소 효과 상쇄
파일 업로드 및 다운로드의 비효율
- GraphQL은 이진 데이터 처리에 부적합
- 실제로는 다운로드 URL을 반환하고, 파일은 REST로 전송
- PDF 등 대용량 데이터를 GraphQL 응답에 포함하면 성능 저하
- 이로 인해 “단일 API”라는 GraphQL의 이상이 깨짐
온보딩과 학습 곡선
- 대부분의 개발자는 REST 경험이 풍부하지만 GraphQL은 학습 필요
- 스키마, 리졸버, 쿼리 구성, 캐싱 규칙, 오류 처리 등 새 개념 학습 필요
- 이로 인해 팀 온보딩 속도 저하
- REST는 “지루하지만 확장성 높은” 접근으로 대규모 팀에 적합
오류 처리의 복잡성
- GraphQL 오류 응답은 nullable 필드, partial data, errors 배열, 확장 상태 코드 등으로 복잡
- 어떤 리졸버가 실패했는지 추적 필요
- REST는 단순히 400/500으로 구분되어 이해와 디버깅이 용이
결론: GraphQL은 틈새 기술
- GraphQL은 특정 상황에서는 유효한 도구
- 그러나 대부분의 엔터프라이즈 환경에서는 이미 BFF와 REST로 문제 해결
- 주요 과제는 overfetching이 아니라 관찰성, 신뢰성, 속도
- 결과적으로 GraphQL은 좁은 문제를 해결하면서 더 넓은 복잡성을 초래
- “GraphQL은 나쁘지 않지만, 대부분의 경우 필요하지 않다”는 결론
Hacker News 의견들
-
GraphQL의 주요 문제를 overfetching이라고 규정한 글에 동의하지 않음
내가 보기엔 진짜 장점은 (a) 엄격한 타입 기반 계약을 강제한다는 점, (b) 스키마 진화가 훨씬 쉽다는 점임
타입 시스템 덕분에 입력과 출력이 항상 정의된 형태를 따르고, 커스텀 스칼라 타입(예: 전화번호, 이메일 등)을 쓰면 버그와 보안 이슈를 크게 줄일 수 있음
또, 새로운 필드 추가나 기존 필드 폐기가 표준화되어 있어서 서버·클라이언트 모두 인지 부하가 적음- 나도 overfetching이 핵심 문제라고 생각하지 않음
내가 GraphQL을 쓰는 이유는 API 조합과 진화 때문임. 특히 M:N 구조의 대규모 시스템에서 “클라이언트가 필요한 걸 기술 → 서버가 조합 → 도메인 서비스가 해결”하는 흐름이 장기적으로 훨씬 관리하기 쉬움
좋은 observability와 결합되면 데이터 접근의 강력한 기반이 됨 - OpenAPI 스펙에서 TypeScript 타입을 생성하면 양방향 계약이 생기므로, 그 부분은 GraphQL이 해결할 문제가 아님
- 나도 GraphQL의 강력한 계약성이 가장 큰 이유라고 생각함
또 하나는 resolver 재사용과 federation이 쉬운 점임. REST에서는 이게 꽤 번거로움 - 요청과 응답을 zod로 pruning하는 건 간단함. 그 이유만으로 GQL을 도입하진 않겠음
스키마 진화는 Protobuf도 잘 처리함 - GraphQL이 protobuf 통신과 뭐가 다른지 모르겠음. 둘 다 정의에 맞게 파싱되니까
- 나도 overfetching이 핵심 문제라고 생각하지 않음
-
GraphQL의 진짜 장점은 UI 데이터를 작은 조각으로 조합할 수 있다는 점임
이 영상처럼 fragment colocation을 활용하면, 하위 컴포넌트를 수정해도 다른 부분에 영향이 없음
쿼리가 fragment 기반으로 자동 생성되므로, 필드 제거 시 다른 컴포넌트가 깨질 위험도 줄어듦
규모가 작거나 빠른 개발이 중요하지 않다면 GraphQL은 과투자처럼 보일 수도 있음- Apollo가 잘못된 방향으로 사람들을 이끌어서 GraphQL의 진짜 장점을 못 느끼게 했다고 생각함
Colocation은 정말 혁신적인 개념인데, Apollo는 이걸 거의 언급하지 않았음 - 문제는 GraphQL이 아니라 Apollo Client임
Relay의 문서화는 여전히 부족하지만, Entrypoint 개념은 훌륭함 - fragment masking에는 동의함
다만 graphql-codegen의 구현은 플러그인 호환성이 떨어져서, 우리는 직접 플러그인을 만들어야 했음
생태계 전반의 일관성이 부족함
- Apollo가 잘못된 방향으로 사람들을 이끌어서 GraphQL의 진짜 장점을 못 느끼게 했다고 생각함
-
overfetching이 GraphQL의 핵심 문제라는 말은 과장임
GraphQL은 ORM이 해결하는 임피던스 불일치를 클라이언트 레벨에서 다루는 도구라고 생각함
Relay 같은 컴파일러 기반 툴링 없이 쓰는 건 안티패턴임
요즘은 AI가 데이터 레이어를 자동 생성해주니 GraphQL의 필요성은 줄어드는 듯함- “컴파일러 없이 GraphQL을 쓰면 안 된다”는 말에 공감하지만, 그게 BFF에서 몇 줄 코드 쓰는 것보다 쉬운지는 의문임
- 좋은 GraphQL 툴링 예시로 Isograph VSCode 확장을 추천함
존재하지 않는 필드를 선택하면 자동으로 생성해주는 등 개발 경험(DevEx) 이 뛰어남 - URQL과 gql.tada는 훌륭한 클라이언트 사이드 툴링임
- GraphQL 툴링으로 뭘 쓰는지 궁금함. IntelliJ와 Postman도 지원이 좋음
- overfetching이 별문제 아니라는 말엔 동의 못함
현대 웹의 느려짐은 대부분 데이터 과다 전송 때문임. 실제로 많은 앱이 0.5% 이하의 효율로 동작함
-
나는 2016년부터 GraphQL을 써왔음
본질적으로 GraphQL은 RPC 스펙임. 서버의 “Action(Args) → ResultType” 맵으로 구현되는 형태임
REST의 여러 엔드포인트 대신, GraphQL은 하나의/query엔드포인트에서 resolver 맵을 통해 동작함
결국 OpenAPI나 gRPC처럼 입력·출력이 타입으로 정의된 구조임- 2015년부터 GraphQL을 써왔는데, Relay 없이 GraphQL을 쓴다면 진짜 강점을 못 느낄 가능성이 큼
Apollo가 fragment masking을 추가하면서 조금 나아졌지만, 여전히 Relay 중심의 사고가 중요함 - 이 설명은 너무 단순함. 쿼리 해석 과정을 완전히 생략했음
- 클라이언트가 요청 필드를 지정할 수 있다는 점을 빼먹었음
- 원문 글과 관련성이 약해 보임
- GraphQL은 React 페이지 전체가 하위 컴포넌트의 요구사항을 모아 하나의 대형 쿼리를 만들고,
백엔드가 이를 단일 SQL 쿼리로 변환해 처리할 때 가장 효율적임
resolver는 DB에서 직접 가져올 수 없는 데이터에만 예외적으로 써야 함
- 2015년부터 GraphQL을 써왔는데, Relay 없이 GraphQL을 쓴다면 진짜 강점을 못 느낄 가능성이 큼
-
예전에 팀을 이끌 때 FE가 GraphQL을 원해서 도입했음
결과적으로 각 페이지마다 하나의 거대한 쿼리로 모든 데이터를 가져오고, 수정 시 전체 JSON blob을 다시 보냈음
앱은 돌아갔지만, 결국 회사가 피벗하면서 그 코드는 사라짐
실제로 많은 GraphQL 프로젝트가 이런 식으로 형식만 갖춘 구현으로 끝나는 것 같음 -
GraphQL의 인증(auth) 흐름은 가장 큰 난제 중 하나임
resolver가 다양한 컨텍스트에서 호출되기 때문에, 모든 경우를 고려해야 함
복잡성과 사고 비용이 너무 커서, 결국 필드를 잠그게 됨
직접 graphql-autharoo를 만들었지만 충분하지 않았음
대부분의 기능은 GraphQL 없이도 더 단순하게 구현 가능함- GitHub에서도 권한 관리(authz) 가 큰 문제임
GraphQL 리소스마다 세밀한 권한을 추가해야 해서, 전체 리소스가 업데이트되기 전까지 쿼리가 작동하지 않음
“REST로 다시 만드는 게 낫다”는 말이 나올 정도로 부담이 큼 -
데코레이터 기반 인증을 써봤는지 묻고 싶음
직접 조합하지 말고 완성도 높은 서버 프레임워크를 쓰는 게 낫다고 생각함
- GitHub에서도 권한 관리(authz) 가 큰 문제임
-
GraphQL은 겉보기엔 좋아 보이지만, 실제로는 시간이 지날수록 유지가 힘든 기술임
많은 기술이 그렇듯, 결국 바퀴는 바퀴일 뿐이라는 교훈을 줌 -
GraphQL의 장점은 스키마에 필드만 추가하면 모든 클라이언트가 바로 쿼리할 수 있다는 점임
“이 필드도 추가해달라”는 FE 요청이 사라져서 협업이 단순해짐
또, 스키마 스냅샷을 만들어 통합 테스트에서 변경 감지하기도 쉬움
REST보다 일관성이 높다고 느꼈지만, 이는 개인적 경험임- 하지만 그 장점이 양날의 검임
A에서 E까지 연쇄적으로 요청할 수 있으니, 성능 저하나 프론트엔드-데이터 구조 결합이 심해짐 - 요즘은 대부분 풀스택 TypeScript 환경이라 이런 협업 문제는 줄어듦
React도 이제는 프레임워크 기반 SSR과 서버 컴포넌트를 기본으로 함
결국 TypeScript가 클라이언트와 서버를 통합하는 방향으로 가고 있음
- 하지만 그 장점이 양날의 검임
-
GraphQL이 DB 부하나 비효율 쿼리 문제를 어떻게 막는지 궁금함
악의적인 쿼리가 내부 상태를 폭발시킬 수도 있지 않나?- 대부분의 서버는 쿼리 복잡도(비용) 제한을 두고, 기준을 넘으면 요청을 거부함
-
REST가 너무 지루해서 GraphQL을 시도 중임
서버와 클라이언트가 요청·응답을 컴파일 타임에 합의할 수 있다는 점이 마음에 듦
블로그들이 “이건 별로야”라고만 하지 말고, 대안 기술도 함께 제시했으면 좋겠음- 만약 isomorphic TypeScript를 좋아한다면, tRPC가 GraphQL보다 더 깔끔한 대안임
두 기술 모두 클라이언트-서버 계약 문제를 잘 해결함
- 만약 isomorphic TypeScript를 좋아한다면, tRPC가 GraphQL보다 더 깔끔한 대안임