실시간 데이터 대시보드 완전 정복

바이브 코딩
실시간 데이터 대시보드 완전 정복

API로 살아있는 데이터를 수집하고, Gemini와 함께 자동 갱신되는 대시보드를 만들어 봅니다.

공공데이터 API Google Apps Script Gemini AI GitHub Pages 카카오톡 자동화 GOOGLEFINANCE
01

실시간 데이터와 API 개념 이해

PART 01 — API가 뭔지 이해하기
핵심 개념 요약
  • "살아있는 데이터"를 다루는 법 — 외부 데이터를 자동으로 가져와 연결하기
  • API 개념 — 복잡한 코드 없이 Gemini를 활용하여 데이터 주고받기
  • 최종 목표 — 국토교통부 공공 API로 자동 갱신되는 아파트 실거래가 대시보드 구축
사전 준비 사항
  • Google 계정
  • Gemini 접속 확인 (gemini.google.com)
  • Chrome 브라우저 최신 버전
  • 공공데이터포털(data.go.kr) 회원가입

지난 시간 복습: 바이브 코딩(Vibe Coding)

정의: "AI에게 일상 언어로 의도를 말하면, AI가 그 분위기에 맞게 구현하는 개발 방식"

구분기존 방식바이브 코딩
중심코드 중심의도 중심
순서문법/구현 먼저목표/느낌 먼저
설계사람이 전부AI와 공동 설계

프롬프트 3요소

  1. (배경) 데이터 구조 — 지금 뭐가 있는지?
  2. (업무) 내가 원하는 결과 — 어떤 일을 하게 할건지?
  3. (결과) 어디에 쓸건지 — 어떤 결과물을 원하는지?

정적 데이터 vs 동적 데이터

구분정적 데이터 (죽어있는)동적 데이터 (살아있는)
예시CSV 파일, 엑셀 파일실시간 주식 시세, 날씨
업데이트수동 (사람이 직접)자동 (시스템이 알아서)
최신성다운받은 시점에서 멈춤항상 최신
대시보드한 번 보고 끝매일 열어볼 가치

API란 무엇인가?

"API = 식당의 웨이터" — 주방에 직접 들어가지 않아도(개발을 몰라도), 웨이터에게 말하면(API를 호출하면) 원하는 데이터를 받을 수 있습니다!

우리의 Apps Script
API 호출 (요청)
국토교통부 서버
JSON 데이터 응답
구글 시트 자동 저장

API 키(Key)란?

  • API를 쓰기 위한 인증 코드 (열쇠)
  • 공공데이터포털에서 무료 발급
  • 다른 사람과 공유 금지 (비밀번호처럼 관리)

오늘의 여정 미리보기

PART 01: API 이해
PART 02: API 키 발급 + 데이터 수집
PART 03: HTML 대시보드 + 배포
PART 04: 자동 갱신 + 마무리
핵심 메시지

오늘도 코딩을 배우는 게 아닙니다. Gemini에게 시키고, 복사-붙여넣기하고, 결과를 다듬는 것. 파트1과 똑같은 방식이에요. 다만 데이터가 "살아있는" 것으로 바뀔 뿐!

02

공공 데이터 API로 구글 시트에 데이터 수집

PART 02 — API 키 발급부터 데이터 자동 수집까지
핵심 개념 요약
  • 공공데이터포털에서 무료 API 키 발급
  • API 요청 구조 — 인증키 + 지역코드 + 거래년월
  • Gemini + Apps Script — 프롬프트로 코드 생성 → 복붙 → 실행

Step 01: 공공데이터포털 가입 & API 키 발급

1
회원가입 — data.go.kr 접속 → 우측 상단 회원가입 → 일반회원 선택
2
API 활용 신청 — "국토교통부 아파트 실거래" 검색 → 오픈 API 탭 → "국토교통부_아파트매매 실거래 상세 자료" 선택 → 활용신청 (활용 목적: "학습 및 개인 프로젝트")
3
API 키 확인 — 마이페이지 → 활용신청 현황 → 상세보기 → Encoding 키(일반 인증키) 복사
종류모양설명
Encoding (인코딩)AbCd%2BEfGh%3D%3D특수문자가 코드로 변환된 버전 (권장)
Decoding (디코딩)AbCd+EfGh==원래 모양 그대로인 버전

Step 02: API 구조 이해하기

