API 사용

이 문서에서는 Google Civic Information API를 사용하기 위해 알아야 할 사항을 설명합니다. API에 관한 몇 가지 기본 정보와 사용자 포럼의 자주 묻는 질문이 포함된 Civic Info API FAQ를 참고하세요. 또한 아래에서 선거 기간 동안 선거인 정보를 조회하는 예시를 확인할 수 있습니다.

Google에 애플리케이션 식별

애플리케이션은 Google Civic Information API에 요청을 보낼 때마다 각 요청에 API 키를 포함하여 자신을 식별해야 합니다.

API 키 획득 및 사용

API 키를 획득하려면 다음 안내를 따르세요.

  1. API 콘솔에서 사용자 인증 정보 페이지를 엽니다.
  2. 이 API는 두 가지 유형의 사용자 인증 정보를 지원합니다. 프로젝트에 적합한 사용자 인증 정보를 만듭니다.
    • OAuth 2.0: 애플리케이션에서 비공개 사용자 데이터를 요청할 때마다 요청과 함께 OAuth 2.0 토큰을 보내야 합니다. 애플리케이션은 먼저 토큰을 획득하기 위해 클라이언트 ID나 클라이언트 보안 비밀번호를 보냅니다. 웹 애플리케이션, 서비스 계정, 설치된 애플리케이션의 OAuth 2.0 사용자 인증 정보를 생성할 수 있습니다.

      참고: 이 API에는 OAuth 2.0 승인이 필요한 메서드가 없으므로 아래에 설명된 API 키만 가져와도 됩니다. 하지만 애플리케이션에서 사용자 승인이 필요한 다른 API를 호출하는 경우에도 OAuth 2.0 사용자 인증 정보가 필요합니다.

      자세한 내용은 OAuth 2.0 문서를 참조하세요.

    • API 키: OAuth 2.0 토큰을 제공하지 않는 요청은 API 키를 전송해야 합니다. 키는 프로젝트를 식별하고 API 액세스, 할당량, 보고서를 제공합니다.

      API는 여러 유형의 API 키 제한 사항을 지원합니다. 필요한 API 키가 아직 없는 경우 사용자 인증 정보 만들기 > API 키를 클릭하여 Console에서 API 키를 만듭니다. 프로덕션 단계에서 사용하기 전에 키 제한을 클릭하고 제한사항 중 하나를 선택하여 키를 제한할 수 있습니다.

API 키를 안전하게 보호하려면 API 키를 안전하게 사용하기 위한 권장사항을 따르세요.

API 키가 있으면 애플리케이션은 모든 요청 URL에 쿼리 매개변수 key=yourAPIKey를 추가할 수 있습니다.

API 키는 URL에 포함하기에 안전합니다. 인코딩이 전혀 필요하지 않습니다.

API 키 제한사항

API 키는 기본적으로 제한이 없으므로 누구나 이 키를 읽거나 (예: 브라우저 내에 있는 경우) 키가 있는 기기에 액세스할 수 있다면 안전하지 않습니다. 무단 사용을 방지하려면 이 API 키에 제한사항을 설정하는 것이 좋습니다.

제한사항을 추가하려면 API 키 생성 완료 대화상자에서 키 제한을 클릭합니다. API 키 구성 패널이 표시됩니다.

선택하는 제한사항 유형은 애플리케이션 요구사항에 따라 다릅니다.

  • 백엔드나 미들웨어를 통하지 않고 API와 직접 상호작용하는 웹 애플리케이션은 HTTP 리퍼러 제한을 추가해야 합니다. 그러나 이러한 애플리케이션은 API 키를 공개적으로 노출하므로 대신 서비스 계정 인증 스키마를 사용하는 것이 좋습니다.
  • 서비스 계정을 지원할 수 없는 백엔드 애플리케이션(예: 클라이언트 라이브러리에 지원되는 언어가 없는 삽입된 기기)은 다른 IP 주소의 클라이언트가 사용하지 못하도록 IP 주소 제한을 추가해야 합니다.
  • Android 애플리케이션은 Android 앱 제한을 추가하고 패키지 이름과 SHA-1 서명 인증서 지문을 추가해야 합니다.
  • iOS 애플리케이션은 iOS 앱 제한을 추가하고 iOS 번들 식별자를 추가하여 API 호출을 이러한 iOS 번들로 제한해야 합니다.

테스트의 경우 제한을 전혀 적용하지 않는 것이 좋습니다. 하지만 애플리케이션을 프로덕션에 배포한 후에는 이 키에 제한사항을 추가하거나 삭제하는 것이 좋습니다.

electionQuery 예시

다음은 electionQuery API를 호출하여 유효한 선거 ID 목록을 가져온 다음, 유권자의 등록된 주소와 함께 voterInfoQuery API를 사용하여 선택한 선거에 관한 정보를 가져오는 예시 (API 버전 'v2' 사용)입니다.

electionQuery를 사용하여 유효한 선거 ID 목록을 가져옵니다.

  https://www.googleapis.com/civicinfo/v2/elections?key=<YOUR_API_KEY>
  

