바이브 코딩
실시간 데이터 대시보드 완전 정복
API로 살아있는 데이터를 수집하고, Gemini와 함께 자동 갱신되는 대시보드를 만들어 봅니다.
실시간 데이터와 API 개념 이해
- "살아있는 데이터"를 다루는 법 — 외부 데이터를 자동으로 가져와 연결하기
- API 개념 — 복잡한 코드 없이 Gemini를 활용하여 데이터 주고받기
- 최종 목표 — 국토교통부 공공 API로 자동 갱신되는 아파트 실거래가 대시보드 구축
- Google 계정
- Gemini 접속 확인 (gemini.google.com)
- Chrome 브라우저 최신 버전
- 공공데이터포털(data.go.kr) 회원가입
지난 시간 복습: 바이브 코딩(Vibe Coding)
정의: "AI에게 일상 언어로 의도를 말하면, AI가 그 분위기에 맞게 구현하는 개발 방식"
| 구분 | 기존 방식 | 바이브 코딩 |
|---|---|---|
| 중심 | 코드 중심 | 의도 중심 |
| 순서 | 문법/구현 먼저 | 목표/느낌 먼저 |
| 설계 | 사람이 전부 | AI와 공동 설계 |
프롬프트 3요소
- (배경) 데이터 구조 — 지금 뭐가 있는지?
- (업무) 내가 원하는 결과 — 어떤 일을 하게 할건지?
- (결과) 어디에 쓸건지 — 어떤 결과물을 원하는지?
정적 데이터 vs 동적 데이터
| 구분 | 정적 데이터 (죽어있는) | 동적 데이터 (살아있는) |
|---|---|---|
| 예시 | CSV 파일, 엑셀 파일 | 실시간 주식 시세, 날씨 |
| 업데이트 | 수동 (사람이 직접) | 자동 (시스템이 알아서) |
| 최신성 | 다운받은 시점에서 멈춤 | 항상 최신 |
| 대시보드 | 한 번 보고 끝 | 매일 열어볼 가치 |
API란 무엇인가?
"API = 식당의 웨이터" — 주방에 직접 들어가지 않아도(개발을 몰라도), 웨이터에게 말하면(API를 호출하면) 원하는 데이터를 받을 수 있습니다!
API 키(Key)란?
- API를 쓰기 위한 인증 코드 (열쇠)
- 공공데이터포털에서 무료 발급
- 다른 사람과 공유 금지 (비밀번호처럼 관리)
오늘의 여정 미리보기
오늘도 코딩을 배우는 게 아닙니다. Gemini에게 시키고, 복사-붙여넣기하고, 결과를 다듬는 것. 파트1과 똑같은 방식이에요. 다만 데이터가 "살아있는" 것으로 바뀔 뿐!
공공 데이터 API로 구글 시트에 데이터 수집
- 공공데이터포털에서 무료 API 키 발급
- API 요청 구조 — 인증키 + 지역코드 + 거래년월
- Gemini + Apps Script — 프롬프트로 코드 생성 → 복붙 → 실행
Step 01: 공공데이터포털 가입 & API 키 발급
| 종류 | 모양 | 설명 |
|---|---|---|
| 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에게 코드 요청
완성 코드 예시
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단계: 실행하기
Step 04: 에러가 났다면? (트러블슈팅)
| 증상 | 원인 | 해결 |
|---|---|---|
| SERVICE_KEY_IS_NOT_REGISTERED_ERROR | API 키 미승인 | 공공데이터포털 승인 상태 확인 |
| Exception: Bad request | 엔드포인트 URL 잘못됨 | 아래 정확한 URL 확인 |
| 시트에 데이터가 안 채워짐 | API 응답 파싱 오류 | Gemini에게 에러 로그 보여주기 |
| Exception: Request failed | API 서버 일시 장애 | 잠시 후 재시도 |
잘못된 URL: http://openapi.molit.go.kr/...
정확한 URL: https://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/...
공공데이터포털 → 마이페이지 → 해당 API 상세보기 → End Point 항목에서 확인
| 학습 내용 | 한 줄 요약 |
|---|---|
| 공공데이터포털 | 정부 데이터를 무료 API로 제공하는 사이트 |
| API 키 발급 | 회원가입 → 데이터 신청 → 인증키 복사 |
| API 요청 구조 | 인증키 + 지역코드 + 거래년월 → 데이터 수신 |
| Gemini + Apps Script | 프롬프트로 코드 생성 → 복붙 → 실행 → 시트에 데이터 |
| 에러 대처 | 에러 메시지를 Gemini에게 보여주고 수정 요청 |
Gemini로 실시간 부동산 대시보드 만들기
- 웹에 게시 — 구글 시트 데이터를 외부 접근 가능한 CSV URL로 공개
- GitHub Pages — HTML 파일을 무료로 웹사이트로 배포
- 프롬프트 반복 — 색상 → 아이콘 → 테이블 → 필터 순서로 단계적 개선
Step 01: 구글 시트 데이터를 "웹에 게시"하기
| 기능 | 용도 | 특징 |
|---|---|---|
| 링크 공유 | 사람이 시트 직접 열기 | 구글 시트 UI로 보임 |
| 웹에 게시 | 프로그램/코드에서 데이터 가져오기 | CSV/HTML 등 원시 데이터 제공 |
Step 02: GitHub Pages 준비하기
apartment-dashboard, Public 선택, README 체크배포 URL: https://[사용자명].github.io/[레포지토리명]/
Step 03: Gemini에게 대시보드 만들어달라고 하기
GitHub에 올려서 확인하기
- 레포지토리에서 "Add File" → "Create new file"
- 파일명: index.html (필수)
- Gemini에서 생성된 코드 복사해서 붙여넣기
- Commit Changes 클릭
- 1~2분 후 배포 URL 접속
Step 04: 프롬프트 반복으로 대시보드 다듬기
Step 05: 완성 코드
<!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 탭 → 빨간 글씨 에러 복사
| 배운 것 | 한 줄 정리 |
|---|---|
| 웹에 게시 | 구글 시트를 외부 접근 가능 URL로 공개 |
| GitHub Pages | HTML 파일 무료 웹사이트 배포 서비스 |
| 프롬프트 반복 | 색상 → 아이콘 → 테이블 → 필터 단계적 개선 |
| 에러 대처 | 브라우저 콘솔 에러를 Gemini에게 보여주고 수정 |
자동 갱신 설정 및 카카오톡 리포트
- 트리거 설정 — Apps Script 자동 실행으로 수동 클릭 제거
- 카카오톡 리포트 — 매일 아침 요약 리포트 자동 전송
- 자동화 완성 — API → 시트 → 대시보드 전체 자동화
Step 01: 자동 실행 트리거 설정하기
트리거(Trigger) = "매일 아침 7시에 이 스크립트를 실행해줘"라는 알람 시계
| 항목 | 설정값 |
|---|---|
| 실행할 함수 | fetchApartmentData |
| 실행할 배포 | Head |
| 이벤트 소스 | 시간 기반 |
| 트리거 기반 시간 유형 | 일 단위 타이머 |
| 시간 | 오전 6시~7시 |
| 데이터 | 추천 주기 | 이유 |
|---|---|---|
| 아파트 실거래가 | 매일 1회 | 신규 거래 매일 등록 |
| 날씨 정보 | 매시간 | 시간마다 변함 |
| 주식 데이터 | 1~5분 | 실시간 변동 (비용 주의) |
공공데이터포털 API: 하루 약 1,000건 제한
- 매일 1회 호출 → 안전
- 매분 호출 → 하루 1,440건 → 제한 초과!
Step 02: 카카오톡으로 일일 리포트 받기
대시보드: 내가 보러 가야 함 → 카톡 리포트: 데이터가 나한테 찾아옴!
카카오 API 설정 프로세스
https://localhost.com 등록 → 동의항목에서 "카카오톡 메시지" 선택 동의code= 뒤의 값 복사https://kauth.kakao.com/oauth/authorize?client_id=여기에_REST_API_키&redirect_uri=https://localhost.com&response_type=code
카카오톡 리포트 코드
// 전역 설정
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());
}
}실행 순서
자동 실행 트리거 추가
| 트리거 | 함수 | 시간 |
|---|---|---|
| 데이터 수집 | fetchApartmentData | 오전 6~7시 |
| 카톡 리포트 | sendKakaoReport | 오전 8~9시 |
카카오 인증 정보는 약 2개월마다 갱신 필요:
- 브라우저에서 인가 코드 URL 재접속
- 새 코드 복사
- 코드에서 인가 코드만 교체
setupKakaoToken다시 실행
전체 정리
| 개념 | 정의 |
|---|---|
| API | 외부 데이터를 자동으로 가져오는 약속된 창구 |
| API 키 | API 사용 인증 열쇠 (무료 발급) |
| Apps Script | 구글 서비스를 코드로 조종 (Gemini 대행) |
| 웹에 게시 | 구글 시트를 외부 접근 가능 URL로 공개 |
| GitHub Pages | HTML 파일 무료 배포 서비스 |
| 트리거 | 정해진 시간에 스크립트 자동 실행 |
| 카카오톡 리포트 | 데이터 요약을 카톡으로 자동 전송 |
파트1 vs 현재 수준
| 항목 | 파트1 | 현재 |
|---|---|---|
| 데이터 | CSV 수동 | API 자동 수집 |
| 업데이트 | 사람 직접 | 트리거 자동 |
| 대시보드 | AI Studio | Gemini + 커스텀 HTML |
| 배포 | 로컬 | GitHub Pages (전 세계) |
| 결과 | 스냅샷 | 살아있는 대시보드 |
| 데이터 | API | 활용 |
|---|---|---|
| 날씨 | 기상청 날씨 API | 지역별 날씨 대시보드 |
| 공공자전거 | 서울 따릉이 API | 실시간 현황 |
| 환율 | 한국수출입은행 | 일별 환율 모니터링 |
| 상가업소 | 공공데이터포털 | 상권 분석 대시보드 |
주식 대시보드 만들기 (GOOGLEFINANCE)
- 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: 대시보드 프롬프트 모음
Step 03: 배포하기
HTML 파일을 더블클릭으로 열면 데이터가 안 뜹 다! 대시보드가 구글 시트에서 데이터를 가져오려면, 웹 서버에서 실행되어야 해요. → GitHub Pages 사용
| 데이터 | 함수 | 대시보드 |
|---|---|---|
| 환율 | =GOOGLEFINANCE("CURRENCY:USDKRW") | 달러/원 환율 추이 |
| 미국 주식 | =GOOGLEFINANCE("AAPL") | 애플, 테슬라 주가 |
| 암호화폐 | =GOOGLEFINANCE("BTCUSD") | 비트코인 시세 |
- 데이터가 20분 정도 지연됨 (실시간 아님)
- 간혹 빈 값이 나올 수 있음
- 10년 이상 오래된 데이터는 미지원
- 한국 주식:
KRX:접두사 필수 - 미국 주식: 티커만 입력 (예:
"AAPL")
YouTube 댓글 분석 대시보드
- 동일한 패턴 — "데이터 소스만 다르고, 방법은 동일합니다"
- YouTube Data API v3 — 하루 10,000 유닛 무료 할당량
- 4단계 — API 키 발급 → Apps Script 수집 → Gemini 대시보드 → 배포
Step 01: YouTube Data API 키 발급
Step 02: 구글 시트에 댓글 수집
실행 절차
- Apps Script 코드 붙여넣기
- 함수 선택 후 ▶ 실행
- 유튜브 영상 URL 입력
- 데이터 자동 수집 → 파일 → 공유 → 웹에 게시 (CSV)
Step 03: Gemini로 분석 대시보드 만들기
개선 아이디어
- 댓글 길이별 분포 파이 차트 추가
- 활발한 시간대 하이라이트
- 영상 이름 입력창 추가
- 다크 테마 적용
"API 키 발급하고 → Gemini에게 시키고 → 복붙하고 → 실행"
이 패턴으로 Instagram, OpenWeatherMap, NewsAPI 등 다양한 API를 연동할 수 있습니다.