GitHub API를 써본 개발자라면 대부분 REST API를 먼저 접한다. 다만 복잡한 저장소 정보를 가져올 때 여러 번의 API 호출이 필요하고, 불필요한 필드까지 받게 되는 비효율성을 경험해봤을 가능성이 높다. 이번에는 GraphQL API로 이 문제를 어떻게 해결하는지, 실제로 어떻게 PHP에서 연동하는지 완벽하게 정리해서 소개하겠다.

 

1단계. GitHub GraphQL API 이해하기
GitHub의 REST API v3는 엔드포인트마다 고정된 데이터 구조를 반환한다. 예를 들어 사용자 정보와 저장소 목록을 동시에 가져오려면 최소 2번 이상의 요청을 해야 한다. 반면 GraphQL API는 클라이언트가 필요한 필드만 명시해서 요청하는 쿼리 방식이다.

REST API vs GraphQL의 핵심 차이를 표로 정리하면 다음과 같다.

 

항목 REST API v3 GraphQL API
요청 방식 고정된 엔드포인트 (GET, POST, etc) 단일 엔드포인트 + 쿼리 본문
필드 선택 정해진 필드 모두 반환 필요한 필드만 명시해서 받음
다중 데이터 조회 여러 번의 요청 필요 한 번의 쿼리로 처리
응답 크기 불필요한 데이터 포함 필요한 것만 포함
Rate Limit 요청당 1건 계산 쿼리 복잡도 기반

 

실제 예제: 사용자 정보 + 저장소 5개 조회
REST API로는 2번 요청이 필요하지만, GraphQL은 한 번에 처리된다. 이것이 GraphQL의 핵심 장점이다.

 

2단계. GitHub 개인 액세스 토큰(PAT) 발급받기
GitHub GraphQL API를 사용하려면 먼저 인증 토큰이 필요하다.

발급 방법:
1. GitHub 계정 로그인 → Settings → Developer settings → Personal access tokens → Tokens (classic)
2. "Generate new token (classic)" 클릭
3. Note: "GitHub GraphQL API" 입력
4. Expiration: 90 days 선택
5. Scopes: "repo" 체크 (저장소 접근 권한)
6. "Generate token" 클릭 후 토큰 복사 (다시 보이지 않음)

 

3단계. PHP에서 GraphQL 쿼리 실행하기
✓ 올바른 코드: cURL로 GraphQL 요청
<?php
$token = 'github_pat_xxxxxxxxxxxxx'; // 발급받은 토큰
$endpoint = 'https://api.github.com/graphql';

// GraphQL 쿼리 정의
$query = <<<'GRAPHQL'
query {
  viewer {
    login
    name
    email
    repositories(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) {
      edges {
        node {
          name
          url
          description
          stargazerCount
          forks {
            totalCount
          }
        }
      }
    }
  }
}
GRAPHQL;

// cURL 요청
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $endpoint,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json',
        'User-Agent: PHP-GitHub-Client',
    ],
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode(['query' => $query]),
    CURLOPT_TIMEOUT => 10,
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code !== 200) {
    die('Error: HTTP ' . $http_code);
}

$data = json_decode($response, true);

// 에러 확인
if (!empty($data['errors'])) {
    echo "GraphQL Error: " . $data['errors'][0]['message'];
    exit;
}

// 결과 출력
$user = $data['data']['viewer'];
echo "User: " . $user['login'] . "n";
echo "Name: " . $user['name'] . "n";
echo "Repositories:n";

foreach ($user['repositories']['edges'] as $repo) {
    $node = $repo['node'];
    echo "  - " . $node['name'] . " (Stars: " . $node['stargazerCount'] . ")n";
}
?>
코드 설명:
- GraphQL 쿼리를 문자열로 정의해서 요청 본문에 담음 - Authorization 헤더에 Bearer 토큰 추가 - JSON 형식으로 POST 요청 - 응답에서 errors 필드 확인 (GraphQL은 HTTP 200이어도 에러 가능) - 원하는 필드(login, name, repositories)만 받음

 

✗ 흔한 실수: 토큰 없이 요청
<?php
// 잘못된 코드 - 토큰 없음
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json'
]);

// 결과: "API rate limit exceeded for user..." 에러
?>
왜 안 되나: 토큰 없으면 익명 사용자로 취급되어 매우 낮은 rate limit (60 requests/hour)이 적용된다. 반드시 Authorization 헤더에 토큰을 포함해야 한다.

 