electionQuery 응답:

{
 "kind": "civicinfo#electionsqueryresponse",
 "elections": [
  {
   "id": "2000",
   "name": "VIP Test Election",
   "electionDay": "2013-06-06"
  },
  {
   "id": "2124",
   "name": "Rhode Island 2012 Primary Election",
   "electionDay": "2012-09-11"
  },
  {
   "id": "2126",
   "name": "Delaware 2012 Primary Election",
   "electionDay": "2012-09-11"
  }
 ]
}

curl을 사용한 voterInfoQuery

curl를 사용하여 VIP 테스트 선거 ID 2000 및 (테스트) 주소 340 Main St, Venice, CA 90291의 유권자에 대한 voterInfoQuery 요청을 보냅니다. voterInfoQuery 응답을 참고하세요.

curl "https://www.googleapis.com/civicinfo/v2/voterinfo?key=<YOUR_API_KEY>&address=340%20Main%20St.%20Venice%20CA&electionId=2000"

JavaScript용 Google API 클라이언트 라이브러리를 사용하는 voterInfoQuery

이 예에서는 이전 curl 예와 동일한 voterInfoQuery를 실행하지만 JavaScript 클라이언트 라이브러리를 사용합니다. voterInfoQuery 응답은 curl 예시 응답과 동일합니다.

<!doctype html>
<html>
  <head>
    <script>
      /**
       * Build and execute request to look up voter info for provided address.
       * @param {string} address Address for which to fetch voter info.
       * @param {function(Object)} callback Function which takes the
       *     response object as a parameter.
       */
       function lookup(address, callback) {
       /**
         * Election ID for which to fetch voter info.
         * @type {number}
         */
        var electionId = 2000;
 
        /**
         * Request object for given parameters.
         * @type {gapi.client.HttpRequest}
         */
        var req = gapi.client.request({
            'path' : '/civicinfo/v2/voterinfo',
            'params' : {'electionId' : electionId, 'address' : address}
        });
       req.execute(callback);
      }

      /**
       * Render results in the DOM.
       * @param {Object} response Response object returned by the API.
       * @param {Object} rawResponse Raw response from the API.
       */
      function renderResults(response, rawResponse) {
        var el = document.getElementById('results');
        if (!response || response.error) {
          el.appendChild(document.createTextNode(
              'Error while trying to fetch polling place'));
          return;
        }
        var normalizedAddress = response.normalizedInput.line1 + ' ' +
            response.normalizedInput.city + ', ' +
            response.normalizedInput.state + ' ' +
            response.normalizedInput.zip;
        if (response.pollingLocations.length > 0) {
          var pollingLocation = response.pollingLocations[0].address;
          var pollingAddress = pollingLocation.locationName + ', ' +
              pollingLocation.line1 + ' ' +
              pollingLocation.city + ', ' +
              pollingLocation.state + ' ' +
              pollingLocation.zip;
          var normEl = document.createElement('strong');
          normEl.appendChild(document.createTextNode(
              'Polling place for ' + normalizedAddress + ': '));
          el.appendChild(normEl);
          el.appendChild(document.createTextNode(pollingAddress));
        } else {
          el.appendChild(document.createTextNode(
              'Could not find polling place for ' + normalizedAddress));
        }
      }

      /**
       * Initialize the API client and make a request.
       */
      function load() {
        gapi.client.setApiKey('YOUR API KEY GOES HERE');
        lookup('1263 Pacific Ave. Kansas City KS', renderResults);
      }
    </script>
    <script src="https://apis.google.com/js/client.js?onload=load"></script>
  </head>
  <body>
    <div id="results"></div>
  </body>
</html>

voterInfoQuery 응답

