방구석컴퍼니
← 이야기 목록
AI 활용2026-08-18

공공데이터 API 키를 브라우저에 두면 안 되는 이유와 대안 구조

인증키를 화면 코드에 박으면 페이지를 연 사람 누구나 키를 가져갑니다. 로컬 서버를 한 겹 두는 구조와, AI가 짜준 코드에서 실제로 나온 집계 오류를 함께 정리했습니다.

방구석컴퍼니

혼자 일하는 사람 · 작업 기록

이 글은 코딩을 직접 하지 않고 AI에게 시켜 공공데이터를 붙여보려는 분들을 위한 글입니다. 필요한 준비물은 발급받은 인증키 하나와 AI 챗봇 하나뿐이고, 읽고 나면 키를 어디 둘지 스스로 정할 수 있게 됩니다.

공공데이터포털이 여는 오픈 API는 11,915개인데, 개발계정 인증키는 계정당 1개뿐입니다. 도구를 몇 개 만들든 지켜야 할 비밀은 그 하나예요.

그래서 첫 도구를 만들 때 지시를 하나 넣었습니다. 키를 화면 쪽 코드에 두지 마라. 이 한 줄이 있고 없고에 따라 결과물 구조가 통째로 갈립니다.

이 글은 그 구조를 왜 그렇게 잡았는지, 그리고 완성된 뒤에 제가 무엇을 놓쳤는지를 적습니다.

인증키를 화면 코드에 넣으면 무엇이 노출되나?

브라우저가 받아 실행하는 코드에 키를 적으면 그 페이지를 연 사람 전원이 키를 볼 수 있습니다. 다만 이건 해킹이 아니라 정상 동작이에요 — 브라우저는 받은 코드를 숨기지 않습니다.

확인은 30초면 끝난다

개발자 도구를 열 필요도 없습니다. 페이지 소스 보기로 끝납니다. 브라우저에서 소스를 열고 키 앞 여섯 글자를 검색해보세요. 걸리면 그 키는 이미 공개된 겁니다.

문제는 그다음입니다. 공공데이터포털 개발계정은 일일 호출 한도가 붙습니다. 제가 쓴 상권 API는 하루 10,000번이었어요. 남이 내 키로 호출하면 내 한도가 줄고, 그 호출 기록도 내 계정에 남습니다.

그래서 어떤 구조로 바꿨나?

브라우저와 정부 서버 사이에 로컬 서버를 한 겹 넣었습니다. 키는 그 서버에만 둡니다.

키를 화면에 둘 때서버를 한 겹 둘 때
키를 아는 쪽페이지를 연 모든 사람내 컴퓨터
브라우저가 부르는 곳data.go.kr 직접내 서버
키가 붙는 시점브라우저에서서버에서
코드 늘어남0줄파일 1개

표에서 봐야 할 칸은 마지막 줄입니다. 비용이 파일 하나뿐이에요. 이 정도 대가로 첫 번째 줄이 뒤집힙니다.

왜 지도까지 키를 받지 않았나?

지도는 인증키가 필요 없는 것으로 골랐습니다. 카카오맵이나 네이버 지도를 쓰면 거기 키를 또 발급받아야 하거든요.

키가 두 개면 지켜야 할 비밀도 두 개고, 설명해야 할 발급 절차도 두 번입니다. 기능을 늘리는 결정이 아니라 지켜야 할 것을 늘리는 결정이라 뺐어요.

AI는 이 구조를 먼저 제안했나?

아니요. 제가 지시했습니다. 다만 코드를 쓰기 시작하기 전에 네 가지를 되묻긴 했어요.

첫 번째는 서버를 어떤 언어로 세울지였고, 두 번째는 주소를 좌표로 바꾸는 처리를 어디서 할지였습니다. 세 번째는 업종을 대분류까지만 셀지 중분류까지 내려갈지였고요.

네 번째가 의미 있습니다. 승인된 키를 이미 갖고 있느냐를 물었거든요. 만들어도 못 돌리는 상태를 먼저 확인한 겁니다.

그런데 키의 보관 위치는 끝내 묻지 않았습니다. 제가 먼저 못 박지 않았다면 화면 쪽 코드에 그대로 들어갔을 거예요.

하지만 내가 만든 첫 버전은 틀린 숫자를 보여줬다

구조를 잘 잡았으니 됐다고 생각했습니다. 화면도 멀쩡했어요.

그런데 화면 위쪽 숫자와 아래쪽 표의 합이 615개 어긋났습니다.

책임은 API에도 AI에도 없었어요. 응답을 6,000건까지만 모으도록 제가 제한을 걸어뒀거든요. 위쪽 값은 서버가 알려준 전체 건수를 그대로 옮기고, 아래쪽은 실제로 손에 들어온 것만 셌습니다. 한 화면에 출처가 다른 두 값이 올라간 겁니다.

고칠 때 제한을 없애는 데서 멈추지 않았습니다. 출처가 다른 값을 나란히 두지 않는 쪽으로 화면을 다시 짰어요.

615개가 순위를 뒤집었다

