REST vs GraphQL: 1인 프로젝트에 GraphQL은 과한 선택인가
GraphQL이 풀려던 과다·과소 수신 문제가 1인 프로젝트에서도 발생하는지 따져보고, 캐싱·N+1·권한 처리 비용까지 비교해 선택 기준을 정리했습니다.
1인 프로젝트에 GraphQL은 과한 선택인가
API를 설계할 때 REST와 GraphQL 중 무엇을 고를지는 오래된 질문입니다. 그런데 팀 규모가 작고 클라이언트가 하나뿐인 사이드 프로젝트에서는 질문의 성격이 달라집니다. GraphQL이 해결하는 문제가 애초에 발생하지 않았을 가능성이 높기 때문입니다.
무엇을 위해 만들어진 기술인지부터 보면 판단이 쉬워집니다.
GraphQL이 해결하려던 문제
GraphQL은 클라이언트가 필요한 필드를 직접 지정하는 질의 언어입니다. 엔드포인트는 하나이고, 무엇을 받을지는 요청이 정합니다.
query {
post(id: "1") {
title
author { name }
comments(first: 5) { body }
}
}
이 설계가 풀려던 문제는 두 가지입니다.
과다 수신(over-fetching): 화면에 제목만 필요한데 REST 응답이 본문과 메타데이터까지 전부 내려주는 상황입니다.
과소 수신(under-fetching): 화면 하나를 그리려고 글, 작성자, 댓글을 각각 다른 엔드포인트에서 세 번 호출해야 하는 상황입니다.
특히 화면 구성이 서로 다른 클라이언트가 여럿(웹, iOS, Android, 파트너 앱) 있고, 각 화면마다 필요한 필드가 다르며, 서버 팀이 클라이언트 요구를 매번 따라갈 수 없을 때 이 문제가 커집니다. GraphQL은 그 조건에서 나온 해법입니다.
클라이언트가 하나고 서버도 내가 짠다면, 필드가 남거나 부족할 때 그냥 응답을 고치면 됩니다. 문제 자체가 크지 않습니다.
REST의 실질적 강점: HTTP를 그대로 쓴다
REST가 유리한 지점은 개념보다 인프라입니다.
GET /api/posts/1은 URL이 곧 자원의 식별자이므로 캐싱이 자연스럽습니다. 브라우저 캐시, CDN, 리버스 프록시가 모두 HTTP 규칙대로 동작합니다. Cache-Control, ETag, 조건부 요청이 별도 작업 없이 적용됩니다.
GraphQL은 보통 단일 엔드포인트로 POST를 보냅니다. 요청 본문이 매번 다르니 URL 기준 캐싱이 걸리지 않습니다. 해결책이 없는 건 아닙니다. 질의를 미리 등록해두고 식별자로 호출하는 방식(persisted query)을 쓰면 GET과 캐싱을 쓸 수 있습니다. 다만 이건 추가로 구축해야 하는 장치입니다.
디버깅 편의도 무시하기 어렵습니다. 브라우저 네트워크 탭에서 어떤 요청이 느린지 URL만 보고 알 수 있고, curl로 재현하기도 쉽습니다.
GraphQL을 도입하면 새로 생기는 숙제
N+1 문제: 리졸버는 필드 단위로 실행됩니다. 글 20개를 가져오고 각 글의 작성자를 채우면, 작성자 조회가 20번 발생합니다. 이걸 막으려면 같은 틱의 요청을 모아 한 번에 조회하는 배칭 계층(DataLoader 류)을 붙여야 합니다. 안 붙이면 조용히 느려집니다.
질의 비용 제한: 클라이언트가 질의를 정한다는 건 무거운 질의도 보낼 수 있다는 뜻입니다. 중첩을 반복해 거대한 응답을 요구하는 것이 가능하므로, 공개 API라면 깊이 제한과 복잡도 계산을 넣어야 합니다.
인증·인가의 위치: REST는 엔드포인트 단위로 권한을 걸면 됩니다. GraphQL은 하나의 질의가 여러 자원을 넘나들기 때문에 필드나 타입 단위로 권한을 확인해야 합니다. 빠뜨린 필드 하나가 그대로 구멍이 됩니다.
에러 처리 관례: GraphQL은 부분 성공이 가능합니다. HTTP 200에 errors 배열이 함께 오는 응답을 클라이언트가 처리해야 하므로, 상태 코드만 보고 분기하던 습관이 통하지 않습니다.
그렇다면 타입 안전성은 어떻게 얻나
GraphQL을 고르는 실제 이유가 스키마와 타입 생성일 때가 많습니다. 그건 GraphQL만의 것이 아닙니다.
REST에서도 OpenAPI 스펙을 두고 클라이언트 타입을 생성할 수 있습니다. 같은 언어로 프론트와 백엔드를 짠다면 타입을 공유하는 것도 가능합니다. 서버와 클라이언트가 한 저장소에 있는 구성이라면 함수 시그니처를 그대로 공유하는 RPC 방식이 더 단순한 답일 수 있습니다.
즉 "타입 안전한 API"를 원해서 GraphQL을 도입하는 것은 목적에 비해 큰 도구를 드는 경우가 많습니다.
선택 기준
REST로 시작하는 편이 나은 경우
- 클라이언트가 하나이고 서버도 직접 만든다
- 캐싱과 CDN을 적극적으로 활용할 계획이다
- 응답 형태가 화면마다 크게 다르지 않다
- 운영 인력이 자신뿐이다
GraphQL이 값을 하는 경우
- 화면 구성이 다른 클라이언트가 여럿이고 각자 다른 필드를 원한다
- 여러 백엔드·외부 API를 하나의 그래프로 합쳐 보여줘야 한다
- 서버 배포와 무관하게 클라이언트가 요구를 바꿔야 한다
나중에 붙이는 경우
- REST로 시작해 실제로 과다·과소 수신이 반복되고, 클라이언트가 늘어난 다음에 도입합니다. 반대 순서보다 되돌리기 쉽습니다.
GraphQL을 나중에 얹을 수 있나요?
가능합니다. 데이터 접근 계층을 API 형태와 분리해두면, 나중에 GraphQL 리졸버가 같은 계층을 호출하는 구조로 얹을 수 있습니다. 실제로 기존 REST를 유지하면서 GraphQL을 병행하는 사례가 많습니다. 그래서 처음에 REST를 고른 것이 막다른 길이 되지는 않습니다.
REST에서 과다 수신을 줄이는 방법은 없나요?
있습니다. 흔한 방식은 화면 단위 엔드포인트를 두는 것입니다. 자원 하나에 하나의 엔드포인트를 고집하는 대신, 특정 화면이 필요한 데이터를 한 번에 조립해 내려주는 엔드포인트를 만드는 방식입니다. 필드 선택 파라미터를 지원하는 방법도 있는데, 조건이 늘어나면 결국 질의 언어를 직접 만드는 셈이 되니 그 지점이 GraphQL을 검토할 신호입니다.
요청 횟수가 많으면 무조건 느린 건가요?
아닙니다. HTTP/2 이상에서는 하나의 연결로 여러 요청이 동시에 처리되므로, 요청 수 자체의 비용이 과거만큼 크지 않습니다. 오히려 각 요청이 개별적으로 캐시된다는 이점이 생깁니다. 병목이 되는 건 요청 수보다 순차 의존(앞 응답을 받아야 다음 요청을 보낼 수 있는 구조)인 경우가 많습니다.
결정보다 관찰이 먼저
과다·과소 수신은 추측이 아니라 관측되는 문제입니다. 네트워크 탭을 열어 화면 하나를 그릴 때 몇 번 호출하고 얼마를 버리는지 세보면, GraphQL이 필요한 상황인지 아닌지가 드러납니다.
만든 프로젝트가 배포된 상태라면 Vibeollio에 프로젝트 등록해서 공개해보세요. 실제 사용자가 쓰기 시작하면 어떤 화면이 자주 열리고 어디가 느린지 패턴이 생깁니다. API 구조는 그 패턴을 본 다음에 정리하는 편이 낫습니다.