항목설명예시
인증키 (serviceKey)발급받은 API 키AbCdEf123...
지역코드 (LAWD_CD)법정동 코드 5자리11680 (서울 강남구)
거래년월 (DEAL_YMD)조회할 연월202501 (2025년 1월)

Step 03: Gemini에게 Apps Script 만들어달라고 하기

1단계: 구글 시트 준비

Google Sheets에서 새 스프레드시트 생성 (sheet.new) → 시트 이름을 "실거래가"로 변경 → 1행에 헤더 입력:

헤더
거래일자 | 아파트명 | 법정동 | 전용면적(㎡) | 층 | 거래금액(만원) | 건축년도

2단계: Apps Script 편집기 열기

메뉴에서 확장 프로그램 → Apps Script 클릭

3단계: Gemini에게 코드 요청

Gemini 프롬프트
국토교통부 아파트매매 실거래 상세 자료 API를 호출해서 구글 시트에 데이터를 저장하는 Google Apps Script를 만들어줘. - API 서비스키: (여기에 본인 키 붙여넣기) - 지역코드: 11680 (서울 강남구) - 거래년월: 오늘 기준으로 최근 3개월치를 가져와줘 - 구글 시트 이름: "실거래가" - 헤더: 거래일자, 아파트명, 법정동, 전용면적, 층, 거래금액, 건축년도

완성 코드 예시

JavaScript — Google Apps Script
function fetchApartmentData() {
  // 설정 — 본인 키와 조건에 맞게 수정하세요
  var SERVICE_KEY = '여기에 키를 넣어주세요!'; // Encoding 키
  var LAWD_CD = '11680';     // 강남구
  var SHEET_NAME = '실거래가';

  // 오늘 기준 최근 3개월 자동 계산
  var today = new Date();
  var months = [];
  for (var i = 2; i >= 0; i--) {
    var d = new Date(today.getFullYear(), today.getMonth() - i, 1);
    months.push(d.getFullYear() + ('0' + (d.getMonth() + 1)).slice(-2));
  }

  // 시트 준비
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName(SHEET_NAME);
  if (!sheet) sheet = ss.insertSheet(SHEET_NAME);
  sheet.clear();

  var headers = ['거래일자', '아파트명', '법정동', '전용면적(㎡)',
                 '층', '거래금액(만원)', '건축년도'];
  sheet.appendRow(headers);

  var allRows = [];

  // 3개월 반복
  for (var m = 0; m < months.length; m++) {
    var url =
      'https://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/getRTMSDataSvcAptTradeDev'
      + '?serviceKey=' + SERVICE_KEY
      + '&LAWD_CD=' + LAWD_CD
      + '&DEAL_YMD=' + months[m]
      + '&numOfRows=1000'
      + '&pageNo=1';

    try {
      var response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
      var statusCode = response.getResponseCode();
      var content = response.getContentText();

      Logger.log(months[m] + ' HTTP 상태: ' + statusCode);
      if (statusCode !== 200) continue;

      var document = XmlService.parse(content);
      var root = document.getRootElement();
      var body = root.getChild('body');
      if (!body || !body.getChild('items')) continue;

      var items = body.getChild('items').getChildren('item');

      items.forEach(function(item) {
        allRows.push([
          (item.getChildText('dealYear') || '') + '-' +
          (item.getChildText('dealMonth') || '').padStart(2, '0') + '-' +
          (item.getChildText('dealDay') || '').padStart(2, '0'),
          item.getChildText('aptNm') || '',
          (item.getChildText('umdNm') || '').trim(),
          item.getChildText('excluUseAr') || '',
          item.getChildText('floor') || '',
          (item.getChildText('dealAmount') || '').trim().replace(/,/g, ''),
          item.getChildText('buildYear') || ''
        ]);
      });

    } catch (e) {
      Logger.log(months[m] + ' 에러: ' + e.toString());
    }
  }

  // 시트에 쓰기
  if (allRows.length > 0) {
    sheet.getRange(2, 1, allRows.length, headers.length)
         .setValues(allRows);
  }

  SpreadsheetApp.getUi().alert(
    allRows.length + '건의 데이터를 가져왔습니다! (' + months.join(', ') + ')'
  );
}

4~6단계: 실행하기

4
Apps Script 편집기에 코드 붙여넣기 → Ctrl+S 저장
5
▶ 실행 버튼 클릭 → 첫 실행 시 권한 승인 (고급 → 안전하지 않음으로 이동 → 허용)
6
구글 시트로 돌아가면 데이터가 자동 입력되어 있음!