4단계. 실전 예제: 특정 저장소의 이슈 조회
✓ 실무 활용 코드
<?php
function getRepositoryIssues($owner, $repo, $token) {
    $endpoint = 'https://api.github.com/graphql';
    
    // 변수와 함께 쿼리 작성 (보안 권장)
    $query = 'query($owner:String!, $repo:String!) {
      repository(owner: $owner, name: $repo) {
        issues(first: 10, states: OPEN, orderBy: {field: UPDATED_AT, direction: DESC}) {
          edges {
            node {
              number
              title
              body
              author {
                login
              }
              createdAt
              comments {
                totalCount
              }
            }
          }
        }
      }
    }';
    
    $variables = [
        'owner' => $owner,
        'repo' => $repo,
    ];
    
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $endpoint,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json',
        ],
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode([
            'query' => $query,
            'variables' => $variables,
        ]),
        CURLOPT_TIMEOUT => 15,
    ]);
    
    $response = curl_exec($ch);
    curl_close($ch);
    
    $data = json_decode($response, true);
    
    if (!empty($data['errors'])) {
        throw new Exception('GraphQL Error: ' . $data['errors'][0]['message']);
    }
    
    return $data['data']['repository']['issues']['edges'];
}

// 사용 예
try {
    $issues = getRepositoryIssues('laravel', 'laravel', 'github_pat_xxxxx');
    
    foreach ($issues as $issue) {
        $node = $issue['node'];
        echo $node['number'] . ': ' . $node['title'] . " (Comments: " . $node['comments']['totalCount'] . ")n";
    }
} catch (Exception $e) {
    echo $e->getMessage();
}
?>
코드 설명:
- 변수($owner, $repo)를 쿼리에 삽입해서 SQL 인젝션처럼 보이는 문제 방지 - issues 필드에서 first: 10으로 상위 10개만 조회 - states: OPEN으로 오픈된 이슈만 필터링 - author, comments 등 필요한 정보만 명시

 

5단계. Rate Limit 확인 및 최적화
GraphQL API는 REST API와 달리 쿼리 복잡도(Query Complexity) 기반으로 rate limit을 계산한다. 따라서 같은 데이터를 가져오더라도 GraphQL이 더 효율적일 수 있다.
✓ Rate Limit 조회 쿼리
<?php
$query = 'query {
  rateLimit {
    limit
    cost
    remaining
    resetAt
  }
}';

// cURL 요청 후 결과 출력
// 출력 예:
// Limit: 5000
// Cost: 1 (현재 쿼리 복잡도)
// Remaining: 4999
// Reset: 2024-01-15T10:30:00Z
?>
최적화 팁:
- 한 쿼리에서 너무 많은 중첩 필드 요청 금지 (복잡도 증가) - pagination 시 first/after로 제한 - 불필요한 필드는 명시하지 않기

 

6단계. 주의사항 및 흔한 실수

✗ 실수 1: 인증 토큰 노출

// 잘못된 코드 - 토큰을 소스코드에 하드코딩
$token = 'github_pat_xxxxxxxxxxxxx';

// 올바른 방법 - 환경 변수 사용
$token = getenv('GITHUB_TOKEN');
// 또는 .env 파일에서 로드
$token = $_ENV['GITHUB_TOKEN'];
?>

✗ 실수 2: 응답 에러 체크 미흡

// 잘못된 코드
$data = json_decode($response, true);
$issues = $data['data']['repository']['issues']; // 에러 발생 가능

// 올바른 코드
if (!empty($data['errors'])) {
    foreach ($data['errors'] as $error) {
        error_log('GraphQL Error: ' . $error['message']);
    }
    return [];
}

if (empty($data['data'])) {
    error_log('No data in response');
    return [];
}

$issues = $data['data']['repository']['issues'];
?>

✗ 실수 3: 타임아웃 설정 없음

// 잘못된 코드 - 무한 대기 가능
curl_setopt($ch, CURLOPT_TIMEOUT, 0);

// 올바른 코드 - 충분하지만 합리적인 타임아웃
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
?>

 

최종 정리: GraphQL API 활용의 핵심

GitHub GraphQL API는 REST API 대비 요청 횟수 감소, 불필요한 데이터 제외, 효율적인 rate limit 사용 등의 장점을 제공한다. 특히 복잡한 저장소 정보나 이슈/PR 데이터를 다루는 경우, GraphQL로 전환하면 API 호출 비용을 획기적으로 줄일 수 있다. 이 글의 4단계 실전 예제 코드를 참고해 자신의 프로젝트에 적용하면, 더 빠르고 안정적인 GitHub 연동 시스템을 구축할 수 있을 것이다.