GraphQL: 엔터프라이즈 허니문은 끝났다

John James 블로그 포스트 번역
2025년 12월 14일 게시 | 읽는 시간 약 3분


나는 Apollo Client와 Server를 사용하여 실제 엔터프라이즈급 애플리케이션에서 GraphQL을 몇 년간 사용해왔다.
장난감 앱이 아니다. 초기 단계의 스타트업도 아니다. 여러 팀, BFF, 다운스트림 서비스, 관찰성 요구사항, 그리고 실제 사용자가 있는 제대로 된 프로덕션 환경이다.
그 모든 시간을 거친 후, 나는 꽤 평범한 결론에 도달했다:
GraphQL은 실제 문제를 해결하지만, 그 문제는 사람들이 인정하는 것보다 훨씬 더 틈새 시장이다. 대부분의 엔터프라이즈 환경에서는 이미 다른 곳에서 해결되었고, 트레이드오프를 모두 고려하면 GraphQL은 종종 순손실이 된다.
이것은 "GraphQL은 나쁘다"는 글이 아니다. "허니문이 끝난 후의 GraphQL"에 관한 글이다.

GraphQL이 해결하려는 것

GraphQL이 해결하려는 주요 문제는 오버페칭(overfetching)이다. 아이디어는 간단하고 매력적이다:
  • 클라이언트가 필요한 필드만 정확히 요청한다
  • 그 이상도 이하도 아니다
  • 낭비되는 바이트가 없다
  • 새로운 UI 요구사항이 생길 때마다 백엔드를 변경할 필요가 없다
종이 위에서는 훌륭하다. 실제로는 훨씬 복잡하다.

오버페칭은 이미 BFF로 해결되었다

대부분의 엔터프라이즈 프론트엔드 아키텍처는 이미 BFF(Backend for Frontend)를 가지고 있다.
그 BFF는 구체적으로 다음을 위해 존재한다:
  • UI를 위해 데이터를 형성한다
  • 여러 다운스트림 호출을 집계한다
  • 백엔드 복잡성을 숨긴다
  • UI가 필요한 것만 정확히 반환한다
BFF 뒤에서 REST를 사용하고 있다면, 오버페칭은 이미 해결 가능하다. BFF는 응답 범위를 좁혀서 UI가 신경 쓰는 것만 반환할 수 있다.
맞다, GraphQL도 이것을 할 수 있다. 하지만 사람들이 간과하는 부분이 있다. 대부분의 다운스트림 서비스는 여전히 REST다.
그래서 이제 GraphQL 레이어는 여전히 다운스트림 REST API에서 오버페칭을 해야 하고, 응답을 다시 형성해야 한다. 오버페칭을 제거한 것이 아니다. 단지 한 계층 아래로 옮긴 것일 뿐이다.
이것만으로도 GraphQL의 주요 판매 포인트를 크게 약화시킨다.
GraphQL이 여기서 이기는 경우가 있다. 여러 페이지가 같은 엔드포인트를 호출하지만 약간 다른 필드가 필요한 경우, GraphQL은 쿼리별로 그 차이를 범위 지정할 수 있다.
하지만 거래에 대해 솔직해지자.
보통 요청당 몇 개의 필드를 절약하는 것에 대해 이야기하고 있는데, 그 대신:
  • 더 많은 설정
  • 더 많은 추상화
  • 더 많은 간접성
  • 유지보수할 더 많은 코드
이것은 몇 킬로바이트를 절약하기 위한 매우 비싼 거래다.

구현 시간이 REST보다 훨씬 길다

GraphQL은 REST BFF보다 구현하는 데 훨씬 더 오래 걸린다.
REST를 사용하면 일반적으로:
  • 다운스트림 서비스를 호출한다
  • 응답을 조정한다
  • UI가 필요한 것을 반환한다
GraphQL을 사용하면 이제 다음을 해야 한다:
  • 스키마를 정의한다
  • 타입을 정의한다
  • 리졸버를 정의한다
  • 데이터 소스를 정의한다
  • 어댑터 함수를 어쨌든 작성한다
  • 스키마, 리졸버, 클라이언트를 동기화 상태로 유지한다
GraphQL은 소비 최적화를 생산 속도의 대가로 한다. 엔터프라이즈 환경에서는 생산 속도가 이론적 우아함보다 더 중요하다.

관찰성이 기본적으로 더 나쁘다

이것은 충분히 논의되지 않는 부분이다. GraphQL은 이상한 상태 코드 규칙을 가지고 있다:
  • 쿼리를 파싱할 수 없으면 400
  • 실행 중에 뭔가 실패하면 errors 배열과 함께 200
  • 성공하거나 부분적으로 성공하면 200
  • 서버에 연결할 수 없으면 500
관찰성 관점에서 이것은 고통스럽다.
REST를 사용하면:
  • 2XX는 성공을 의미한다
  • 4XX는 클라이언트 오류를 의미한다
  • 5XX는 서버 오류를 의미한다