Step 04: 에러가 났다면? (트러블슈팅)

증상원인해결
SERVICE_KEY_IS_NOT_REGISTERED_ERRORAPI 키 미승인공공데이터포털 승인 상태 확인
Exception: Bad request엔드포인트 URL 잘못됨아래 정확한 URL 확인
시트에 데이터가 안 채워짐API 응답 파싱 오류Gemini에게 에러 로그 보여주기
Exception: Request failedAPI 서버 일시 장애잠시 후 재시도
엔드포인트 주의

잘못된 URL: http://openapi.molit.go.kr/...
정확한 URL: https://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/...

공공데이터포털 → 마이페이지 → 해당 API 상세보기 → End Point 항목에서 확인

에러 해결 프롬프트
아래 Google Apps Script를 실행했더니 에러가 나와. [에러 메시지] (여기에 에러 메시지 붙여넣기) [코드] (여기에 전체 코드 붙여넣기) 원인을 찾아서 수정된 전체 코드를 다시 작성해줘.
PART 02 정리
학습 내용한 줄 요약
공공데이터포털정부 데이터를 무료 API로 제공하는 사이트
API 키 발급회원가입 → 데이터 신청 → 인증키 복사
API 요청 구조인증키 + 지역코드 + 거래년월 → 데이터 수신
Gemini + Apps Script프롬프트로 코드 생성 → 복붙 → 실행 → 시트에 데이터
에러 대처에러 메시지를 Gemini에게 보여주고 수정 요청
03

Gemini로 실시간 부동산 대시보드 만들기

PART 03 — 데이터를 시각화 대시보드로 변환
핵심 개념 요약
  • 웹에 게시 — 구글 시트 데이터를 외부 접근 가능한 CSV URL로 공개
  • GitHub Pages — HTML 파일을 무료로 웹사이트로 배포
  • 프롬프트 반복 — 색상 → 아이콘 → 테이블 → 필터 순서로 단계적 개선

Step 01: 구글 시트 데이터를 "웹에 게시"하기

1
구글 시트에서 파일 → 공유 → 웹에 게시 클릭
2
게시 형식을 "쉼표로 구분된 값(.csv)"로 선택
3
나오는 URL 복사 (메모장에 보관)
기능용도특징
링크 공유사람이 시트 직접 열기구글 시트 UI로 보임
웹에 게시프로그램/코드에서 데이터 가져오기CSV/HTML 등 원시 데이터 제공

Step 02: GitHub Pages 준비하기

1
GitHub 가입 — github.com 접속 → Sign up
2
새 저장소 만들기 — Repository name: apartment-dashboard, Public 선택, README 체크
3
GitHub Pages 활성화 — Settings → Pages → Source: "Deploy from a branch" → Branch: main /(root) → Save
결과

배포 URL: https://[사용자명].github.io/[레포지토리명]/

Step 03: Gemini에게 대시보드 만들어달라고 하기

첫 번째 프롬프트 (Canvas 기능 활성화 필수)
아래 구글 시트 CSV URL에서 데이터를 불러와서 아파트 실거래가 대시보드를 HTML로 만들어줘. [데이터 URL] (웹에 게시 URL 붙여넣기) [데이터 구조] - 거래일자, 아파트명, 법정동, 전용면적(㎡), 층, 거래금액(만원), 건축년도 [대시보드에 포함할 것] - 제목: "우리 동네 아파트 실거래가 대시보드" - 월별 평균 거래가격 차트 - 전체 거래 건수 - 평균/최고/최저 거래가격 요약 - 거래 목록 테이블 단일 HTML 파일로 만들어줘. 차트 라이브러리는 CDN으로 불러와줘.

GitHub에 올려서 확인하기

  1. 레포지토리에서 "Add File" → "Create new file"
  2. 파일명: index.html (필수)
  3. Gemini에서 생성된 코드 복사해서 붙여넣기
  4. Commit Changes 클릭
  5. 1~2분 후 배포 URL 접속

Step 04: 프롬프트 반복으로 대시보드 다듬기

