Guide
이 컨트롤은 무엇인가요?
Guide는 화면 안내(온보딩) 컨트롤입니다. 화면에 이미 존재하는 다른 요소들(버튼, 입력창, 목록 등)을 순서대로 하이라이트하면서 "이게 뭐고 어떻게 쓰는지"를 설명해 주는 투어 기능을 만들 때 사용합니다.
syn_guide 태 그 자체는 화면에 아무것도 그리지 않습니다(ContextMenu처럼 로드 후 display:none 처리됨). 대신 syn-options의 items 배열에 "어떤 요소(selector)를 어떤 방식(helpType)으로 안내할지"를 정의해 두면, Guide가 내부적으로 아래 3가지 외부 라이브러리 중 하나를 골라 연결해 줍니다.
helpType: 'I'→ intro.js 기반 단계별 투어(스포트라이트 + 다음/이전 버튼)helpType: 'T'→ tippy.js 기반 툴팁(마우스 올리면 뜨는 설명)helpType: 'P'→ superplaceholder 기반 입력창 placeholder 타이핑 애니메이션helpType: 'U'→ 프로그램(화면) 도움말 링크/팝업 (별도 "도움말" 버튼 등에서openUIHelp로 호출)
언제 사용하나요?
- 신규 사용자에게 화면의 주요 버튼/입력창을 순서대로 소개하는 "처음 사용 가이드"가 필요할 때 (
helpType: 'I') - 특정 항목에 마우스를 올렸을 때 짧은 설명을 보여주고 싶을 때 (
helpType: 'T') - 검색창/입력창에 "이렇게 입력하세요" 같은 예시 문구를 타이핑 애니메이션으로 계속 보여주고 싶을 때 (
helpType: 'P') - 화면 우 측 상단 "도움말(?)" 버튼을 눌렀을 때 외부 매뉴얼 링크를 열거나, 팝업 창으로 도움말 본문을 보여주고 싶을 때 (
helpType: 'U')
빠른 시작
<div id="divRangeButtonGroup">조회 기간 버튼 그룹</div>
<div id="divTransactionDetail">거래 상세 영역</div>
<syn_guide id="hlpDataSource" syn-options="{
items: [
{ helpType: 'I', selector: '#divRangeButtonGroup', subject: '조회 기간을 설정합니다.', sentence: '오늘/일주일/한달 단위로 조회 기간을 선택할 수 있습니다.', sortingNo: 1 },
{ helpType: 'I', selector: '#divTransactionDetail', subject: '거래 상세 내역입니다.', sentence: '선택한 기간의 거래 내역이 표시됩니다.', sortingNo: 2 }
]
}" syn-events="['complete', 'exit']"></syn_guide>
<input type="button" id="btnStartGuide" value="사용법 안내 시작" syn-events="['click']" />
'use strict';
let $samplePage = {
event: {
btnStartGuide_click() {
// 버튼을 눌러야 투어가 시작됩니다(페이지 로드시 자동 시작되지 않음).
syn.uicontrols.$guide.introStart('hlpDataSource');
},
hlpDataSource_complete() {
syn.$l.eventLog('hlpDataSource_complete', '투어를 끝까지 마쳤습니다.');
},
hlpDataSource_exit() {
syn.$l.eventLog('hlpDataSource_exit', '투어를 중간에 닫았습니다.');
}
}
}
핵심 포인트:
items의 각 항목은 "안내할 방식(helpType)"과 "안내 대상(selector)", "제목(subject)", "본문(sentence)"으로 구성됩니다.selector가 가리키는 요소는 Guide보다 먼저(또는 같은 시점에) 화면에 존재해야 합니다. 존재하지 않으면 해당 단계는 조용히 건너뜁니다.helpType: 'I'투어는 자동 시작되지 않습니다. 반드시syn.uicontrols.$guide.introStart(elID)를 버튼 클릭 등에서 직접 호출해야 합니다.sortingNo로 intro 단계 순서를 정할 수 있습니다.- 페이지에
<script src="/js/syn.loader.js"></script>한 줄만 있으면 intro.js, tippy.js, popper.js, superplaceholder와 Guide의 CSS/JS가 모두 자동으로 로드됩니다(추가pageLoadFiles설정이 필요 없습니다).
예제 실행하기
example/ 폴더의 각 HTML 파일을 handstack의 wwwroot 정적 서버(예: rdy 프로젝트) 경로 아래에 두고 브라우저로 열면 바로 동작을 확인할 수 있습니다.
basic.html: 화면 요소 3개를 순서대로 안내하는 intro.js 투어(helpType: 'I') 예제. 버튼을 눌러 투어를 시작하고,complete/exit이벤트를 로그로 확인합니다.help-types.html: 툴팁(T), 입력창 placeholder 애니메이션(P), 도움말 링크/팝업(U)까지 나머지 3가지helpType을 한 화면에서 비교하는 예제.
각 예제는 화면 하단 로그 영역(syn.$l.eventLog 출력)에서 이벤트 발생 순서와 전달값을 확인할 수 있습니다.
더 알아보기
- API 상세는 같은 폴더의
API.md를 참고하세요. - 실제 소스:
wwwroot/uicontrols/Guide/Guide.js,Guide.css - 이 컨트롤은
wwwroot/sample/uicontrol/에 별도 샘플 페이지가 없습니다. 이 문서의 API 내용은Guide.js소스 코드를 직접 읽고 확정한 것이며, 마크업 형태는 qcn.groupware 프로젝트의 실사용 코드를 참고했습니다. - intro.js 옵션/API 원본 문서: https://introjs.com/docs/intro/api
- tippy.js 옵션 원본 문서: https://atomiks.github.io/tippyjs/v6/all-props/
- superplaceholder 옵션 원본 문서: https://github.com/fabiospampinato/superplaceholder.js
실전 예제 페이지
/uicontrols/Guide/example/ 경로의 예제를 아래 iframe에서 바로 확인할 수 있습니다.
basic.html
help-types.html
소스와 로드 파일
| 항목 | 내용 |
|---|---|
| 컨트롤 | Guide |
| 전역 별칭 | syn.uicontrols.$guide |
| 버전 | v2025.3.1 |
| 소스 | uicontrols/Guide/Guide.js |
| 스타일 | uicontrols/Guide/Guide.css |
API 참조
싱글턴 객체: syn.uicontrols.$guide
소스 파일: wwwroot/uicontrols/Guide/Guide.js (version: 'v2025.3.1'), wwwroot/uicontrols/Guide/Guide.css
마크업
<syn_guide id="hlpDataSource" syn-options="{
items: [
{ helpType: 'I', selector: '#btnNewDataSource', subject: '신규 데이터 원본을 설정하세요.', sentence: 'SqlServer, Oracle, MySQL & MariaDB, PostgreSQL, SQLite 데이터베이스를 연동하세요.', sortingNo: 1 },
{ helpType: 'I', selector: '#lstDataSource', subject: '연결 가능한 데이터 원본입니다.', sentence: '앱에서 데이터베이스 요청을 실행하는 dbclient 모듈의 계약 정보에 사용하는 DataSourceID 목록입니다.', sortingNo: 2 },
{ helpType: 'T', selector: 'span.badge.bg-primary', subject: '앱에서 사용하는 기본 데이터베이스 입니다.', sentence: '기본 데이터 원본 정보는 제공자와 연결 문자열을 편집할 수 없습니다.', applyDelay: 1000 },
{ helpType: 'P', selector: '#txtConnectionString', subject: '중요', sentence: '개발 및 테스트 목적의 데이터베이스 연결문자열을 입력해야 합니다.' },
{ helpType: 'U', subject: '설치 가이드', sentence: 'https://handstack.kr/docs/startup/install/지원-운영체제', options: '{"contentType": "link"}' }
]
}" syn-events="['complete', 'exit']"></syn_guide>
id는 페이지 내에서 유일해야 하며,syn.uicontrols.$guide의 각종 메서드에서 이id(elID)를 사용합니다.- 태그 자체는
controlLoad실행 후display:none으로 숨겨집니다(화면에 그려지는 요소가 아니라 안내 동작 정의용 태그입니다). 내부적으로<input>처럼value속성을 가지므로getValue/setValue/clear가 존재하지만, 실질적인 값(투어 진행 상태 등)을 담는 용도로 쓰이지는 않습니다. - Guide는 자기 자신이 아니라
items[].selector로 지정한 다른 화면 요소들 위에서 동작합니다. 따라서 각selector가 가리키는 요소는 Guide 태그보다 먼저(또는 같은 시점에) 화면(DOM)에 존재해야 합니다.controlLoad시점에selector로 요소를 찾지 못하면:helpType: 'I'항목은 해당 단계를 건너뜁니다(steps에 추가되지 않음).helpType: 'T'항목은applyDelay가 0이면 건너뛰고,applyDelay가 있으면 지연 이후 시점에 다시 셀렉터로 찾아 연결을 시도합니다.helpType: 'P'항목은 해당 항목을 건너뜁니다.helpType: 'U'항목은selector를 사용하지 않으므로 영향이 없습니다.
Options (defaultSetting)
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
items | array | [] | 안내 단계 목록. 아래 "items 배열 구조" 참고 |
introOptions | object | intro.js 기본 옵션(아래) | helpType: 'I' 투어 전체에 적용되는 intro.js 옵션. intro.js 옵션 문서 참고 |
tooltipOptions | object | 객체 설정 | helpType: 'T' 툴팁 전체에 적용되는 tippy.js 기본 옵션 |
placeholderOptions | object | 객체 설정 | helpType: 'P' 애니메이션 전체에 적용되는 superplaceholder 기본 옵션 |
introOptions 기본값
{
prevLabel: '<svg ... chevron-left ... />', // 이전 버튼 아이콘(SVG)
nextLabel: '<svg ... chevron-right ... />', // 다음 버튼 아이콘(SVG)
doneLabel: '완료',
skipLabel: '<svg ... x(닫기) ... />',
dontShowAgainLabel: '다시 안보기',
exitOnEsc: true,
showStepNumbers: true,
showBullets: false,
tooltipPosition: 'auto',
overlayOpacity: 0.2,
steps: [] // Guide가 items(helpType:'I')로부터 자동 생성하여 채움
}
items 배열 구조 (itemTemplate)
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
helpType | string | 필수 | 안내 방식. 'I'(intro.js 투어) / 'T'(tippy 툴팁) / 'P'(superplaceholder) / 'U'(도움말 링크·팝업) 중 하나 |
selector | string (CSS 셀렉터) | I/T/P에 필수, U는 미사용 | 안내 대상 요소를 찾는 셀렉터(syn.$l.querySelector 사용) |
subject | string (HTML 가능) | 선택 | 제목. I는 스텝 제목, T는 툴팁 상단에 <strong>으로 굵게 표시, P는 문장 앞에 [제목] 으로 붙음, U는 팝업 제목(<h1>) |
sentence | string | array (HTML 가능) | 선택 | 본문. I/T는 문자열, P는 문자열 또는 문자열 배열(여러 문구를 순환 타이핑), U는 contentType:'link'일 때 이동할 URL, 그 외엔 팝업에 표시할 HTML 본문 |
options | string (JSON 문자열, HTML 엔티티 이스케이프 가능) | 선택 | 해당 항목에만 적용할 추가 옵션. I는 introOptions에, T는 tooltipOptions에, P는 placeholderOptions에 병합됨. U는 객체 설정 또는 객체 설정 지정에 사용(내부에서 $string.toCharHtml로 언이스케이프 후 JSON.parse) |
sortingNo | number | 선택(기본 0) | helpType: 'I' 단계 순서 정렬 기준($array.objectSort로 오름차순 정렬) |
applyDelay | number (ms) | T에서만 사용 | 지정 시 해당 시간(ms) 후에 tippy를 지연 연결. 아직 DOM에 없는 요소(동적 렌더링)에 툴팁을 붙일 때 사용 |
helpType: 'U'는 실제로는 하나의 "도움말" 링크/본문 역할만 하며, 여러 개를 넣어도openUIHelp는items에서 첫 번째로 발견되는helpType:'U'항목만 사용합니다.
메서드
syn.uicontrols.$guide.<메서드명>(...) 형태로 호출합니다.
| 메서드 | 설명 |
|---|---|
introStart(elID) | helpType:'I' 로 구성된 intro.js 투어를 시작합니다(intro.start()). 페이지 로드시 자동 시작되지 않으므로 버튼 클릭 등에서 직접 호출해야 합니다. |
openUIHelp(elID) | items에서 첫 번째 helpType:'U' 항목을 찾아 도움말을 엽니다. options.contentType === 'link'이면 sentence(URL)를 새 창(window.open(url, 'help'))으로 열고, 그 외에는 subject/sentence로 구성한 HTML을 팝업 창에 출력합니다. 팝업이 차단되면 syn.$w.alert로 안내합니다. |
getGuideControl(elID) | 내부 등록 정보 객체 설정를 반환합니다(디버깅/직접 제어용). tooltip은 tippy 인스턴스 배열, intro는 introJs 인스턴스, placeholder는 superplaceholder 인스턴스 배열입니다. |
dataRefresh(elID, items) | 기존 안내 구성(tooltip/intro/placeholder)을 모두 정리(clear)한 뒤, 전달한 새 items 배열로 다시 구성합니다. 서버 응답 등으로 안내 항목을 동적으로 교체할 때 사용합니다. |
getValue(elID) | syn.$l.get(elID).value를 반환(다른 컨트롤과 인터페이스를 맞추기 위한 자리표시자, Guide 고유 값 개념 없음) |
setValue(elID, value) | elID 요소의 value를 설정(위와 동일한 이유의 자리표시자) |
clear(elID) | value를 비우고, 등록된 tippy 인스턴스를 모두 destroy(), intro.js 투어를 exit(), superplaceholder 인스턴스를 모두 destroy()합니다. |
setLocale(elID, translations, control, options) | 다국 어 텍스트 적용 훅(Guide는 별도 구현 없음, 빈 함수) |
이벤트 (syn-events)
syn-events 배열에 이름을 넣으면 페이지 스크립트의 event 객체에서 {elID}_{이벤트명} 함수를 자동으로 연결해 줍니다. 이 이벤트들은 helpType: 'I'(intro.js) 투어에만 연결됩니다. T/P/U 항목에는 syn-events 훅이 연결되지 않습니다(Guide.js 소스상 intros.on... 호출만 존재).
| 이벤트명 | intro.js API | 발생 시점 |
|---|---|---|
complete | intro.oncomplete(fn) | 마지막 단계에서 "완료" 버튼을 눌러 투어를 정상적으로 끝냈을 때 |
exit | intro.onexit(fn) | 투어가 (완료 여부와 관계없이) 종료될 때 — ESC, 닫기(X) 버튼, clear() 호출 등 |
beforeexit | intro.onbeforeexit(fn) | 투어가 종료되기 직전 |
change | intro.onchange(fn) | 스텝이 전환된 직후 |
beforechange | intro.onbeforechange(fn) | 스텝이 전환되기 직전 |
afterchange | intro.onafterchange(fn) | 스텝 전환 애니메이션까지 끝난 직후 |
핸들러 등록 예:
let $samplePage = {
event: {
hlpDataSource_complete() {
syn.$l.eventLog('hlpDataSource_complete', '투어 완료');
},
hlpDataSource_exit() {
syn.$l.eventLog('hlpDataSource_exit', '투어 종료');
}
}
}
각 콜백의 인자 형태(예: change/beforechange/afterchange가 전달하는 대상 엘리먼트 등)는 intro.js 원본 API 문서를 따릅니다: https://introjs.com/docs/intro/api
참고
- 이 컨트롤은
wwwroot/sample/uicontrol/에 별도 샘플 페이지가 없습니다. 위 Options/메서드/이벤트 표는 모두wwwroot/uicontrols/Guide/Guide.js소스 코드(defaultSetting,itemTemplate,controlLoad,introStart,openUIHelp,dataRefresh,clear함수)를 직접 읽고 확정한 내용입니다. - 마크업 형태(
items배열에helpType/selector/subject/sentence를 넣는 방식)는 실사용 프로젝트인 qcn.groupware의 코드 패턴을 참고했습니다. helpType: 'I'/'T'/'P'가 동작하려면 각각window.introJs,window.tippy,window.superplaceholder가 로드되어 있어야 합니다.syn.loader.js가syn_guide태그를 인식하면intro.js,tippy.js(+popper.js),superplaceholder,Guide.js/Guide.css를 자동으로 함께 로드하므로 별도 설정 없이 사용할 수 있습니다.- intro.js 옵션/API 원본 문서: https://introjs.com/docs/intro/api
- tippy.js 옵션 원본 문서: https://atomiks.github.io/tippyjs/v6/all-props/
- superplaceholder 옵션 원본 문서: https://github.com/fabiospampinato/superplaceholder.js