상한에 걸린 상태에서 업종 3위는 교육이었습니다. 전량을 받아 세니 소매로 바뀌었어요.

오차가 커진 게 아니라 읽어낸 답이 달라진 겁니다. 이 화면을 보고 "이 동네는 교육 수요가 있다"고 판단했다면 근거가 통째로 잘못된 거예요.

검산은 어디에 넣어야 하나?

위쪽 값과 아래쪽 합을 화면에서 나란히 보여주면 됩니다. 응답을 다 받아오기 부담스러운 상황도 있으니, 그럴 땐 "일부만 집계했다"를 화면에 적어두는 쪽이 맞습니다.

AI가 짠 코드는 문법 오류를 거의 안 냅니다. 그래서 틀리면 에러가 아니라 그럴듯한 숫자로 틀립니다. 검산 자리를 코드가 아니라 화면에 만들어둬야 하는 이유예요.

같은 조건으로 잰다는 게 왜 중요한가?

강남역과 방배역을 각각 반경 500m로 재면 6,615개와 1,315개가 나옵니다. 반경과 데이터 기준연월을 똑같이 맞췄기 때문에 이 둘은 비교가 됩니다.

여기서 판정이 하나 나옵니다. 강남역 상위 업종은 경영 컨설팅 842개, 광고 대행 433개, 세무사 344개예요. 방배역은 부동산 중개 66개가 1위이고 백반집·미용실·카페가 뒤를 잇습니다.

한쪽은 일하러 오는 동네, 한쪽은 사는 동네입니다. 이 판정의 근거는 제 인상이 아니라 같은 축으로 잰 수치입니다.

이 데이터로 하면 안 되는 것은?

정확한 개수를 세는 일에는 부적합합니다. 자료 시점이 두 달 뒤처져 있고, 같은 좌표를 반복해서 불러도 결과가 0.5% 안쪽에서 움직이거든요.

문 닫은 지 얼마 안 된 곳이 목록에 남아 있기도 합니다. 그래서 "몇 개인가"보다 **"어느 쪽이 더 많은가"**를 묻는 데 쓰는 편이 안전합니다.

이걸로 서비스를 만들어 수익을 내려 한다면 위치정보 관련 신고 절차를 따로 확인해야 합니다. 혼자 연습하는 단계에서는 해당되지 않습니다.

응답 형식이 API마다 다른데 왜 문제가 안 되나?

형식이 다른 건 맞습니다. 실거래가는 XML로 오고 관광 정보는 JSON으로 옵니다. 다만 이건 사람이 외울 문제가 아니라 문서 주소를 넘겨주면 되는 문제예요.

파서를 직접 고르지 않는 이유

제가 형식을 지정하면 그 API에만 맞는 코드가 됩니다. 대신 "이 문서 주소를 보고 응답 형태에 맞춰 달라"고 넘기면, 갈아끼울 때 같은 요청을 한 번 더 하면 됩니다.

기상청 단기예보는 여기서 한 번 더 갈립니다. 응답이 TMP·POP·SKY 같은 코드로 오거든요. 카테고리 코드표를 같이 주지 않으면 값을 받아도 해석을 못 합니다.

이 구조를 다른 데이터로 옮기면 무엇이 바뀌나?

바뀌는 건 셋뿐입니다. 부르는 주소, 넣는 조건의 이름, 받아오는 형태. 나머지 골격은 그대로 갑니다.

상권 도구를 실거래가로 바꿔본 결과

주소를 storeListInRadius에서 getRTMSDataSvcAptTrade로 바꾸고, 조건을 좌표·반경 대신 지역코드와 연월로 넣었습니다. 서버 구조와 키를 다루는 방식은 손대지 않았어요.

그래서 이 글이 정리하는 건 상권 데이터 활용법이 아닙니다. 데이터를 내 도구에 붙이는 자리가 어디인지에 관한 이야기예요. 상권은 제가 고른 재료일 뿐입니다.

정리 — 네 가지 결정, 네 가지 확인

이 도구를 만들며 내린 결정이 넷이고, 각각에 대응하는 확인이 하나씩 있습니다.

  1. 키를 서버에만 둔다 → 페이지 소스 보기로 키가 안 보이는지 확인
  2. 키가 필요한 외부 서비스를 늘리지 않는다 → 발급받아야 할 키 개수를 세어보기
  3. 수집 상한을 걸지 않는다 → 총계와 분류 합계가 같은지 화면에서 비교
  4. 같은 조건으로만 비교한다 → 반경과 기준연월이 같은지 확인

네 번째가 제일 자주 깨집니다. 조건이 다른 두 수치를 나란히 놓는 순간, 그래프는 그럴듯해지고 결론은 틀리거든요.

만드신 도구에서 지금 화면에 떠 있는 숫자 두 개는 같은 출처인가요? 그것부터 맞춰보시면 좋겠습니다.

새 글 알림

이런 작업 기록, 계속 받아보실래요?

네이버 블로그 이웃추가하면 새 글이 피드로 바로 떠요. 광고 없이 진짜 이야기만.