서브그래프 쿼리 작성을 위한 기본 문법

서브그래프 쿼리: 데이터 패턴 추출의 핵심 도구
서브그래프(The Graph)는 블록체인 데이터를 인덱싱하고 GraphQL 쿼리를 통해 효율적으로 제공하는 프로토콜입니다. 서브그래프를 배포한 후, 해당 데이터를 활용하는 핵심 과정이 바로 쿼리 작성입니다. 정확한 쿼리 문법은 원하는 데이터를 빠르고 정확하게 조회하는 기본이며, 잘못 구성된 쿼리는 성능 저하나 오류를 초래할 수 있습니다. 본 가이드는 서브그래프 스키마에 맞춰 GraphQL 쿼리를 작성하는 기본 문법과 실용적 패턴을 설명합니다.

GraphQL 쿼리 기본 구조 이해
서브그래프에 대한 모든 쿼리는 배포된 서브그래프의 스키마(Schema)를 기반으로 합니다. 스키마는 schema.graphql 파일에 정의된 엔티티(Entity) 타입과 필드로 구성되며, 쿼리는 이 구조를 정확히 따라야 합니다. 기본적인 쿼리 구조는 데이터를 ‘요청’하는 형태입니다.
query {
엔티티명(필터조건) {
필드1
필드2
중첩엔티티 {
중첩필드
}
}
}
단일 엔티티 및 필드 조회
가장 기본적인 형태로, 특정 엔티티의 모든 인스턴스나 특정 필드를 조회합니다. 실제로, ‘User’ 엔티티가 있다고 가정할 때의 쿼리 패턴입니다.
- 모든 User 엔티티 조회:
query { users { id address } }
이 쿼리는 ‘users’ 컬렉션에서 각 항목의 ‘id’와 ‘address’ 필드만을 반환합니다. 스키마에 정의된 다른 필드는 포함되지 않음. - 특정 필드만 선택적 조회:
query { users { id totalTransactions } }
GraphQL의 핵심 장점인 오버페칭(Over-fetching) 방지가 가능함. 클라이언트가 필요한 데이터만 정확히 요청할 수 있습니다.
데이터 필터링: where 인자 활용
실제 활용에서 모든 데이터를 가져오는 경우는 드뭅니다. 특정 조건을 만족하는 데이터만 추출하기 위해 where 인자를 사용한 필터링이 필수적입니다. where 인자 내부에서는 엔티티의 필드를 조건으로 사용합니다.
주의: where 조건에 사용하는 필드는 반드시 서브그래프 스키마에서 인덱싱(
@index)된 필드여야 쿼리 성능이 보장됩니다. 비인덱스 필드로 필터링 시 전체 테이블 스캔이 발생하여 응답 시간이 급격히 느려질 수 있습니다.
- 동등 조건 필터링:
query { users(where: {id: "0x1234..."}) { id address } }
ID가 정확히 일치하는 단일 User 엔티티를 조회합니다. - 비교 연산자 필터링:
query { transactions(where: {amount_gt: "1000000000000000000"}) { id amount } }‘amount’ 필드가 1 ETH(10^18 wei)보다 큰 모든 트랜잭션을 조회합니다. 주요 비교 연산자는 다음과 같습니다._gt: Greater than (초과)_lt: Less than (미만)_gte: Greater than or equal (이상)_lte: Less than or equal (이하)_not: Not equal (아님)_in,_not_in: 리스트 내 포함/미포함
- 복합 조건 필터링:
query { swaps(where: {pool: "0xabc...", timestamp_gte: "1640995200"}) { id amountUSD } }
특정 풀 주소(‘pool’)에서 발생했으며, 타임스탬프가 2022년 1월 1일 이후인 모든 스왑을 조회합니다. 쉼표로 구분하여 여러 조건을 AND 관계로 결합 가능합니다.
결과 정렬 및 페이징 처리
대량의 데이터를 다룰 때는 결과의 순서와 양을 제어하는 것이 중요합니다. 가령 블록체인 데이터는 시간순 분석이 빈번하므로 정렬과 페이징은 필수 기술입니다.
정렬: orderBy 및 orderDirection
orderBy와 orderDirection 인자를 사용하여 결과를 특정 필드 기준으로 정렬합니다.
query { transactions(orderBy: timestamp, orderDirection: desc, first: 10) { id timestamp } }
이 쿼리는 가장 최근 타임스탬프를 가진 트랜잭션 10개를 조회합니다. orderDirection은 asc(오름차순) 또는 desc(내림차순)으로 설정합니다.
페이징: first, skip
모든 결과를 한 번에 가져오는 것은 비효율적이고 제한적일 수 있습니다. first와 skip 인자를 활용하여 결과를 페이지 단위로 나누어 가져옵니다.
- 상위 N개 결과 조회:
query { users(first: 100, orderBy: id) { id } }
ID 기준으로 정렬된 상위 100명의 사용자를 조회합니다. - 페이징 구현 (예: 3번째 페이지):
query { users(first: 100, skip: 200, orderBy: id) { id } }
상위 200개 결과를 건너뛰고(skip), 그 다음 100개 결과를 가져옵니다. 이는 페이지 크기가 100일 때 3번째 페이지에 해당합니다.
대규모 데이터 처리 시 skip 설정값이 일정 범위를 초과하면 쿼리 엔진의 연산 부하가 증가하여 응답 속도가 지연될 가능성이 큽니다. 에이지옵저버토리의 데이터 접근 효율화 표준 가이드라인에 명시된 바와 같이 where 조건절에 특정 식별자 필터를 결합한 커서 기반 페이징을 설계하면 전체 레코드를 탐색하지 않고도 필요한 데이터 구간에 직접 접근이 가능해집니다. 이와 같은 구조적 개선은 인덱스 활용도를 극대화하여 하드웨어 리소스를 효율적으로 배분하고 대용량 환경에서의 트랜잭션 안정성을 확보하는 기술적 기반이 됩니다.
중첩 엔티티 및 관계형 데이터 조회
서브그래프 스키마에서 엔티티 간 관계(1:1, 1:N)가 정의되었다면, 단일 쿼리로 관련된 모든 데이터를 계층적으로 가져올 수 있습니다. 이는 GraphQL의 가장 강력한 기능 중 하나입니다.
예를 들어, ‘Pool’ 엔티티가 여러 ‘Swap’ 엔티티를 가질 수 있는 관계(1:N)라고 가정합니다.
query {
pools(first: 5) {
id
totalSwapVolume
swaps(first: 10, orderBy: timestamp, orderDirection: desc) {
id
amountUSD
timestamp
}
}
}
이 쿼리는 5개의 풀을 조회하면서, 각 풀에 연결된 가장 최근 10개의 스왑 데이터를 함께 가져옵니다. 클라이언트는 여러 번의 API 호출 없이 한 번의 요청으로 완전한 데이터 구조를 얻을 수 있습니다, 중첩된 관계에서도 where, orderby, first 등의 인자를 동일하게 적용하여 데이터를 제어할 수 있습니다. 쿼리 결과의 정확성을 높이기 위해서는 기술적 근간인 온체인 이벤트 로그와 데이터 동기화의 관계를 이해하고 이를 바탕으로 데이터 구조를 설계하는 것이 중요합니다.
쿼리 성능 최적화 체크리스트
복잡한 쿼리나 대량의 데이터를 다룰 때 응답 시간을 최소화하려면 다음 사항을 반드시 점검해야 합니다.
- 필수 필드만 요청: 스키마의 모든 필드를 요청하지 말고, 애플리케이션에 실제로 필요한 필드만 선택하여 네트워크 대역폭과 처리 시간을 절약합니다.
- 인덱스 필드 활용 필터링:
where조건의 주체는 가능한 한@index가 적용된 필드(예:id,timestamp, 특정 주소)를 사용합니다. 비인덱스 필드(amountUSD등)로의 필터링은 성능 저하의 주요 원인입니다. - 적절한 페이지 크기 설정 시 first 값을 지나치게 크게 설정하면 단일 응답의 처리 부하가 증가하고 시간 초과를 유발할 수 있습니다. 대규모 데이터 조회의 효율성을 높이기 위한 페이지네이션(Pagination)의 이론적 배경과 정의를 시스템 설계에 대입하여 분석해 보면, 데이터 처리 단위를 세분화하는 것이 서버 가용성 확보의 핵심 근거가 됨을 알 수 있습니다. 이러한 원리에 따라 100에서 500 사이의 합리적인 페이지 크기를 유지하는 것이 성능 최적화의 실질적인 방안이 됩니다.
- 중첩 깊이 제어: 지나치게 깊은 중첩 쿼리(예: Pool -> Swaps -> User -> Transactions…)는 서버 리소스를 과도하게 소모할 수 있습니다, 필요한 수준에서 쿼리 깊이를 최적화합니다.
- 정렬 기준 명시: 특히
first를 사용할 때는 반드시orderby를 함께 명시하여 결과의 예측 가능성과 일관성을 보장합니다.
서브그래프 쿼리 작성은 배포된 스키마를 정확히 이해하는 것에서 시작합니다. subgraph.yaml의 매니페스트와 schema.graphql 파일을 상시 참조하여, 정의된 엔티티, 필드 타입(String, Bytes, BigInt 등), 그리고 관계를 정확히 파악한 후 위의 문법을 적용해야 합니다. 이를 통해 블록체인 데이터를 대상으로 한 강력하고 효율적인 데이터 조회 인터페이스를 구축할 수 있습니다.