대시보드를 2XX로 필터링하면 그 요청들이 성공했다는 것을 안다. GraphQL을 사용하면 200은 여전히 부분 또는 완전한 실패를 의미할 수 있다.
맞다, Apollo는 이 동작을 커스터마이즈할 수 있게 해준다. 하지만 그것이 바로 요점이다. 당신은 REST가 기본으로 제공하는 것으로 돌아가기 위해 추가 설정, 추가 규칙, 추가 정신적 오버헤드에서 계속 세금을 낸다.
이것은 당신이 온콜 상태일 때 중요하지, 블로그 포스트를 읽을 때는 아니다.

캐싱은 놀라워 보이지만 실제로 사용하면 다르다

Apollo의 정규화된 캐싱은 정말 인상적이다.
이론상으로는. 실제로는 취약하다.
단 하나의 필드만 다른 두 개의 쿼리가 있으면, Apollo는 그것들을 별개의 쿼리로 취급한다. 그러면 다음과 같이 수동으로 연결해야 한다:
  • 기존 필드는 캐시에서 온다
  • 다른 필드만 가져온다
그 시점에서:
  • 여전히 왕복이 있다
  • 더 많은 코드를 추가했다
  • 캐시 문제 디버깅이 자체 문제가 된다
한편, REST는 행복하게 몇 개의 추가 필드를 오버페칭하고, 전체 응답을 캐시하고, 계속 진행한다. 추가 킬로바이트는 싸다. 복잡성은 아니다.

ID 요구사항은 누수되는 추상화다

Apollo는 기본적으로 모든 객체가 id 또는 _id 필드를 가지기를 기대하거나, 커스텀 식별자를 설정해야 한다.
그 가정은 많은 엔터프라이즈 API에서 성립하지 않는다.
많은 API들:
  • ID를 반환하지 않는다
  • 자연스러운 고유 키가 없다
  • 전역적으로 식별 가능한 엔티티로 모델링되지 않는다
그래서 이제 BFF는 GraphQL 클라이언트를 만족시키기 위해 로컬에서 ID를 생성해야 한다.
그것은 다음을 의미한다:
  • 더 많은 로직
  • 더 많은 필드
  • 어쨌든 항상 하나의 추가 필드를 가져온다
오버페칭을 줄이는 것이 원래 목표였다는 점을 고려하면 아이러니하다.
REST 클라이언트는 이런 종류의 제약을 부과하지 않는다.

파일 업로드와 다운로드는 어색하다

GraphQL은 단순히 바이너리 데이터에 적합하지 않다.
실제로는 다음과 같이 끝난다:
  • 다운로드 URL을 반환한다
  • 그 다음 REST를 사용해서 파일을 어쨌든 가져온다
PDF 같은 큰 페이로드를 GraphQL 응답에 직접 포함시키면 응답이 부풀어지고 성능이 악화된다.
이것만으로도 "단일 API" 이야기를 깨뜨린다.

온보딩이 더 느리다

대부분의 프론트엔드 및 풀스택 개발자는 GraphQL보다 REST에 훨씬 더 경험이 많다.
GraphQL을 도입하는 것은 다음을 의미한다:
  • 스키마를 가르친다
  • 리졸버를 가르친다
  • 쿼리 구성을 가르친다
  • 캐싱 규칙을 가르친다
  • 오류 의미론을 가르친다
그 학습 곡선은 마찰을 만들고, 특히 팀이 빠르게 움직여야 할 때 그렇다.
REST는 지루하지만, 지루함은 매우 잘 확장된다.

오류 처리가 필요한 것보다 더 어렵다

GraphQL 오류 응답은... 이상하다.
당신은 다음을 가지고 있다:
  • nullable vs non-nullable 필드
  • 부분 데이터
  • errors 배열
  • 커스텀 상태 코드가 있는 extensions
  • 어느 리졸버가 실패했고 왜 실패했는지 추적할 필요
이 모든 것이 간접성을 추가한다.
간단한 REST 설정과 비교해보자:
  • 입력 검증 실패, 400 반환
  • 백엔드 실패, 500 반환
  • zod 오류, 완료
간단한 오류가 우아한 오류보다 이해하기 쉽다.

최종 결과

GraphQL은 절대적으로 유효한 사용 사례가 있다.
하지만 대부분의 엔터프라이즈 환경에서:
  • 이미 BFF가 있다
  • 다운스트림 서비스는 REST다
  • 오버페칭이 가장 큰 문제가 아니다
  • 관찰성, 신뢰성, 속도가 더 중요하다
모든 것을 합산하면, GraphQL은 종종 좁은 문제를 해결하면서 더 광범위한 새로운 문제들을 도입하게 된다.
그래서 프로덕션에서 몇 년간 사용한 후, 나는 이렇게 말하고 싶다:
GraphQL은 나쁘지 않다. 단지 틈새 시장일 뿐이다. 그리고 당신은 아마 그것이 필요하지 않을 것이다.
특히 당신의 아키텍처가 이미 그것이 설계된 문제를 해결했다면 말이다.
1
35

댓글

?

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!

Inkyu Oh님의 다른 글

더보기

유사한 내용의 글