소개
시도하고자 했던 것과 그 이유를 알려주세요.
Google Calendar는 미팅록을 Google Docs로 관리하기 쉽게 자동으로 연결 가능합니다. Google Docs는 협업에는 훌륭하지만, 지식 체계 구축에는 한계가 있다고 느낍니다. 무엇보다 내 데이터에 대한 주도권을 갖고 싶었습니다.
마크다운 파일로 문서를 관리하면 다음과 같은 장점이 있습니다:
완전한 데이터 소유권: 로컬 파일로 저장되어 플랫폼 종속성 없음
버전 관리 용이: Git으로 변경 이력 추적 가능
높은 이식성: 어떤 에디터에서든 열 수 있는 순수 텍 스트 파일
강력한 연결성: Obsidian의 백링크, 그래프 뷰로 지식 네트워크 구축
자유로운 커스터마이징: 플러그인과 CSS로 완전한 환경 제어
그래서 Google Calendar의 미팅 일정을 자동으로 감지하여 마크다운 파일로 미팅록을 생성하고, 이를 Obsidian에서 체계적으로 관리하는 시스템을 구축하기로 했습니다.
이 시스템의 목표는:
Google Calendar는 일정 관리 도구로만 사용
실제 미팅록은 마크다운으로 작성하여 데이터 주도권 확보
Obsidian을 통해 미팅록을 개인 지식 베이스와 연결
진행 방법
어떤 도구를 사용했고, 어떻게 활용하셨나요?
사용한 도구:
Google Apps Script (2개 프로젝트)
Google Calendar API
Google Drive API
Obsidian (Google Drive 동기화)
Obsidian URI Protocol
1단계: Calendar 이벤트 모니터링 및 노트 생성 스크립트
30분마다 실행되는 트리거를 설정하여 향후 2시간 내의 미팅을 감지하고 자동으로 노트를 생성합니다.
핵심 기능:
// 미팅 확인 및 처리
function checkUpcomingMeetings() {
const now = new Date();
const later = new Date(now.getTime() + CONFIG.LOOKAHEAD_HOURS * 60 * 60 * 1000);
const events = CalendarApp.getDefaultCalendar()
.getEvents(now, later);
events.forEach(event => {
// 참석자가 있고 아직 처리되지 않은 미팅만 처리
if (event.getGuestList().length > 0 && !isProcessed(event)) {
try {
createMeetingNotes(event);
} catch (error) {
console.error(`미팅 노트 생성 실패: ${event.getTitle()}`, error);
}
}
});
}
미팅록 템플릿 생성:
function generateNoteContent(event) {
// YAML frontmatter와 함께 구조화된 마크다운 생성
return `---
date: ${dateStr}
time: "${Utilities.formatDate(startTime, 'Asia/Seoul', 'HH:mm')}"
type: meeting
title: "${title}"
participants:
${guests.map(g => ` - ${g.getEmail()}`).join('\n')}
tags: [meeting, ${dateStr.substring(0,7)}]
---
# ${title}
## 📅 미팅 정보
- **날짜:** ${dateStr}
- **시간:** ${startTime} - ${endTime}
- **장소:** ${location}
## 👥 참석자
${guests.map(g => `- ${g.getGuestStatus() === 'YES' ? '✅' : '❓'} ${g.getName()}`).join('\n')}
## 📋 안건
${description}
## 📝 회의 내용
## 🎯 액션 아이템
- [ ]
## 💡 주요 결정사항
`;
}
2단계: Obsidian Deep Link 리다이렉트 서비스
Google Calendar는 보안상 obsidian:// 프로토콜을 직접 지원하지 않아, 중간 리다이렉트 서비스를 구현했습니다.
function doGet(e) {
const params = e.parameter;
const vault = params.vault || '';
const file = params.file || '';
const obsidianUri = `obsidian://open?vault=${encodeURIComponent(vault)}&file=${encodeURIComponent(file)}`;
// 사용자 친화적인 HTML 페이지 생성
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Obsidian 노트 열기</title>
<style>
.container {
background: white;
padding: 40px;
border-radius: 20px;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.btn-primary {
padding: 16px 32px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>미팅 노트 열기</h1>
<button onclick="openObsidian()" class="btn-primary">
📝 Obsidian에서 열기
</button>
</div>
<script>
function openObsidian() {
window.location.href = ${JSON.stringify(obsidianUri)};
}
// 페이지 로드 시 자동 시도
window.addEventListener('load', () => {
setTimeout(() => {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = ${JSON.stringify(obsidianUri)};
document.body.appendChild(iframe);
}, 500);
});
</script>
</body>
</html>
`;
return HtmlService.createHtmlOutput(html);
}
3단계: Calendar 이벤트에 링크 추가
생성된 노트의 링크를 Calendar 이벤트 설명에 자동 추가:
function updateEventWithLinks(event, file, dateStr, fileName) {
const driveLink = file.getUrl();
const filePath = `meeting_note/${dateStr}/${fileName.replace('.md', '')}`;
// Redirect Service를 통한 Obsidian 링크
const obsidianLink = `${REDIRECT_SERVICE_URL}?app=obsidian&vault=${CONFIG.OBSIDIAN_VAULT_NAME}&file=${encodeURIComponent(filePath)}`;
const linksSection = `
📝 미팅 노트:
<a href="${driveLink}">📁 Google Drive에서 보기</a>
<a href="${obsidianLink}">📝 Obsidian에서 열기</a>
━━━━━━━━━━━━━━━━━━━`;
event.setDescription(currentDesc + linksSection);
}
실제 Calendar 이벤트 화면:
결과와 배운 점
배운 점과 나만의 꿀팁
파일명 안전 변환의 중요성: Calendar 에서 사용가능한 문자와 Obsidian 파일 명에서 사용가능한 문자가 달라 중간에 변경작업이 필요함
Event ID 기반 중복 처리 방지: 이벤트 설명에 마커를 추가하여 중복 생성 방지
function isProcessed(event) { return event.getDescription().includes('📝 미팅 노트:'); }시간대 처리: 한국 시간대 명시적 설정으로 일관성 유지
Utilities.formatDate(date, 'Asia/Seoul', 'yyyy-MM-dd')
과정 중에 겪은 시행착오
Deep Link 직접 연결 실패: Google Calendar의 보안 정책으로
obsidian://프로토콜 직접 사용 불가해결: 별도 Apps Script 웹앱으로 리다이렉트 서비스 구현
파일명 특수문자 문제: 미팅 제목의 '/', ':', '?' 등이 파일 시스템 오류 발생
해결: 포괄적인 sanitize 함수 구현
도움이 필요한 부분
Obsidian 모바일 앱에서의 Deep Link 처리 개선 방법
미팅 종료 후 자동으로 녹화 링크나 액션 아이템 동기화
앞으로의 계획
AI 통합: Google Meeting 전사 Obsidian으로 자동 업데이트하는 파이프라인 구성
Slack 연동: 액션 아이템을 Slack으로 자동 전송
템플릿 다양화: 미팅 유형별(1:1, 팀미팅, 고객미팅) 템플릿 자동 선택