구글 시트의 데이터를 실시간으로 연동하여 세련된 주간 계획표를 표시하는 웹앱을 구글 Apps Script로 제작하는 방법입니다.
A열: date (날짜)
B열: title (제목)
C열: content (내용)
**볼드**, *이탤릭*, - 리스트, [링크](URL) 등A1: date | B1: title | C1: content
A2: 2025-06-09 | B2: 팀 회의 | C2: ## 안건\n- 프로젝트 진행상황\n- **예산 검토**
A3: 2025-06-10 | B3: 개발 업무 | C3: ### 할 일\n1. 코드 리뷰\n2. 테스트 작성function doGet() {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getScheduleData() {
const SHEET_ID = '여기에_스프레드시트_ID_입력'; // 중요: 실제 시트 ID로 변경
const SHEET_NAME = 'schedule';
try {
let sheet;
try {
sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_NAME);
} catch (e) {
sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
}
if (!sheet) {
console.error('시트를 찾을 수 없습니다.');
return [];
}
const lastRow = sheet.getLastRow();
if (lastRow < 2) return [];
const dataRange = sheet.getRange(2, 1, lastRow - 1, 3);
const values = dataRange.getValues();
const backgrounds = dataRange.getBackgrounds();
const scheduleData = [];
for (let i = 0; i < values.length; i++) {
const [date, title, content] = values[i];
const titleBackgroundColor = backgrounds[i][1];
if (!date && !title) continue;
let formattedDate;
// 다양한 날짜 형식 처리
if (Object.prototype.toString.call(date) === '[object Date]') {
formattedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
} else if (typeof date === 'string') {
const parsedDate = new Date(date);
if (!isNaN(parsedDate.getTime())) {
formattedDate = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
} else {
continue;
}
} else if (date && typeof date === 'object' && typeof date.getFullYear === 'function') {
try {
formattedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
} catch (e) {
continue;
}
} else {
try {
const attemptDate = new Date(date);
if (!isNaN(attemptDate.getTime())) {
formattedDate = new Date(attemptDate.getFullYear(), attemptDate.getMonth(), attemptDate.getDate());
} else {
continue;
}
} catch (e) {
continue;
}
}
if (!formattedDate || isNaN(formattedDate.getTime())) continue;
const year = formattedDate.getFullYear();
const month = String(formattedDate.getMonth() + 1).padStart(2, '0');
const day = String(formattedDate.getDate()).padStart(2, '0');
const dateString = `${year}-${month}-${day}`;
scheduleData.push({
date: dateString,
title: title || '제목 없음',
content: content || '',
backgroundColor: titleBackgroundColor || '#ffffff'
});
}
return scheduleData;
} catch (error) {
console.error('getScheduleData 오류:', error.toString());
return { error: true, message: error.toString() };
}
}
function getCurrentWeekDates(weekOffset = 0) {
try {
const today = new Date();
const currentDay = today.getDay();
const weekDates = [];
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() - currentDay + (weekOffset * 7));
const dayNames = ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'];
for (let i = 0; i < 7; i++) {
const date = new Date(startOfWeek);
date.setDate(startOfWeek.getDate() + i);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const dateString = `${year}-${month}-${day}`;
weekDates.push({
date: dateString,
month: date.getMonth() + 1,
day: date.getDate(),
dayName: dayNames[i],
dateString: `${date.getMonth() + 1}월 ${date.getDate()}일`,
isToday: date.toDateString() === today.toDateString()
});
}
return weekDates;
} catch (error) {
console.error('getCurrentWeekDates 오류:', error.toString());
return { error: true, message: error.toString() };
}
}
function getWeekNumber(weekOffset = 0) {
try {
const today = new Date();
const targetDate = new Date(today);
targetDate.setDate(today.getDate() + (weekOffset * 7));
const firstDayOfMonth = new Date(targetDate.getFullYear(), targetDate.getMonth(), 1);
const firstWeekDay = firstDayOfMonth.getDay();
const weekNumber = Math.ceil((targetDate.getDate() + firstWeekDay) / 7);
return weekNumber;
} catch (error) {
console.error('getWeekNumber 오류:', error.toString());
return 1;
}
}매우 긴 코드이므로 주요 부분만 설명:
https://docs.google.com/spreadsheets/d/1sl4YJPJd4hk4MdXHQ8tPls_ei0BJe5b5rr-zW0YYb0I/edit1sl4YJPJd4hk4MdXHQ8tPls_ei0BJe5b5rr-zW0YYb0ISHEET_ID 변수에 붙여넣기CSS에서 다음 변수들 수정:
/* 배경 그라데이션 */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
/* 기본 카테고리 색상 */
--item-color: #667eea;parseMarkdown() 함수에 새로운 정규식 추가:
// 하이라이트: ==텍스트==
html = html.replace(/==(.*?)==/g, '<mark>$1</mark>');.weekly-grid { grid-template-columns: repeat(7, 1fr); }.day-column { min-height: 500px; }console.log 출력)이 가이드를 따라하면 전문가 수준의 주간 계획표 웹앱을 만들 수 있습니다! 🎉