기본 프롬프트
색상 변경
아이콘 추가
테이블 꾸미기
필터 추가
랭킹 추가
개선 1: 색상 바꾸기
대시보드 전체 색상 톤을 바꿔줘. - 메인 색상을 파란색 대신 초록색 계열로 - 카드 숫자 색상도 맞춰서 변경 - 차트 색상도 초록 계열로 통일
개선 2: 카드에 아이콘 넣기
요약 카드에 이모지 아이콘을 추가해줘. - 전체 거래 건수: 🏠 - 평균 거래가격: 💰 - 최고 거래가격: 📈 - 최저 거래가격: 📉
개선 3: 테이블 꾸미기
거래 목록 테이블을 좀 더 보기 좋게 해줘. - 행에 마우스 올리면 배경색 바뀌게 - 거래금액이 5억 이상이면 빨간색 글씨로 강조 - 짝수/홀수 행 배경색 다르게 (줄무늬)
개선 4: 동네 검색 필터 추가
대시보드에 동네(법정동) 필터 기능을 추가해줘. - 상단에 법정동 드롭다운 메뉴 - 드롭다운 옵션은 데이터에서 자동으로 가져오기 - "전체"를 기본값으로, 선택 시 차트/테이블/카드 모두 변경
개선 5: 아파트 가격 랭킹
대시보드에 "우리 동네 비싼 아파트 TOP 5" 섹션 추가 - 거래금액 기준 내림차순 상위 5개 - 1위 금색, 2위 은색, 3위 동색 강조

Step 05: 완성 코드

