Na-update 1 buwan ang nakalipas
2012년, Facebook의 모바일 앱은 심각한 문제를 겪고 있었습니다. 사용자가 피드를 스크롤하면 앱은 요청을 연달아 보냈습니다—게시물 하나, 댓글 하나, 좋아요 하나, 사용자 정보 하나. 요청이 오갈 때마다 지연이 쌓였습니다. 셀룰러 네트워크에서는 앱을 쓰는 것 자체가 고역이었습니다.
엔지니어링 팀은 선택에 직면했습니다: 더 특화된 엔드포인트를 추가하며 REST API를 계속 땜질하거나, 접근 방식 자체를 다시 생각하거나. 그들은 다시 생각하기로 했습니다. 2015년, Facebook은 GraphQL을 세상에 내놓았습니다.
이것은 단순한 기술적 업그레이드가 아니었습니다. 모든 API가 반드시 답해야 하는 질문에 대한 근본적으로 다른 대답이었습니다: 누가 어떤 데이터를 네트워크로 보낼지 결정하는가?
핵심 차이
REST: 서버가 무엇을 줄지 결정한다.
GraphQL: 클라이언트가 무엇이 필요한지 결정한다.
나머지 모든 차이점은 여기서 비롯됩니다.
REST의 작동 방식
REST는 API를 리소스 중심으로 구성합니다—사용자, 게시물, 댓글 각각에 고유한 URL이 있습니다. 리소스를 요청하면 해당 리소스를 받습니다. 응답의 형태는 엔드포인트를 만든 사람이 결정합니다.
최근 게시물과 그 게시물의 댓글을 포함한 사용자 프로필을 보여주고 싶다면? 최소 세 번의 요청이 필요합니다. 전체 프로필이 아니라 이름과 이메일만 필요하다면? 어쩔 수 없습니다—전부 받거나 아무것도 받지 못합니다.
서버가 결정하는 것입니다. 엔드포인트를 만든 사람이 무엇을 포함할지 선택했고, 그 선택을 따라야 합니다.
GraphQL의 작동 방식
GraphQL은 단일 엔드포인트 하나만 노출합니다. URL로 리소스를 요청하는 대신, 원하는 것을 정확히 기술한 쿼리를 보냅니다:
요청 한 번. 응답에는 요청한 것만 정확히 담깁니다—사용자의 이름과 이메일, 게시물 제목, 각 게시물의 댓글, 각 댓글 작성자의 이름. 그 이상도 그 이하도 아닙니다.
클라이언트가 결정하는 것입니다. 원하는 형태를 직접 지정하고, 서버는 그 사양대로 응답합니다.
이 차이가 중요한 이유
대역폭 문제
REST API에서 사용자를 요청하면 3개의 필드가 필요할 때 30개의 필드를 받을 수 있습니다. 이것이 오버페칭(over-fetching)입니다—필요 이상의 데이터를 받는 것. 빠른 사무실 네트워크에서는 별로 신경 쓸 일이 없습니다. 하지만 인도네시아 농촌의 셀룰러 네트워크에서는 쓸 만한 앱과 사람들이 지워버리는 앱의 차이가 됩니다.
GraphQL은 설계 자체가 오버페칭을 방지합니다. 세 개의 필드를 요청하면 세 개만 받습니다.
요청 횟수 문제
복잡한 UI—예컨대 사용자 정보, 최근 활동, 알림, 추천을 한 화면에 보여주는 대시보드를 생각해 보세요. REST라면 510개의 요청이 필요할 수 있습니다. 요청마다 지연이 쌓입니다. 라운드트립이 200ms인 네트워크라면 화면에 무언가가 뜨기까지 12초를 기다려야 합니다.
GraphQL은 이 모든 요청을 하나로 줄입니다. 쿼리 자체는 더 클 수 있지만, 지연은 한 번만 생깁니다. 열 번이 아니라.
유연성 문제
모바일 앱은 대역폭을 아끼기 위해 최소한의 데이터가 필요합니다. 웹 앱은 풍부한 기능을 위해 더 많은 데이터가 필요합니다. 관리자 대시보드는 모든 것이 필요합니다.
REST라면 불편한 선택을 해야 합니다: 클라이언트마다 별도 엔드포인트를 만들거나, 모든 것을 반환하고 클라이언트가 필요 없는 부분을 무시하게 하거나, 일부 클라이언트가 최적이 아닌 응답을 받는 것을 감수하거나.
GraphQL이라면 각 클라이언트가 정확히 필요한 것만 쿼리로 요청합니다. API는 그대로입니다. 클라이언트가 다른 질문을 할 뿐입니다.
버전 관리 문제
REST 엔드포인트의 구조를 바꾸면 그에 의존하는 모든 클라이언트가 깨질 수 있습니다. 그래서 버전이 늘어납니다—/v1/users, /v2/users, /v3/users—그리고 모두를 유지해야 하는 부담도 함께 늘어납니다.
GraphQL은 다르게 진화합니다. 새 필드는 자유롭게 추가할 수 있습니다—기존 쿼리는 새 필드를 요청하지 않으니 깨지지 않습니다. 오래된 필드는 deprecated로 표시해 서서히 제거하면 됩니다—스키마가 이를 명시하고, 클라이언트는 여유 있게 마이그레이션할 수 있습니다. 버전 폭발은 없습니다.
GraphQL의 단점
캐싱이 까다로워집니다
REST는 HTTP 위에서 동작하고, HTTP에는 캐싱이 기본으로 내장되어 있습니다. GET /users/123은 URL을 기반으로 브라우저, CDN, 프록시가 캐시할 수 있습니다. 변경이 없으면 캐시가 반복 요청을 즉시 처리합니다.
GraphQL 요청은 /graphql에 대한 POST 요청이고, 쿼리는 요청 본문에 담깁니다. 표준 HTTP 캐싱이 적용되지 않습니다. persisted query, 응답 캐싱, 정규화 캐시 같은 특수한 전략이 필요하고, 이는 별도의 고민과 구현을 요구합니다.
N+1 문제
이런 쿼리를 생각해 보세요:
무해해 보입니다. 하지만 게시물이 100개라면, 단순하게 구현하면 게시물을 한 번 가져오고(쿼리 1번), 작성자를 각각 따로 가져옵니다(쿼리 100번). GraphQL 쿼리 하나에 데이터베이스 호출이 101번입니다.
이것이 N+1 문제입니다. 해결하지 않으면 데이터베이스가 버티지 못합니다. DataLoader 같은 도구로 요청을 일괄 처리할 수 있지만, 제대로 이해하고 신중하게 구현해야 합니다. 잘못 구현하면 클라이언트의 쿼리 하나가 서버를 무릎 꿇게 만들 수 있습니다.
복잡도가 급격히 높아집니다
REST API는 도구가 거의 없어도 만들 수 있습니다. 라우트를 정의하고 JSON을 반환하면 끝입니다.
GraphQL은 스키마 정의, 모든 필드에 대한 리졸버 함수, 쿼리 실행 엔진이 필요하고, fragment, variable, directive 같은 개념도 이해해야 합니다. 진입 장벽이 있고, 운영 복잡도도 더 높습니다.
요청 제한이 까다로워집니다
REST에서 요청 제한은 단순합니다: /users에 시간당 100번의 요청을 허용합니다. 명확합니다.
GraphQL은 엔드포인트가 하나입니다. 필드 하나만 가져오는 쿼리와 데이터베이스 전체를 긁어오는 쿼리가 단순한 요청 제한 입장에서는 동일하게 보입니다. 쿼리 복잡도 분석이 필요합니다—각 쿼리의 "비용"을 계산하고, 횟수가 아닌 비용 기준으로 제한합니다. 불가능하진 않지만 결코 단순하지 않습니다.
REST가 빛나는 상황
때로는 단순함이 이깁니다
URL은 리소스에 대응합니다. HTTP 메서드는 동작에 대응합니다. GET /users는 사용자를 가져옵니다. POST /users는 생성합니다. 이 개념 모델은 한 문장으로 설명됩니다. 처음 접하는 개발자도 금방 이해합니다.
리소스 몇 개에 단순한 CRUD 작업 정도라면 GraphQL의 복잡성은 얻는 것 없는 부담일 뿐입니다.
캐싱이 저절로 됩니다
API가 동일한 데이터를 반복해서 제공한다면—제품 목록, 공개 콘텐츠, 참조 데이터—HTTP 캐싱이 별도 작업 없이 엄청난 성능 향상을 가져다 줍니다. CDN이 응답을 캐시하고, 브라우저 캐시가 반복 요청을 즉시 처리합니다. 추가 구현이 필요 없습니다.
생태계가 탄탄합니다
REST는 20년 넘게 사용되어 왔습니다. 모든 언어에 HTTP 라이브러리가 있습니다. 모든 디버깅 도구가 지원합니다. 모든 개발자가 써봤습니다. 이 성숙함은 예상치 못한 문제가 적고, 자료가 풍부하고, 팀 구성이 더 쉽다는 것을 의미합니다.
공개 API에는 관례가 있습니다
외부 개발자는 REST를 기대합니다. HTTP 상태 코드, 콘텐츠 협상, 표준 인증 패턴을 이미 알고 있습니다. 문서화 관례도 확립되어 있습니다. GraphQL은 새로운 것을 배우도록 요구합니다—API 채택의 장벽이 됩니다.
현실에 기반한 선택
GraphQL이 맞는 경우:
- UI가 한 화면에서 여러 리소스의 데이터를 필요로 할 때
- 모바일 성능이 중요할 때 (대역폭과 지연 시간 모두)
- 여러 클라이언트가 같은 API에서 서로 다른 데이터를 필요로 할 때
- 프론트엔드 팀이 백엔드 변경을 기다리지 않고 빠르게 움직이고 싶을 때
- 여러 마이크로서비스의 데이터를 통합할 때
REST가 맞는 경우:
- API가 리소스 중심이고 작업이 단순할 때
- HTTP 캐싱이 성능 전략의 핵심일 때
- 외부 개발자를 위한 공개 API를 만들 때
- 팀이 REST에는 익숙하지만 GraphQL을 새로 배워야 할 때
- 파일 업로드와 다운로드가 핵심 기능일 때
둘 다 쓸 수 있습니다:
많은 조직이 실제로 그렇게 합니다. 공개 API와 파일 처리에는 REST, 모바일 앱과 복잡한 내부 도구에는 GraphQL. 두 방식은 서로를 배제하지 않습니다.
Facebook이 던진 질문
처음 이야기로 돌아가 봅시다. Facebook에는 요청을 너무 많이 보내고, 데이터를 너무 많이 전송하고, 셀룰러 네트워크에서 고통받는 모바일 앱이 있었습니다.
GraphQL이 바로 그 문제를 해결했습니다. 클라이언트가 필요한 것을 요청 한 번으로 정확히 요청할 수 있게 했습니다. Facebook의 사례—복잡한 소셜 데이터, 모바일 우선, 다양한 클라이언트—에는 맞는 답이었습니다.
하지만 모든 문제가 Facebook의 문제는 아닙니다. API가 빠른 연결의 웹 브라우저에 단순하고 캐시 가능한 리소스를 제공한다면, REST의 단순함과 캐싱 이점이 GraphQL의 유연성보다 더 중요할 수 있습니다.
어느 방식이 더 좋은가는 질문이 아닙니다. 진짜 질문은 이겁니다: 누가 어떤 데이터를 네트워크로 보낼지 결정해야 하는가? 그리고 여러분의 사용자, 여러분의 네트워크, 여러분의 데이터 요구사항에서—올바른 답은 무엇인가?
GraphQL vs. REST 자주 묻는 질문
REST에서 GraphQL로 단계적으로 전환할 수 있나요?
네. 기존 REST API 옆에 GraphQL 엔드포인트를 추가하세요. 혜택이 가장 큰 클라이언트(모바일 앱, 복잡한 대시보드)부터 하나씩 전환하세요. REST가 잘 작동하는 클라이언트는 그대로 두면 됩니다. 둘 중 하나만 써야 한다는 규칙은 없습니다.
GraphQL과 REST 중 어느 것이 더 빠른가요?
어느 쪽이 더 빠르다고 단정할 수 없습니다. GraphQL이 빠를 수도 있고(라운드트립 횟수 감소, 데이터 전송량 감소), 느릴 수도 있습니다(N+1 쿼리 문제, HTTP 캐싱 불가). REST도 마찬가지입니다—빠를 수도 있고(적극적인 캐싱, 단순한 쿼리), 느릴 수도 있습니다(여러 번의 라운드트립, 오버페칭). 성능은 구현 품질과 사용 사례의 적합성에 달려 있습니다.
GraphQL이 REST를 대체하나요?
아닙니다. 서로 다른 문제를 잘 해결합니다. GraphQL은 다양한 클라이언트를 위한 유연한 데이터 조회에 강합니다. REST는 단순하고 캐시 가능한 리소스 중심 API에 강합니다. 많은 조직이 둘 다 씁니다.
GraphQL에서 인증은 어떻게 하나요?
REST와 동일합니다—헤더의 토큰, 쿠키 등의 방식을 사용합니다. GraphQL 자체는 인증을 규정하지 않습니다. 인증은 GraphQL 서버가 쿼리를 처리하기 전, HTTP 계층에서 이루어집니다.
실시간 데이터는 어떻게 처리하나요?
GraphQL은 실시간 업데이트를 위한 구독(subscription)을 지원하지만 구현 복잡도는 경우마다 다릅니다. REST는 일반적으로 실시간 기능에 WebSocket이나 Server-Sent Events를 활용합니다. 실시간 처리에서 어느 방식이 본질적으로 더 낫다고는 할 수 없습니다. 둘 다 추가 인프라가 필요합니다.
Nakatulong ba ang pahinang ito?