방문자 통계는 Google 스프레드시트와 연동 후 수집됩니다. 기본 설정 탭에서 GAS URL을 설정하고, 아래 안내대로 Apps Script 코드를 업데이트해 주세요.
📅
-오늘 방문
📆
-어제 방문
📊
-이번 주 합계
📱
-모바일 비율
🆕
-오늘 신규
📈 최근 7일 방문자 추이
⏳ 로딩 중...
📋 날짜별 방문자 내역 (최근 30일)
로딩 중...
🔧 Apps Script 코드 업데이트 필요
방문자 수집을 시작하려면 기존 Google Apps Script 코드를 아래 새 코드로 교체하고 재배포해야 합니다.
const SS = '상담신청';
const CT = '콘텐츠';
const VS = '방문자';
function doGet(e) {
var p = e.parameter, ss = SpreadsheetApp.getActiveSpreadsheet();
if (p.action === 'write') {
var sh = ss.getSheetByName(SS) || ss.insertSheet(SS);
if (sh.getLastRow() === 0)
sh.appendRow(['신청시각','성함','연락처','부지지역','희망평형','예상예산','문의내용','상태']);
sh.appendRow([p['신청시각']||'',p['성함']||'',p['연락처']||'',p['부지지역']||'',
p['희망평형']||'',p['예상예산']||'',p['문의내용']||'','미확인']);
var r = JSON.stringify({success:true});
return ContentService.createTextOutput(p.callback?p.callback+'('+r+')':r)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
if (p.action === 'pageview') {
var vsh = ss.getSheetByName(VS) || ss.insertSheet(VS);
if (vsh.getLastRow() === 0)
vsh.appendRow(['날짜','시각','기기','유입경로','신규여부']);
var now = new Date();
var dateStr = Utilities.formatDate(now,'Asia/Seoul','yyyy-MM-dd');
var timeStr = Utilities.formatDate(now,'Asia/Seoul','HH:mm');
vsh.appendRow([dateStr,timeStr,p.ua||'unknown',p.ref||'direct',p.new==='1'?'신규':'재방문']);
return ContentService.createTextOutput('ok');
}
if (p.action === 'read_visitors') {
var vsh = ss.getSheetByName(VS);
var statsObj = {};
if (vsh && vsh.getLastRow() > 1) {
var rows = vsh.getDataRange().getValues();
for (var i = 1; i < rows.length; i++) {
var d = String(rows[i][0]); if (!d) continue;
if (!statsObj[d]) statsObj[d] = {total:0,mobile:0,desktop:0,'new':0};
statsObj[d].total++;
if (rows[i][2]==='mobile') statsObj[d].mobile++;
else statsObj[d].desktop++;
if (rows[i][4]==='신규') statsObj[d]['new']++;
}
}
var json = JSON.stringify(statsObj);
return ContentService.createTextOutput(p.callback?p.callback+'('+json+')':json)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
if (p.action === 'read_content') {
var csh = ss.getSheetByName(CT); var obj = {};
if (csh && csh.getLastRow() > 1) {
var rows = csh.getDataRange().getValues();
for (var i = 1; i < rows.length; i++) { if (rows[i][0]) obj[rows[i][0]] = rows[i][1]; }
}
var json = JSON.stringify(obj);
return ContentService.createTextOutput(p.callback?p.callback+'('+json+')':json)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
var sh = ss.getSheetByName(SS); var data = [];
if (sh && sh.getLastRow() > 1) {
var rows = sh.getDataRange().getValues(); var h = rows[0];
data = rows.slice(1).reverse().map(function(r,i){
var o={id:Date.now()-i}; h.forEach(function(k,j){o[k]=r[j];}); return o;
});
}
var json = JSON.stringify(data);
return ContentService.createTextOutput(p.callback?p.callback+'('+json+')':json)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
function doPost(e) {
try {
var p = e.parameter, ss = SpreadsheetApp.getActiveSpreadsheet();
if (p.action === 'write_content') {
var sh = ss.getSheetByName(CT) || ss.insertSheet(CT);
if (sh.getLastRow() === 0) sh.appendRow(['key','value','updated']);
var rowIdx = -1;
if (sh.getLastRow() > 1) {
var keys = sh.getRange(2,1,sh.getLastRow()-1,1).getValues();
for (var i = 0; i < keys.length; i++) { if (keys[i][0]===p.key){rowIdx=i+2;break;} }
}
var now = Utilities.formatDate(new Date(),'Asia/Seoul','yyyy-MM-dd HH:mm:ss');
if (rowIdx > 0) { sh.getRange(rowIdx,2).setValue(p.value); sh.getRange(rowIdx,3).setValue(now); }
else { sh.appendRow([p.key, p.value, now]); }
return ContentService.createTextOutput('ok');
}
return ContentService.createTextOutput('unknown');
} catch(err) { return ContentService.createTextOutput('err:'+err.message); }
}
코드 교체 후: 배포 → 새 배포가 아닌 배포 → 기존 배포 관리 → ✏️ 편집 아이콘 → 버전: 새 버전으로 업데이트해야 합니다.
🏠
0시공사례
🌱
0사회공헌 활동
🎬
0등록 영상
📞
-대표 전화
📋
0상담 신청
📖 사용 가이드
1
사진 업로드 방법
이미지 URL을 입력하거나 파일을 직접 드래그&드롭 하세요. 구글 포토, 네이버 블로그 등에서 이미지 주소를 복사해서 붙여넣기 가능합니다.
2
유튜브 동영상 추가
유튜브 영상 URL에서 비디오 ID를 복사해주세요. 예: youtube.com/watch?v=ABC123 → ABC123 입력
3
변경사항 저장
각 섹션에서 저장 버튼을 누르면 즉시 홈페이지에 반영됩니다. 저장 후 홈페이지 미리보기로 확인해보세요.
4
백업 & 복원
설정 탭에서 전체 데이터를 JSON으로 내보내거나 가져올 수 있습니다. 다른 기기에서도 동일한 내용을 사용할 수 있습니다.
📊
Google 스프레드시트가 연동되지 않았습니다
Google 시트를 연동하면 PC·모바일 어느 기기에서 신청해도 이곳에서 한눈에 확인할 수 있습니다. 설정은 기본 설정 탭에서 Google 시트 URL을 입력하면 됩니다.
📧
이메일 알림이 설정되지 않았습니다
고객이 상담 신청 시 이메일로 즉시 알림을 받으려면 아래 이메일 설정을 완료해주세요. 설정 전까지는 이 관리자 패널에서만 신청 내역을 확인할 수 있습니다.
전체 0건
· 미확인 0건
🖼️ 이미지 관리: 홈페이지 회사소개 섹션의 주택 사진을 변경할 수 있습니다. 저장 후 모든 기기에 즉시 반영됩니다.
🏠 회사소개 섹션 주택 사진
홈페이지 중간 회사소개 섹션에 표시되는 주택 사진을 변경합니다.
이미지 없음
헨델프로젝트 섹션 이미지는 전용 메뉴에서 관리하세요.
🏛️ 헨델프로젝트 이미지: 홈페이지 헨델프로젝트 섹션 내용 바로 아래에 표시되는 사진 최대 6장을 관리합니다. 저장 후 PC·모바일 모든 기기에 반영됩니다.
🏛️ 헨델프로젝트 소개 사진 (최대 6장)
사진을 클릭하거나 파일을 드래그하여 업로드하세요. GitHub 토큰이 설정된 경우 자동으로 서버에 업로드되어 PC·모바일 모두에서 표시됩니다.
⚠️ GitHub 토큰이 설정되지 않아 이미지가 이 기기에서만 저장됩니다.
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
시공사례 추가
⚠️ GitHub 토큰이 설정되지 않아 이미지가 이 기기에서만 저장됩니다.
📸 사진을 클릭하거나 드래그&드롭으로 업로드하세요. 순서대로 홈페이지에 표시됩니다.
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
클릭하거나 드래그
🌱 사회공헌 섹션 안내: 첫 번째 활동이 홈페이지 메인 대표 활동으로 표시됩니다. 나머지는 카드 형식으로 나열됩니다.
사회공헌 활동 추가
이미지 없음
이미지 없음
이미지 없음
🎬 유튜브 영상 추가 방법: 유튜브 영상 주소(예: https://www.youtube.com/watch?v=XXXXX)에서 XXXXX 부분(비디오 ID)을 복사해 입력하세요.
동영상 추가
유튜브 URL: youtube.com/watch?v=여기 값
✏️ 홈페이지 각 섹션의 텍스트를 수정할 수 있습니다. 저장 후 즉시 반영됩니다.
🧭 상단 메뉴 이름
헤더 및 모바일 메뉴에 표시되는 항목 이름을 수정합니다.
🏠 히어로 섹션
📊 통계 바 (히어로 아래)
메인 화면 바로 아래에 표시되는 4개 통계 항목을 수정합니다.
📋 회사소개 섹션
🌱 사회공헌 섹션 텍스트
📢 CTA 배너
🔄 모든 기기 즉시 반영 설정
관리자가 저장하면 2~3초 후 자동으로 GitHub에 커밋됩니다. 모바일·PC 모든 기기에서 1~2분 내 반영됩니다.
1
GitHub Personal Access Token 발급
github.com/settings/tokens/new 접속
Note(이름): 아무거나 입력 → Expiration: No expiration → repo 체크 → Generate token
발급된 토큰을 복사해서 아래에 붙여넣기
2
배포 브랜치 확인
GitHub 저장소 → Settings → Pages → Branch 항목에 표시된 브랜치명을 아래에 입력 (보통 gh-pages)
메뉴 확장 프로그램 → Apps Script 클릭 → 아래 코드 전체를 복사해서 붙여넣기 → 💾 저장
const SS = '상담신청';
const CT = '콘텐츠';
function doGet(e) {
var p = e.parameter, ss = SpreadsheetApp.getActiveSpreadsheet();
if (p.action === 'write') {
var sh = ss.getSheetByName(SS) || ss.insertSheet(SS);
if (sh.getLastRow() === 0)
sh.appendRow(['신청시각','성함','연락처','부지지역','희망평형','예상예산','문의내용','상태']);
sh.appendRow([p['신청시각']||'',p['성함']||'',p['연락처']||'',p['부지지역']||'',
p['희망평형']||'',p['예상예산']||'',p['문의내용']||'','미확인']);
var r = JSON.stringify({success:true});
return ContentService.createTextOutput(p.callback?p.callback+'('+r+')':r)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
if (p.action === 'read_content') {
var csh = ss.getSheetByName(CT); var obj = {};
if (csh && csh.getLastRow() > 1) {
var rows = csh.getDataRange().getValues();
for (var i = 1; i < rows.length; i++) { if (rows[i][0]) obj[rows[i][0]] = rows[i][1]; }
}
var json = JSON.stringify(obj);
return ContentService.createTextOutput(p.callback?p.callback+'('+json+')':json)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
var sh = ss.getSheetByName(SS); var data = [];
if (sh && sh.getLastRow() > 1) {
var rows = sh.getDataRange().getValues(); var h = rows[0];
data = rows.slice(1).reverse().map(function(r,i){
var o={id:Date.now()-i}; h.forEach(function(k,j){o[k]=r[j];}); return o;
});
}
var json = JSON.stringify(data);
return ContentService.createTextOutput(p.callback?p.callback+'('+json+')':json)
.setMimeType(p.callback?ContentService.MimeType.JAVASCRIPT:ContentService.MimeType.JSON);
}
function doPost(e) {
try {
var p = e.parameter, ss = SpreadsheetApp.getActiveSpreadsheet();
if (p.action === 'write_content') {
var sh = ss.getSheetByName(CT) || ss.insertSheet(CT);
if (sh.getLastRow() === 0) sh.appendRow(['key','value','updated']);
var rowIdx = -1;
if (sh.getLastRow() > 1) {
var keys = sh.getRange(2,1,sh.getLastRow()-1,1).getValues();
for (var i = 0; i < keys.length; i++) { if (keys[i][0]===p.key){rowIdx=i+2;break;} }
}
var now = Utilities.formatDate(new Date(),'Asia/Seoul','yyyy-MM-dd HH:mm:ss');
if (rowIdx > 0) { sh.getRange(rowIdx,2).setValue(p.value); sh.getRange(rowIdx,3).setValue(now); }
else { sh.appendRow([p.key, p.value, now]); }
return ContentService.createTextOutput('ok');
}
return ContentService.createTextOutput('unknown');
} catch(err) { return ContentService.createTextOutput('err:'+err.message); }
}
3
배포 → 웹 앱으로 배포
상단 배포 → 새 배포 클릭 → 유형: 웹 앱 → 액세스 권한: 모든 사용자 → 배포 클릭 생성된 웹 앱 URL을 아래에 붙여넣기
설정 저장 후 고객이 상담 신청하면 Google 스프레드시트에 자동으로 기록되고, 이 패널에서도 바로 확인할 수 있습니다.
📧 상담 신청 이메일 알림 설정
1
web3forms.com 접속
web3forms.com 에 접속하여 알림 받을 이메일 주소를 입력하세요. (무료, 회원가입 불필요)
2
이메일로 액세스 키 확인
입력한 이메일로 액세스 키(Access Key)가 발송됩니다. 이메일을 확인하세요.
3
아래에 입력 후 저장
이메일로 받은 액세스 키를 아래에 붙여넣고 저장하면 설정이 완료됩니다.
✅ 액세스 키가 웹사이트 코드에 적용되어 있습니다.
PC·모바일 모든 기기에서 이메일 알림이 정상 작동합니다.
📞 연락처 정보
🔑 비밀번호 변경
💾 데이터 백업 & 복원
모든 콘텐츠(시공사례, 사회공헌, 영상, 텍스트)를 JSON 파일로 내보내거나 가져올 수 있습니다. 다른 기기에서도 동일한 설정을 사용할 때 활용하세요.
사회공헌 활동 추가