HTML — 아파트 실거래가 대시보드
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>우리 동네 아파트 실거래가 대시보드</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>
  <style>
    :root {
      --primary-color: #059669;
      --primary-light: rgba(5, 150, 105, 0.1);
      --bg-color: #f8fafc;
      --card-bg: #ffffff;
      --text-main: #1e293b;
      --highlight-red: #dc2626;
    }
    body {
      font-family: 'Pretendard', -apple-system, sans-serif;
      background: var(--bg-color); color: var(--text-main);
      margin: 0; padding: 20px;
    }
    .container { max-width: 1200px; margin: 0 auto; }
    header { text-align: center; margin-bottom: 30px; }
    h1 { font-size: 2rem; color: var(--primary-color); }
    .filter-section {
      background: var(--card-bg); padding: 15px 20px;
      border-radius: 12px; box-shadow: 0 4px 6px -1px rgb(0 0 0/0.1);
      margin-bottom: 20px; display: flex; align-items: center; gap: 12px;
    }
    .summary-container {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 20px; margin-bottom: 30px;
    }
    .card {
      background: var(--card-bg); padding: 20px; border-radius: 12px;
      box-shadow: 0 4px 6px -1px rgb(0 0 0/0.1); text-align: center;
      border-top: 4px solid var(--primary-color); transition: transform 0.2s;
    }
    .card:hover { transform: translateY(-5px); }
    .card .label { font-size: 1rem; color: #64748b; margin-bottom: 10px; }
    .card .value { font-size: 1.6rem; font-weight: bold; color: var(--primary-color); }
    .chart-section, .ranking-section, .table-section {
      background: var(--card-bg); border-radius: 12px;
      box-shadow: 0 4px 6px -1px rgb(0 0 0/0.1); margin-bottom: 30px;
      overflow: hidden;
    }
    .chart-section { padding: 20px; }
    .ranking-list { padding: 0; margin: 0; list-style: none; }
    .ranking-item {
      display: flex; align-items: center; padding: 14px 20px;
      border-bottom: 1px solid #f1f5f9;
    }
    .ranking-badge {
      width: 32px; height: 32px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font-weight: 800; font-size: 0.85rem; margin-right: 16px;
    }
    .ranking-badge.gold { background: #fef3c7; color: #b45309; }
    .ranking-badge.silver { background: #f1f5f9; color: #475569; }
    .ranking-badge.bronze { background: #fed7aa; color: #c2410c; }
    .ranking-badge.normal { background: #f1f5f9; color: #94a3b8; }
    table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
    th { background: #f1f5f9; padding: 18px 15px; border-bottom: 2px solid #e2e8f0; }
    td { padding: 15px; text-align: center; border-bottom: 1px solid #f1f5f9; }
    tbody tr:nth-child(even) { background: #fbfcfd; }
    tbody tr:hover { background: #f0fdf4 !important; }
    .price-high { color: var(--highlight-red) !important; font-weight: 800; }
    .price-normal { color: var(--primary-color); font-weight: 600; }
  </style>
</head>
<body>
  <div class="container">
    <header><h1>우리 동네 아파트 실거래가 대시보드</h1></header>
    <div id="loading">데이터를 분석하고 있습니다...</div>
    <div id="dashboard-content" style="display:none">
      <!-- 필터/카드/차트/랭킹/테이블 구조 -->
    </div>
  </div>
  <script>
    const CSV_URL = '여기에_웹에_게시_URL_붙여넣기';
    // ... (CSV 파싱, 요약 카드, 차트, 랭킹, 테이블 렌더링 로직)
    window.onload = initDashboard;
  </script>
</body>
</html>
에러 대처 핵심 원칙

에러 → Gemini에게 보여주기 → 수정 받기 → GitHub 올리기 → 확인

브라우저 콘솔 확인법: F12 (또는 Ctrl+Shift+I) → Console 탭 → 빨간 글씨 에러 복사

PART 03 정리
배운 것한 줄 정리
웹에 게시구글 시트를 외부 접근 가능 URL로 공개
GitHub PagesHTML 파일 무료 웹사이트 배포 서비스
프롬프트 반복색상 → 아이콘 → 테이블 → 필터 단계적 개선
에러 대처브라우저 콘솔 에러를 Gemini에게 보여주고 수정
04

자동 갱신 설정 및 카카오톡 리포트

PART 04 — 트리거 자동화 + 카카오톡 알림
핵심 개념 요약
  • 트리거 설정 — Apps Script 자동 실행으로 수동 클릭 제거
  • 카카오톡 리포트 — 매일 아침 요약 리포트 자동 전송
  • 자동화 완성 — API → 시트 → 대시보드 전체 자동화

Step 01: 자동 실행 트리거 설정하기

트리거(Trigger) = "매일 아침 7시에 이 스크립트를 실행해줘"라는 알람 시계

1
구글 시트 → 확장 프로그램 → Apps Script 이동
2
왼쪽 메뉴 ⏰ 트리거 (시계 아이콘) 클릭
3
"+ 트리거 추가" 버튼 클릭
항목설정값
실행할 함수fetchApartmentData
실행할 배포Head
이벤트 소스시간 기반
트리거 기반 시간 유형일 단위 타이머
시간오전 6시~7시
데이터추천 주기이유
아파트 실거래가매일 1회신규 거래 매일 등록
날씨 정보매시간시간마다 변함
주식 데이터1~5분실시간 변동 (비용 주의)
API 호출 제한 주의

공공데이터포털 API: 하루 약 1,000건 제한

  • 매일 1회 호출 → 안전
  • 매분 호출 → 하루 1,440건 → 제한 초과!

Step 02: 카카오톡으로 일일 리포트 받기

대시보드: 내가 보러 가야 함 → 카톡 리포트: 데이터가 나한테 찾아옴!

카카오 API 설정 프로세스

1
Kakao Developers 가입 & 앱 생성 — developers.kakao.com 접속 → 카카오 로그인 → 앱 생성
2
REST API 키 확인 — 앱 대시보드 → 플랫폼 키 → Default Rest API Key 복사
3
카카오 로그인 설정 — 카카오 로그인 활성화 ON → Redirect URI에 https://localhost.com 등록 → 동의항목에서 "카카오톡 메시지" 선택 동의
4
인가 코드 받기 — 브라우저 주소창에 아래 URL 입력 (REST API 키 교체) → 카카오 로그인 → 동의 → code= 뒤의 값 복사
URL
https://kauth.kakao.com/oauth/authorize?client_id=여기에_REST_API_키&redirect_uri=https://localhost.com&response_type=code
Gemini 프롬프트
Google Apps Script를 만들어줘. 두 개의 함수: setupKakaoToken (최초 1회)과 sendKakaoReport (매일 자동). [함수 1: setupKakaoToken] - REST API 키: (여기에 REST API 키) - 인가 코드: (여기에 인가 코드) - redirect_uri: https://localhost.com - 카카오 OAuth 토큰 발급 → PropertiesService에 저장 [함수 2: sendKakaoReport] - refresh_token으로 새 access_token 발급 - 구글 시트 "실거래가" 데이터 분석 - 총 거래 건수, 평균 거래금액(억 단위), 최고가, 최저가, 최다 거래 동 - 카카오톡 "나에게 보내기" API로 전송

카카오톡 리포트 코드

JavaScript — Google Apps Script (카카오톡)
// 전역 설정
const CLIENT_ID = '여기에 REST API 키를 입력';
const REDIRECT_URI = 'https://localhost.com';

/**
 * [함수 1] 최초 1회 실행: 인가 코드로 토큰 발급 및 저장
 */
function setupKakaoToken() {
  const AUTH_CODE = '여기에 인가코드를 입력';
  const url = 'https://kauth.kakao.com/oauth/token';

  const payload = {
    grant_type: 'authorization_code',
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    code: AUTH_CODE
  };

  try {
    const response = UrlFetchApp.fetch(url, { method: 'post', payload: payload });
    const result = JSON.parse(response.getContentText());

    const props = PropertiesService.getScriptProperties();
    props.setProperty('access_token', result.access_token);
    props.setProperty('refresh_token', result.refresh_token);

    console.log("✅ 토큰 발급 및 저장 성공!");
  } catch (e) {
    console.error("❌ 토큰 발급 실패: " + e.toString());
  }
}

/**
 * [함수 2] 매일 실행: 토큰 갱신 → 데이터 분석 → 메시지 전송
 */
function sendKakaoReport() {
  const props = PropertiesService.getScriptProperties();
  let refreshToken = props.getProperty('refresh_token');

  if (!refreshToken) {
    console.error("Refresh Token이 없습니다. setupKakaoToken을 먼저 실행하세요.");
    return;
  }

  // 1. 토큰 갱신
  const tokenUrl = 'https://kauth.kakao.com/oauth/token';
  const tokenPayload = {
    grant_type: 'refresh_token',
    client_id: CLIENT_ID,
    refresh_token: refreshToken
  };

  const tokenResponse = UrlFetchApp.fetch(tokenUrl, { method: 'post', payload: tokenPayload });
  const tokenResult = JSON.parse(tokenResponse.getContentText());

  const accessToken = tokenResult.access_token;
  props.setProperty('access_token', accessToken);
  if (tokenResult.refresh_token) {
    props.setProperty('refresh_token', tokenResult.refresh_token);
  }

  // 2. 구글 시트 데이터 분석
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("실거래가");
  const data = sheet.getDataRange().getValues();
  const rows = data.slice(1);

  if (rows.length === 0) return;

  let totalCount = rows.length;
  let totalPrice = 0;
  let maxDeal = rows[0];
  let minDeal = rows[0];
  let dongCounts = {};

  rows.forEach(row => {
    const [date, name, dong, area, floor, price, year] = row;
    totalPrice += price;
    if (price > maxDeal[5]) maxDeal = row;
    if (price < minDeal[5]) minDeal = row;
    dongCounts[dong] = (dongCounts[dong] || 0) + 1;
  });

  const avgPriceEok = (totalPrice / totalCount / 10000).toFixed(2);
  let mostFrequentDong = Object.keys(dongCounts)
    .reduce((a, b) => dongCounts[a] > dongCounts[b] ? a : b);

  // 3. 메시지 구성
  const message = `[🏠 실거래가 데일리 리포트]

✅ 요약
- 총 거래: ${totalCount}건
- 평균가: ${avgPriceEok}억
- 최다 거래 지역: ${mostFrequentDong}(${dongCounts[mostFrequentDong]}건)

🏆 최고가
- ${maxDeal[1]}
- ${maxDeal[5].toLocaleString()}만원 (${maxDeal[3]}㎡, ${maxDeal[4]}층)

📉 최저가
- ${minDeal[1]}
- ${minDeal[5].toLocaleString()}만원 (${minDeal[3]}㎡, ${minDeal[4]}층)`;

  // 4. 카카오톡 전송
  const sendUrl = 'https://kapi.kakao.com/v2/api/talk/memo/default/send';
  const templateObject = {
    object_type: 'text',
    text: message,
    link: { web_url: REDIRECT_URI, mobile_web_url: REDIRECT_URI },
    button_title: '시트 확인'
  };

  const sendOptions = {
    method: 'post',
    headers: { 'Authorization': 'Bearer ' + accessToken },
    payload: { template_object: JSON.stringify(templateObject) }
  };

  try {
    UrlFetchApp.fetch(sendUrl, sendOptions);
    console.log("🚀 리포트 전송 완료!");
  } catch (e) {
    console.error("전송 실패: " + e.toString());
  }
}

실행 순서

A
setupKakaoToken 선택 → ▶ 실행 (최초 1회만!)
B
sendKakaoReport 선택 → ▶ 실행 → 카카오톡 확인

자동 실행 트리거 추가

트리거함수시간
데이터 수집fetchApartmentData오전 6~7시
카톡 리포트sendKakaoReport오전 8~9시
카톡이 안 올 때

카카오 인증 정보는 약 2개월마다 갱신 필요:

  1. 브라우저에서 인가 코드 URL 재접속
  2. 새 코드 복사
  3. 코드에서 인가 코드만 교체
  4. setupKakaoToken 다시 실행

전체 정리

학습한 개념 요약
개념정의
API외부 데이터를 자동으로 가져오는 약속된 창구
API 키API 사용 인증 열쇠 (무료 발급)
Apps Script구글 서비스를 코드로 조종 (Gemini 대행)
웹에 게시구글 시트를 외부 접근 가능 URL로 공개
GitHub PagesHTML 파일 무료 배포 서비스
트리거정해진 시간에 스크립트 자동 실행
카카오톡 리포트데이터 요약을 카톡으로 자동 전송

파트1 vs 현재 수준

항목파트1현재
데이터CSV 수동API 자동 수집
업데이트사람 직접트리거 자동
대시보드AI StudioGemini + 커스텀 HTML
배포로컬GitHub Pages (전 세계)
결과스냅샷살아있는 대시보드
다른 데이터에도 적용 가능
데이터API활용
날씨기상청 날씨 API지역별 날씨 대시보드
공공자전거서울 따릉이 API실시간 현황
환율한국수출입은행일별 환율 모니터링
상가업소공공데이터포털상권 분석 대시보드
05

주식 대시보드 만들기 (GOOGLEFINANCE)

EXTRA 01 — API 키 없이 주식 데이터 대시보드
핵심 개념 요약
  • GOOGLEFINANCE 함수 — API 키 불필요, 구글 시트 함수 하나로 주식 데이터 수집
  • 동일한 패턴 — 구글 시트 → 웹에 게시 → Gemini로 대시보드 → GitHub Pages 배포
  • 다양한 대시보드 — 단일 종목, 종목 비교, 포트폴리오 관리

Step 01: GOOGLEFINANCE 함수

구글 시트 함수
=GOOGLEFINANCE("종목코드", "속성", 시작일, 종료일, 주기)

// 예시: 삼성전자 일별 종가
=GOOGLEFINANCE("KRX:005930", "price", DATE(2025,1,1), TODAY(), "DAILY")

주요 한국 종목코드

종목코드
삼성전자KRX:005930
SK하이닉스KRX:000660
현대자동차KRX:005380
카카오KRX:035720
네이버KRX:035420
LG에너지솔루션KRX:373220

주요 미국 종목코드

기업명티커함수 입력
애플AAPL"NASDAQ:AAPL"
마이크로소프트MSFT"NASDAQ:MSFT"
구글GOOGL"NASDAQ:GOOGL"
테슬라TSLA"NASDAQ:TSLA"
엔비디아NVDA"NASDAQ:NVDA"

유용한 함수들

구글 시트 함수 모음
// 종목명
=GOOGLEFINANCE(A2, "name")

// 현재가
=GOOGLEFINANCE(A2, "price")

// 등락률
=GOOGLEFINANCE(A2, "changepct")/100

// 환율 (달러/원)
=GOOGLEFINANCE("CURRENCY:USDKRW")

// 비트코인 시세
=GOOGLEFINANCE("BTCUSD")

// 특정 값만 추출
=INDEX(GOOGLEFINANCE("KRX:005930","price",DATE(2025,1,1),TODAY()), ,2)

Step 02: 대시보드 프롬프트 모음

대시보드 A: 단일 종목 주가 추이
아래 구글 시트 CSV URL에서 데이터를 불러와서 주식 가격 대시보드를 HTML로 만들어줘. [데이터 URL] (웹에 게시 URL) [데이터 구조] Date: 날짜, Close: 종가 [포함할 것] - 제목: "삼성전자 주가 대시보드" - 일별 종가 라인 차트, 현재가/최고가/최저가/평균가 카드 - 최근 30일 가격 변동률, 거래 데이터 테이블 단일 HTML 파일, 차트 CDN.
대시보드 B: 종목 비교
아래 구글 시트 CSV URL들에서 데이터를 불러와서 주식 비교 대시보드를 HTML로 만들어줘. [데이터 URL] 삼성전자: (URL 1) / SK하이닉스: (URL 2) [포함할 것] - "반도체 대장주 비교 대시보드" - 두 종목 주가를 하나의 라인 차트에 겹쳐서 비교 - 기간별 수익률 비교 (1개월, 3개월, 6개월) 단일 HTML 파일, 차트 CDN.
대시보드 C: 내 포트폴리오
포트폴리오 대시보드를 HTML로 만들어줘. | 종목명 | 매수가 | 보유수량 | | 삼성전자 | 72000 | 10 | | SK하이닉스 | 180000 | 5 | | 카카오 | 45000 | 20 | [포함할 것] - 총 투자금액, 현재 평가금액, 총 수익률 카드 - 종목별 비중 파이 차트 - 종목별 수익률 바 차트 (수익 초록, 손실 빨강) 색상: 네이비 + 골드 계열. 단일 HTML.

Step 03: 배포하기

중요

HTML 파일을 더블클릭으로 열면 데이터가 안 뜹 다! 대시보드가 구글 시트에서 데이터를 가져오려면, 웹 서버에서 실행되어야 해요. → GitHub Pages 사용

데이터함수대시보드
환율=GOOGLEFINANCE("CURRENCY:USDKRW")달러/원 환율 추이
미국 주식=GOOGLEFINANCE("AAPL")애플, 테슬라 주가
암호화폐=GOOGLEFINANCE("BTCUSD")비트코인 시세
GOOGLEFINANCE 주의사항
  • 데이터가 20분 정도 지연됨 (실시간 아님)
  • 간혹 빈 값이 나올 수 있음
  • 10년 이상 오래된 데이터는 미지원
  • 한국 주식: KRX: 접두사 필수
  • 미국 주식: 티커만 입력 (예: "AAPL")
06

YouTube 댓글 분석 대시보드

EXTRA 02 — YouTube Data API 활용
핵심 개념 요약
  • 동일한 패턴 — "데이터 소스만 다르고, 방법은 동일합니다"
  • YouTube Data API v3 — 하루 10,000 유닛 무료 할당량
  • 4단계 — API 키 발급 → Apps Script 수집 → Gemini 대시보드 → 배포

Step 01: YouTube Data API 키 발급

1
Google Cloud Console 접속 — console.cloud.google.com
2
새 프로젝트 생성
3
API 및 서비스 → 라이브러리 → "YouTube Data API v3" 검색 → 활성화
4
사용자 인증 정보에서 API 키 생성 → 키 복사

Step 02: 구글 시트에 댓글 수집

Gemini 프롬프트
YouTube Data API v3로 특정 영상의 댓글을 수집하는 Google Apps Script를 만들어줘. - 함수명: fetchYouTubeComments - API 키: (여기에 발급받은 키) - 영상 URL은 실행 시 입력받도록 - 수집 데이터: 작성자, 댓글내용, 좋아요, 작성일시, 답글수 - 페이지네이션으로 최대 100개씩 전체 댓글 수집 - 헤더 행 자동 생성 - 구글 시트에 자동 저장

실행 절차

  1. Apps Script 코드 붙여넣기
  2. 함수 선택 후 ▶ 실행
  3. 유튜브 영상 URL 입력
  4. 데이터 자동 수집 → 파일 → 공유 → 웹에 게시 (CSV)

Step 03: Gemini로 분석 대시보드 만들기

대시보드 프롬프트
아래 구글 시트 CSV URL에서 데이터를 불러와서 YouTube 댓글 분석 대시보드를 HTML로 만들어줘. [데이터 URL] (웹에 게시 URL) [데이터 구조] 작성자, 댓글내용, 좋아요, 작성일시, 답글수 [포함할 것] - 요약 카드: 총 댓글 수, 평균 좋아요, 총 답글 수, 좋아요 0인 비율 - 일별 댓글 추이 라인 차트 - 시간대별 댓글 분포 바 차트 - 좋아요 TOP 10 댓글 테이블 - 답글 많은 TOP 5 테이블 모던하고 깔끔한 디자인. Chart.js, PapaParse CDN 사용. 단일 HTML 파일.

개선 아이디어

  • 댓글 길이별 분포 파이 차트 추가
  • 활발한 시간대 하이라이트
  • 영상 이름 입력창 추가
  • 다크 테마 적용
핵심 패턴 정리

"API 키 발급하고 → Gemini에게 시키고 → 복붙하고 → 실행"

이 패턴으로 Instagram, OpenWeatherMap, NewsAPI 등 다양한 API를 연동할 수 있습니다.

API 키 발급
Gemini에게 코드 요청
구글 시트 수집
대시보드 생성
GitHub Pages 배포