{
  "kind": "civicinfo#voterinforesponse",
  "status": "success",
  "election": {
  "id": "2000",
  "name": "VIP Test Election",
  "electionDay": "2025-06-06",
  "ocdDivisionId": "ocd-division/country:us"
  },
  "normalizedInput": {
    "line1": "340 Main Street",
    "city": "Venice",
    "state": "CA",
    "zip": "90291"
  },
  "pollingLocations": [
    {
      "address": {
        "locationName": "WESTMINSTER AVENUE ELEMENTARY SCHOOL",
        "line1": "1010 ABBOT KINNEY BLVD",
        "city": "VENICE",
        "state": "CA",
        "zip": "90291"
      },
      "pollingHours": "",
      "latitude": 33.9919351,
      "longitude": -118.4722031,
      "startDate": "2024-03-05",
      "endDate": "2024-03-05",
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ]
    },
    {
      "address": {
        "locationName": "POP UP VOTE CENTER 5",
        "line1": "12400 IMPERIAL HWY",
        "city": "NORWALK",
        "state": "CA",
        "zip": "90650"
      },
      "latitude": 33.915989,
      "longitude": -118.0677283,
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ]
    }
  ],
  "dropOffLocations": [
    {
      "address": {
        "locationName": "FLEX VOTE CENTER 9",
        "line1": "12400 IMPERIAL HWY",
        "city": "NORWALK",
        "state": "CA",
        "zip": "90650"
      },
      "latitude": 33.915989,
      "longitude": -118.0677283,
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ]
    },
  ],
  "contests": [
    {
      "type": "General",
      "ballotTitle": "UNITED STATES REPRESENTATIVE, 36th District",
      "district": {
        "name": "36TH US CONGRESSIONAL",
        "scope": "congressional"
      },
      "numberElected": "1",
      "numberVotingFor": "1",
      "ballotPlacement": "103",
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ],
      "candidates": [
        {
          "name": "ARIANA HAKAMI",
          "party": "Party Preference: Republican"
        },
        {
          "name": "CLAIRE RAGGE ANDERSON",
          "party": "Party Preference: None"
        },
        {
          "name": "MELISSA TOOMIM",
          "party": "Party Preference: Republican"
        },
        {
          "name": "TED W. LIEU",
          "party": "Party Preference: Democratic"
        }
      ]
    },
    {
      "type": "ballot-measure",
      "ballotTitle": "LOS ANGELES CITY MUNICIPAL ELECTION - MEASURE HLA",
      "district": {
        "name": "CITY OF LOS ANGELES",
        "scope": "citywide"
      },
      "ballotPlacement": "116",
      "referendumTitle": "LOS ANGELES CITY MUNICIPAL ELECTION - MEASURE HLA",
      "referendumText": "CITY MOBILITY PLAN STREET IMPROVEMENT MEASURES. INITIATIVE ORDINANCE HLA. Shall an ordinance providing that when the City of Los Angeles makes a qualifying improvement to a City-owned street (e.g., a paving project), the City must also install certain street enhancements described in the City's Mobility Plan network of pedestrian, bicycle, transit, and vehicle routes; and requiring the City to provide publicly accessible information regarding street improvements; be adopted?",
      "referendumPassageThreshold": "MAJORITY OF VOTES CAST",
      "referendumBallotResponses": [
        "YES",
        "NO"
      ],
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ]
    },
    {
      "type": "General",
      "ballotTitle": "DISTRICT ATTORNEY",
      "district": {
        "name": "LOS ANGELES COUNTY",
        "scope": "countywide"
      },
      "numberElected": "1",
      "numberVotingFor": "1",
      "ballotPlacement": "129",
      "sources": [
        {
          "name": "Voting Information Project",
          "official": true
        }
      ],
      "candidates": [
        {
          "name": "GEORGE GASCÓN"
        },
        {
          "name": "JONATHAN HATAMI"
        },
        {
          "name": "NATHAN HOCHMAN"
        },
        {
          "name": "DEBRA ARCHULETA"
        },
        {
          "name": "JEFF CHEMERINSKY"
        },
        {
          "name": "ERIC SIDDALL"
        },
        {
          "name": "MARIA RAMIREZ"
        },
        {
          "name": "DAN KAPELOVITZ"
        },
        {
          "name": "LLOYD \"BOBCAT\" MASSON"
        },
        {
          "name": "JOHN MCKINNEY"
        },
        {
          "name": "CRAIG J. MITCHELL"
        },
        {
          "name": "DAVID S. MILTON"
        }
      ]
    }
  ],
  "state": [
    {
      "name": "California",
      "electionAdministrationBody": {
        "name": "Secretary of State",
        "electionInfoUrl": "https://www.sos.ca.gov/elections/",
        "electionRegistrationUrl": "https://registertovote.ca.gov/?t=s",
        "electionRegistrationConfirmationUrl": "https://voterstatus.sos.ca.gov",
        "absenteeVotingInfoUrl": "https://elections.cdn.sos.ca.gov/vote-by-mail/replacement-application.pdf",
        "votingLocationFinderUrl": "https://voterstatus.sos.ca.gov",
        "ballotInfoUrl": "https://www.sos.ca.gov/elections/ballot-status/wheres-my-ballot/",
        "correspondenceAddress": {
          "line1": "1500 11th Street, 5th Floor",
          "city": "Sacramento",
          "state": "California",
          "zip": "95814"
        }
      },
      "local_jurisdiction": {
        "name": "Los Angeles",
        "electionAdministrationBody": {
          "name": "Registrar-Recorder/County Clerk",
          "electionInfoUrl": "http://www.lavote.gov/",
          "electionRegistrationUrl": "http://registertovote.ca.gov/",
          "electionRegistrationConfirmationUrl": "https://lavote.gov/vrstatus/",
          "absenteeVotingInfoUrl": "",
          "ballotInfoUrl": "http://www.lavote.gov/Locator",
          "physicalAddress": {
            "locationName": "Registrar-Recorder/County Clerk",
            "line1": "12400 Imperial Highway",
            "city": "Norwalk",
            "state": "CA",
            "zip": "90650"
          }
        },
        "sources": [
          {
            "name": "Voting Information Project",
            "official": true
          }
        ]
      }
    }
  ]
}